summary refs log tree commit diff
path: root/drivers/media/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/pci')
-rw-r--r--drivers/media/pci/Kconfig47
-rw-r--r--drivers/media/pci/Makefile26
-rw-r--r--drivers/media/pci/b2c2/Kconfig15
-rw-r--r--drivers/media/pci/b2c2/Makefile9
-rw-r--r--drivers/media/pci/b2c2/flexcop-dma.c172
-rw-r--r--drivers/media/pci/b2c2/flexcop-pci.c450
-rw-r--r--drivers/media/pci/bt8xx/Kconfig43
-rw-r--r--drivers/media/pci/bt8xx/Makefile11
-rw-r--r--drivers/media/pci/bt8xx/bt848.h369
-rw-r--r--drivers/media/pci/bt8xx/bt878.c609
-rw-r--r--drivers/media/pci/bt8xx/bt878.h159
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c382
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.h23
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c4895
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c4630
-rw-r--r--drivers/media/pci/bt8xx/bttv-gpio.c189
-rw-r--r--drivers/media/pci/bt8xx/bttv-i2c.c397
-rw-r--r--drivers/media/pci/bt8xx/bttv-if.c121
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c589
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c909
-rw-r--r--drivers/media/pci/bt8xx/bttv-vbi.c459
-rw-r--r--drivers/media/pci/bt8xx/bttv.h376
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h535
-rw-r--r--drivers/media/pci/bt8xx/dst.c1873
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c726
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.h58
-rw-r--r--drivers/media/pci/bt8xx/dst_common.h182
-rw-r--r--drivers/media/pci/bt8xx/dst_priv.h35
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c975
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.h63
-rw-r--r--drivers/media/pci/cx18/Kconfig35
-rw-r--r--drivers/media/pci/cx18/Makefile13
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-main.c295
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.c175
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.h23
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.c356
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.h27
-rw-r--r--drivers/media/pci/cx18/cx18-alsa.h75
-rw-r--r--drivers/media/pci/cx18/cx18-audio.c92
-rw-r--r--drivers/media/pci/cx18/cx18-audio.h24
-rw-r--r--drivers/media/pci/cx18/cx18-av-audio.c471
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c1401
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.h391
-rw-r--r--drivers/media/pci/cx18/cx18-av-firmware.c225
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c313
-rw-r--r--drivers/media/pci/cx18/cx18-cards.c638
-rw-r--r--drivers/media/pci/cx18/cx18-cards.h157
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c131
-rw-r--r--drivers/media/pci/cx18/cx18-controls.h24
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c1360
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h730
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c609
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.h25
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c881
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.h41
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.c459
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.h25
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.c347
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.h34
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.c330
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.h29
-rw-r--r--drivers/media/pci/cx18/cx18-io.c97
-rw-r--r--drivers/media/pci/cx18/cx18-io.h191
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c1190
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.h31
-rw-r--r--drivers/media/pci/cx18/cx18-irq.c81
-rw-r--r--drivers/media/pci/cx18/cx18-irq.h35
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.c870
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.h95
-rw-r--r--drivers/media/pci/cx18/cx18-queue.c443
-rw-r--r--drivers/media/pci/cx18/cx18-queue.h98
-rw-r--r--drivers/media/pci/cx18/cx18-scb.c122
-rw-r--r--drivers/media/pci/cx18/cx18-scb.h280
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c1059
-rw-r--r--drivers/media/pci/cx18/cx18-streams.h62
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.c277
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.h26
-rw-r--r--drivers/media/pci/cx18/cx18-version.h28
-rw-r--r--drivers/media/pci/cx18/cx18-video.c32
-rw-r--r--drivers/media/pci/cx18/cx18-video.h22
-rw-r--r--drivers/media/pci/cx18/cx23418.h492
-rw-r--r--drivers/media/pci/cx23885/Kconfig50
-rw-r--r--drivers/media/pci/cx23885/Makefile15
-rw-r--r--drivers/media/pci/cx23885/altera-ci.c839
-rw-r--r--drivers/media/pci/cx23885/altera-ci.h100
-rw-r--r--drivers/media/pci/cx23885/cimax2.c536
-rw-r--r--drivers/media/pci/cx23885/cimax2.h47
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c1790
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c535
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.c35
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.h27
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c1694
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c2234
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c1412
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.c177
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-i2c.c396
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c365
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.h30
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.c208
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.h39
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.c117
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.h31
-rw-r--r--drivers/media/pci/cx23885/cx23885-reg.h452
-rw-r--r--drivers/media/pci/cx23885/cx23885-vbi.c295
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c1926
-rw-r--r--drivers/media/pci/cx23885/cx23885.h654
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c1271
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.h28
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.c107
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.h42
-rw-r--r--drivers/media/pci/cx23885/netup-init.c125
-rw-r--r--drivers/media/pci/cx23885/netup-init.h25
-rw-r--r--drivers/media/pci/cx25821/Kconfig34
-rw-r--r--drivers/media/pci/cx25821/Makefile13
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c784
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio-upstream.c778
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio-upstream.h62
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio.h62
-rw-r--r--drivers/media/pci/cx25821/cx25821-biffuncs.h45
-rw-r--r--drivers/media/pci/cx25821/cx25821-cards.c72
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c1502
-rw-r--r--drivers/media/pci/cx25821/cx25821-gpio.c98
-rw-r--r--drivers/media/pci/cx25821/cx25821-i2c.c416
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-defines.h42
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-reg.h455
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.c787
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.h49
-rw-r--r--drivers/media/pci/cx25821/cx25821-reg.h1592
-rw-r--r--drivers/media/pci/cx25821/cx25821-sram.h261
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c802
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h138
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream.c856
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream.h139
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c1990
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.h186
-rw-r--r--drivers/media/pci/cx25821/cx25821.h615
-rw-r--r--drivers/media/pci/cx88/Kconfig86
-rw-r--r--drivers/media/pci/cx88/Makefile16
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c975
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c1299
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c3811
-rw-r--r--drivers/media/pci/cx88/cx88-core.c1131
-rw-r--r--drivers/media/pci/cx88/cx88-dsp.c322
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c1778
-rw-r--r--drivers/media/pci/cx88/cx88-i2c.c184
-rw-r--r--drivers/media/pci/cx88/cx88-input.c635
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c929
-rw-r--r--drivers/media/pci/cx88/cx88-reg.h836
-rw-r--r--drivers/media/pci/cx88/cx88-tvaudio.c1059
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c245
-rw-r--r--drivers/media/pci/cx88/cx88-video.c2075
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.c159
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.h41
-rw-r--r--drivers/media/pci/cx88/cx88.h748
-rw-r--r--drivers/media/pci/ddbridge/Kconfig18
-rw-r--r--drivers/media/pci/ddbridge/Makefile14
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c1730
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-regs.h151
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h185
-rw-r--r--drivers/media/pci/dm1105/Kconfig20
-rw-r--r--drivers/media/pci/dm1105/Makefile3
-rw-r--r--drivers/media/pci/dm1105/dm1105.c1248
-rw-r--r--drivers/media/pci/ivtv/Kconfig61
-rw-r--r--drivers/media/pci/ivtv/Makefile16
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-main.c303
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.c175
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.h23
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c356
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.h27
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa.h75
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.c1370
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.h309
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c163
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.h28
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c1536
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h850
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.c1073
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c402
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.h31
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.c374
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.h29
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c760
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.h32
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c1927
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.h35
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.c1088
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.h53
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.c387
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.h35
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.c297
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.h96
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.c119
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.h27
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.c1039
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.h37
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.c234
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.h48
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.c549
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.h34
-rw-r--r--drivers/media/pci/ivtv/ivtv-version.h26
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.c1296
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.h44
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c1317
-rw-r--r--drivers/media/pci/mantis/Kconfig38
-rw-r--r--drivers/media/pci/mantis/Makefile28
-rw-r--r--drivers/media/pci/mantis/hopper_cards.c277
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.c88
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.h30
-rw-r--r--drivers/media/pci/mantis/mantis_ca.c209
-rw-r--r--drivers/media/pci/mantis/mantis_ca.h27
-rw-r--r--drivers/media/pci/mantis/mantis_cards.c307
-rw-r--r--drivers/media/pci/mantis/mantis_common.h179
-rw-r--r--drivers/media/pci/mantis/mantis_core.c235
-rw-r--r--drivers/media/pci/mantis/mantis_core.h57
-rw-r--r--drivers/media/pci/mantis/mantis_dma.c230
-rw-r--r--drivers/media/pci/mantis/mantis_dma.h30
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.c301
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.h35
-rw-r--r--drivers/media/pci/mantis/mantis_evm.c117
-rw-r--r--drivers/media/pci/mantis/mantis_hif.c239
-rw-r--r--drivers/media/pci/mantis/mantis_hif.h29
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.c266
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.h30
-rw-r--r--drivers/media/pci/mantis/mantis_input.c159
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.c124
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.h51
-rw-r--r--drivers/media/pci/mantis/mantis_link.h83
-rw-r--r--drivers/media/pci/mantis/mantis_pci.c170
-rw-r--r--drivers/media/pci/mantis/mantis_pci.h27
-rw-r--r--drivers/media/pci/mantis/mantis_pcmcia.c121
-rw-r--r--drivers/media/pci/mantis/mantis_reg.h197
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c188
-rw-r--r--drivers/media/pci/mantis/mantis_uart.h58
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.c212
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.h30
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.c120
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.h33
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.c357
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.h33
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.c188
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.h30
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.c187
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.h32
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.c38
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.h33
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.c105
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.h30
-rw-r--r--drivers/media/pci/meye/Kconfig13
-rw-r--r--drivers/media/pci/meye/Makefile1
-rw-r--r--drivers/media/pci/meye/meye.c1964
-rw-r--r--drivers/media/pci/meye/meye.h324
-rw-r--r--drivers/media/pci/ngene/Kconfig13
-rw-r--r--drivers/media/pci/ngene/Makefile14
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c823
-rw-r--r--drivers/media/pci/ngene/ngene-core.c1707
-rw-r--r--drivers/media/pci/ngene/ngene-dvb.c261
-rw-r--r--drivers/media/pci/ngene/ngene-i2c.c176
-rw-r--r--drivers/media/pci/ngene/ngene.h921
-rw-r--r--drivers/media/pci/pluto2/Kconfig15
-rw-r--r--drivers/media/pci/pluto2/Makefile3
-rw-r--r--drivers/media/pci/pluto2/pluto2.c815
-rw-r--r--drivers/media/pci/pt1/Kconfig12
-rw-r--r--drivers/media/pci/pt1/Makefile5
-rw-r--r--drivers/media/pci/pt1/pt1.c1246
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.c736
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.h46
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.c536
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.h46
-rw-r--r--drivers/media/pci/saa7134/Kconfig64
-rw-r--r--drivers/media/pci/saa7134/Makefile16
-rw-r--r--drivers/media/pci/saa7134/saa6752hs.c1012
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c1209
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c8026
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c1368
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c1936
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c590
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c435
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c1041
-rw-r--r--drivers/media/pci/saa7134/saa7134-reg.h378
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c327
-rw-r--r--drivers/media/pci/saa7134/saa7134-tvaudio.c1087
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c255
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c2661
-rw-r--r--drivers/media/pci/saa7134/saa7134.h855
-rw-r--r--drivers/media/pci/saa7146/Kconfig38
-rw-r--r--drivers/media/pci/saa7146/Makefile5
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c430
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c502
-rw-r--r--drivers/media/pci/saa7146/mxb.c886
-rw-r--r--drivers/media/pci/saa7164/Kconfig18
-rw-r--r--drivers/media/pci/saa7164/Makefile12
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c1524
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c322
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c475
-rw-r--r--drivers/media/pci/saa7164/saa7164-cards.c773
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c589
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c1488
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c556
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c1500
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c613
-rw-r--r--drivers/media/pci/saa7164/saa7164-i2c.c125
-rw-r--r--drivers/media/pci/saa7164/saa7164-reg.h219
-rw-r--r--drivers/media/pci/saa7164/saa7164-types.h442
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c1374
-rw-r--r--drivers/media/pci/saa7164/saa7164.h616
-rw-r--r--drivers/media/pci/sta2x11/Kconfig12
-rw-r--r--drivers/media/pci/sta2x11/Makefile1
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c1550
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.h40
-rw-r--r--drivers/media/pci/ttpci/Kconfig159
-rw-r--r--drivers/media/pci/ttpci/Makefile21
-rw-r--r--drivers/media/pci/ttpci/av7110.c2939
-rw-r--r--drivers/media/pci/ttpci/av7110.h314
-rw-r--r--drivers/media/pci/ttpci/av7110_av.c1626
-rw-r--r--drivers/media/pci/ttpci/av7110_av.h30
-rw-r--r--drivers/media/pci/ttpci/av7110_ca.c387
-rw-r--r--drivers/media/pci/ttpci/av7110_ca.h14
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.c1208
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.h495
-rw-r--r--drivers/media/pci/ttpci/av7110_ipack.c403
-rw-r--r--drivers/media/pci/ttpci/av7110_ipack.h12
-rw-r--r--drivers/media/pci/ttpci/av7110_ir.c415
-rw-r--r--drivers/media/pci/ttpci/av7110_v4l.c966
-rw-r--r--drivers/media/pci/ttpci/budget-av.c1640
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c1591
-rw-r--r--drivers/media/pci/ttpci/budget-core.c602
-rw-r--r--drivers/media/pci/ttpci/budget-patch.c680
-rw-r--r--drivers/media/pci/ttpci/budget.c871
-rw-r--r--drivers/media/pci/ttpci/budget.h124
-rw-r--r--drivers/media/pci/ttpci/ttpci-eeprom.c176
-rw-r--r--drivers/media/pci/ttpci/ttpci-eeprom.h34
-rw-r--r--drivers/media/pci/zoran/Kconfig74
-rw-r--r--drivers/media/pci/zoran/Makefile6
-rw-r--r--drivers/media/pci/zoran/videocodec.c407
-rw-r--r--drivers/media/pci/zoran/videocodec.h353
-rw-r--r--drivers/media/pci/zoran/zoran.h403
-rw-r--r--drivers/media/pci/zoran/zoran_card.c1528
-rw-r--r--drivers/media/pci/zoran/zoran_card.h54
-rw-r--r--drivers/media/pci/zoran/zoran_device.c1640
-rw-r--r--drivers/media/pci/zoran/zoran_device.h95
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c3090
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.c225
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.h36
-rw-r--r--drivers/media/pci/zoran/zr36016.c524
-rw-r--r--drivers/media/pci/zoran/zr36016.h111
-rw-r--r--drivers/media/pci/zoran/zr36050.c900
-rw-r--r--drivers/media/pci/zoran/zr36050.h184
-rw-r--r--drivers/media/pci/zoran/zr36057.h168
-rw-r--r--drivers/media/pci/zoran/zr36060.c1010
-rw-r--r--drivers/media/pci/zoran/zr36060.h220
352 files changed, 174539 insertions, 0 deletions
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
new file mode 100644
index 000000000000..d4e2ed3f27e5
--- /dev/null
+++ b/drivers/media/pci/Kconfig
@@ -0,0 +1,47 @@
+menuconfig MEDIA_PCI_SUPPORT
+	bool "Media PCI Adapters"
+	depends on PCI && MEDIA_SUPPORT
+	help
+	  Enable media drivers for PCI/PCIe bus.
+	  If you have such devices, say Y.
+
+if MEDIA_PCI_SUPPORT
+
+if MEDIA_CAMERA_SUPPORT
+	comment "Media capture support"
+source "drivers/media/pci/meye/Kconfig"
+source "drivers/media/pci/sta2x11/Kconfig"
+endif
+
+if MEDIA_ANALOG_TV_SUPPORT
+	comment "Media capture/analog TV support"
+source "drivers/media/pci/ivtv/Kconfig"
+source "drivers/media/pci/zoran/Kconfig"
+source "drivers/media/pci/saa7146/Kconfig"
+endif
+
+if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT
+	comment "Media capture/analog/hybrid TV support"
+source "drivers/media/pci/cx18/Kconfig"
+source "drivers/media/pci/cx23885/Kconfig"
+source "drivers/media/pci/cx25821/Kconfig"
+source "drivers/media/pci/cx88/Kconfig"
+source "drivers/media/pci/bt8xx/Kconfig"
+source "drivers/media/pci/saa7134/Kconfig"
+source "drivers/media/pci/saa7164/Kconfig"
+
+endif
+
+if MEDIA_DIGITAL_TV_SUPPORT
+	comment "Media digital TV PCI Adapters"
+source "drivers/media/pci/ttpci/Kconfig"
+source "drivers/media/pci/b2c2/Kconfig"
+source "drivers/media/pci/pluto2/Kconfig"
+source "drivers/media/pci/dm1105/Kconfig"
+source "drivers/media/pci/pt1/Kconfig"
+source "drivers/media/pci/mantis/Kconfig"
+source "drivers/media/pci/ngene/Kconfig"
+source "drivers/media/pci/ddbridge/Kconfig"
+endif
+
+endif #MEDIA_PCI_SUPPORT
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
new file mode 100644
index 000000000000..35cc57862c01
--- /dev/null
+++ b/drivers/media/pci/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for the kernel multimedia device drivers.
+#
+
+obj-y        +=	ttpci/		\
+		b2c2/		\
+		pluto2/		\
+		dm1105/		\
+		pt1/		\
+		mantis/		\
+		ngene/		\
+		ddbridge/	\
+		b2c2/		\
+		saa7146/
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
+obj-$(CONFIG_VIDEO_ZORAN) += zoran/
+obj-$(CONFIG_VIDEO_CX18) += cx18/
+obj-$(CONFIG_VIDEO_CX23885) += cx23885/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
+obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_BT848) += bt8xx/
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
+obj-$(CONFIG_VIDEO_MEYE) += meye/
+obj-$(CONFIG_STA2X11_VIP) += sta2x11/
diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig
new file mode 100644
index 000000000000..58761a21caa0
--- /dev/null
+++ b/drivers/media/pci/b2c2/Kconfig
@@ -0,0 +1,15 @@
+config DVB_B2C2_FLEXCOP_PCI
+	tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI"
+	depends on DVB_CORE && I2C
+	help
+	  Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2.
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_B2C2_FLEXCOP_PCI_DEBUG
+	bool "Enable debug for the B2C2 FlexCop drivers"
+	depends on DVB_B2C2_FLEXCOP_PCI
+	select DVB_B2C2_FLEXCOP_DEBUG
+	help
+	Say Y if you want to enable the module option to control debug messages
+	of all B2C2 FlexCop drivers.
diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile
new file mode 100644
index 000000000000..b894320a5f21
--- /dev/null
+++ b/drivers/media/pci/b2c2/Makefile
@@ -0,0 +1,9 @@
+ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),)
+b2c2-flexcop-pci-objs += flexcop-dma.o
+endif
+
+b2c2-flexcop-pci-objs += flexcop-pci.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/common/b2c2/
diff --git a/drivers/media/pci/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c
new file mode 100644
index 000000000000..2881e0d956ad
--- /dev/null
+++ b/drivers/media/pci/b2c2/flexcop-dma.c
@@ -0,0 +1,172 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-dma.c - configuring and controlling the DMA of the FlexCop
+ * see flexcop.c for copyright information
+ */
+#include "flexcop.h"
+
+int flexcop_dma_allocate(struct pci_dev *pdev,
+		struct flexcop_dma *dma, u32 size)
+{
+	u8 *tcpu;
+	dma_addr_t tdma = 0;
+
+	if (size % 2) {
+		err("dma buffersize has to be even.");
+		return -EINVAL;
+	}
+
+	if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) {
+		dma->pdev = pdev;
+		dma->cpu_addr0 = tcpu;
+		dma->dma_addr0 = tdma;
+		dma->cpu_addr1 = tcpu + size/2;
+		dma->dma_addr1 = tdma + size/2;
+		dma->size = size/2;
+		return 0;
+	}
+	return -ENOMEM;
+}
+EXPORT_SYMBOL(flexcop_dma_allocate);
+
+void flexcop_dma_free(struct flexcop_dma *dma)
+{
+	pci_free_consistent(dma->pdev, dma->size*2,
+			dma->cpu_addr0, dma->dma_addr0);
+	memset(dma,0,sizeof(struct flexcop_dma));
+}
+EXPORT_SYMBOL(flexcop_dma_free);
+
+int flexcop_dma_config(struct flexcop_device *fc,
+		struct flexcop_dma *dma,
+		flexcop_dma_index_t dma_idx)
+{
+	flexcop_ibi_value v0x0,v0x4,v0xc;
+	v0x0.raw = v0x4.raw = v0xc.raw = 0;
+
+	v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
+	v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
+	v0x4.dma_0x4_write.dma_addr_size = dma->size / 4;
+
+	if ((dma_idx & FC_DMA_1) == dma_idx) {
+		fc->write_ibi_reg(fc,dma1_000,v0x0);
+		fc->write_ibi_reg(fc,dma1_004,v0x4);
+		fc->write_ibi_reg(fc,dma1_00c,v0xc);
+	} else if ((dma_idx & FC_DMA_2) == dma_idx) {
+		fc->write_ibi_reg(fc,dma2_010,v0x0);
+		fc->write_ibi_reg(fc,dma2_014,v0x4);
+		fc->write_ibi_reg(fc,dma2_01c,v0xc);
+	} else {
+		err("either DMA1 or DMA2 can be configured within one "
+			"flexcop_dma_config call.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(flexcop_dma_config);
+
+/* start the DMA transfers, but not the DMA IRQs */
+int flexcop_dma_xfer_control(struct flexcop_device *fc,
+		flexcop_dma_index_t dma_idx,
+		flexcop_dma_addr_index_t index,
+		int onoff)
+{
+	flexcop_ibi_value v0x0,v0xc;
+	flexcop_ibi_register r0x0,r0xc;
+
+	if ((dma_idx & FC_DMA_1) == dma_idx) {
+		r0x0 = dma1_000;
+		r0xc = dma1_00c;
+	} else if ((dma_idx & FC_DMA_2) == dma_idx) {
+		r0x0 = dma2_010;
+		r0xc = dma2_01c;
+	} else {
+		err("either transfer DMA1 or DMA2 can be started within one "
+			"flexcop_dma_xfer_control call.");
+		return -EINVAL;
+	}
+
+	v0x0 = fc->read_ibi_reg(fc,r0x0);
+	v0xc = fc->read_ibi_reg(fc,r0xc);
+
+	deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw);
+	deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw);
+
+	if (index & FC_DMA_SUBADDR_0)
+		v0x0.dma_0x0.dma_0start = onoff;
+
+	if (index & FC_DMA_SUBADDR_1)
+		v0xc.dma_0xc.dma_1start = onoff;
+
+	fc->write_ibi_reg(fc,r0x0,v0x0);
+	fc->write_ibi_reg(fc,r0xc,v0xc);
+
+	deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw);
+	deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw);
+	return 0;
+}
+EXPORT_SYMBOL(flexcop_dma_xfer_control);
+
+static int flexcop_dma_remap(struct flexcop_device *fc,
+		flexcop_dma_index_t dma_idx,
+		int onoff)
+{
+	flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c;
+	flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
+	deb_info("%s\n",__func__);
+	v.dma_0xc.remap_enable = onoff;
+	fc->write_ibi_reg(fc,r,v);
+	return 0;
+}
+
+int flexcop_dma_control_size_irq(struct flexcop_device *fc,
+		flexcop_dma_index_t no,
+		int onoff)
+{
+	flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
+
+	if (no & FC_DMA_1)
+		v.ctrl_208.DMA1_IRQ_Enable_sig = onoff;
+
+	if (no & FC_DMA_2)
+		v.ctrl_208.DMA2_IRQ_Enable_sig = onoff;
+
+	fc->write_ibi_reg(fc,ctrl_208,v);
+	return 0;
+}
+EXPORT_SYMBOL(flexcop_dma_control_size_irq);
+
+int flexcop_dma_control_timer_irq(struct flexcop_device *fc,
+		flexcop_dma_index_t no,
+		int onoff)
+{
+	flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
+
+	if (no & FC_DMA_1)
+		v.ctrl_208.DMA1_Timer_Enable_sig = onoff;
+
+	if (no & FC_DMA_2)
+		v.ctrl_208.DMA2_Timer_Enable_sig = onoff;
+
+	fc->write_ibi_reg(fc,ctrl_208,v);
+	return 0;
+}
+EXPORT_SYMBOL(flexcop_dma_control_timer_irq);
+
+/* 1 cycles = 1.97 msec */
+int flexcop_dma_config_timer(struct flexcop_device *fc,
+		flexcop_dma_index_t dma_idx, u8 cycles)
+{
+	flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
+	flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
+
+	flexcop_dma_remap(fc,dma_idx,0);
+
+	deb_info("%s\n",__func__);
+	v.dma_0x4_write.dmatimer = cycles;
+	fc->write_ibi_reg(fc,r,v);
+	return 0;
+}
+EXPORT_SYMBOL(flexcop_dma_config_timer);
+
diff --git a/drivers/media/pci/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c
new file mode 100644
index 000000000000..44f8fb5f17ff
--- /dev/null
+++ b/drivers/media/pci/b2c2/flexcop-pci.c
@@ -0,0 +1,450 @@
+/*
+ * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-pci.c - covers the PCI part including DMA transfers
+ * see flexcop.c for copyright information
+ */
+
+#define FC_LOG_PREFIX "flexcop-pci"
+#include "flexcop-common.h"
+
+static int enable_pid_filtering = 1;
+module_param(enable_pid_filtering, int, 0444);
+MODULE_PARM_DESC(enable_pid_filtering,
+	"enable hardware pid filtering: supported values: 0 (fullts), 1");
+
+static int irq_chk_intv = 100;
+module_param(irq_chk_intv, int, 0644);
+MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog.");
+
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level,args...) \
+	do { if ((debug & level)) printk(args); } while (0)
+#define DEBSTATUS ""
+#else
+#define dprintk(level,args...)
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_reg(args...) dprintk(0x02, args)
+#define deb_ts(args...) dprintk(0x04, args)
+#define deb_irq(args...) dprintk(0x08, args)
+#define deb_chk(args...) dprintk(0x10, args)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,
+	"set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))."
+	DEBSTATUS);
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_NAME "flexcop-pci"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+
+struct flexcop_pci {
+	struct pci_dev *pdev;
+
+#define FC_PCI_INIT     0x01
+#define FC_PCI_DMA_INIT 0x02
+	int init_state;
+
+	void __iomem *io_mem;
+	u32 irq;
+	/* buffersize (at least for DMA1, need to be % 188 == 0,
+	 * this logic is required */
+#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
+#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
+	struct flexcop_dma dma[2];
+
+	int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
+	u32 last_dma1_cur_pos;
+	/* position of the pointer last time the timer/packet irq occurred */
+	int count;
+	int count_prev;
+	int stream_problem;
+
+	spinlock_t irq_lock;
+	unsigned long last_irq;
+
+	struct delayed_work irq_check_work;
+	struct flexcop_device *fc_dev;
+};
+
+static int lastwreg, lastwval, lastrreg, lastrval;
+
+static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc,
+		flexcop_ibi_register r)
+{
+	struct flexcop_pci *fc_pci = fc->bus_specific;
+	flexcop_ibi_value v;
+	v.raw = readl(fc_pci->io_mem + r);
+
+	if (lastrreg != r || lastrval != v.raw) {
+		lastrreg = r; lastrval = v.raw;
+		deb_reg("new rd: %3x: %08x\n", r, v.raw);
+	}
+
+	return v;
+}
+
+static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc,
+		flexcop_ibi_register r, flexcop_ibi_value v)
+{
+	struct flexcop_pci *fc_pci = fc->bus_specific;
+
+	if (lastwreg != r || lastwval != v.raw) {
+		lastwreg = r; lastwval = v.raw;
+		deb_reg("new wr: %3x: %08x\n", r, v.raw);
+	}
+
+	writel(v.raw, fc_pci->io_mem + r);
+	return 0;
+}
+
+static void flexcop_pci_irq_check_work(struct work_struct *work)
+{
+	struct flexcop_pci *fc_pci =
+		container_of(work, struct flexcop_pci, irq_check_work.work);
+	struct flexcop_device *fc = fc_pci->fc_dev;
+
+	if (fc->feedcount) {
+
+		if (fc_pci->count == fc_pci->count_prev) {
+			deb_chk("no IRQ since the last check\n");
+			if (fc_pci->stream_problem++ == 3) {
+				struct dvb_demux_feed *feed;
+				deb_info("flexcop-pci: stream problem, resetting pid filter\n");
+
+				spin_lock_irq(&fc->demux.lock);
+				list_for_each_entry(feed, &fc->demux.feed_list,
+						list_head) {
+					flexcop_pid_feed_control(fc, feed, 0);
+				}
+
+				list_for_each_entry(feed, &fc->demux.feed_list,
+						list_head) {
+					flexcop_pid_feed_control(fc, feed, 1);
+				}
+				spin_unlock_irq(&fc->demux.lock);
+
+				fc_pci->stream_problem = 0;
+			}
+		} else {
+			fc_pci->stream_problem = 0;
+			fc_pci->count_prev = fc_pci->count;
+		}
+	}
+
+	schedule_delayed_work(&fc_pci->irq_check_work,
+			msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));
+}
+
+/* When PID filtering is turned on, we use the timer IRQ, because small amounts
+ * of data need to be passed to the user space instantly as well. When PID
+ * filtering is turned off, we use the page-change-IRQ */
+static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
+{
+	struct flexcop_pci *fc_pci = dev_id;
+	struct flexcop_device *fc = fc_pci->fc_dev;
+	unsigned long flags;
+	flexcop_ibi_value v;
+	irqreturn_t ret = IRQ_HANDLED;
+
+	spin_lock_irqsave(&fc_pci->irq_lock, flags);
+	v = fc->read_ibi_reg(fc, irq_20c);
+
+	/* errors */
+	if (v.irq_20c.Data_receiver_error)
+		deb_chk("data receiver error\n");
+	if (v.irq_20c.Continuity_error_flag)
+		deb_chk("Contunuity error flag is set\n");
+	if (v.irq_20c.LLC_SNAP_FLAG_set)
+		deb_chk("LLC_SNAP_FLAG_set is set\n");
+	if (v.irq_20c.Transport_Error)
+		deb_chk("Transport error\n");
+
+	if ((fc_pci->count % 1000) == 0)
+		deb_chk("%d valid irq took place so far\n", fc_pci->count);
+
+	if (v.irq_20c.DMA1_IRQ_Status == 1) {
+		if (fc_pci->active_dma1_addr == 0)
+			flexcop_pass_dmx_packets(fc_pci->fc_dev,
+					fc_pci->dma[0].cpu_addr0,
+					fc_pci->dma[0].size / 188);
+		else
+			flexcop_pass_dmx_packets(fc_pci->fc_dev,
+					fc_pci->dma[0].cpu_addr1,
+					fc_pci->dma[0].size / 188);
+
+		deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
+		fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
+		/* for the timer IRQ we only can use buffer dmx feeding, because we don't have
+		 * complete TS packets when reading from the DMA memory */
+	} else if (v.irq_20c.DMA1_Timer_Status == 1) {
+		dma_addr_t cur_addr =
+			fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
+		u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
+
+		deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, "
+			"last_cur_pos: %08x ",
+				jiffies_to_usecs(jiffies - fc_pci->last_irq),
+				v.raw, (unsigned long long)cur_addr, cur_pos,
+				fc_pci->last_dma1_cur_pos);
+		fc_pci->last_irq = jiffies;
+
+		/* buffer end was reached, restarted from the beginning
+		 * pass the data from last_cur_pos to the buffer end to the demux
+		 */
+		if (cur_pos < fc_pci->last_dma1_cur_pos) {
+			deb_irq(" end was reached: passing %d bytes ",
+				(fc_pci->dma[0].size*2 - 1) -
+				fc_pci->last_dma1_cur_pos);
+			flexcop_pass_dmx_data(fc_pci->fc_dev,
+				fc_pci->dma[0].cpu_addr0 +
+					fc_pci->last_dma1_cur_pos,
+				(fc_pci->dma[0].size*2) -
+					fc_pci->last_dma1_cur_pos);
+			fc_pci->last_dma1_cur_pos = 0;
+		}
+
+		if (cur_pos > fc_pci->last_dma1_cur_pos) {
+			deb_irq(" passing %d bytes ",
+				cur_pos - fc_pci->last_dma1_cur_pos);
+			flexcop_pass_dmx_data(fc_pci->fc_dev,
+				fc_pci->dma[0].cpu_addr0 +
+					fc_pci->last_dma1_cur_pos,
+				cur_pos - fc_pci->last_dma1_cur_pos);
+		}
+		deb_irq("\n");
+
+		fc_pci->last_dma1_cur_pos = cur_pos;
+		fc_pci->count++;
+	} else {
+		deb_irq("isr for flexcop called, "
+			"apparently without reason (%08x)\n", v.raw);
+		ret = IRQ_NONE;
+	}
+
+	spin_unlock_irqrestore(&fc_pci->irq_lock, flags);
+	return ret;
+}
+
+static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
+{
+	struct flexcop_pci *fc_pci = fc->bus_specific;
+	if (onoff) {
+		flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1);
+		flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2);
+		flexcop_dma_config_timer(fc, FC_DMA_1, 0);
+		flexcop_dma_xfer_control(fc, FC_DMA_1,
+				FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1);
+		deb_irq("DMA xfer enabled\n");
+
+		fc_pci->last_dma1_cur_pos = 0;
+		flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1);
+		deb_irq("IRQ enabled\n");
+		fc_pci->count_prev = fc_pci->count;
+	} else {
+		flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0);
+		deb_irq("IRQ disabled\n");
+
+		flexcop_dma_xfer_control(fc, FC_DMA_1,
+			 FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0);
+		deb_irq("DMA xfer disabled\n");
+	}
+	return 0;
+}
+
+static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
+{
+	int ret;
+	ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0],
+			FC_DEFAULT_DMA1_BUFSIZE);
+	if (ret != 0)
+		return ret;
+
+	ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1],
+			FC_DEFAULT_DMA2_BUFSIZE);
+	if (ret != 0) {
+		flexcop_dma_free(&fc_pci->dma[0]);
+		return ret;
+	}
+
+	flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA |
+			FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
+	flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO |
+			FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
+	fc_pci->init_state |= FC_PCI_DMA_INIT;
+	return ret;
+}
+
+static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)
+{
+	if (fc_pci->init_state & FC_PCI_DMA_INIT) {
+		flexcop_dma_free(&fc_pci->dma[0]);
+		flexcop_dma_free(&fc_pci->dma[1]);
+	}
+	fc_pci->init_state &= ~FC_PCI_DMA_INIT;
+}
+
+static int flexcop_pci_init(struct flexcop_pci *fc_pci)
+{
+	int ret;
+
+	info("card revision %x", fc_pci->pdev->revision);
+
+	if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
+		return ret;
+	pci_set_master(fc_pci->pdev);
+
+	if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
+		goto err_pci_disable_device;
+
+	fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);
+
+	if (!fc_pci->io_mem) {
+		err("cannot map io memory\n");
+		ret = -EIO;
+		goto err_pci_release_regions;
+	}
+
+	pci_set_drvdata(fc_pci->pdev, fc_pci);
+	spin_lock_init(&fc_pci->irq_lock);
+	if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
+					IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
+		goto err_pci_iounmap;
+
+	fc_pci->init_state |= FC_PCI_INIT;
+	return ret;
+
+err_pci_iounmap:
+	pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
+	pci_set_drvdata(fc_pci->pdev, NULL);
+err_pci_release_regions:
+	pci_release_regions(fc_pci->pdev);
+err_pci_disable_device:
+	pci_disable_device(fc_pci->pdev);
+	return ret;
+}
+
+static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
+{
+	if (fc_pci->init_state & FC_PCI_INIT) {
+		free_irq(fc_pci->pdev->irq, fc_pci);
+		pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
+		pci_set_drvdata(fc_pci->pdev, NULL);
+		pci_release_regions(fc_pci->pdev);
+		pci_disable_device(fc_pci->pdev);
+	}
+	fc_pci->init_state &= ~FC_PCI_INIT;
+}
+
+static int flexcop_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *ent)
+{
+	struct flexcop_device *fc;
+	struct flexcop_pci *fc_pci;
+	int ret = -ENOMEM;
+
+	if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {
+		err("out of memory\n");
+		return -ENOMEM;
+	}
+
+	/* general flexcop init */
+	fc_pci = fc->bus_specific;
+	fc_pci->fc_dev = fc;
+
+	fc->read_ibi_reg = flexcop_pci_read_ibi_reg;
+	fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
+	fc->i2c_request = flexcop_i2c_request;
+	fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
+	fc->stream_control = flexcop_pci_stream_control;
+
+	if (enable_pid_filtering)
+		info("will use the HW PID filter.");
+	else
+		info("will pass the complete TS to the demuxer.");
+
+	fc->pid_filtering = enable_pid_filtering;
+	fc->bus_type = FC_PCI;
+	fc->dev = &pdev->dev;
+	fc->owner = THIS_MODULE;
+
+	/* bus specific part */
+	fc_pci->pdev = pdev;
+	if ((ret = flexcop_pci_init(fc_pci)) != 0)
+		goto err_kfree;
+
+	/* init flexcop */
+	if ((ret = flexcop_device_initialize(fc)) != 0)
+		goto err_pci_exit;
+
+	/* init dma */
+	if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
+		goto err_fc_exit;
+
+	INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work);
+
+	if (irq_chk_intv > 0)
+		schedule_delayed_work(&fc_pci->irq_check_work,
+				msecs_to_jiffies(irq_chk_intv < 100 ?
+					100 :
+					irq_chk_intv));
+	return ret;
+
+err_fc_exit:
+	flexcop_device_exit(fc);
+err_pci_exit:
+	flexcop_pci_exit(fc_pci);
+err_kfree:
+	flexcop_device_kfree(fc);
+	return ret;
+}
+
+/* in theory every _exit function should be called exactly two times,
+ * here and in the bail-out-part of the _init-function
+ */
+static void flexcop_pci_remove(struct pci_dev *pdev)
+{
+	struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
+
+	if (irq_chk_intv > 0)
+		cancel_delayed_work(&fc_pci->irq_check_work);
+
+	flexcop_pci_dma_exit(fc_pci);
+	flexcop_device_exit(fc_pci->fc_dev);
+	flexcop_pci_exit(fc_pci);
+	flexcop_device_kfree(fc_pci->fc_dev);
+}
+
+static struct pci_device_id flexcop_pci_tbl[] = {
+	{ PCI_DEVICE(0x13d0, 0x2103) },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);
+
+static struct pci_driver flexcop_pci_driver = {
+	.name     = "b2c2_flexcop_pci",
+	.id_table = flexcop_pci_tbl,
+	.probe    = flexcop_pci_probe,
+	.remove   = flexcop_pci_remove,
+};
+
+static int __init flexcop_pci_module_init(void)
+{
+	return pci_register_driver(&flexcop_pci_driver);
+}
+
+static void __exit flexcop_pci_module_exit(void)
+{
+	pci_unregister_driver(&flexcop_pci_driver);
+}
+
+module_init(flexcop_pci_module_init);
+module_exit(flexcop_pci_module_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig
new file mode 100644
index 000000000000..61d09e010814
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Kconfig
@@ -0,0 +1,43 @@
+config VIDEO_BT848
+	tristate "BT848 Video For Linux"
+	depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
+	select I2C_ALGOBIT
+	select VIDEO_BTCX
+	select VIDEOBUF_DMA_SG
+	depends on RC_CORE
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  Support for BT848 based frame grabber/overlay boards. This includes
+	  the Miro, Hauppauge and STB boards. Please read the material in
+	  <file:Documentation/video4linux/bttv/> for more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bttv.
+
+config DVB_BT8XX
+	tristate "DVB/ATSC Support for bt878 based TV cards"
+	depends on DVB_CORE && PCI && I2C && VIDEO_BT848
+	select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+	  the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
+	  the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and
+	  some AVerMedia cards.
+
+	  Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y if you own such a device and want to use it.
diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile
new file mode 100644
index 000000000000..5f06597c6a6e
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Makefile
@@ -0,0 +1,11 @@
+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-audio-hook.o
+
+obj-$(CONFIG_VIDEO_BT848) += bttv.o
+obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/bt8xx/bt848.h b/drivers/media/pci/bt8xx/bt848.h
new file mode 100644
index 000000000000..c37e6acffded
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt848.h
@@ -0,0 +1,369 @@
+/*
+    bt848.h - Bt848 register offsets
+
+    Copyright (C) 1996,97,98 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 _BT848_H_
+#define _BT848_H_
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BT848
+#define PCI_DEVICE_ID_BT848     0x350
+#endif
+#ifndef PCI_DEVICE_ID_BT849
+#define PCI_DEVICE_ID_BT849     0x351
+#endif
+#ifndef PCI_DEVICE_ID_FUSION879
+#define PCI_DEVICE_ID_FUSION879	0x36c
+#endif
+
+#ifndef PCI_DEVICE_ID_BT878
+#define PCI_DEVICE_ID_BT878     0x36e
+#endif
+#ifndef PCI_DEVICE_ID_BT879
+#define PCI_DEVICE_ID_BT879     0x36f
+#endif
+
+/* Brooktree 848 registers */
+
+#define BT848_DSTATUS          0x000
+#define BT848_DSTATUS_PRES     (1<<7)
+#define BT848_DSTATUS_HLOC     (1<<6)
+#define BT848_DSTATUS_FIELD    (1<<5)
+#define BT848_DSTATUS_NUML     (1<<4)
+#define BT848_DSTATUS_CSEL     (1<<3)
+#define BT848_DSTATUS_PLOCK    (1<<2)
+#define BT848_DSTATUS_LOF      (1<<1)
+#define BT848_DSTATUS_COF      (1<<0)
+
+#define BT848_IFORM            0x004
+#define BT848_IFORM_HACTIVE    (1<<7)
+#define BT848_IFORM_MUXSEL     (3<<5)
+#define BT848_IFORM_MUX0       (2<<5)
+#define BT848_IFORM_MUX1       (3<<5)
+#define BT848_IFORM_MUX2       (1<<5)
+#define BT848_IFORM_XTSEL      (3<<3)
+#define BT848_IFORM_XT0        (1<<3)
+#define BT848_IFORM_XT1        (2<<3)
+#define BT848_IFORM_XTAUTO     (3<<3)
+#define BT848_IFORM_XTBOTH     (3<<3)
+#define BT848_IFORM_NTSC       1
+#define BT848_IFORM_NTSC_J     2
+#define BT848_IFORM_PAL_BDGHI  3
+#define BT848_IFORM_PAL_M      4
+#define BT848_IFORM_PAL_N      5
+#define BT848_IFORM_SECAM      6
+#define BT848_IFORM_PAL_NC     7
+#define BT848_IFORM_AUTO       0
+#define BT848_IFORM_NORM       7
+
+#define BT848_TDEC             0x008
+#define BT848_TDEC_DEC_FIELD   (1<<7)
+#define BT848_TDEC_FLDALIGN    (1<<6)
+#define BT848_TDEC_DEC_RAT     (0x1f)
+
+#define BT848_E_CROP           0x00C
+#define BT848_O_CROP           0x08C
+
+#define BT848_E_VDELAY_LO      0x010
+#define BT848_O_VDELAY_LO      0x090
+
+#define BT848_E_VACTIVE_LO     0x014
+#define BT848_O_VACTIVE_LO     0x094
+
+#define BT848_E_HDELAY_LO      0x018
+#define BT848_O_HDELAY_LO      0x098
+
+#define BT848_E_HACTIVE_LO     0x01C
+#define BT848_O_HACTIVE_LO     0x09C
+
+#define BT848_E_HSCALE_HI      0x020
+#define BT848_O_HSCALE_HI      0x0A0
+
+#define BT848_E_HSCALE_LO      0x024
+#define BT848_O_HSCALE_LO      0x0A4
+
+#define BT848_BRIGHT           0x028
+
+#define BT848_E_CONTROL        0x02C
+#define BT848_O_CONTROL        0x0AC
+#define BT848_CONTROL_LNOTCH    (1<<7)
+#define BT848_CONTROL_COMP      (1<<6)
+#define BT848_CONTROL_LDEC      (1<<5)
+#define BT848_CONTROL_CBSENSE   (1<<4)
+#define BT848_CONTROL_CON_MSB   (1<<2)
+#define BT848_CONTROL_SAT_U_MSB (1<<1)
+#define BT848_CONTROL_SAT_V_MSB (1<<0)
+
+#define BT848_CONTRAST_LO      0x030
+#define BT848_SAT_U_LO         0x034
+#define BT848_SAT_V_LO         0x038
+#define BT848_HUE              0x03C
+
+#define BT848_E_SCLOOP         0x040
+#define BT848_O_SCLOOP         0x0C0
+#define BT848_SCLOOP_CAGC       (1<<6)
+#define BT848_SCLOOP_CKILL      (1<<5)
+#define BT848_SCLOOP_HFILT_AUTO (0<<3)
+#define BT848_SCLOOP_HFILT_CIF  (1<<3)
+#define BT848_SCLOOP_HFILT_QCIF (2<<3)
+#define BT848_SCLOOP_HFILT_ICON (3<<3)
+
+#define BT848_SCLOOP_PEAK       (1<<7)
+#define BT848_SCLOOP_HFILT_MINP (1<<3)
+#define BT848_SCLOOP_HFILT_MEDP (2<<3)
+#define BT848_SCLOOP_HFILT_MAXP (3<<3)
+
+
+#define BT848_OFORM            0x048
+#define BT848_OFORM_RANGE      (1<<7)
+#define BT848_OFORM_CORE0      (0<<5)
+#define BT848_OFORM_CORE8      (1<<5)
+#define BT848_OFORM_CORE16     (2<<5)
+#define BT848_OFORM_CORE32     (3<<5)
+
+#define BT848_E_VSCALE_HI      0x04C
+#define BT848_O_VSCALE_HI      0x0CC
+#define BT848_VSCALE_YCOMB     (1<<7)
+#define BT848_VSCALE_COMB      (1<<6)
+#define BT848_VSCALE_INT       (1<<5)
+#define BT848_VSCALE_HI        15
+
+#define BT848_E_VSCALE_LO      0x050
+#define BT848_O_VSCALE_LO      0x0D0
+#define BT848_TEST             0x054
+#define BT848_ADELAY           0x060
+#define BT848_BDELAY           0x064
+
+#define BT848_ADC              0x068
+#define BT848_ADC_RESERVED     (2<<6)
+#define BT848_ADC_SYNC_T       (1<<5)
+#define BT848_ADC_AGC_EN       (1<<4)
+#define BT848_ADC_CLK_SLEEP    (1<<3)
+#define BT848_ADC_Y_SLEEP      (1<<2)
+#define BT848_ADC_C_SLEEP      (1<<1)
+#define BT848_ADC_CRUSH        (1<<0)
+
+#define BT848_WC_UP            0x044
+#define BT848_WC_DOWN          0x078
+
+#define BT848_E_VTC            0x06C
+#define BT848_O_VTC            0x0EC
+#define BT848_VTC_HSFMT        (1<<7)
+#define BT848_VTC_VFILT_2TAP   0
+#define BT848_VTC_VFILT_3TAP   1
+#define BT848_VTC_VFILT_4TAP   2
+#define BT848_VTC_VFILT_5TAP   3
+
+#define BT848_SRESET           0x07C
+
+#define BT848_COLOR_FMT             0x0D4
+#define BT848_COLOR_FMT_O_RGB32     (0<<4)
+#define BT848_COLOR_FMT_O_RGB24     (1<<4)
+#define BT848_COLOR_FMT_O_RGB16     (2<<4)
+#define BT848_COLOR_FMT_O_RGB15     (3<<4)
+#define BT848_COLOR_FMT_O_YUY2      (4<<4)
+#define BT848_COLOR_FMT_O_BtYUV     (5<<4)
+#define BT848_COLOR_FMT_O_Y8        (6<<4)
+#define BT848_COLOR_FMT_O_RGB8      (7<<4)
+#define BT848_COLOR_FMT_O_YCrCb422  (8<<4)
+#define BT848_COLOR_FMT_O_YCrCb411  (9<<4)
+#define BT848_COLOR_FMT_O_RAW       (14<<4)
+#define BT848_COLOR_FMT_E_RGB32     0
+#define BT848_COLOR_FMT_E_RGB24     1
+#define BT848_COLOR_FMT_E_RGB16     2
+#define BT848_COLOR_FMT_E_RGB15     3
+#define BT848_COLOR_FMT_E_YUY2      4
+#define BT848_COLOR_FMT_E_BtYUV     5
+#define BT848_COLOR_FMT_E_Y8        6
+#define BT848_COLOR_FMT_E_RGB8      7
+#define BT848_COLOR_FMT_E_YCrCb422  8
+#define BT848_COLOR_FMT_E_YCrCb411  9
+#define BT848_COLOR_FMT_E_RAW       14
+
+#define BT848_COLOR_FMT_RGB32       0x00
+#define BT848_COLOR_FMT_RGB24       0x11
+#define BT848_COLOR_FMT_RGB16       0x22
+#define BT848_COLOR_FMT_RGB15       0x33
+#define BT848_COLOR_FMT_YUY2        0x44
+#define BT848_COLOR_FMT_BtYUV       0x55
+#define BT848_COLOR_FMT_Y8          0x66
+#define BT848_COLOR_FMT_RGB8        0x77
+#define BT848_COLOR_FMT_YCrCb422    0x88
+#define BT848_COLOR_FMT_YCrCb411    0x99
+#define BT848_COLOR_FMT_RAW         0xee
+
+#define BT848_VTOTAL_LO             0xB0
+#define BT848_VTOTAL_HI             0xB4
+
+#define BT848_COLOR_CTL                0x0D8
+#define BT848_COLOR_CTL_EXT_FRMRATE    (1<<7)
+#define BT848_COLOR_CTL_COLOR_BARS     (1<<6)
+#define BT848_COLOR_CTL_RGB_DED        (1<<5)
+#define BT848_COLOR_CTL_GAMMA          (1<<4)
+#define BT848_COLOR_CTL_WSWAP_ODD      (1<<3)
+#define BT848_COLOR_CTL_WSWAP_EVEN     (1<<2)
+#define BT848_COLOR_CTL_BSWAP_ODD      (1<<1)
+#define BT848_COLOR_CTL_BSWAP_EVEN     (1<<0)
+
+#define BT848_CAP_CTL                  0x0DC
+#define BT848_CAP_CTL_DITH_FRAME       (1<<4)
+#define BT848_CAP_CTL_CAPTURE_VBI_ODD  (1<<3)
+#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define BT848_CAP_CTL_CAPTURE_ODD      (1<<1)
+#define BT848_CAP_CTL_CAPTURE_EVEN     (1<<0)
+
+#define BT848_VBI_PACK_SIZE    0x0E0
+
+#define BT848_VBI_PACK_DEL     0x0E4
+#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc
+#define BT848_VBI_PACK_DEL_EXT_FRAME  2
+#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
+
+
+#define BT848_INT_STAT         0x100
+#define BT848_INT_MASK         0x104
+
+#define BT848_INT_ETBF         (1<<23)
+
+#define BT848_INT_RISCS   (0xf<<28)
+#define BT848_INT_RISC_EN (1<<27)
+#define BT848_INT_RACK    (1<<25)
+#define BT848_INT_FIELD   (1<<24)
+#define BT848_INT_SCERR   (1<<19)
+#define BT848_INT_OCERR   (1<<18)
+#define BT848_INT_PABORT  (1<<17)
+#define BT848_INT_RIPERR  (1<<16)
+#define BT848_INT_PPERR   (1<<15)
+#define BT848_INT_FDSR    (1<<14)
+#define BT848_INT_FTRGT   (1<<13)
+#define BT848_INT_FBUS    (1<<12)
+#define BT848_INT_RISCI   (1<<11)
+#define BT848_INT_GPINT   (1<<9)
+#define BT848_INT_I2CDONE (1<<8)
+#define BT848_INT_VPRES   (1<<5)
+#define BT848_INT_HLOCK   (1<<4)
+#define BT848_INT_OFLOW   (1<<3)
+#define BT848_INT_HSYNC   (1<<2)
+#define BT848_INT_VSYNC   (1<<1)
+#define BT848_INT_FMTCHG  (1<<0)
+
+
+#define BT848_GPIO_DMA_CTL             0x10C
+#define BT848_GPIO_DMA_CTL_GPINTC      (1<<15)
+#define BT848_GPIO_DMA_CTL_GPINTI      (1<<14)
+#define BT848_GPIO_DMA_CTL_GPWEC       (1<<13)
+#define BT848_GPIO_DMA_CTL_GPIOMODE    (3<<11)
+#define BT848_GPIO_DMA_CTL_GPCLKMODE   (1<<10)
+#define BT848_GPIO_DMA_CTL_PLTP23_4    (0<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_8    (1<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_16   (2<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_32   (3<<6)
+#define BT848_GPIO_DMA_CTL_PLTP1_4     (0<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_8     (1<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_16    (2<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_32    (3<<4)
+#define BT848_GPIO_DMA_CTL_PKTP_4      (0<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_8      (1<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_16     (2<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_32     (3<<2)
+#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1)
+#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
+
+#define BT848_I2C              0x110
+#define BT878_I2C_MODE         (1<<7)
+#define BT878_I2C_RATE         (1<<6)
+#define BT878_I2C_NOSTOP       (1<<5)
+#define BT878_I2C_NOSTART      (1<<4)
+#define BT848_I2C_DIV          (0xf<<4)
+#define BT848_I2C_SYNC         (1<<3)
+#define BT848_I2C_W3B	       (1<<2)
+#define BT848_I2C_SCL          (1<<1)
+#define BT848_I2C_SDA          (1<<0)
+
+#define BT848_RISC_STRT_ADD    0x114
+#define BT848_GPIO_OUT_EN      0x118
+#define BT848_GPIO_REG_INP     0x11C
+#define BT848_RISC_COUNT       0x120
+#define BT848_GPIO_DATA        0x200
+
+
+/* Bt848 RISC commands */
+
+/* only for the SYNC RISC command */
+#define BT848_FIFO_STATUS_FM1  0x06
+#define BT848_FIFO_STATUS_FM3  0x0e
+#define BT848_FIFO_STATUS_SOL  0x02
+#define BT848_FIFO_STATUS_EOL4 0x01
+#define BT848_FIFO_STATUS_EOL3 0x0d
+#define BT848_FIFO_STATUS_EOL2 0x09
+#define BT848_FIFO_STATUS_EOL1 0x05
+#define BT848_FIFO_STATUS_VRE  0x04
+#define BT848_FIFO_STATUS_VRO  0x0c
+#define BT848_FIFO_STATUS_PXV  0x00
+
+#define BT848_RISC_RESYNC      (1<<15)
+
+/* WRITE and SKIP */
+/* disable which bytes of each DWORD */
+#define BT848_RISC_BYTE0       (1U<<12)
+#define BT848_RISC_BYTE1       (1U<<13)
+#define BT848_RISC_BYTE2       (1U<<14)
+#define BT848_RISC_BYTE3       (1U<<15)
+#define BT848_RISC_BYTE_ALL    (0x0fU<<12)
+#define BT848_RISC_BYTE_NONE   0
+/* cause RISCI */
+#define BT848_RISC_IRQ         (1U<<24)
+/* RISC command is last one in this line */
+#define BT848_RISC_EOL         (1U<<26)
+/* RISC command is first one in this line */
+#define BT848_RISC_SOL         (1U<<27)
+
+#define BT848_RISC_WRITE       (0x01U<<28)
+#define BT848_RISC_SKIP        (0x02U<<28)
+#define BT848_RISC_WRITEC      (0x05U<<28)
+#define BT848_RISC_JUMP        (0x07U<<28)
+#define BT848_RISC_SYNC        (0x08U<<28)
+
+#define BT848_RISC_WRITE123    (0x09U<<28)
+#define BT848_RISC_SKIP123     (0x0aU<<28)
+#define BT848_RISC_WRITE1S23   (0x0bU<<28)
+
+
+/* Bt848A and higher only !! */
+#define BT848_TGLB             0x080
+#define BT848_TGCTRL           0x084
+#define BT848_FCAP             0x0E8
+#define BT848_PLL_F_LO         0x0F0
+#define BT848_PLL_F_HI         0x0F4
+
+#define BT848_PLL_XCI          0x0F8
+#define BT848_PLL_X            (1<<7)
+#define BT848_PLL_C            (1<<6)
+
+#define BT848_DVSIF            0x0FC
+
+/* Bt878 register */
+
+#define BT878_DEVCTRL 0x40
+#define BT878_EN_TBFX 0x02
+#define BT878_EN_VSFX 0x04
+
+#endif
diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
new file mode 100644
index 000000000000..b34fa95185e4
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt878.c
@@ -0,0 +1,609 @@
+/*
+ * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card
+ *
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
+ *
+ * large parts based on the bttv driver
+ * Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@metzlerbros.de)
+ *                        & Marcus Metzler (mocm@metzlerbros.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "bt878.h"
+#include "dst_priv.h"
+
+
+/**************************************/
+/* Miscellaneous utility  definitions */
+/**************************************/
+
+static unsigned int bt878_verbose = 1;
+static unsigned int bt878_debug;
+
+module_param_named(verbose, bt878_verbose, int, 0444);
+MODULE_PARM_DESC(verbose,
+		 "verbose startup messages, default is 1 (yes)");
+module_param_named(debug, bt878_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off).");
+
+int bt878_num;
+struct bt878 bt878[BT878_MAX];
+
+EXPORT_SYMBOL(bt878_num);
+EXPORT_SYMBOL(bt878);
+
+#define btwrite(dat,adr)    bmtwrite((dat), (bt->bt878_mem+(adr)))
+#define btread(adr)         bmtread(bt->bt878_mem+(adr))
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#if defined(dprintk)
+#undef dprintk
+#endif
+#define dprintk(fmt, arg...) \
+	do { \
+		if (bt878_debug) \
+			printk(KERN_DEBUG fmt, ##arg); \
+	} while (0)
+
+static void bt878_mem_free(struct bt878 *bt)
+{
+	if (bt->buf_cpu) {
+		pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu,
+				    bt->buf_dma);
+		bt->buf_cpu = NULL;
+	}
+
+	if (bt->risc_cpu) {
+		pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu,
+				    bt->risc_dma);
+		bt->risc_cpu = NULL;
+	}
+}
+
+static int bt878_mem_alloc(struct bt878 *bt)
+{
+	if (!bt->buf_cpu) {
+		bt->buf_size = 128 * 1024;
+
+		bt->buf_cpu =
+		    pci_alloc_consistent(bt->dev, bt->buf_size,
+					 &bt->buf_dma);
+
+		if (!bt->buf_cpu)
+			return -ENOMEM;
+
+		memset(bt->buf_cpu, 0, bt->buf_size);
+	}
+
+	if (!bt->risc_cpu) {
+		bt->risc_size = PAGE_SIZE;
+		bt->risc_cpu =
+		    pci_alloc_consistent(bt->dev, bt->risc_size,
+					 &bt->risc_dma);
+
+		if (!bt->risc_cpu) {
+			bt878_mem_free(bt);
+			return -ENOMEM;
+		}
+
+		memset(bt->risc_cpu, 0, bt->risc_size);
+	}
+
+	return 0;
+}
+
+/* RISC instructions */
+#define RISC_WRITE		(0x01 << 28)
+#define RISC_JUMP		(0x07 << 28)
+#define RISC_SYNC		(0x08 << 28)
+
+/* RISC bits */
+#define RISC_WR_SOL		(1 << 27)
+#define RISC_WR_EOL		(1 << 26)
+#define RISC_IRQ		(1 << 24)
+#define RISC_STATUS(status)	((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))
+#define RISC_SYNC_RESYNC	(1 << 15)
+#define RISC_SYNC_FM1		0x06
+#define RISC_SYNC_VRO		0x0C
+
+#define RISC_FLUSH()		bt->risc_pos = 0
+#define RISC_INSTR(instr)	bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
+
+static int bt878_make_risc(struct bt878 *bt)
+{
+	bt->block_bytes = bt->buf_size >> 4;
+	bt->block_count = 1 << 4;
+	bt->line_bytes = bt->block_bytes;
+	bt->line_count = bt->block_count;
+
+	while (bt->line_bytes > 4095) {
+		bt->line_bytes >>= 1;
+		bt->line_count <<= 1;
+	}
+
+	if (bt->line_count > 255) {
+		printk(KERN_ERR "bt878: buffer size error!\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin)
+{
+	u32 buf_pos = 0;
+	u32 line;
+
+	RISC_FLUSH();
+	RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin);
+	RISC_INSTR(0);
+
+	dprintk("bt878: risc len lines %u, bytes per line %u\n",
+			bt->line_count, bt->line_bytes);
+	for (line = 0; line < bt->line_count; line++) {
+		// At the beginning of every block we issue an IRQ with previous (finished) block number set
+		if (!(buf_pos % bt->block_bytes))
+			RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+				   RISC_IRQ |
+				   RISC_STATUS(((buf_pos /
+						 bt->block_bytes) +
+						(bt->block_count -
+						 1)) %
+					       bt->block_count) | bt->
+				   line_bytes);
+		else
+			RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+				   bt->line_bytes);
+		RISC_INSTR(bt->buf_dma + buf_pos);
+		buf_pos += bt->line_bytes;
+	}
+
+	RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO);
+	RISC_INSTR(0);
+
+	RISC_INSTR(RISC_JUMP);
+	RISC_INSTR(bt->risc_dma);
+
+	btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN);
+}
+
+/*****************************/
+/* Start/Stop grabbing funcs */
+/*****************************/
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+		u32 irq_err_ignore)
+{
+	u32 int_mask;
+
+	dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg);
+	/* complete the writing of the risc dma program now we have
+	 * the card specifics
+	 */
+	bt878_risc_program(bt, op_sync_orin);
+	controlreg &= ~0x1f;
+	controlreg |= 0x1b;
+
+	btwrite(bt->risc_dma, BT878_ARISC_START);
+
+	/* original int mask had :
+	 *    6    2    8    4    0
+	 * 1111 1111 1000 0000 0000
+	 * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI
+	 * Hacked for DST to:
+	 * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI
+	 */
+	int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT |
+		BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT |
+		BT878_AFBUS | BT878_ARISCI;
+
+
+	/* ignore pesky bits */
+	int_mask &= ~irq_err_ignore;
+
+	btwrite(int_mask, BT878_AINT_MASK);
+	btwrite(controlreg, BT878_AGPIO_DMA_CTL);
+}
+
+void bt878_stop(struct bt878 *bt)
+{
+	u32 stat;
+	int i = 0;
+
+	dprintk("bt878 debug: bt878_stop\n");
+
+	btwrite(0, BT878_AINT_MASK);
+	btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+	do {
+		stat = btread(BT878_AINT_STAT);
+		if (!(stat & BT878_ARISC_EN))
+			break;
+		i++;
+	} while (i < 500);
+
+	dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n",
+		bt->nr, i, stat);
+}
+
+EXPORT_SYMBOL(bt878_start);
+EXPORT_SYMBOL(bt878_stop);
+
+/*****************************/
+/* Interrupt service routine */
+/*****************************/
+
+static irqreturn_t bt878_irq(int irq, void *dev_id)
+{
+	u32 stat, astat, mask;
+	int count;
+	struct bt878 *bt;
+
+	bt = (struct bt878 *) dev_id;
+
+	count = 0;
+	while (1) {
+		stat = btread(BT878_AINT_STAT);
+		mask = btread(BT878_AINT_MASK);
+		if (!(astat = (stat & mask)))
+			return IRQ_NONE;	/* this interrupt is not for me */
+/*		dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */
+		btwrite(astat, BT878_AINT_STAT);	/* try to clear interrupt condition */
+
+
+		if (astat & (BT878_ASCERR | BT878_AOCERR)) {
+			if (bt878_verbose) {
+				printk(KERN_INFO
+				       "bt878(%d): irq%s%s risc_pc=%08x\n",
+				       bt->nr,
+				       (astat & BT878_ASCERR) ? " SCERR" :
+				       "",
+				       (astat & BT878_AOCERR) ? " OCERR" :
+				       "", btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) {
+			if (bt878_verbose) {
+				printk(KERN_INFO
+				     "bt878(%d): irq%s%s%s risc_pc=%08x\n",
+				     bt->nr,
+				     (astat & BT878_APABORT) ? " PABORT" :
+				     "",
+				     (astat & BT878_ARIPERR) ? " RIPERR" :
+				     "",
+				     (astat & BT878_APPERR) ? " PPERR" :
+				     "", btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) {
+			if (bt878_verbose) {
+				printk(KERN_INFO
+				     "bt878(%d): irq%s%s%s risc_pc=%08x\n",
+				     bt->nr,
+				     (astat & BT878_AFDSR) ? " FDSR" : "",
+				     (astat & BT878_AFTRGT) ? " FTRGT" :
+				     "",
+				     (astat & BT878_AFBUS) ? " FBUS" : "",
+				     btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & BT878_ARISCI) {
+			bt->finished_block = (stat & BT878_ARISCS) >> 28;
+			tasklet_schedule(&bt->tasklet);
+			break;
+		}
+		count++;
+		if (count > 20) {
+			btwrite(0, BT878_AINT_MASK);
+			printk(KERN_ERR
+			       "bt878(%d): IRQ lockup, cleared int mask\n",
+			       bt->nr);
+			break;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+int
+bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp)
+{
+	int retval;
+
+	retval = 0;
+	if (mutex_lock_interruptible(&bt->gpio_lock))
+		return -ERESTARTSYS;
+	/* special gpio signal */
+	switch (cmd) {
+	    case DST_IG_ENABLE:
+		// dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable);
+		retval = bttv_gpio_enable(bt->bttv_nr,
+				mp->enb.mask,
+				mp->enb.enable);
+		break;
+	    case DST_IG_WRITE:
+		// dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals);
+		retval = bttv_write_gpio(bt->bttv_nr,
+				mp->outp.mask,
+				mp->outp.highvals);
+
+		break;
+	    case DST_IG_READ:
+		/* read */
+		retval =  bttv_read_gpio(bt->bttv_nr, &mp->rd.value);
+		// dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value);
+		break;
+	    case DST_IG_TS:
+		/* Set packet size */
+		bt->TS_Size = mp->psize;
+		break;
+
+	    default:
+		retval = -EINVAL;
+		break;
+	}
+	mutex_unlock(&bt->gpio_lock);
+	return retval;
+}
+
+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 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 */
+/***********************/
+
+static int __devinit bt878_probe(struct pci_dev *dev,
+				 const struct pci_device_id *pci_id)
+{
+	int result = 0;
+	unsigned char lat;
+	struct bt878 *bt;
+#if defined(__powerpc__)
+	unsigned int cmd;
+#endif
+	unsigned int cardid;
+
+	printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
+	       bt878_num);
+	if (bt878_num >= BT878_MAX) {
+		printk(KERN_ERR "bt878: Too many devices inserted\n");
+		result = -ENOMEM;
+		goto fail0;
+	}
+	if (pci_enable_device(dev))
+		return -EIO;
+
+	cardid = dev->subsystem_device << 16;
+	cardid |= dev->subsystem_vendor;
+
+	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;
+	bt->nr = bt878_num;
+	bt->shutdown = 0;
+
+	bt->id = dev->device;
+	bt->irq = dev->irq;
+	bt->bt878_adr = pci_resource_start(dev, 0);
+	if (!request_mem_region(pci_resource_start(dev, 0),
+				pci_resource_len(dev, 0), "bt878")) {
+		result = -EBUSY;
+		goto fail0;
+	}
+
+	bt->revision = dev->revision;
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+
+
+	printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ",
+	       bt878_num, bt->id, bt->revision, dev->bus->number,
+	       PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+	printk("irq: %d, latency: %d, memory: 0x%lx\n",
+	       bt->irq, lat, bt->bt878_adr);
+
+
+#if defined(__powerpc__)
+	/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+	/* response on cards with no firmware is not enabled by OF */
+	pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+	cmd = (cmd | PCI_COMMAND_MEMORY);
+	pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+
+#ifdef __sparc__
+	bt->bt878_mem = (unsigned char *) bt->bt878_adr;
+#else
+	bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000);
+#endif
+
+	/* clear interrupt mask */
+	btwrite(0, BT848_INT_MASK);
+
+	result = request_irq(bt->irq, bt878_irq,
+			     IRQF_SHARED | IRQF_DISABLED, "bt878",
+			     (void *) bt);
+	if (result == -EINVAL) {
+		printk(KERN_ERR "bt878(%d): Bad irq number or handler\n",
+		       bt878_num);
+		goto fail1;
+	}
+	if (result == -EBUSY) {
+		printk(KERN_ERR
+		       "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n",
+		       bt878_num, bt->irq);
+		goto fail1;
+	}
+	if (result < 0)
+		goto fail1;
+
+	pci_set_master(dev);
+	pci_set_drvdata(dev, bt);
+
+	if ((result = bt878_mem_alloc(bt))) {
+		printk(KERN_ERR "bt878: failed to allocate memory!\n");
+		goto fail2;
+	}
+
+	bt878_make_risc(bt);
+	btwrite(0, BT878_AINT_MASK);
+	bt878_num++;
+
+	return 0;
+
+      fail2:
+	free_irq(bt->irq, bt);
+      fail1:
+	release_mem_region(pci_resource_start(bt->dev, 0),
+			   pci_resource_len(bt->dev, 0));
+      fail0:
+	pci_disable_device(dev);
+	return result;
+}
+
+static void __devexit bt878_remove(struct pci_dev *pci_dev)
+{
+	u8 command;
+	struct bt878 *bt = pci_get_drvdata(pci_dev);
+
+	if (bt878_verbose)
+		printk(KERN_INFO "bt878(%d): unloading\n", bt->nr);
+
+	/* turn off all capturing, DMA and IRQs */
+	btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+	/* first disable interrupts before unmapping the memory! */
+	btwrite(0, BT878_AINT_MASK);
+	btwrite(~0U, BT878_AINT_STAT);
+
+	/* disable PCI bus-mastering */
+	pci_read_config_byte(bt->dev, PCI_COMMAND, &command);
+	/* Should this be &=~ ?? */
+	command &= ~PCI_COMMAND_MASTER;
+	pci_write_config_byte(bt->dev, PCI_COMMAND, command);
+
+	free_irq(bt->irq, bt);
+	printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem);
+	if (bt->bt878_mem)
+		iounmap(bt->bt878_mem);
+
+	release_mem_region(pci_resource_start(bt->dev, 0),
+			   pci_resource_len(bt->dev, 0));
+	/* wake up any waiting processes
+	   because shutdown flag is set, no new processes (in this queue)
+	   are expected
+	 */
+	bt->shutdown = 1;
+	bt878_mem_free(bt);
+
+	pci_set_drvdata(pci_dev, NULL);
+	pci_disable_device(pci_dev);
+	return;
+}
+
+static struct pci_driver bt878_pci_driver = {
+      .name	= "bt878",
+      .id_table = bt878_pci_tbl,
+      .probe	= bt878_probe,
+      .remove	= __devexit_p(bt878_remove),
+};
+
+/*******************************/
+/* Module management functions */
+/*******************************/
+
+static int __init bt878_init_module(void)
+{
+	bt878_num = 0;
+
+	printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n",
+	       (BT878_VERSION_CODE >> 16) & 0xff,
+	       (BT878_VERSION_CODE >> 8) & 0xff,
+	       BT878_VERSION_CODE & 0xff);
+
+	return pci_register_driver(&bt878_pci_driver);
+}
+
+static void __exit bt878_cleanup_module(void)
+{
+	pci_unregister_driver(&bt878_pci_driver);
+}
+
+module_init(bt878_init_module);
+module_exit(bt878_cleanup_module);
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h
new file mode 100644
index 000000000000..d19b59299d78
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt878.h
@@ -0,0 +1,159 @@
+/*
+    bt878.h - Bt878 audio module (register offsets)
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.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 _BT878_H_
+#define _BT878_H_
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+
+#include "bt848.h"
+#include "bttv.h"
+
+#define BT878_VERSION_CODE 0x000000
+
+#define BT878_AINT_STAT		0x100
+#define BT878_ARISCS		(0xf<<28)
+#define BT878_ARISC_EN		(1<<27)
+#define BT878_ASCERR		(1<<19)
+#define BT878_AOCERR		(1<<18)
+#define BT878_APABORT		(1<<17)
+#define BT878_ARIPERR		(1<<16)
+#define BT878_APPERR		(1<<15)
+#define BT878_AFDSR		(1<<14)
+#define BT878_AFTRGT		(1<<13)
+#define BT878_AFBUS		(1<<12)
+#define BT878_ARISCI		(1<<11)
+#define BT878_AOFLOW		(1<<3)
+
+#define BT878_AINT_MASK		0x104
+
+#define BT878_AGPIO_DMA_CTL	0x10c
+#define BT878_A_GAIN		(0xf<<28)
+#define BT878_A_G2X		(1<<27)
+#define BT878_A_PWRDN		(1<<26)
+#define BT878_A_SEL		(3<<24)
+#define BT878_DA_SCE		(1<<23)
+#define BT878_DA_LRI		(1<<22)
+#define BT878_DA_MLB		(1<<21)
+#define BT878_DA_LRD		(0x1f<<16)
+#define BT878_DA_DPM		(1<<15)
+#define BT878_DA_SBR		(1<<14)
+#define BT878_DA_ES2		(1<<13)
+#define BT878_DA_LMT		(1<<12)
+#define BT878_DA_SDR		(0xf<<8)
+#define BT878_DA_IOM		(3<<6)
+#define BT878_DA_APP		(1<<5)
+#define BT878_ACAP_EN		(1<<4)
+#define BT878_PKTP		(3<<2)
+#define BT878_RISC_EN		(1<<1)
+#define BT878_FIFO_EN		1
+
+#define BT878_APACK_LEN		0x110
+#define BT878_AFP_LEN		(0xff<<16)
+#define BT878_ALP_LEN		0xfff
+
+#define BT878_ARISC_START	0x114
+
+#define BT878_ARISC_PC		0x120
+
+/* BT878 FUNCTION 0 REGISTERS */
+#define BT878_GPIO_DMA_CTL	0x10c
+
+/* Interrupt register */
+#define BT878_INT_STAT		0x100
+#define BT878_INT_MASK		0x104
+#define BT878_I2CRACK		(1<<25)
+#define BT878_I2CDONE		(1<<8)
+
+#define BT878_MAX 4
+
+#define BT878_RISC_SYNC_MASK	(1 << 15)
+
+
+#define BTTV_BOARD_UNKNOWN                 0x00
+#define BTTV_BOARD_PINNACLESAT             0x5e
+#define BTTV_BOARD_NEBULA_DIGITV           0x68
+#define BTTV_BOARD_PC_HDTV                 0x70
+#define BTTV_BOARD_TWINHAN_DST             0x71
+#define BTTV_BOARD_AVDVBT_771              0x7b
+#define BTTV_BOARD_AVDVBT_761              0x7c
+#define BTTV_BOARD_DVICO_DVBT_LITE         0x80
+#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
+
+extern int bt878_num;
+
+struct bt878 {
+	struct mutex gpio_lock;
+	unsigned int nr;
+	unsigned int bttv_nr;
+	struct i2c_adapter *adapter;
+	struct pci_dev *dev;
+	unsigned int id;
+	unsigned int TS_Size;
+	unsigned char revision;
+	unsigned int irq;
+	unsigned long bt878_adr;
+	volatile void __iomem *bt878_mem; /* function 1 */
+
+	volatile u32 finished_block;
+	volatile u32 last_block;
+	u32 block_count;
+	u32 block_bytes;
+	u32 line_bytes;
+	u32 line_count;
+
+	u32 buf_size;
+	u8 *buf_cpu;
+	dma_addr_t buf_dma;
+
+	u32 risc_size;
+	__le32 *risc_cpu;
+	dma_addr_t risc_dma;
+	u32 risc_pos;
+
+	struct tasklet_struct tasklet;
+	int shutdown;
+};
+
+extern struct bt878 bt878[BT878_MAX];
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+		u32 irq_err_ignore);
+void bt878_stop(struct bt878 *bt);
+
+#if defined(__powerpc__)	/* big-endian */
+static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
+{
+	st_le32(addr, val);
+	eieio();
+}
+
+#define bmtwrite(dat,adr)  io_st_le32((adr),(dat))
+#define bmtread(adr)       ld_le32((adr))
+#else
+#define bmtwrite(dat,adr)  writel((dat), (adr))
+#define bmtread(adr)       readl(adr)
+#endif
+
+#endif
diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c
new file mode 100644
index 000000000000..2364d16586b3
--- /dev/null
+++ b/drivers/media/pci/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/pci/bt8xx/bttv-audio-hook.h b/drivers/media/pci/bt8xx/bttv-audio-hook.h
new file mode 100644
index 000000000000..159d07adeff8
--- /dev/null
+++ b/drivers/media/pci/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/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
new file mode 100644
index 000000000000..38952faaffda
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -0,0 +1,4895 @@
+/*
+
+    bttv-cards.c
+
+    this file has configuration informations - card-specific stuff
+    like the big tvcards array for the most part
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+			   & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.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.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <net/checksum.h>
+
+#include <asm/unaligned.h>
+#include <asm/io.h>
+
+#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);
+static void hauppauge_eeprom(struct bttv *btv);
+static void avermedia_eeprom(struct bttv *btv);
+static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
+static void modtec_eeprom(struct bttv *btv);
+static void init_PXC200(struct bttv *btv);
+static void init_RTV24(struct bttv *btv);
+
+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);
+static void ivc120_muxsel(struct bttv *btv, unsigned int input);
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input);
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input);
+
+static void picolo_tetra_muxsel(struct bttv *btv, unsigned int input);
+static void picolo_tetra_init(struct bttv *btv);
+
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input);
+static void tibetCS16_init(struct bttv *btv);
+
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input);
+static void kodicom4400r_init(struct bttv *btv);
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input);
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input);
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input);
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input);
+
+static void gv800s_muxsel(struct bttv *btv, unsigned int input);
+static void gv800s_init(struct bttv *btv);
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input);
+
+static int terratec_active_radio_upgrade(struct bttv *btv);
+static int tea5757_read(struct bttv *btv);
+static int tea5757_write(struct bttv *btv, int value);
+static void identify_by_eeprom(struct bttv *btv,
+			       unsigned char eeprom_data[256]);
+static int __devinit pvr_boot(struct bttv *btv);
+
+/* config variables */
+static unsigned int triton1;
+static unsigned int vsfx;
+static unsigned int latency = UNSET;
+int no_overlay=-1;
+
+static unsigned int card[BTTV_MAX]   = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int pll[BTTV_MAX]    = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int tuner[BTTV_MAX]  = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int svhs[BTTV_MAX]   = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int audiodev[BTTV_MAX];
+static unsigned int saa6588[BTTV_MAX];
+static struct bttv  *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL };
+static unsigned int autoload = UNSET;
+static unsigned int gpiomask = UNSET;
+static unsigned int audioall = UNSET;
+static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET };
+
+/* insmod options */
+module_param(triton1,    int, 0444);
+module_param(vsfx,       int, 0444);
+module_param(no_overlay, int, 0444);
+module_param(latency,    int, 0444);
+module_param(gpiomask,   int, 0444);
+module_param(audioall,   int, 0444);
+module_param(autoload,   int, 0444);
+
+module_param_array(card,     int, NULL, 0444);
+module_param_array(pll,      int, NULL, 0444);
+module_param_array(tuner,    int, NULL, 0444);
+module_param_array(svhs,     int, NULL, 0444);
+module_param_array(remote,   int, NULL, 0444);
+module_param_array(audiodev, int, NULL, 0444);
+module_param_array(audiomux, int, NULL, 0444);
+
+MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
+		 "[enable bug compatibility for triton1 + others]");
+MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
+		 "[yet another chipset flaw workaround]");
+MODULE_PARM_DESC(latency,"pci latency timer");
+MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
+MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore");
+MODULE_PARM_DESC(audiodev, "specify audio device:\n"
+		"\t\t-1 = no audio\n"
+		"\t\t 0 = autodetect (default)\n"
+		"\t\t 1 = msp3400\n"
+		"\t\t 2 = tda7432\n"
+		"\t\t 3 = tvaudio");
+MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition.");
+MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+		" [some VIA/SIS chipsets are known to have problem with overlay]");
+
+/* ----------------------------------------------------------------------- */
+/* list of card IDs for bt878+ cards                                       */
+
+static struct CARD {
+	unsigned id;
+	int cardnr;
+	char *name;
+} cards[] __devinitdata = {
+	{ 0x13eb0070, BTTV_BOARD_HAUPPAUGE878,  "Hauppauge WinTV" },
+	{ 0x39000070, BTTV_BOARD_HAUPPAUGE878,  "Hauppauge WinTV-D" },
+	{ 0x45000070, BTTV_BOARD_HAUPPAUGEPVR,  "Hauppauge WinTV/PVR" },
+	{ 0xff000070, BTTV_BOARD_OSPREY1x0,     "Osprey-100" },
+	{ 0xff010070, BTTV_BOARD_OSPREY2x0_SVID,"Osprey-200" },
+	{ 0xff020070, BTTV_BOARD_OSPREY500,     "Osprey-500" },
+	{ 0xff030070, BTTV_BOARD_OSPREY2000,    "Osprey-2000" },
+	{ 0xff040070, BTTV_BOARD_OSPREY540,     "Osprey-540" },
+	{ 0xff070070, BTTV_BOARD_OSPREY440,     "Osprey-440" },
+
+	{ 0x00011002, BTTV_BOARD_ATI_TVWONDER,  "ATI TV Wonder" },
+	{ 0x00031002, BTTV_BOARD_ATI_TVWONDERVE,"ATI TV Wonder/VE" },
+
+	{ 0x6606107d, BTTV_BOARD_WINFAST2000,   "Leadtek WinFast TV 2000" },
+	{ 0x6607107d, BTTV_BOARD_WINFASTVC100,  "Leadtek WinFast VC 100" },
+	{ 0x6609107d, BTTV_BOARD_WINFAST2000,   "Leadtek TV 2000 XP" },
+	{ 0x263610b4, BTTV_BOARD_STB2,          "STB TV PCI FM, Gateway P/N 6000704" },
+	{ 0x264510b4, BTTV_BOARD_STB2,          "STB TV PCI FM, Gateway P/N 6000704" },
+	{ 0x402010fc, BTTV_BOARD_GVBCTV3PCI,    "I-O Data Co. GV-BCTV3/PCI" },
+	{ 0x405010fc, BTTV_BOARD_GVBCTV4PCI,    "I-O Data Co. GV-BCTV4/PCI" },
+	{ 0x407010fc, BTTV_BOARD_GVBCTV5PCI,    "I-O Data Co. GV-BCTV5/PCI" },
+	{ 0xd01810fc, BTTV_BOARD_GVBCTV5PCI,    "I-O Data Co. GV-BCTV5/PCI" },
+
+	{ 0x001211bd, BTTV_BOARD_PINNACLE,      "Pinnacle PCTV" },
+	/* some cards ship with byteswapped IDs ... */
+	{ 0x1200bd11, BTTV_BOARD_PINNACLE,      "Pinnacle PCTV [bswap]" },
+	{ 0xff00bd11, BTTV_BOARD_PINNACLE,      "Pinnacle PCTV [bswap]" },
+	/* this seems to happen as well ... */
+	{ 0xff1211bd, BTTV_BOARD_PINNACLE,      "Pinnacle PCTV" },
+
+	{ 0x3000121a, BTTV_BOARD_VOODOOTV_200,  "3Dfx VoodooTV 200" },
+	{ 0x263710b4, BTTV_BOARD_VOODOOTV_FM,   "3Dfx VoodooTV FM" },
+	{ 0x3060121a, BTTV_BOARD_STB2,	  "3Dfx VoodooTV 100/ STB OEM" },
+
+	{ 0x3000144f, BTTV_BOARD_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" },
+	{ 0xa005144f, BTTV_BOARD_MAGICTVIEW063, "CPH06X TView99-Card" },
+	{ 0x3002144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" },
+	{ 0x3005144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" },
+	{ 0x5000144f, BTTV_BOARD_MAGICTVIEW061, "Askey CPH050" },
+	{ 0x300014ff, BTTV_BOARD_MAGICTVIEW061, "TView 99 (CPH061)" },
+	{ 0x300214ff, BTTV_BOARD_PHOEBE_TVMAS,  "Phoebe TV Master (CPH060)" },
+
+	{ 0x00011461, BTTV_BOARD_AVPHONE98,     "AVerMedia TVPhone98" },
+	{ 0x00021461, BTTV_BOARD_AVERMEDIA98,   "AVermedia TVCapture 98" },
+	{ 0x00031461, BTTV_BOARD_AVPHONE98,     "AVerMedia TVPhone98" },
+	{ 0x00041461, BTTV_BOARD_AVERMEDIA98,   "AVerMedia TVCapture 98" },
+	{ 0x03001461, BTTV_BOARD_AVERMEDIA98,   "VDOMATE TV TUNER CARD" },
+
+	{ 0x1117153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue (Philips PAL B/G)" },
+	{ 0x1118153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue (Temic PAL B/G)" },
+	{ 0x1119153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue (Philips PAL I)" },
+	{ 0x111a153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue (Temic PAL I)" },
+
+	{ 0x1123153b, BTTV_BOARD_TERRATVRADIO,  "Terratec TV Radio+" },
+	{ 0x1127153b, BTTV_BOARD_TERRATV,       "Terratec TV+ (V1.05)"    },
+	/* clashes with FlyVideo
+	 *{ 0x18521852, BTTV_BOARD_TERRATV,     "Terratec TV+ (V1.10)"    }, */
+	{ 0x1134153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue (LR102)" },
+	{ 0x1135153b, BTTV_BOARD_TERRATVALUER,  "Terratec TValue Radio" }, /* LR102 */
+	{ 0x5018153b, BTTV_BOARD_TERRATVALUE,   "Terratec TValue" },       /* ?? */
+	{ 0xff3b153b, BTTV_BOARD_TERRATVALUER,  "Terratec TValue Radio" }, /* ?? */
+
+	{ 0x400015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+	{ 0x400a15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+	{ 0x400d15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+	{ 0x401015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+	{ 0x401615b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+
+	{ 0x1430aa00, BTTV_BOARD_PV143,         "Provideo PV143A" },
+	{ 0x1431aa00, BTTV_BOARD_PV143,         "Provideo PV143B" },
+	{ 0x1432aa00, BTTV_BOARD_PV143,         "Provideo PV143C" },
+	{ 0x1433aa00, BTTV_BOARD_PV143,         "Provideo PV143D" },
+	{ 0x1433aa03, BTTV_BOARD_PV143,         "Security Eyes" },
+
+	{ 0x1460aa00, BTTV_BOARD_PV150,         "Provideo PV150A-1" },
+	{ 0x1461aa01, BTTV_BOARD_PV150,         "Provideo PV150A-2" },
+	{ 0x1462aa02, BTTV_BOARD_PV150,         "Provideo PV150A-3" },
+	{ 0x1463aa03, BTTV_BOARD_PV150,         "Provideo PV150A-4" },
+
+	{ 0x1464aa04, BTTV_BOARD_PV150,         "Provideo PV150B-1" },
+	{ 0x1465aa05, BTTV_BOARD_PV150,         "Provideo PV150B-2" },
+	{ 0x1466aa06, BTTV_BOARD_PV150,         "Provideo PV150B-3" },
+	{ 0x1467aa07, BTTV_BOARD_PV150,         "Provideo PV150B-4" },
+
+	{ 0xa132ff00, BTTV_BOARD_IVC100,        "IVC-100"  },
+	{ 0xa1550000, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550001, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550002, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550003, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550100, BTTV_BOARD_IVC200,        "IVC-200G" },
+	{ 0xa1550101, BTTV_BOARD_IVC200,        "IVC-200G" },
+	{ 0xa1550102, BTTV_BOARD_IVC200,        "IVC-200G" },
+	{ 0xa1550103, BTTV_BOARD_IVC200,        "IVC-200G" },
+	{ 0xa1550800, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550801, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550802, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa1550803, BTTV_BOARD_IVC200,        "IVC-200"  },
+	{ 0xa182ff00, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff01, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff02, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff03, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff04, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff05, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff06, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff07, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff08, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff09, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0a, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0b, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0c, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0d, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0e, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xa182ff0f, BTTV_BOARD_IVC120,        "IVC-120G" },
+	{ 0xf0500000, BTTV_BOARD_IVCE8784,      "IVCE-8784" },
+	{ 0xf0500001, BTTV_BOARD_IVCE8784,      "IVCE-8784" },
+	{ 0xf0500002, BTTV_BOARD_IVCE8784,      "IVCE-8784" },
+	{ 0xf0500003, BTTV_BOARD_IVCE8784,      "IVCE-8784" },
+
+	{ 0x41424344, BTTV_BOARD_GRANDTEC,      "GrandTec Multi Capture" },
+	{ 0x01020304, BTTV_BOARD_XGUARD,        "Grandtec Grand X-Guard" },
+
+	{ 0x18501851, BTTV_BOARD_CHRONOS_VS2,   "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+	{ 0xa0501851, BTTV_BOARD_CHRONOS_VS2,   "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+	{ 0x18511851, BTTV_BOARD_FLYVIDEO98EZ,  "FlyVideo 98EZ (LR51)/ CyberMail AV" },
+	{ 0x18521852, BTTV_BOARD_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
+	{ 0x41a0a051, BTTV_BOARD_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" },
+	{ 0x18501f7f, BTTV_BOARD_FLYVIDEO_98,   "Lifeview Flyvideo 98" },
+
+	{ 0x010115cb, BTTV_BOARD_GMV1,          "AG GMV1" },
+	{ 0x010114c7, BTTV_BOARD_MODTEC_205,    "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV" },
+
+	{ 0x10b42636, BTTV_BOARD_HAUPPAUGE878,  "STB ???" },
+	{ 0x217d6606, BTTV_BOARD_WINFAST2000,   "Leadtek WinFast TV 2000" },
+	{ 0xfff6f6ff, BTTV_BOARD_WINFAST2000,   "Leadtek WinFast TV 2000" },
+	{ 0x03116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 311" },
+	{ 0x06116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 611" },
+	{ 0x00790e11, BTTV_BOARD_WINDVR,        "Canopus WinDVR PCI" },
+	{ 0xa0fca1a0, BTTV_BOARD_ZOLTRIX,       "Face to Face Tvmax" },
+	{ 0x82b2aa6a, BTTV_BOARD_SIMUS_GVC1100, "SIMUS GVC1100" },
+	{ 0x146caa0c, BTTV_BOARD_PV951,         "ituner spectra8" },
+	{ 0x200a1295, BTTV_BOARD_PXC200,        "ImageNation PXC200A" },
+
+	{ 0x40111554, BTTV_BOARD_PV_BT878P_9B,  "Prolink Pixelview PV-BT" },
+	{ 0x17de0a01, BTTV_BOARD_KWORLD,        "Mecer TV/FM/Video Tuner" },
+
+	{ 0x01051805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #1" },
+	{ 0x01061805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #2" },
+	{ 0x01071805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #3" },
+	{ 0x01081805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #4" },
+
+	{ 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" },
+
+	{ 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" },
+	{ 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" },
+
+	/* likely broken, vendor id doesn't match the other magic views ...
+	 * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */
+
+	/* Duplicate PCI ID, reconfigure for this board during the eeprom read.
+	* { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB,  "Hauppauge ImpactVCB" }, */
+
+	{ 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2,	"Conceptronic CTVFMi v2"},
+
+	/* DVB cards (using pci function .1 for mpeg data xfer) */
+	{ 0x001c11bd, BTTV_BOARD_PINNACLESAT,   "Pinnacle PCTV Sat" },
+	{ 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+	{ 0x20007063, BTTV_BOARD_PC_HDTV,       "pcHDTV HD-2000 TV"},
+	{ 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" },
+	{ 0x07611461, BTTV_BOARD_AVDVBT_761,    "AverMedia AverTV DVB-T 761" },
+	{ 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" },
+	{ 0x00261822, BTTV_BOARD_TWINHAN_DST,	"DNTV Live! Mini "},
+	{ 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2,	"DViCO FusionHDTV 2" },
+	{ 0x763c008a, BTTV_BOARD_GEOVISION_GV600,	"GeoVision GV-600" },
+	{ 0x18011000, BTTV_BOARD_ENLTV_FM_2,	"Encore ENL TV-FM-2" },
+	{ 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" },
+	{ 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL,	"GeoVision GV-800(S) (slave)" },
+	{ 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL,	"GeoVision GV-800(S) (slave)" },
+	{ 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL,	"GeoVision GV-800(S) (slave)" },
+
+	{ 0x15401830, BTTV_BOARD_PV183,         "Provideo PV183-1" },
+	{ 0x15401831, BTTV_BOARD_PV183,         "Provideo PV183-2" },
+	{ 0x15401832, BTTV_BOARD_PV183,         "Provideo PV183-3" },
+	{ 0x15401833, BTTV_BOARD_PV183,         "Provideo PV183-4" },
+	{ 0x15401834, BTTV_BOARD_PV183,         "Provideo PV183-5" },
+	{ 0x15401835, BTTV_BOARD_PV183,         "Provideo PV183-6" },
+	{ 0x15401836, BTTV_BOARD_PV183,         "Provideo PV183-7" },
+	{ 0x15401837, BTTV_BOARD_PV183,         "Provideo PV183-8" },
+	{ 0x3116f200, BTTV_BOARD_TVT_TD3116,	"Tongwei Video Technology TD-3116" },
+	{ 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
+	{ 0, -1, NULL }
+};
+
+/* ----------------------------------------------------------------------- */
+/* array with description for bt848 / bt878 tv/grabber cards               */
+
+struct tvcard bttv_tvcards[] = {
+	/* ---- card 0x00 ---------------------------------- */
+	[BTTV_BOARD_UNKNOWN] = {
+		.name		= " *** UNKNOWN/GENERIC *** ",
+		.video_inputs	= 4,
+		.svhs		= 2,
+		.muxsel		= MUXSEL(2, 3, 1, 0),
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MIRO] = {
+		.name		= "MIRO PCTV",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 2, 0, 0, 0 },
+		.gpiomute 	= 10,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_HAUPPAUGE] = {
+		.name		= "Hauppauge (bt848)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_STB] = {
+		.name		= "STB, Gateway P/N 6000699 (bt848)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 4, 0, 2, 3 },
+		.gpiomute 	= 1,
+		.no_msp34xx	= 1,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+		.has_radio      = 1,
+	},
+
+	/* ---- card 0x04 ---------------------------------- */
+	[BTTV_BOARD_INTEL] = {
+		.name		= "Intel Create and Share PCI/ Smart Video Recorder III",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= 2,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0 },
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_DIAMOND] = {
+		.name		= "Diamond DTV2000",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 3,
+		.muxsel		= MUXSEL(2, 3, 1, 0),
+		.gpiomux 	= { 0, 1, 0, 1 },
+		.gpiomute 	= 3,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_AVERMEDIA] = {
+		.name		= "AVerMedia TVPhone",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomask	= 0x0f,
+		.gpiomux 	= { 0x0c, 0x04, 0x08, 0x04 },
+		/*                0x04 for some cards ?? */
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= avermedia_tvphone_audio,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_MATRIX_VISION] = {
+		.name		= "MATRIX-Vision MV-Delta",
+		.video_inputs	= 5,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 0, 0),
+		.gpiomux 	= { 0 },
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x08 ---------------------------------- */
+	[BTTV_BOARD_FLYVIDEO] = {
+		.name		= "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xc00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0xc00, 0x800, 0x400 },
+		.gpiomute 	= 0xc00,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TURBOTV] = {
+		.name		= "IMS/IXmicro TurboTV",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 3,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 1, 1, 2, 3 },
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TEMIC_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_HAUPPAUGE878] = {
+		.name		= "Hauppauge (bt878)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x0f, /* old: 7 */
+		.muxsel		= MUXSEL(2, 0, 1, 1),
+		.gpiomux 	= { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MIROPRO] = {
+		.name		= "MIRO PCTV pro",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x3014f,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x20001,0x10001, 0, 0 },
+		.gpiomute 	= 10,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x0c ---------------------------------- */
+	[BTTV_BOARD_ADSTECH_TV] = {
+		.name		= "ADS Technologies Channel Surfer TV (bt848)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 13, 14, 11, 7 },
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_AVERMEDIA98] = {
+		.name		= "AVerMedia TVCapture 98",
+		.video_inputs	= 3,
+		/* .audio_inputs= 4, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 13, 14, 11, 7 },
+		.msp34xx_alt    = 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= avermedia_tv_stereo_audio,
+		.no_gpioirq     = 1,
+	},
+	[BTTV_BOARD_VHX] = {
+		.name		= "Aimslab Video Highway Xtreme (VHX)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */
+		.gpiomute 	= 4,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ZOLTRIX] = {
+		.name		= "Zoltrix TV-Max",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0, 1, 0 },
+		.gpiomute 	= 10,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x10 ---------------------------------- */
+	[BTTV_BOARD_PIXVIEWPLAYTV] = {
+		.name		= "Prolink Pixelview PlayTV (bt878)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x01fe00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		/* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */
+		.gpiomux        = { 0x001e00, 0, 0x018000, 0x014000 },
+		.gpiomute 	= 0x002000,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr     = ADDR_UNSET,
+	},
+	[BTTV_BOARD_WINVIEW_601] = {
+		.name		= "Leadtek WinView 601",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x8300f8,
+		.muxsel		= MUXSEL(2, 3, 1, 1, 0),
+		.gpiomux 	= { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 },
+		.gpiomute 	= 0xcfa007,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.volume_gpio	= winview_volume,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_AVEC_INTERCAP] = {
+		.name		= "AVEC Intercapture",
+		.video_inputs	= 3,
+		/* .audio_inputs= 2, */
+		.svhs		= 2,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 1, 0, 0, 0 },
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_LIFE_FLYKIT] = {
+		.name		= "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x8dff00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0 },
+		.no_msp34xx	= 1,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x14 ---------------------------------- */
+	[BTTV_BOARD_CEI_RAFFLES] = {
+		.name		= "CEI Raffles Card",
+		.video_inputs	= 3,
+		/* .audio_inputs= 3, */
+		.svhs		= 2,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_CONFERENCETV] = {
+		.name		= "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
+		.video_inputs	= 4,
+		/* .audio_inputs= 2,  tuner, line in */
+		.svhs		= 2,
+		.gpiomask	= 0x1800,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0x800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_PHOEBE_TVMAS] = {
+		.name		= "Askey CPH050/ Phoebe Tv Master + FM",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xc00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 1, 0x800, 0x400 },
+		.gpiomute 	= 0xc00,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MODTEC_205] = {
+		.name		= "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= NO_SVHS,
+		.has_dig_in	= 1,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 3, 0), /* input 2 is digital */
+		/* .digital_mode= DIGITAL_MODE_CAMERA, */
+		.gpiomux 	= { 0, 0, 0, 0 },
+		.no_msp34xx	= 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ALPS_TSBB5_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x18 ---------------------------------- */
+	[BTTV_BOARD_MAGICTVIEW061] = {
+		.name		= "Askey CPH05X/06X (bt878) [many vendors]",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xe00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= {0x400, 0x400, 0x400, 0x400 },
+		.gpiomute 	= 0xc00,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+		.has_radio	= 1,  /* not every card has radio */
+	},
+	[BTTV_BOARD_VOBIS_BOOSTAR] = {
+		.name           = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask       = 0x1f0fff,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0x20000, 0x30000, 0x10000, 0 },
+		.gpiomute 	= 0x40000,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= terratv_audio,
+	},
+	[BTTV_BOARD_HAUPPAUG_WCAM] = {
+		.name		= "Hauppauge WinCam newer (bt878)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 0, 1, 1),
+		.gpiomux 	= { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MAXI] = {
+		.name		= "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
+		.video_inputs	= 4,
+		/* .audio_inputs= 2, */
+		.svhs		= 2,
+		.gpiomask	= 0x1800,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0x800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_PHILIPS_SECAM,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x1c ---------------------------------- */
+	[BTTV_BOARD_TERRATV] = {
+		.name           = "Terratec TerraTV+ Version 1.1 (bt878)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x1f0fff,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x20000, 0x30000, 0x10000, 0x00000 },
+		.gpiomute 	= 0x40000,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= terratv_audio,
+		/* GPIO wiring:
+		External 20 pin connector (for Active Radio Upgrade board)
+		gpio00: i2c-sda
+		gpio01: i2c-scl
+		gpio02: om5610-data
+		gpio03: om5610-clk
+		gpio04: om5610-wre
+		gpio05: om5610-stereo
+		gpio06: rds6588-davn
+		gpio07: Pin 7 n.c.
+		gpio08: nIOW
+		gpio09+10: nIOR, nSEL ?? (bt878)
+			gpio09: nIOR (bt848)
+			gpio10: nSEL (bt848)
+		Sound Routing:
+		gpio16: u2-A0 (1st 4052bt)
+		gpio17: u2-A1
+		gpio18: u2-nEN
+		gpio19: u4-A0 (2nd 4052)
+		gpio20: u4-A1
+			u4-nEN - GND
+		Btspy:
+			00000 : Cdrom (internal audio input)
+			10000 : ext. Video audio input
+			20000 : TV Mono
+			a0000 : TV Mono/2
+		1a0000 : TV Stereo
+			30000 : Radio
+			40000 : Mute
+	*/
+
+	},
+	[BTTV_BOARD_PXC200] = {
+		/* Jannik Fritsch <jannik@techfak.uni-bielefeld.de> */
+		.name		= "Imagenation PXC200",
+		.video_inputs	= 5,
+		/* .audio_inputs= 1, */
+		.svhs		= 1, /* was: 4 */
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 0, 0),
+		.gpiomux 	= { 0 },
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.muxsel_hook    = PXC200_muxsel,
+
+	},
+	[BTTV_BOARD_FLYVIDEO_98] = {
+		.name		= "Lifeview FlyVideo 98 LR50",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x1800,  /* 0x8dfe00 */
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0x0800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll            = PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_IPROTV] = {
+		.name		= "Formac iProTV, Formac ProTV I (bt848)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.gpiomask	= 1,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 1, 0, 0, 0 },
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x20 ---------------------------------- */
+	[BTTV_BOARD_INTEL_C_S_PCI] = {
+		.name		= "Intel Create and Share PCI/ Smart Video Recorder III",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= 2,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0 },
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TERRATVALUE] = {
+		.name           = "Terratec TerraTValue Version Bt878",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xffff00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x500, 0, 0x300, 0x900 },
+		.gpiomute 	= 0x900,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_WINFAST2000] = {
+		.name		= "Leadtek WinFast 2000/ WinFast 2000 XP",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		/* TV, CVid, SVid, CVid over SVid connector */
+		.muxsel		= MUXSEL(2, 3, 1, 1, 0),
+		/* Alexander Varakin <avarakin@hotmail.com> [stereo version] */
+		.gpiomask	= 0xb33000,
+		.gpiomux 	= { 0x122000,0x1000,0x0000,0x620000 },
+		.gpiomute 	= 0x800000,
+		/* Audio Routing for "WinFast 2000 XP" (no tv stereo !)
+			gpio23 -- hef4052:nEnable (0x800000)
+			gpio12 -- hef4052:A1
+			gpio13 -- hef4052:A0
+		0x0000: external audio
+		0x1000: FM
+		0x2000: TV
+		0x3000: n.c.
+		Note: There exists another variant "Winfast 2000" with tv stereo !?
+		Note: eeprom only contains FF and pci subsystem id 107d:6606
+		*/
+		.pll		= PLL_28,
+		.has_radio	= 1,
+		.tuner_type	= TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= winfast2000_audio,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_CHRONOS_VS2] = {
+		.name		= "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
+		.video_inputs	= 4,
+		/* .audio_inputs= 3, */
+		.svhs		= 2,
+		.gpiomask	= 0x1800,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0x800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x24 ---------------------------------- */
+	[BTTV_BOARD_TYPHOON_TVIEW] = {
+		.name		= "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
+		.video_inputs	= 4,
+		/* .audio_inputs= 3, */
+		.svhs		= 2,
+		.gpiomask	= 0x1800,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0x800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_PXELVWPLTVPRO] = {
+		.name		= "Prolink PixelView PlayTV pro",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xff,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x21, 0x20, 0x24, 0x2c },
+		.gpiomute 	= 0x29,
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MAGICTVIEW063] = {
+		.name		= "Askey CPH06X TView99",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x551e00,
+		.muxsel		= MUXSEL(2, 3, 1, 0),
+		.gpiomux 	= { 0x551400, 0x551200, 0, 0 },
+		.gpiomute 	= 0x551c00,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_PINNACLE] = {
+		.name		= "Pinnacle PCTV Studio/Rave",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x03000F,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 2, 0xd0001, 0, 0 },
+		.gpiomute 	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x28 ---------------------------------- */
+	[BTTV_BOARD_STB2] = {
+		.name		= "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 7,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 4, 0, 2, 3 },
+		.gpiomute 	= 1,
+		.no_msp34xx	= 1,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+		.has_radio      = 1,
+	},
+	[BTTV_BOARD_AVPHONE98] = {
+		.name		= "AVerMedia TVPhone 98",
+		.video_inputs	= 3,
+		/* .audio_inputs= 4, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 13, 4, 11, 7 },
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio	= 1,
+		.audio_mode_gpio= avermedia_tvphone_audio,
+	},
+	[BTTV_BOARD_PV951] = {
+		.name		= "ProVideo PV951", /* pic16c54 */
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0, 0, 0},
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ONAIR_TV] = {
+		.name		= "Little OnAir TV",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xe00b,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 },
+		.gpiomute 	= 0xff3ffc,
+		.no_msp34xx	= 1,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x2c ---------------------------------- */
+	[BTTV_BOARD_SIGMA_TVII_FM] = {
+		.name		= "Sigma TVII-FM",
+		.video_inputs	= 2,
+		/* .audio_inputs= 1, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 3,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 1, 1, 0, 2 },
+		.gpiomute 	= 3,
+		.no_msp34xx	= 1,
+		.pll		= PLL_NONE,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MATRIX_VISION2] = {
+		.name		= "MATRIX-Vision MV-Delta 2",
+		.video_inputs	= 5,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.gpiomask	= 0,
+		.muxsel		= MUXSEL(2, 3, 1, 0, 0),
+		.gpiomux 	= { 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ZOLTRIX_GENIE] = {
+		.name		= "Zoltrix Genie TV/FM",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xbcf03f,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0xbc803f, 0xbc903f, 0xbcb03f, 0 },
+		.gpiomute 	= 0xbcb03f,
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TEMIC_4039FR5_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TERRATVRADIO] = {
+		.name		= "Terratec TV/Radio+",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x70000,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x20000, 0x30000, 0x10000, 0 },
+		.gpiomute 	= 0x40000,
+		.no_msp34xx	= 1,
+		.pll		= PLL_35,
+		.tuner_type	= TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio	= 1,
+	},
+
+	/* ---- card 0x30 ---------------------------------- */
+	[BTTV_BOARD_DYNALINK] = {
+		.name		= "Askey CPH03x/ Dynalink Magic TView",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= {2,0,0,0 },
+		.gpiomute 	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_GVBCTV3PCI] = {
+		.name		= "IODATA GV-BCTV3/PCI",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x010f00,
+		.muxsel		= MUXSEL(2, 3, 0, 0),
+		.gpiomux 	= {0x10000, 0, 0x10000, 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_ALPS_TSHC6_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= gvbctv3pci_audio,
+	},
+	[BTTV_BOARD_PXELVWPLTVPAK] = {
+		.name		= "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
+		.video_inputs	= 5,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.has_dig_in	= 1,
+		.gpiomask	= 0xAA0000,
+		.muxsel		= MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+		/* .digital_mode= DIGITAL_MODE_CAMERA, */
+		.gpiomux 	= { 0x20000, 0, 0x80000, 0x80000 },
+		.gpiomute 	= 0xa8000,
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote	= 1,
+		/* GPIO wiring: (different from Rev.4C !)
+			GPIO17: U4.A0 (first hef4052bt)
+			GPIO19: U4.A1
+			GPIO20: U5.A1 (second hef4052bt)
+			GPIO21: U4.nEN
+			GPIO22: BT832 Reset Line
+			GPIO23: A5,A0, U5,nEN
+		Note: At i2c=0x8a is a Bt832 chip, which changes to 0x88 after being reset via GPIO22
+		*/
+	},
+	[BTTV_BOARD_EAGLE] = {
+		.name           = "Eagle Wireless Capricorn2 (bt878A)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 7,
+		.muxsel         = MUXSEL(2, 0, 1, 1),
+		.gpiomux        = { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.pll            = PLL_28,
+		.tuner_type     = UNSET /* TUNER_ALPS_TMDH2_NTSC */,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x34 ---------------------------------- */
+	[BTTV_BOARD_PINNACLEPRO] = {
+		/* David Härdeman <david@2gen.com> */
+		.name           = "Pinnacle PCTV Studio Pro",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 3,
+		.gpiomask       = 0x03000F,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 1, 0xd0001, 0, 0 },
+		.gpiomute 	= 10,
+				/* sound path (5 sources):
+				MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable)
+					0= ext. Audio IN
+					1= from MUX2
+					2= Mono TV sound from Tuner
+					3= not connected
+				MUX2 (mask 0x30000):
+					0,2,3= from MSP34xx
+					1= FM stereo Radio from Tuner */
+		.pll            = PLL_28,
+		.tuner_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TVIEW_RDS_FM] = {
+		/* Claas Langbehn <claas@bigfoot.com>,
+		Sven Grothklags <sven@upb.de> */
+		.name		= "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS",
+		.video_inputs	= 4,
+		/* .audio_inputs= 3, */
+		.svhs		= 2,
+		.gpiomask	= 0x1c,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0, 0, 0x10, 8 },
+		.gpiomute 	= 4,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_LIFETEC_9415] = {
+		/* Tim Röstermundt <rosterm@uni-muenster.de>
+		in de.comp.os.unix.linux.hardware:
+			options bttv card=0 pll=1 radio=1 gpiomask=0x18e0
+			gpiomux =0x44c71f,0x44d71f,0,0x44d71f,0x44dfff
+			options tuner type=5 */
+		.name		= "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x18e0,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x0000,0x0800,0x1000,0x1000 },
+		.gpiomute 	= 0x18e0,
+			/* For cards with tda9820/tda9821:
+				0x0000: Tuner normal stereo
+				0x0080: Tuner A2 SAP (second audio program = Zweikanalton)
+				0x0880: Tuner A2 stereo */
+		.pll		= PLL_28,
+		.tuner_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_BESTBUY_EASYTV] = {
+		/* Miguel Angel Alvarez <maacruz@navegalia.com>
+		old Easy TV BT848 version (model CPH031) */
+		.name           = "Askey CPH031/ BESTBUY Easy TV",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0xF,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 2, 0, 0, 0 },
+		.gpiomute 	= 10,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TEMIC_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x38 ---------------------------------- */
+	[BTTV_BOARD_FLYVIDEO_98FM] = {
+		/* Gordon Heydon <gjheydon@bigfoot.com ('98) */
+		.name           = "Lifeview FlyVideo 98FM LR50",
+		.video_inputs   = 4,
+		/* .audio_inputs= 3, */
+		.svhs           = 2,
+		.gpiomask       = 0x1800,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 0x800, 0x1000, 0x1000 },
+		.gpiomute 	= 0x1800,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		/* This is the ultimate cheapo capture card
+		* just a BT848A on a small PCB!
+		* Steve Hosgood <steve@equiinet.com> */
+	[BTTV_BOARD_GRANDTEC] = {
+		.name           = "GrandTec 'Grand Video Capture' (Bt848)",
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = 1,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(3, 1),
+		.gpiomux        = { 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_35,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ASKEY_CPH060] = {
+		/* Daniel Herrington <daniel.herrington@home.com> */
+		.name           = "Askey CPH060/ Phoebe TV Master Only (No FM)",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0xe00,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0x400, 0x400, 0x400, 0x400 },
+		.gpiomute 	= 0x800,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_TEMIC_4036FY5_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ASKEY_CPH03X] = {
+		/* Matti Mottus <mottus@physic.ut.ee> */
+		.name		= "Askey CPH03x TV Capturer",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask       = 0x03000F,
+		.muxsel		= MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 2, 0, 0, 0 },
+		.gpiomute 	= 1,
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_TEMIC_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote	= 1,
+	},
+
+	/* ---- card 0x3c ---------------------------------- */
+	[BTTV_BOARD_MM100PCTV] = {
+		/* Philip Blundell <philb@gnu.org> */
+		.name           = "Modular Technology MM100PCTV",
+		.video_inputs   = 2,
+		/* .audio_inputs= 2, */
+		.svhs		= NO_SVHS,
+		.gpiomask       = 11,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 2, 0, 0, 1 },
+		.gpiomute 	= 8,
+		.pll            = PLL_35,
+		.tuner_type     = TUNER_TEMIC_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_GMV1] = {
+		/* Adrian Cox <adrian@humboldt.co.uk */
+		.name		= "AG Electronics GMV1",
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs		= 1,
+		.gpiomask       = 0xF,
+		.muxsel		= MUXSEL(2, 2),
+		.gpiomux        = { },
+		.no_msp34xx     = 1,
+		.pll		= PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_BESTBUY_EASYTV2] = {
+		/* Miguel Angel Alvarez <maacruz@navegalia.com>
+		new Easy TV BT878 version (model CPH061)
+		special thanks to Informatica Mieres for providing the card */
+		.name           = "Askey CPH061/ BESTBUY Easy TV (bt878)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 2, */
+		.svhs           = 2,
+		.gpiomask       = 0xFF,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 1, 0, 4, 4 },
+		.gpiomute 	= 9,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_ATI_TVWONDER] = {
+		/* Lukas Gebauer <geby@volny.cz> */
+		.name		= "ATI TV-Wonder",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xf03f,
+		.muxsel		= MUXSEL(2, 3, 1, 0),
+		.gpiomux 	= { 0xbffe, 0, 0xbfff, 0 },
+		.gpiomute 	= 0xbffe,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TEMIC_4006FN5_MULTI_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x40 ---------------------------------- */
+	[BTTV_BOARD_ATI_TVWONDERVE] = {
+		/* Lukas Gebauer <geby@volny.cz> */
+		.name		= "ATI TV-Wonder VE",
+		.video_inputs	= 2,
+		/* .audio_inputs= 1, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 1,
+		.muxsel		= MUXSEL(2, 3, 0, 1),
+		.gpiomux 	= { 0, 0, 1, 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TEMIC_4006FN5_MULTI_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_FLYVIDEO2000] = {
+		/* DeeJay <deejay@westel900.net (2000S) */
+		.name           = "Lifeview FlyVideo 2000S LR90",
+		.video_inputs   = 3,
+		/* .audio_inputs= 3, */
+		.svhs           = 2,
+		.gpiomask	= 0x18e0,
+		.muxsel		= MUXSEL(2, 3, 0, 1),
+				/* Radio changed from 1e80 to 0x800 to make
+				FlyVideo2000S in .hu happy (gm)*/
+				/* -dk-???: set mute=0x1800 for tda9874h daughterboard */
+		.gpiomux 	= { 0x0000,0x0800,0x1000,0x1000 },
+		.gpiomute 	= 0x1800,
+		.audio_mode_gpio= fv2000s_audio,
+		.no_msp34xx	= 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TERRATVALUER] = {
+		.name		= "Terratec TValueRadio",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0xffff00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x500, 0x500, 0x300, 0x900 },
+		.gpiomute 	= 0x900,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_GVBCTV4PCI] = {
+		/* TANAKA Kei <peg00625@nifty.com> */
+		.name           = "IODATA GV-BCTV4/PCI",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x010f00,
+		.muxsel         = MUXSEL(2, 3, 0, 0),
+		.gpiomux        = {0x10000, 0, 0x10000, 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_SHARP_2U5JF5540_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= gvbctv3pci_audio,
+	},
+
+	/* ---- card 0x44 ---------------------------------- */
+	[BTTV_BOARD_VOODOOTV_FM] = {
+		.name           = "3Dfx VoodooTV FM (Euro)",
+		/* try "insmod msp3400 simple=0" if you have
+		* sound problems with this card. */
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0x4f8a00,
+		/* 0x100000: 1=MSP enabled (0=disable again)
+		* 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+		.gpiomux        = {0x947fff, 0x987fff,0x947fff,0x947fff },
+		.gpiomute 	= 0x947fff,
+		/* tvtuner, radio,   external,internal, mute,  stereo
+		* tuner, Composit, SVid, Composit-on-Svid-adapter */
+		.muxsel         = MUXSEL(2, 3, 0, 1),
+		.tuner_type     = TUNER_MT2032,
+		.tuner_addr	= ADDR_UNSET,
+		.pll		= PLL_28,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_VOODOOTV_200] = {
+		.name           = "VoodooTV 200 (USA)",
+		/* try "insmod msp3400 simple=0" if you have
+		* sound problems with this card. */
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0x4f8a00,
+		/* 0x100000: 1=MSP enabled (0=disable again)
+		* 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+		.gpiomux        = {0x947fff, 0x987fff,0x947fff,0x947fff },
+		.gpiomute 	= 0x947fff,
+		/* tvtuner, radio,   external,internal, mute,  stereo
+		* tuner, Composit, SVid, Composit-on-Svid-adapter */
+		.muxsel         = MUXSEL(2, 3, 0, 1),
+		.tuner_type     = TUNER_MT2032,
+		.tuner_addr	= ADDR_UNSET,
+		.pll		= PLL_28,
+		.has_radio	= 1,
+	},
+	[BTTV_BOARD_AIMMS] = {
+		/* Philip Blundell <pb@nexus.co.uk> */
+		.name           = "Active Imaging AIMMS",
+		.video_inputs   = 1,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+		.muxsel         = MUXSEL(2),
+		.gpiomask       = 0
+	},
+	[BTTV_BOARD_PV_BT878P_PLUS] = {
+		/* Tomasz Pyra <hellfire@sedez.iq.pl> */
+		.name           = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)",
+		.video_inputs   = 3,
+		/* .audio_inputs= 4, */
+		.svhs           = 2,
+		.gpiomask       = 15,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */
+		.gpiomute 	= 13,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_LG_PAL_I_FM,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+		/* GPIO wiring:
+			GPIO0: U4.A0 (hef4052bt)
+			GPIO1: U4.A1
+			GPIO2: U4.A1 (second hef4052bt)
+			GPIO3: U4.nEN, U5.A0, A5.nEN
+			GPIO8-15: vrd866b ?
+		*/
+	},
+	[BTTV_BOARD_FLYVIDEO98EZ] = {
+		.name		= "Lifeview FlyVideo 98EZ (capture only) LR51",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= 2,
+		/* AV1, AV2, SVHS, CVid adapter on SVHS */
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.pll		= PLL_28,
+		.no_msp34xx	= 1,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+	/* ---- card 0x48 ---------------------------------- */
+	[BTTV_BOARD_PV_BT878P_9B] = {
+		/* Dariusz Kowalewski <darekk@automex.pl> */
+		.name		= "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x3f,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 0x01, 0x00, 0x03, 0x03 },
+		.gpiomute 	= 0x09,
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.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:
+			GPIO0: A0 hef4052
+			GPIO1: A1 hef4052
+			GPIO3: nEN hef4052
+			GPIO8-15: vrd866b
+			GPIO20,22,23: R30,R29,R28
+		*/
+	},
+	[BTTV_BOARD_SENSORAY311_611] = {
+		/* Clay Kunz <ckunz@mail.arc.nasa.gov> */
+		/* you must jumper JP5 for the 311 card (PC/104+) to work */
+		.name           = "Sensoray 311/611",
+		.video_inputs   = 5,
+		/* .audio_inputs= 0, */
+		.svhs           = 4,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 3, 1, 0, 0),
+		.gpiomux        = { 0 },
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_RV605] = {
+		/* Miguel Freitas <miguel@cetuc.puc-rio.br> */
+		.name           = "RemoteVision MX (RV605)",
+		.video_inputs   = 16,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0x00,
+		.gpiomask2      = 0x07ff,
+		.muxsel         = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+		.no_msp34xx     = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.muxsel_hook    = rv605_muxsel,
+	},
+	[BTTV_BOARD_POWERCLR_MTV878] = {
+		.name           = "Powercolor MTV878/ MTV878R/ MTV878F",
+		.video_inputs   = 3,
+		/* .audio_inputs= 2, */
+		.svhs           = 2,
+		.gpiomask       = 0x1C800F,  /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */
+		.muxsel         = MUXSEL(2, 1, 1),
+		.gpiomux        = { 0, 1, 2, 2 },
+		.gpiomute 	= 4,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.pll		= PLL_28,
+		.has_radio	= 1,
+	},
+
+	/* ---- card 0x4c ---------------------------------- */
+	[BTTV_BOARD_WINDVR] = {
+		/* Masaki Suzuki <masaki@btree.org> */
+		.name           = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x140007,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= windvr_audio,
+	},
+	[BTTV_BOARD_GRANDTEC_MULTI] = {
+		.name           = "GrandTec Multi Capture Card (Bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_KWORLD] = {
+		.name           = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
+		.video_inputs   = 4,
+		/* .audio_inputs= 3, */
+		.svhs           = 2,
+		.gpiomask       = 7,
+		/* Tuner, SVid, SVHS, SVid to SVHS connector */
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio!
+						* This card lacks external Audio In, so we mute it on Ext. & Int.
+						* The PCB can take a sbx1637/sbx1673, wiring unknown.
+						* This card lacks PCI subsystem ID, sigh.
+						* gpiomux =1: lower volume, 2+3: mute
+						* btwincap uses 0x80000/0x80003
+						*/
+		.gpiomute 	= 4,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		/* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and
+		radio signal strength indicators work fine. */
+		.has_radio	= 1,
+		/* GPIO Info:
+			GPIO0,1:   HEF4052 A0,A1
+			GPIO2:     HEF4052 nENABLE
+			GPIO3-7:   n.c.
+			GPIO8-13:  IRDC357 data0-5 (data6 n.c. ?) [chip not present on my card]
+			GPIO14,15: ??
+			GPIO16-21: n.c.
+			GPIO22,23: ??
+			??       : mtu8b56ep microcontroller for IR (GPIO wiring unknown)*/
+	},
+	[BTTV_BOARD_DSP_TCVIDEO] = {
+		/* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */
+		.name           = "DSP Design TCVIDEO",
+		.video_inputs   = 4,
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.pll            = PLL_28,
+		.tuner_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+		/* ---- card 0x50 ---------------------------------- */
+	[BTTV_BOARD_HAUPPAUGEPVR] = {
+		.name           = "Hauppauge WinTV PVR",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel         = MUXSEL(2, 0, 1, 1),
+		.pll            = PLL_28,
+		.tuner_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+
+		.gpiomask       = 7,
+		.gpiomux        = {7},
+	},
+	[BTTV_BOARD_GVBCTV5PCI] = {
+		.name           = "IODATA GV-BCTV5/PCI",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x0f0f80,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = {0x030000, 0x010000, 0, 0 },
+		.gpiomute 	= 0x020000,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= gvbctv5pci_audio,
+		.has_radio      = 1,
+	},
+	[BTTV_BOARD_OSPREY1x0] = {
+		.name           = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */
+		.video_inputs   = 4,                  /* id-inputs-clock */
+		/* .audio_inputs= 0, */
+		.svhs           = 3,
+		.muxsel         = MUXSEL(3, 2, 0, 1),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY1x0_848] = {
+		.name           = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */
+		.video_inputs   = 3,
+		/* .audio_inputs= 0, */
+		.svhs           = 2,
+		.muxsel         = MUXSEL(2, 3, 1),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+
+		/* ---- card 0x54 ---------------------------------- */
+	[BTTV_BOARD_OSPREY101_848] = {
+		.name           = "Osprey 101 (848)", /* 0x05-40C0-C1 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(3, 1),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY1x1] = {
+		.name           = "Osprey 101/151",       /* 0x1(4|5)-0004-C4 */
+		.video_inputs   = 1,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(0),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY1x1_SVID] = {
+		.name           = "Osprey 101/151 w/ svid",  /* 0x(16|17|20)-00C4-C1 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(0, 1),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY2xx] = {
+		.name           = "Osprey 200/201/250/251",  /* 0x1(8|9|E|F)-0004-C4 */
+		.video_inputs   = 1,
+		/* .audio_inputs= 1, */
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(0),
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+
+		/* ---- card 0x58 ---------------------------------- */
+	[BTTV_BOARD_OSPREY2x0_SVID] = {
+		.name           = "Osprey 200/250",   /* 0x1(A|B)-00C4-C1 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 1, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(0, 1),
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY2x0] = {
+		.name           = "Osprey 210/220/230",   /* 0x1(A|B)-04C0-C1 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 1, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(2, 3),
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY500] = {
+		.name           = "Osprey 500",   /* 500 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 1, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(2, 3),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	[BTTV_BOARD_OSPREY540] = {
+		.name           = "Osprey 540",   /* 540 */
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+
+		/* ---- card 0x5C ---------------------------------- */
+	[BTTV_BOARD_OSPREY2000] = {
+		.name           = "Osprey 2000",  /* 2000 */
+		.video_inputs   = 2,
+		/* .audio_inputs= 1, */
+		.svhs           = 1,
+		.muxsel         = MUXSEL(2, 3),
+		.pll            = PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,      /* must avoid, conflicts with the bt860 */
+	},
+	[BTTV_BOARD_IDS_EAGLE] = {
+		/* M G Berberich <berberic@forwiss.uni-passau.de> */
+		.name           = "IDS Eagle",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 2, 2, 2),
+		.muxsel_hook    = eagle_muxsel,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_PINNACLESAT] = {
+		.name           = "Pinnacle PCTV Sat",
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.muxsel         = MUXSEL(3, 1),
+		.pll            = PLL_28,
+		.no_gpioirq     = 1,
+		.has_dvb        = 1,
+	},
+	[BTTV_BOARD_FORMAC_PROTV] = {
+		.name           = "Formac ProTV II (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 3,
+		.gpiomask       = 2,
+		/* TV, Comp1, Composite over SVID con, SVID */
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 2, 2, 0, 0 },
+		.pll            = PLL_28,
+		.has_radio      = 1,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+	/* sound routing:
+		GPIO=0x00,0x01,0x03: mute (?)
+		0x02: both TV and radio (tuner: FM1216/I)
+		The card has onboard audio connectors labeled "cdrom" and "board",
+		not soldered here, though unknown wiring.
+		Card lacks: external audio in, pci subsystem id.
+	*/
+	},
+
+		/* ---- card 0x60 ---------------------------------- */
+	[BTTV_BOARD_MACHTV] = {
+		.name           = "MachTV",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 7,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 3},
+		.gpiomute 	= 4,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_EURESYS_PICOLO] = {
+		.name           = "Euresys Picolo",
+		.video_inputs   = 3,
+		/* .audio_inputs= 0, */
+		.svhs           = 2,
+		.gpiomask       = 0,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.muxsel         = MUXSEL(2, 0, 1),
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_PV150] = {
+		/* Luc Van Hoeylandt <luc@e-magic.be> */
+		.name           = "ProVideo PV150", /* 0x4f */
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 3),
+		.gpiomux        = { 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_AD_TVK503] = {
+		/* Hiroshi Takekawa <sian@big.or.jp> */
+		/* This card lacks subsystem ID */
+		.name           = "AD-TVK503", /* 0x63 */
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x001e8007,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		/*                  Tuner, Radio, external, internal, off,  on */
+		.gpiomux        = { 0x08,  0x0f,  0x0a,     0x08 },
+		.gpiomute 	= 0x0f,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.audio_mode_gpio= adtvk503_audio,
+	},
+
+		/* ---- card 0x64 ---------------------------------- */
+	[BTTV_BOARD_HERCULES_SM_TV] = {
+		.name           = "Hercules Smart TV Stereo",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		/* Notes:
+		- card lacks subsystem ID
+		- stereo variant w/ daughter board with tda9874a @0xb0
+		- Audio Routing:
+			always from tda9874 independent of GPIO (?)
+			external line in: unknown
+		- Other chips: em78p156elp @ 0x96 (probably IR remote control)
+			hef4053 (instead 4052) for unknown function
+		*/
+	},
+	[BTTV_BOARD_PACETV] = {
+		.name           = "Pace TV & Radio Card",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		/* Tuner, CVid, SVid, CVid over SVid connector */
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomask       = 0,
+		.no_tda7432     = 1,
+		.tuner_type     = TUNER_PHILIPS_PAL_I,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio      = 1,
+		.pll            = PLL_28,
+		/* Bt878, Bt832, FI1246 tuner; no pci subsystem id
+		only internal line out: (4pin header) RGGL
+		Radio must be decoded by msp3410d (not routed through)*/
+		/*
+		.digital_mode   = DIGITAL_MODE_CAMERA,  todo!
+		*/
+	},
+	[BTTV_BOARD_IVC200] = {
+		/* Chris Willing <chris@vislab.usyd.edu.au> */
+		.name           = "IVC-200",
+		.video_inputs   = 1,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0xdf,
+		.muxsel         = MUXSEL(2),
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_IVCE8784] = {
+		.name           = "IVCE-8784",
+		.video_inputs   = 1,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr     = ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0xdf,
+		.muxsel         = MUXSEL(2),
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_XGUARD] = {
+		.name           = "Grand X-Guard / Trust 814PCI",
+		.video_inputs   = 16,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.gpiomask2      = 0xff,
+		.muxsel         = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0),
+		.muxsel_hook    = xguard_muxsel,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.pll            = PLL_28,
+	},
+
+		/* ---- card 0x68 ---------------------------------- */
+	[BTTV_BOARD_NEBULA_DIGITV] = {
+		.name           = "Nebula Electronics DigiTV",
+		.video_inputs   = 1,
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.has_dvb        = 1,
+		.has_remote	= 1,
+		.gpiomask	= 0x1b,
+		.no_gpioirq     = 1,
+	},
+	[BTTV_BOARD_PV143] = {
+		/* Jorge Boncompte - DTI2 <jorge@dti2.net> */
+		.name           = "ProVideo PV143",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
+		/* M.Klahr@phytec.de */
+		.name           = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = 3,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_VD009X1_VD011_COMBI] = {
+		.name           = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = 3,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+
+		/* ---- card 0x6c ---------------------------------- */
+	[BTTV_BOARD_VD009_MINIDIN] = {
+		.name           = "PHYTEC VD-009 MiniDIN (bt878)",
+		.video_inputs   = 10,
+		/* .audio_inputs= 0, */
+		.svhs           = 9,
+		.gpiomask       = 0x00,
+		.gpiomask2      = 0x03, /* used for external vodeo mux */
+		.muxsel         = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0),
+		.muxsel_hook	= phytec_muxsel,
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_VD009_COMBI] = {
+		.name           = "PHYTEC VD-009 Combi (bt878)",
+		.video_inputs   = 10,
+		/* .audio_inputs= 0, */
+		.svhs           = 9,
+		.gpiomask       = 0x00,
+		.gpiomask2      = 0x03, /* used for external vodeo mux */
+		.muxsel         = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1),
+		.muxsel_hook	= phytec_muxsel,
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_IVC100] = {
+		.name           = "IVC-100",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0xdf,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_IVC120] = {
+		/* IVC-120G - Alan Garfield <alan@fromorbit.com> */
+		.name           = "IVC-120G",
+		.video_inputs   = 16,
+		/* .audio_inputs= 0, */
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,   /* card has no svhs */
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+		.muxsel_hook    = ivc120_muxsel,
+		.pll            = PLL_28,
+	},
+
+		/* ---- card 0x70 ---------------------------------- */
+	[BTTV_BOARD_PC_HDTV] = {
+		.name           = "pcHDTV HD-2000 TV",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.tuner_type     = TUNER_PHILIPS_FCV1236D,
+		.tuner_addr	= ADDR_UNSET,
+		.has_dvb        = 1,
+	},
+	[BTTV_BOARD_TWINHAN_DST] = {
+		.name           = "Twinhan DST + clones",
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.no_video       = 1,
+		.has_dvb        = 1,
+	},
+	[BTTV_BOARD_WINFASTVC100] = {
+		.name           = "Winfast VC100",
+		.video_inputs   = 3,
+		/* .audio_inputs= 0, */
+		.svhs           = 1,
+		/* Vid In, SVid In, Vid over SVid in connector */
+		.muxsel		= MUXSEL(3, 1, 1, 3),
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+	},
+	[BTTV_BOARD_TEV560] = {
+		.name           = "Teppro TEV-560/InterVision IV-560",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 3,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 1, 1, 1, 1 },
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_35,
+	},
+
+		/* ---- card 0x74 ---------------------------------- */
+	[BTTV_BOARD_SIMUS_GVC1100] = {
+		.name           = "SIMUS GVC1100",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+		.muxsel         = MUXSEL(2, 2, 2, 2),
+		.gpiomask       = 0x3F,
+		.muxsel_hook    = gvc1100_muxsel,
+	},
+	[BTTV_BOARD_NGSTV_PLUS] = {
+		/* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */
+		.name           = "NGS NGSTV+",
+		.video_inputs   = 3,
+		.svhs           = 2,
+		.gpiomask       = 0x008007,
+		.muxsel         = MUXSEL(2, 3, 0, 0),
+		.gpiomux        = { 0, 0, 0, 0 },
+		.gpiomute 	= 0x000003,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_LMLBT4] = {
+		/* http://linuxmedialabs.com */
+		.name           = "LMLBT4",
+		.video_inputs   = 4, /* IN1,IN2,IN3,IN4 */
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TEKRAM_M205] = {
+		/* Helmroos Harri <harri.helmroos@pp.inet.fi> */
+		.name           = "Tekram M205 PRO",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = 2,
+		.gpiomask       = 0x68,
+		.muxsel         = MUXSEL(2, 3, 1),
+		.gpiomux        = { 0x68, 0x68, 0x61, 0x61 },
+		.pll            = PLL_28,
+	},
+
+		/* ---- card 0x78 ---------------------------------- */
+	[BTTV_BOARD_CONTVFMI] = {
+		/* Javier Cendan Ares <jcendan@lycos.es> */
+		/* bt878 TV + FM without subsystem ID */
+		.name           = "Conceptronic CONTVFMi",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x008007,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 2 },
+		.gpiomute 	= 3,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+		.has_radio      = 1,
+	},
+	[BTTV_BOARD_PICOLO_TETRA_CHIP] = {
+		/*Eric DEBIEF <debief@telemsa.com>*/
+		/*EURESYS Picolo Tetra : 4 Conexant Fusion 878A, no audio, video input set with analog multiplexers GPIO controlled*/
+		/* adds picolo_tetra_muxsel(), picolo_tetra_init(), the following declaration strucure, and #define BTTV_BOARD_PICOLO_TETRA_CHIP*/
+		/*0x79 in bttv.h*/
+		.name           = "Euresys Picolo Tetra",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.gpiomask2      = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		/*878A input is always MUX0, see above.*/
+		.muxsel         = MUXSEL(2, 2, 2, 2),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.muxsel_hook    = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_SPIRIT_TV] = {
+		/* Spirit TV Tuner from http://spiritmodems.com.au */
+		/* Stafford Goodsell <surge@goliath.homeunix.org> */
+		.name           = "Spirit TV Tuner",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x0000000f,
+		.muxsel         = MUXSEL(2, 1, 1),
+		.gpiomux        = { 0x02, 0x00, 0x00, 0x00 },
+		.tuner_type     = TUNER_TEMIC_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.no_msp34xx     = 1,
+	},
+	[BTTV_BOARD_AVDVBT_771] = {
+		/* Wolfram Joost <wojo@frokaschwei.de> */
+		.name           = "AVerMedia AVerTV DVB-T 771",
+		.video_inputs   = 2,
+		.svhs           = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.muxsel         = MUXSEL(3, 3),
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.pll            = PLL_28,
+		.has_dvb        = 1,
+		.no_gpioirq     = 1,
+		.has_remote     = 1,
+	},
+		/* ---- card 0x7c ---------------------------------- */
+	[BTTV_BOARD_AVDVBT_761] = {
+		/* Matt Jesson <dvb@jesson.eclipse.co.uk> */
+		/* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */
+		.name           = "AverMedia AverTV DVB-T 761",
+		.video_inputs   = 2,
+		.svhs           = 1,
+		.muxsel         = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.has_dvb        = 1,
+		.no_gpioirq     = 1,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_MATRIX_VISIONSQ] = {
+		/* andre.schwarz@matrix-vision.de */
+		.name		= "MATRIX Vision Sigma-SQ",
+		.video_inputs	= 16,
+		/* .audio_inputs= 0, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x0,
+		.muxsel		= MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3),
+		.muxsel_hook	= sigmaSQ_muxsel,
+		.gpiomux	= { 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MATRIX_VISIONSLC] = {
+		/* andre.schwarz@matrix-vision.de */
+		.name		= "MATRIX Vision Sigma-SLC",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x0,
+		.muxsel		= MUXSEL(2, 2, 2, 2),
+		.muxsel_hook	= sigmaSLC_muxsel,
+		.gpiomux	= { 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		/* BTTV_BOARD_APAC_VIEWCOMP */
+	[BTTV_BOARD_APAC_VIEWCOMP] = {
+		/* Attila Kondoros <attila.kondoros@chello.hu> */
+		/* bt878 TV + FM 0x00000000 subsystem ID */
+		.name           = "APAC Viewcomp 878(AMAX)",
+		.video_inputs   = 2,
+		/* .audio_inputs= 1, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0xFF,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 2, 0, 0, 0 },
+		.gpiomute 	= 10,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,   /* miniremote works, see ir-kbd-gpio.c */
+		.has_radio      = 1,   /* not every card has radio */
+	},
+
+		/* ---- card 0x80 ---------------------------------- */
+	[BTTV_BOARD_DVICO_DVBT_LITE] = {
+		/* Chris Pascoe <c.pascoe@itee.uq.edu.au> */
+		.name           = "DViCO FusionHDTV DVB-T Lite",
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.pll            = PLL_28,
+		.no_video       = 1,
+		.has_dvb        = 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_VGEAR_MYVCD] = {
+		/* Steven <photon38@pchome.com.tw> */
+		.name           = "V-Gear MyVCD",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x3f,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.gpiomux        = {0x31, 0x31, 0x31, 0x31 },
+		.gpiomute 	= 0x31,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio      = 0,
+	},
+	[BTTV_BOARD_SUPER_TV] = {
+		/* Rick C <cryptdragoon@gmail.com> */
+		.name           = "Super TV Tuner",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+		.gpiomask       = 0x008007,
+		.gpiomux        = { 0, 0x000001,0,0 },
+		.has_radio      = 1,
+	},
+	[BTTV_BOARD_TIBET_CS16] = {
+		/* Chris Fanning <video4linux@haydon.net> */
+		.name           = "Tibet Systems 'Progress DVR' CS16",
+		.video_inputs   = 16,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+		.pll		= PLL_28,
+		.no_msp34xx     = 1,
+		.no_tda7432	= 1,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.muxsel_hook    = tibetCS16_muxsel,
+	},
+	[BTTV_BOARD_KODICOM_4400R] = {
+		/* Bill Brack <wbrack@mmm.com.hk> */
+		/*
+		* Note that, because of the card's wiring, the "master"
+		* BT878A chip (i.e. the one which controls the analog switch
+		* and must use this card type) is the 2nd one detected.  The
+		* other 3 chips should use card type 0x85, whose description
+		* follows this one.  There is a EEPROM on the card (which is
+		* connected to the I2C of one of those other chips), but is
+		* not currently handled.  There is also a facility for a
+		* "monitor", which is also not currently implemented.
+		*/
+		.name           = "Kodicom 4400R (master)",
+		.video_inputs	= 16,
+		/* .audio_inputs= 0, */
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs		= NO_SVHS,
+		/* GPIO bits 0-9 used for analog switch:
+		*   00 - 03:	camera selector
+		*   04 - 06:	channel (controller) selector
+		*   07:	data (1->on, 0->off)
+		*   08:	strobe
+		*   09:	reset
+		* bit 16 is input from sync separator for the channel
+		*/
+		.gpiomask	= 0x0003ff,
+		.no_gpioirq     = 1,
+		.muxsel		= MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+		.pll		= PLL_28,
+		.no_msp34xx	= 1,
+		.no_tda7432	= 1,
+		.muxsel_hook	= kodicom4400r_muxsel,
+	},
+	[BTTV_BOARD_KODICOM_4400R_SL] = {
+		/* Bill Brack <wbrack@mmm.com.hk> */
+		/* Note that, for reasons unknown, the "master" BT878A chip (i.e. the
+		* one which controls the analog switch, and must use the card type)
+		* is the 2nd one detected.  The other 3 chips should use this card
+		* type
+		*/
+		.name		= "Kodicom 4400R (slave)",
+		.video_inputs	= 16,
+		/* .audio_inputs= 0, */
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x010000,
+		.no_gpioirq     = 1,
+		.muxsel		= MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+		.pll		= PLL_28,
+		.no_msp34xx	= 1,
+		.no_tda7432	= 1,
+		.muxsel_hook	= kodicom4400r_muxsel,
+	},
+		/* ---- card 0x86---------------------------------- */
+	[BTTV_BOARD_ADLINK_RTV24] = {
+		/* Michael Henson <mhenson@clarityvi.com> */
+		/* Adlink RTV24 with special unlock codes */
+		.name           = "Adlink RTV24",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.tuner_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.pll            = PLL_28,
+	},
+		/* ---- card 0x87---------------------------------- */
+	[BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = {
+		/* Michael Krufky <mkrufky@m1k.net> */
+		.name           = "DViCO FusionHDTV 5 Lite",
+		.tuner_type     = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+		.tuner_addr	= ADDR_UNSET,
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel		= MUXSEL(2, 3, 1),
+		.gpiomask       = 0x00e00007,
+		.gpiomux        = { 0x00400005, 0, 0x00000001, 0 },
+		.gpiomute 	= 0x00c00007,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+		.has_dvb        = 1,
+	},
+		/* ---- card 0x88---------------------------------- */
+	[BTTV_BOARD_ACORP_Y878F] = {
+		/* Mauro Carvalho Chehab <mchehab@infradead.org> */
+		.name		= "Acorp Y878F",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x01fe00,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0x001e00, 0, 0x018000, 0x014000 },
+		.gpiomute 	= 0x002000,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_YMEC_TVF66T5_B_DFF,
+		.tuner_addr	= 0xc1 >>1,
+		.has_radio	= 1,
+	},
+		/* ---- card 0x89 ---------------------------------- */
+	[BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = {
+		.name           = "Conceptronic CTVFMi v2",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x001c0007,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 2 },
+		.gpiomute 	= 3,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_TENA_9533_DI,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote     = 1,
+		.has_radio      = 1,
+	},
+		/* ---- card 0x8a ---------------------------------- */
+	[BTTV_BOARD_PV_BT878P_2E] = {
+		.name		= "Prolink Pixelview PV-BT878P+ (Rev.2E)",
+		.video_inputs	= 5,
+		/* .audio_inputs= 1, */
+		.svhs		= 3,
+		.has_dig_in	= 1,
+		.gpiomask	= 0x01fe00,
+		.muxsel		= MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+		/* .digital_mode= DIGITAL_MODE_CAMERA, */
+		.gpiomux	= { 0x00400, 0x10400, 0x04400, 0x80000 },
+		.gpiomute	= 0x12400,
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_LG_PAL_FM,
+		.tuner_addr	= ADDR_UNSET,
+		.has_remote	= 1,
+	},
+		/* ---- card 0x8b ---------------------------------- */
+	[BTTV_BOARD_PV_M4900] = {
+		/* Sérgio Fortier <sergiofortier@yahoo.com.br> */
+		.name           = "Prolink PixelView PlayTV MPEG2 PV-M4900",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x3f,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0x21, 0x20, 0x24, 0x2c },
+		.gpiomute 	= 0x29,
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_YMEC_TVF_5533MF,
+		.tuner_addr     = ADDR_UNSET,
+		.has_radio      = 1,
+		.has_remote     = 1,
+	},
+		/* ---- card 0x8c ---------------------------------- */
+	/* Has four Bt878 chips behind a PCI bridge, each chip has:
+	     one external BNC composite input (mux 2)
+	     three internal composite inputs (unknown muxes)
+	     an 18-bit stereo A/D (CS5331A), which has:
+	       one external stereo unblanced (RCA) audio connection
+	       one (or 3?) internal stereo balanced (XLR) audio connection
+	       input is selected via gpio to a 14052B mux
+		 (mask=0x300, unbal=0x000, bal=0x100, ??=0x200,0x300)
+	       gain is controlled via an X9221A chip on the I2C bus @0x28
+	       sample rate is controlled via gpio to an MK1413S
+		 (mask=0x3, 32kHz=0x0, 44.1kHz=0x1, 48kHz=0x2, ??=0x3)
+	     There is neither a tuner nor an svideo input. */
+	[BTTV_BOARD_OSPREY440]  = {
+		.name           = "Osprey 440",
+		.video_inputs   = 4,
+		/* .audio_inputs= 2, */
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */
+		.gpiomask	= 0x303,
+		.gpiomute	= 0x000, /* int + 32kHz */
+		.gpiomux	= { 0, 0, 0x000, 0x100},
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr     = ADDR_UNSET,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+		/* ---- card 0x8d ---------------------------------- */
+	[BTTV_BOARD_ASOUND_SKYEYE] = {
+		.name		= "Asound Skyeye PCTV",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 15,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 2, 0, 0, 0 },
+		.gpiomute 	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_PHILIPS_NTSC,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		/* ---- card 0x8e ---------------------------------- */
+	[BTTV_BOARD_SABRENT_TVFM] = {
+		.name		= "Sabrent TV-FM (bttv version)",
+		.video_inputs	= 3,
+		/* .audio_inputs= 1, */
+		.svhs		= 2,
+		.gpiomask	= 0x108007,
+		.muxsel		= MUXSEL(2, 3, 1, 1),
+		.gpiomux 	= { 100000, 100002, 100002, 100000 },
+		.no_msp34xx	= 1,
+		.no_tda7432     = 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_TNF_5335MF,
+		.tuner_addr	= ADDR_UNSET,
+		.has_radio      = 1,
+	},
+	/* ---- card 0x8f ---------------------------------- */
+	[BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = {
+		.name		= "Hauppauge ImpactVCB (bt878)",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x0f, /* old: 7 */
+		.muxsel		= MUXSEL(0, 1, 3, 2), /* Composite 0-3 */
+		.no_msp34xx	= 1,
+		.no_tda7432     = 1,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_MACHTV_MAGICTV] = {
+		/* Julian Calaby <julian.calaby@gmail.com>
+		 * Slightly different from original MachTV definition (0x60)
+
+		 * FIXME: RegSpy says gpiomask should be "0x001c800f", but it
+		 * stuffs up remote chip. Bug is a pin on the jaecs is not set
+		 * properly (methinks) causing no keyup bits being set */
+
+		.name           = "MagicTV", /* rebranded MachTV */
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 7,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 3 },
+		.gpiomute 	= 4,
+		.tuner_type     = TUNER_TEMIC_4009FR5_PAL,
+		.tuner_addr     = ADDR_UNSET,
+		.pll            = PLL_28,
+		.has_radio      = 1,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_SSAI_SECURITY] = {
+		.name		= "SSAI Security Video Interface",
+		.video_inputs	= 4,
+		/* .audio_inputs= 0, */
+		.svhs		= NO_SVHS,
+		.muxsel		= MUXSEL(0, 1, 2, 3),
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_SSAI_ULTRASOUND] = {
+		.name		= "SSAI Ultrasound Video Interface",
+		.video_inputs	= 2,
+		/* .audio_inputs= 0, */
+		.svhs		= 1,
+		.muxsel		= MUXSEL(2, 0, 1, 3),
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	/* ---- card 0x94---------------------------------- */
+	[BTTV_BOARD_DVICO_FUSIONHDTV_2] = {
+		.name           = "DViCO FusionHDTV 2",
+		.tuner_type     = TUNER_PHILIPS_FCV1236D,
+		.tuner_addr	= ADDR_UNSET,
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.muxsel		= MUXSEL(2, 3, 1),
+		.gpiomask       = 0x00e00007,
+		.gpiomux        = { 0x00400005, 0, 0x00000001, 0 },
+		.gpiomute 	= 0x00c00007,
+		.no_msp34xx     = 1,
+		.no_tda7432     = 1,
+	},
+	/* ---- card 0x95---------------------------------- */
+	[BTTV_BOARD_TYPHOON_TVTUNERPCI] = {
+		.name           = "Typhoon TV-Tuner PCI (50684)",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x3014f,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0x20001,0x10001, 0, 0 },
+		.gpiomute       = 10,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_PHILIPS_PAL_I,
+		.tuner_addr     = ADDR_UNSET,
+	},
+	[BTTV_BOARD_GEOVISION_GV600] = {
+		/* emhn@usb.ve */
+		.name		= "Geovision GV-600",
+		.video_inputs	= 16,
+		/* .audio_inputs= 0, */
+		.svhs		= NO_SVHS,
+		.gpiomask	= 0x0,
+		.muxsel		= MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+		.muxsel_hook	= geovision_muxsel,
+		.gpiomux	= { 0 },
+		.no_msp34xx	= 1,
+		.pll		= PLL_28,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_KOZUMI_KTV_01C] = {
+		/* Mauro Lacy <mauro@lacy.com.ar>
+		 * Based on MagicTV and Conceptronic CONTVFMi */
+
+		.name           = "Kozumi KTV-01C",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		.gpiomask       = 0x008007,
+		.muxsel         = MUXSEL(2, 3, 1, 1),
+		.gpiomux        = { 0, 1, 2, 2 }, /* CONTVFMi */
+		.gpiomute 	= 3, /* CONTVFMi */
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */
+		.tuner_addr     = ADDR_UNSET,
+		.pll            = PLL_28,
+		.has_radio      = 1,
+		.has_remote     = 1,
+	},
+	[BTTV_BOARD_ENLTV_FM_2] = {
+		/* Encore TV Tuner Pro ENL TV-FM-2
+		   Mauro Carvalho Chehab <mchehab@infradead.org */
+		.name           = "Encore ENL TV-FM-2",
+		.video_inputs   = 3,
+		/* .audio_inputs= 1, */
+		.svhs           = 2,
+		/* bit 6          -> IR disabled
+		   bit 18/17 = 00 -> mute
+			       01 -> enable external audio input
+			       10 -> internal audio input (mono?)
+			       11 -> internal audio input
+		 */
+		.gpiomask       = 0x060040,
+		.muxsel         = MUXSEL(2, 3, 3),
+		.gpiomux        = { 0x60000, 0x60000, 0x20000, 0x20000 },
+		.gpiomute 	= 0,
+		.tuner_type	= TUNER_TCL_MF02GIP_5N,
+		.tuner_addr     = ADDR_UNSET,
+		.pll            = PLL_28,
+		.has_radio      = 1,
+		.has_remote     = 1,
+	},
+		[BTTV_BOARD_VD012] = {
+		/* D.Heer@Phytec.de */
+		.name           = "PHYTEC VD-012 (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(0, 2, 3, 1),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		[BTTV_BOARD_VD012_X1] = {
+		/* D.Heer@Phytec.de */
+		.name           = "PHYTEC VD-012-X1 (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = 3,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(2, 3, 1),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		[BTTV_BOARD_VD012_X2] = {
+		/* D.Heer@Phytec.de */
+		.name           = "PHYTEC VD-012-X2 (bt878)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 0, */
+		.svhs           = 3,
+		.gpiomask       = 0x00,
+		.muxsel         = MUXSEL(3, 2, 1),
+		.gpiomux        = { 0, 0, 0, 0 }, /* card has no audio */
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+		[BTTV_BOARD_GEOVISION_GV800S] = {
+		/* Bruno Christo <bchristo@inf.ufsm.br>
+		 *
+		 * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+		 * 	1 audio input  per BT878A = 4 audio inputs
+		 * 	4 video inputs per BT878A = 16 video inputs
+		 * This is the first BT878A chip of the GV-800(S). It's the
+		 * "master" chip and it controls the video inputs through an
+		 * analog multiplexer (a CD22M3494) via some GPIO pins. The
+		 * slaves should use card type 0x9e (following this one).
+		 * There is a EEPROM on the card which is currently not handled.
+		 * The audio input is not working yet.
+		 */
+		.name           = "Geovision GV-800(S) (master)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask	= 0xf107f,
+		.no_gpioirq     = 1,
+		.muxsel		= MUXSEL(2, 2, 2, 2),
+		.pll		= PLL_28,
+		.no_msp34xx	= 1,
+		.no_tda7432	= 1,
+		.muxsel_hook    = gv800s_muxsel,
+	},
+		[BTTV_BOARD_GEOVISION_GV800S_SL] = {
+		/* Bruno Christo <bchristo@inf.ufsm.br>
+		 *
+		 * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+		 * 	1 audio input  per BT878A = 4 audio inputs
+		 * 	4 video inputs per BT878A = 16 video inputs
+		 * The 3 other BT878A chips are "slave" chips of the GV-800(S)
+		 * and should use this card type.
+		 * The audio input is not working yet.
+		 */
+		.name           = "Geovision GV-800(S) (slave)",
+		.video_inputs   = 4,
+		/* .audio_inputs= 1, */
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+		.svhs           = NO_SVHS,
+		.gpiomask	= 0x00,
+		.no_gpioirq     = 1,
+		.muxsel		= MUXSEL(2, 2, 2, 2),
+		.pll		= PLL_28,
+		.no_msp34xx	= 1,
+		.no_tda7432	= 1,
+		.muxsel_hook    = gv800s_muxsel,
+	},
+	[BTTV_BOARD_PV183] = {
+		.name           = "ProVideo PV183", /* 0x9f */
+		.video_inputs   = 2,
+		/* .audio_inputs= 0, */
+		.svhs           = NO_SVHS,
+		.gpiomask       = 0,
+		.muxsel         = MUXSEL(2, 3),
+		.gpiomux        = { 0 },
+		.no_msp34xx     = 1,
+		.pll            = PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+		.tuner_addr	= ADDR_UNSET,
+	},
+	[BTTV_BOARD_TVT_TD3116] = {
+		.name           = "Tongwei Video Technology TD-3116",
+		.video_inputs   = 16,
+		.gpiomask       = 0xc00ff,
+		.muxsel         = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+		.muxsel_hook    = td3116_muxsel,
+		.svhs           = NO_SVHS,
+		.pll		= PLL_28,
+		.tuner_type     = TUNER_ABSENT,
+	},
+	[BTTV_BOARD_APOSONIC_WDVR] = {
+		.name           = "Aposonic W-DVR",
+		.video_inputs   = 4,
+		.svhs           = NO_SVHS,
+		.muxsel         = MUXSEL(2, 3, 1, 0),
+		.tuner_type     = TUNER_ABSENT,
+	},
+
+};
+
+static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned char eeprom_data[256];
+
+/*
+ * identify card
+ */
+void __devinit bttv_idcard(struct bttv *btv)
+{
+	unsigned int gpiobits;
+	int i,type;
+
+	/* read PCI subsystem ID */
+	btv->cardid  = btv->c.pci->subsystem_device << 16;
+	btv->cardid |= btv->c.pci->subsystem_vendor;
+
+	if (0 != btv->cardid && 0xffffffff != btv->cardid) {
+		/* look for the card */
+		for (type = -1, i = 0; cards[i].id != 0; i++)
+			if (cards[i].id  == btv->cardid)
+				type = i;
+
+		if (type != -1) {
+			/* found it */
+			pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n",
+				btv->c.nr, cards[type].name, cards[type].cardnr,
+				btv->cardid & 0xffff,
+				(btv->cardid >> 16) & 0xffff);
+			btv->c.type = cards[type].cardnr;
+		} else {
+			/* 404 */
+			pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n",
+				btv->c.nr, btv->cardid & 0xffff,
+				(btv->cardid >> 16) & 0xffff);
+			pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n");
+		}
+	}
+
+	/* let the user override the autodetected type */
+	if (card[btv->c.nr] < bttv_num_tvcards)
+		btv->c.type=card[btv->c.nr];
+
+	/* print which card config we are using */
+	pr_info("%d: using: %s [card=%d,%s]\n",
+		btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type,
+		card[btv->c.nr] < bttv_num_tvcards
+		? "insmod option" : "autodetected");
+
+	/* overwrite gpio stuff ?? */
+	if (UNSET == audioall && UNSET == audiomux[0])
+		return;
+
+	if (UNSET != audiomux[0]) {
+		gpiobits = 0;
+		for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+			bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i];
+			gpiobits |= audiomux[i];
+		}
+	} else {
+		gpiobits = audioall;
+		for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+			bttv_tvcards[btv->c.type].gpiomux[i] = audioall;
+		}
+	}
+	bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
+	pr_info("%d: gpio config override: mask=0x%x, mux=",
+		btv->c.nr, bttv_tvcards[btv->c.type].gpiomask);
+	for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+		pr_cont("%s0x%x",
+			i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
+	}
+	pr_cont("\n");
+}
+
+/*
+ * (most) board specific initialisations goes here
+ */
+
+/* Some Modular Technology cards have an eeprom, but no subsystem ID */
+static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256])
+{
+	int type = -1;
+
+	if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13))
+		type = BTTV_BOARD_MODTEC_205;
+	else if (0 == strncmp(eeprom_data+20,"Picolo",7))
+		type = BTTV_BOARD_EURESYS_PICOLO;
+	else if (eeprom_data[0] == 0x84 && eeprom_data[2]== 0)
+		type = BTTV_BOARD_HAUPPAUGE; /* old bt848 */
+
+	if (-1 != type) {
+		btv->c.type = type;
+		pr_info("%d: detected by eeprom: %s [card=%d]\n",
+			btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
+	}
+}
+
+static void flyvideo_gpio(struct bttv *btv)
+{
+	int gpio, has_remote, has_radio, is_capture_only;
+	int is_lr90, has_tda9820_tda9821;
+	int tuner_type = UNSET, ttype;
+
+	gpio_inout(0xffffff, 0);
+	udelay(8);  /* without this we would see the 0x1800 mask */
+	gpio = gpio_read();
+	/* FIXME: must restore OUR_EN ??? */
+
+	/* all cards provide GPIO info, some have an additional eeprom
+	 * LR50: GPIO coding can be found lower right CP1 .. CP9
+	 *       CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1.
+	 *       GPIO14-12: n.c.
+	 * LR90: GP9=GPIO23 .. GP1=GPIO15 (right above the bt878)
+
+	 * lowest 3 bytes are remote control codes (no handshake needed)
+	 * xxxFFF: No remote control chip soldered
+	 * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered
+	 * Note: Some bits are Audio_Mask !
+	 */
+	ttype = (gpio & 0x0f0000) >> 16;
+	switch (ttype) {
+	case 0x0:
+		tuner_type = 2;  /* NTSC, e.g. TPI8NSR11P */
+		break;
+	case 0x2:
+		tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */
+		break;
+	case 0x4:
+		tuner_type = 5;  /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */
+		break;
+	case 0x6:
+		tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */
+		break;
+	case 0xC:
+		tuner_type = 3;  /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */
+		break;
+	default:
+		pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr);
+		break;
+	}
+
+	has_remote          =   gpio & 0x800000;
+	has_radio	    =   gpio & 0x400000;
+	/*   unknown                   0x200000;
+	 *   unknown2                  0x100000; */
+	is_capture_only     = !(gpio & 0x008000); /* GPIO15 */
+	has_tda9820_tda9821 = !(gpio & 0x004000);
+	is_lr90             = !(gpio & 0x002000); /* else LR26/LR50 (LR38/LR51 f. capture only) */
+	/*
+	 * gpio & 0x001000    output bit for audio routing */
+
+	if (is_capture_only)
+		tuner_type = TUNER_ABSENT; /* No tuner present */
+
+	pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
+		btv->c.nr, has_radio ? "yes" : "no",
+		has_remote ? "yes" : "no", tuner_type, gpio);
+	pr_info("%d: FlyVideo  LR90=%s tda9821/tda9820=%s capture_only=%s\n",
+		btv->c.nr, is_lr90 ? "yes" : "no",
+		has_tda9820_tda9821 ? "yes" : "no",
+		is_capture_only ? "yes" : "no");
+
+	if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */
+		btv->tuner_type = tuner_type;
+	btv->has_radio = has_radio;
+
+	/* 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_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,
+			       14,2,17,1, 4,1,4,3,  1,2,16,1, 4,4,4,4 };
+static int miro_fmtuner[]  = { 0,0,0,0,   0,0,0,0,  0,0,0,0,  0,0,0,1,
+			       1,1,1,1,   1,1,1,0,  0,0,0,0,  0,1,0,0 };
+
+static void miro_pinnacle_gpio(struct bttv *btv)
+{
+	int id,msp,gpio;
+	char *info;
+
+	gpio_inout(0xffffff, 0);
+	gpio = gpio_read();
+	id   = ((gpio>>10) & 63) -1;
+	msp  = bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx");
+	if (id < 32) {
+		btv->tuner_type = miro_tunermap[id];
+		if (0 == (gpio & 0x20)) {
+			btv->has_radio = 1;
+			if (!miro_fmtuner[id]) {
+				btv->has_matchbox = 1;
+				btv->mbox_we    = (1<<6);
+				btv->mbox_most  = (1<<7);
+				btv->mbox_clk   = (1<<8);
+				btv->mbox_data  = (1<<9);
+				btv->mbox_mask  = (1<<6)|(1<<7)|(1<<8)|(1<<9);
+			}
+		} else {
+			btv->has_radio = 0;
+		}
+		if (-1 != msp) {
+			if (btv->c.type == BTTV_BOARD_MIRO)
+				btv->c.type = BTTV_BOARD_MIROPRO;
+			if (btv->c.type == BTTV_BOARD_PINNACLE)
+				btv->c.type = BTTV_BOARD_PINNACLEPRO;
+		}
+		pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
+			btv->c.nr, id+1, btv->tuner_type,
+			!btv->has_radio ? "no" :
+			(btv->has_matchbox ? "matchbox" : "fmtuner"),
+			(-1 == msp) ? "no" : "yes");
+	} else {
+		/* new cards with microtune tuner */
+		id = 63 - id;
+		btv->has_radio = 0;
+		switch (id) {
+		case 1:
+			info = "PAL / mono";
+			btv->tda9887_conf = TDA9887_INTERCARRIER;
+			break;
+		case 2:
+			info = "PAL+SECAM / stereo";
+			btv->has_radio = 1;
+			btv->tda9887_conf = TDA9887_QSS;
+			break;
+		case 3:
+			info = "NTSC / stereo";
+			btv->has_radio = 1;
+			btv->tda9887_conf = TDA9887_QSS;
+			break;
+		case 4:
+			info = "PAL+SECAM / mono";
+			btv->tda9887_conf = TDA9887_QSS;
+			break;
+		case 5:
+			info = "NTSC / mono";
+			btv->tda9887_conf = TDA9887_INTERCARRIER;
+			break;
+		case 6:
+			info = "NTSC / stereo";
+			btv->tda9887_conf = TDA9887_INTERCARRIER;
+			break;
+		case 7:
+			info = "PAL / stereo";
+			btv->tda9887_conf = TDA9887_INTERCARRIER;
+			break;
+		default:
+			info = "oops: unknown card";
+			break;
+		}
+		if (-1 != msp)
+			btv->c.type = BTTV_BOARD_PINNACLEPRO;
+		pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
+			btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
+		btv->tuner_type = TUNER_MT2032;
+	}
+}
+
+/* GPIO21   L: Buffer aktiv, H: Buffer inaktiv */
+#define LM1882_SYNC_DRIVE     0x200000L
+
+static void init_ids_eagle(struct bttv *btv)
+{
+	gpio_inout(0xffffff,0xFFFF37);
+	gpio_write(0x200020);
+
+	/* flash strobe inverter ?! */
+	gpio_write(0x200024);
+
+	/* switch sync drive off */
+	gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+
+	/* set BT848 muxel to 2 */
+	btaor((2)<<5, ~(2<<5), BT848_IFORM);
+}
+
+/* Muxsel helper for the IDS Eagle.
+ * the eagles does not use the standard muxsel-bits but
+ * has its own multiplexer */
+static void eagle_muxsel(struct bttv *btv, unsigned int input)
+{
+	gpio_bits(3, input & 3);
+
+	/* composite */
+	/* set chroma ADC to sleep */
+	btor(BT848_ADC_C_SLEEP, BT848_ADC);
+	/* set to composite video */
+	btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+	btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+
+	/* switch sync drive off */
+	gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+}
+
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
+{
+	static const int masks[] = {0x30, 0x01, 0x12, 0x23};
+	gpio_write(masks[input%4]);
+}
+
+/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and
+   alarms output
+
+   GPIObit    | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+   assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1|   |   |
+
+   IN - sensor inputs, INx - sensor inputs and TI XORed together
+   O1,O2,O3 - alarm outputs (relays)
+
+   OUT ENABLE   1    1   0  . 1  1   0   0 . 0   0   0    0   = 0x6C0
+
+*/
+
+static void init_lmlbt4x(struct bttv *btv)
+{
+	pr_debug("LMLBT4x init\n");
+	btwrite(0x000000, BT848_GPIO_REG_INP);
+	gpio_inout(0xffffff, 0x0006C0);
+	gpio_write(0x000000);
+}
+
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input)
+{
+	unsigned int inmux = input % 8;
+	gpio_inout( 0xf, 0xf );
+	gpio_bits( 0xf, inmux );
+}
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input)
+{
+	unsigned int inmux = input % 4;
+	gpio_inout( 3<<9, 3<<9 );
+	gpio_bits( 3<<9, inmux<<9 );
+}
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input)
+{
+	unsigned int inmux = input % 16;
+	gpio_inout(0xf, 0xf);
+	gpio_bits(0xf, inmux);
+}
+
+/*
+ * The TD3116 has 2 74HC4051 muxes wired to the MUX0 input of a bt878.
+ * The first 74HC4051 has the lower 8 inputs, the second one the higher 8.
+ * The muxes are controlled via a 74HC373 latch which is connected to
+ * GPIOs 0-7. GPIO 18 is connected to the LE signal of the latch.
+ * Q0 of the latch is connected to the Enable (~E) input of the first
+ * 74HC4051. Q1 - Q3 are connected to S0 - S2 of the same 74HC4051.
+ * Q4 - Q7 are connected to the second 74HC4051 in the same way.
+ */
+
+static void td3116_latch_value(struct bttv *btv, u32 value)
+{
+	gpio_bits((1<<18) | 0xff, value);
+	gpio_bits((1<<18) | 0xff, (1<<18) | value);
+	udelay(1);
+	gpio_bits((1<<18) | 0xff, value);
+}
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input)
+{
+	u32 value;
+	u32 highbit;
+
+	highbit = (input & 0x8) >> 3 ;
+
+	/* Disable outputs and set value in the mux */
+	value = 0x11; /* Disable outputs */
+	value |= ((input & 0x7) << 1)  << (4 * highbit);
+	td3116_latch_value(btv, value);
+
+	/* Enable the correct output */
+	value &= ~0x11;
+	value |= ((highbit ^ 0x1) << 4) | highbit;
+	td3116_latch_value(btv, value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bttv_reset_audio(struct bttv *btv)
+{
+	/*
+	 * BT878A has a audio-reset register.
+	 * 1. This register is an audio reset function but it is in
+	 *    function-0 (video capture) address space.
+	 * 2. It is enough to do this once per power-up of the card.
+	 * 3. There is a typo in the Conexant doc -- it is not at
+	 *    0x5B, but at 0x058. (B is an odd-number, obviously a typo!).
+	 * --//Shrikumar 030609
+	 */
+	if (btv->id != 878)
+		return;
+
+	if (bttv_debug)
+		pr_debug("%d: BT878A ARESET\n", btv->c.nr);
+	btwrite((1<<7), 0x058);
+	udelay(10);
+	btwrite(     0, 0x058);
+}
+
+/* initialization part one -- before registering i2c bus */
+void __devinit bttv_init_card1(struct bttv *btv)
+{
+	switch (btv->c.type) {
+	case BTTV_BOARD_HAUPPAUGE:
+	case BTTV_BOARD_HAUPPAUGE878:
+		boot_msp34xx(btv,5);
+		break;
+	case BTTV_BOARD_VOODOOTV_200:
+	case BTTV_BOARD_VOODOOTV_FM:
+		boot_msp34xx(btv,20);
+		break;
+	case BTTV_BOARD_AVERMEDIA98:
+		boot_msp34xx(btv,11);
+		break;
+	case BTTV_BOARD_HAUPPAUGEPVR:
+		pvr_boot(btv);
+		break;
+	case BTTV_BOARD_TWINHAN_DST:
+	case BTTV_BOARD_AVDVBT_771:
+	case BTTV_BOARD_PINNACLESAT:
+		btv->use_i2c_hw = 1;
+		break;
+	case BTTV_BOARD_ADLINK_RTV24:
+		init_RTV24( btv );
+		break;
+
+	}
+	if (!bttv_tvcards[btv->c.type].has_dvb)
+		bttv_reset_audio(btv);
+}
+
+/* initialization part two -- after registering i2c bus */
+void __devinit bttv_init_card2(struct bttv *btv)
+{
+	btv->tuner_type = UNSET;
+
+	if (BTTV_BOARD_UNKNOWN == btv->c.type) {
+		bttv_readee(btv,eeprom_data,0xa0);
+		identify_by_eeprom(btv,eeprom_data);
+	}
+
+	switch (btv->c.type) {
+	case BTTV_BOARD_MIRO:
+	case BTTV_BOARD_MIROPRO:
+	case BTTV_BOARD_PINNACLE:
+	case BTTV_BOARD_PINNACLEPRO:
+		/* miro/pinnacle */
+		miro_pinnacle_gpio(btv);
+		break;
+	case BTTV_BOARD_FLYVIDEO_98:
+	case BTTV_BOARD_MAXI:
+	case BTTV_BOARD_LIFE_FLYKIT:
+	case BTTV_BOARD_FLYVIDEO:
+	case BTTV_BOARD_TYPHOON_TVIEW:
+	case BTTV_BOARD_CHRONOS_VS2:
+	case BTTV_BOARD_FLYVIDEO_98FM:
+	case BTTV_BOARD_FLYVIDEO2000:
+	case BTTV_BOARD_FLYVIDEO98EZ:
+	case BTTV_BOARD_CONFERENCETV:
+	case BTTV_BOARD_LIFETEC_9415:
+		flyvideo_gpio(btv);
+		break;
+	case BTTV_BOARD_HAUPPAUGE:
+	case BTTV_BOARD_HAUPPAUGE878:
+	case BTTV_BOARD_HAUPPAUGEPVR:
+		/* pick up some config infos from the eeprom */
+		bttv_readee(btv,eeprom_data,0xa0);
+		hauppauge_eeprom(btv);
+		break;
+	case BTTV_BOARD_AVERMEDIA98:
+	case BTTV_BOARD_AVPHONE98:
+		bttv_readee(btv,eeprom_data,0xa0);
+		avermedia_eeprom(btv);
+		break;
+	case BTTV_BOARD_PXC200:
+		init_PXC200(btv);
+		break;
+	case BTTV_BOARD_PICOLO_TETRA_CHIP:
+		picolo_tetra_init(btv);
+		break;
+	case BTTV_BOARD_VHX:
+		btv->has_radio    = 1;
+		btv->has_matchbox = 1;
+		btv->mbox_we      = 0x20;
+		btv->mbox_most    = 0;
+		btv->mbox_clk     = 0x08;
+		btv->mbox_data    = 0x10;
+		btv->mbox_mask    = 0x38;
+		break;
+	case BTTV_BOARD_VOBIS_BOOSTAR:
+	case BTTV_BOARD_TERRATV:
+		terratec_active_radio_upgrade(btv);
+		break;
+	case BTTV_BOARD_MAGICTVIEW061:
+		if (btv->cardid == 0x3002144f) {
+			btv->has_radio=1;
+			pr_info("%d: radio detected by subsystem id (CPH05x)\n",
+				btv->c.nr);
+		}
+		break;
+	case BTTV_BOARD_STB2:
+		if (btv->cardid == 0x3060121a) {
+			/* Fix up entry for 3DFX VoodooTV 100,
+			   which is an OEM STB card variant. */
+			btv->has_radio=0;
+			btv->tuner_type=TUNER_TEMIC_NTSC;
+		}
+		break;
+	case BTTV_BOARD_OSPREY1x0:
+	case BTTV_BOARD_OSPREY1x0_848:
+	case BTTV_BOARD_OSPREY101_848:
+	case BTTV_BOARD_OSPREY1x1:
+	case BTTV_BOARD_OSPREY1x1_SVID:
+	case BTTV_BOARD_OSPREY2xx:
+	case BTTV_BOARD_OSPREY2x0_SVID:
+	case BTTV_BOARD_OSPREY2x0:
+	case BTTV_BOARD_OSPREY440:
+	case BTTV_BOARD_OSPREY500:
+	case BTTV_BOARD_OSPREY540:
+	case BTTV_BOARD_OSPREY2000:
+		bttv_readee(btv,eeprom_data,0xa0);
+		osprey_eeprom(btv, eeprom_data);
+		break;
+	case BTTV_BOARD_IDS_EAGLE:
+		init_ids_eagle(btv);
+		break;
+	case BTTV_BOARD_MODTEC_205:
+		bttv_readee(btv,eeprom_data,0xa0);
+		modtec_eeprom(btv);
+		break;
+	case BTTV_BOARD_LMLBT4:
+		init_lmlbt4x(btv);
+		break;
+	case BTTV_BOARD_TIBET_CS16:
+		tibetCS16_init(btv);
+		break;
+	case BTTV_BOARD_KODICOM_4400R:
+		kodicom4400r_init(btv);
+		break;
+	case BTTV_BOARD_GEOVISION_GV800S:
+		gv800s_init(btv);
+		break;
+	}
+
+	/* pll configuration */
+	if (!(btv->id==848 && btv->revision==0x11)) {
+		/* defaults from card list */
+		if (PLL_28 == bttv_tvcards[btv->c.type].pll) {
+			btv->pll.pll_ifreq=28636363;
+			btv->pll.pll_crystal=BT848_IFORM_XT0;
+		}
+		if (PLL_35 == bttv_tvcards[btv->c.type].pll) {
+			btv->pll.pll_ifreq=35468950;
+			btv->pll.pll_crystal=BT848_IFORM_XT1;
+		}
+		/* insmod options can override */
+		switch (pll[btv->c.nr]) {
+		case 0: /* none */
+			btv->pll.pll_crystal = 0;
+			btv->pll.pll_ifreq   = 0;
+			btv->pll.pll_ofreq   = 0;
+			break;
+		case 1: /* 28 MHz */
+		case 28:
+			btv->pll.pll_ifreq   = 28636363;
+			btv->pll.pll_ofreq   = 0;
+			btv->pll.pll_crystal = BT848_IFORM_XT0;
+			break;
+		case 2: /* 35 MHz */
+		case 35:
+			btv->pll.pll_ifreq   = 35468950;
+			btv->pll.pll_ofreq   = 0;
+			btv->pll.pll_crystal = BT848_IFORM_XT1;
+			break;
+		}
+	}
+	btv->pll.pll_current = -1;
+
+	/* tuner configuration (from card list / autodetect / insmod option) */
+	if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
+		if (UNSET == btv->tuner_type)
+			btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
+	if (UNSET != tuner[btv->c.nr])
+		btv->tuner_type = tuner[btv->c.nr];
+
+	if (btv->tuner_type == TUNER_ABSENT)
+		pr_info("%d: tuner absent\n", btv->c.nr);
+	else if (btv->tuner_type == UNSET)
+		pr_warn("%d: tuner type unset\n", btv->c.nr);
+	else
+		pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type);
+
+	if (autoload != UNSET) {
+		pr_warn("%d: the autoload option is obsolete\n", btv->c.nr);
+		pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n",
+			btv->c.nr);
+	}
+
+	if (UNSET == btv->tuner_type)
+		btv->tuner_type = TUNER_ABSENT;
+
+	btv->dig = bttv_tvcards[btv->c.type].has_dig_in ?
+		   bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET;
+	btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ?
+		    UNSET : bttv_tvcards[btv->c.type].svhs;
+	if (svhs[btv->c.nr] != UNSET)
+		btv->svhs = svhs[btv->c.nr];
+	if (remote[btv->c.nr] != UNSET)
+		btv->has_remote = remote[btv->c.nr];
+
+	if (bttv_tvcards[btv->c.type].has_radio)
+		btv->has_radio = 1;
+	if (bttv_tvcards[btv->c.type].has_remote)
+		btv->has_remote = 1;
+	if (!bttv_tvcards[btv->c.type].no_gpioirq)
+		btv->gpioirq = 1;
+	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 (btv->tuner_type == TUNER_ABSENT)
+		return;  /* no tuner or related drivers to load */
+
+	if (btv->has_saa6588 || saa6588[btv->c.nr]) {
+		/* Probe for RDS receiver chip */
+		static const unsigned short addrs[] = {
+			0x20 >> 1,
+			0x22 >> 1,
+			I2C_CLIENT_END
+		};
+		struct v4l2_subdev *sd;
+
+		sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+			&btv->c.i2c_adap, "saa6588", 0, addrs);
+		btv->has_saa6588 = (sd != NULL);
+	}
+
+	/* try to detect audio/fader chips */
+
+	/* First check if the user specified the audio chip via a module
+	   option. */
+
+	switch (audiodev[btv->c.nr]) {
+	case -1:
+		return;	/* do not load any audio module */
+
+	case 0: /* autodetect */
+		break;
+
+	case 1: {
+		/* The user specified that we should probe for msp3400 */
+		static const unsigned short addrs[] = {
+			I2C_ADDR_MSP3400 >> 1,
+			I2C_ADDR_MSP3400_ALT >> 1,
+			I2C_CLIENT_END
+		};
+
+		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+			&btv->c.i2c_adap, "msp3400", 0, addrs);
+		if (btv->sd_msp34xx)
+			return;
+		goto no_audio;
+	}
+
+	case 2: {
+		/* The user specified that we should probe for tda7432 */
+		static const unsigned short addrs[] = {
+			I2C_ADDR_TDA7432 >> 1,
+			I2C_CLIENT_END
+		};
+
+		if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+				&btv->c.i2c_adap, "tda7432", 0, addrs))
+			return;
+		goto no_audio;
+	}
+
+	case 3: {
+		/* The user specified that we should probe for tvaudio */
+		btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+			&btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+		if (btv->sd_tvaudio)
+			return;
+		goto no_audio;
+	}
+
+	default:
+		pr_warn("%d: unknown audiodev value!\n", btv->c.nr);
+		return;
+	}
+
+	/* There were no overrides, so now we try to discover this through the
+	   card definition */
+
+	/* probe for msp3400 first: this driver can detect whether or not
+	   it really is a msp3400, so it will return NULL when the device
+	   found is really something else (e.g. a tea6300). */
+	if (!bttv_tvcards[btv->c.type].no_msp34xx) {
+		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+			&btv->c.i2c_adap, "msp3400",
+			0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1));
+	} else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
+		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+			&btv->c.i2c_adap, "msp3400",
+			0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1));
+	}
+
+	/* If we found a msp34xx, then we're done. */
+	if (btv->sd_msp34xx)
+		return;
+
+	/* it might also be a tda7432. */
+	if (!bttv_tvcards[btv->c.type].no_tda7432) {
+		static const unsigned short addrs[] = {
+			I2C_ADDR_TDA7432 >> 1,
+			I2C_CLIENT_END
+		};
+
+		if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+				&btv->c.i2c_adap, "tda7432", 0, addrs))
+			return;
+	}
+
+	/* Now see if we can find one of the tvaudio devices. */
+	btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+		&btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+	if (btv->sd_tvaudio)
+		return;
+
+no_audio:
+	pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr);
+}
+
+
+/* initialize the tuner */
+void __devinit bttv_init_tuner(struct bttv *btv)
+{
+	int addr = ADDR_UNSET;
+
+	if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
+		addr = bttv_tvcards[btv->c.type].tuner_addr;
+
+	if (btv->tuner_type != TUNER_ABSENT) {
+		struct tuner_setup tun_setup;
+
+		/* Load tuner module before issuing tuner config call! */
+		if (btv->has_radio)
+			v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+				&btv->c.i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+		v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+				&btv->c.i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+		v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+				&btv->c.i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
+
+		tun_setup.mode_mask = T_ANALOG_TV;
+		tun_setup.type = btv->tuner_type;
+		tun_setup.addr = addr;
+
+		if (btv->has_radio)
+			tun_setup.mode_mask |= T_RADIO;
+
+		bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
+	}
+
+	if (btv->tda9887_conf) {
+		struct v4l2_priv_tun_config tda9887_cfg;
+
+		tda9887_cfg.tuner = TUNER_TDA9887;
+		tda9887_cfg.priv = &btv->tda9887_conf;
+
+		bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void modtec_eeprom(struct bttv *btv)
+{
+	if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
+		btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
+		pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+			btv->c.nr, &eeprom_data[0x1e]);
+	} else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
+		btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
+		pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+			btv->c.nr, &eeprom_data[0x1e]);
+	} else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) {
+		btv->tuner_type=TUNER_PHILIPS_NTSC;
+		pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+			btv->c.nr, &eeprom_data[0x1e]);
+	} else {
+		pr_info("%d: Modtec: Unknown TunerString: %s\n",
+			btv->c.nr, &eeprom_data[0x1e]);
+	}
+}
+
+static void __devinit hauppauge_eeprom(struct bttv *btv)
+{
+	struct tveeprom tv;
+
+	tveeprom_hauppauge_analog(&btv->i2c_client, &tv, eeprom_data);
+	btv->tuner_type = tv.tuner_type;
+	btv->has_radio  = tv.has_radio;
+
+	pr_info("%d: Hauppauge eeprom indicates model#%d\n",
+		btv->c.nr, tv.model);
+
+	/*
+	 * Some of the 878 boards have duplicate PCI IDs. Switch the board
+	 * type based on model #.
+	 */
+	if(tv.model == 64900) {
+		pr_info("%d: Switching board type from %s to %s\n",
+			btv->c.nr,
+			bttv_tvcards[btv->c.type].name,
+			bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name);
+		btv->c.type = BTTV_BOARD_HAUPPAUGE_IMPACTVCB;
+	}
+
+	/* The 61334 needs the msp3410 to do the radio demod to get sound */
+	if (tv.model == 61334)
+		btv->radio_uses_msp_demodulator = 1;
+}
+
+static int terratec_active_radio_upgrade(struct bttv *btv)
+{
+	int freq;
+
+	btv->has_radio    = 1;
+	btv->has_matchbox = 1;
+	btv->mbox_we      = 0x10;
+	btv->mbox_most    = 0x20;
+	btv->mbox_clk     = 0x08;
+	btv->mbox_data    = 0x04;
+	btv->mbox_mask    = 0x3c;
+
+	btv->mbox_iow     = 1 <<  8;
+	btv->mbox_ior     = 1 <<  9;
+	btv->mbox_csel    = 1 << 10;
+
+	freq=88000/62.5;
+	tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
+	if (0x1ed8 == tea5757_read(btv)) {
+		pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
+		btv->has_radio    = 1;
+		btv->has_saa6588  = 1;
+		btv->has_matchbox = 1;
+	} else {
+		btv->has_radio    = 0;
+		btv->has_matchbox = 0;
+	}
+	return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * minimal bootstrap for the WinTV/PVR -- upload altera firmware.
+ *
+ * The hcwamc.rbf firmware file is on the Hauppauge driver CD.  Have
+ * a look at Pvr/pvr45xxx.EXE (self-extracting zip archive, can be
+ * unpacked with unzip).
+ */
+#define PVR_GPIO_DELAY		10
+
+#define BTTV_ALT_DATA		0x000001
+#define BTTV_ALT_DCLK		0x100000
+#define BTTV_ALT_NCONFIG	0x800000
+
+static int __devinit pvr_altera_load(struct bttv *btv, const u8 *micro,
+				     u32 microlen)
+{
+	u32 n;
+	u8 bits;
+	int i;
+
+	gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG);
+	gpio_write(0);
+	udelay(PVR_GPIO_DELAY);
+
+	gpio_write(BTTV_ALT_NCONFIG);
+	udelay(PVR_GPIO_DELAY);
+
+	for (n = 0; n < microlen; n++) {
+		bits = micro[n];
+		for (i = 0 ; i < 8 ; i++) {
+			gpio_bits(BTTV_ALT_DCLK,0);
+			if (bits & 0x01)
+				gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
+			else
+				gpio_bits(BTTV_ALT_DATA,0);
+			gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+			bits >>= 1;
+		}
+	}
+	gpio_bits(BTTV_ALT_DCLK,0);
+	udelay(PVR_GPIO_DELAY);
+
+	/* begin Altera init loop (Not necessary,but doesn't hurt) */
+	for (i = 0 ; i < 30 ; i++) {
+		gpio_bits(BTTV_ALT_DCLK,0);
+		gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+	}
+	gpio_bits(BTTV_ALT_DCLK,0);
+	return 0;
+}
+
+static int __devinit pvr_boot(struct bttv *btv)
+{
+	const struct firmware *fw_entry;
+	int rc;
+
+	rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
+	if (rc != 0) {
+		pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr);
+		return rc;
+	}
+	rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
+	pr_info("%d: altera firmware upload %s\n",
+		btv->c.nr, (rc < 0) ? "failed" : "ok");
+	release_firmware(fw_entry);
+	return rc;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some osprey specific stuff                                              */
+
+static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256])
+{
+	int i;
+	u32 serial = 0;
+	int cardid = -1;
+
+	/* This code will nevery actually get called in this case.... */
+	if (btv->c.type == BTTV_BOARD_UNKNOWN) {
+		/* this might be an antique... check for MMAC label in eeprom */
+		if (!strncmp(ee, "MMAC", 4)) {
+			u8 checksum = 0;
+			for (i = 0; i < 21; i++)
+				checksum += ee[i];
+			if (checksum != ee[21])
+				return;
+			cardid = BTTV_BOARD_OSPREY1x0_848;
+			for (i = 12; i < 21; i++)
+				serial *= 10, serial += ee[i] - '0';
+		}
+	} else {
+		unsigned short type;
+
+		for (i = 4*16; i < 8*16; i += 16) {
+			u16 checksum = ip_compute_csum(ee + i, 16);
+
+			if ((checksum&0xff) + (checksum>>8) == 0xff)
+				break;
+		}
+		if (i >= 8*16)
+			return;
+		ee += i;
+
+		/* found a valid descriptor */
+		type = get_unaligned_be16((__be16 *)(ee+4));
+
+		switch(type) {
+		/* 848 based */
+		case 0x0004:
+			cardid = BTTV_BOARD_OSPREY1x0_848;
+			break;
+		case 0x0005:
+			cardid = BTTV_BOARD_OSPREY101_848;
+			break;
+
+		/* 878 based */
+		case 0x0012:
+		case 0x0013:
+			cardid = BTTV_BOARD_OSPREY1x0;
+			break;
+		case 0x0014:
+		case 0x0015:
+			cardid = BTTV_BOARD_OSPREY1x1;
+			break;
+		case 0x0016:
+		case 0x0017:
+		case 0x0020:
+			cardid = BTTV_BOARD_OSPREY1x1_SVID;
+			break;
+		case 0x0018:
+		case 0x0019:
+		case 0x001E:
+		case 0x001F:
+			cardid = BTTV_BOARD_OSPREY2xx;
+			break;
+		case 0x001A:
+		case 0x001B:
+			cardid = BTTV_BOARD_OSPREY2x0_SVID;
+			break;
+		case 0x0040:
+			cardid = BTTV_BOARD_OSPREY500;
+			break;
+		case 0x0050:
+		case 0x0056:
+			cardid = BTTV_BOARD_OSPREY540;
+			/* bttv_osprey_540_init(btv); */
+			break;
+		case 0x0060:
+		case 0x0070:
+		case 0x00A0:
+			cardid = BTTV_BOARD_OSPREY2x0;
+			/* enable output on select control lines */
+			gpio_inout(0xffffff,0x000303);
+			break;
+		case 0x00D8:
+			cardid = BTTV_BOARD_OSPREY440;
+			break;
+		default:
+			/* unknown...leave generic, but get serial # */
+			pr_info("%d: osprey eeprom: unknown card type 0x%04x\n",
+				btv->c.nr, type);
+			break;
+		}
+		serial = get_unaligned_be32((__be32 *)(ee+6));
+	}
+
+	pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n",
+		btv->c.nr, cardid,
+		cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial);
+
+	if (cardid<0 || btv->c.type == cardid)
+		return;
+
+	/* card type isn't set correctly */
+	if (card[btv->c.nr] < bttv_num_tvcards) {
+		pr_warn("%d: osprey eeprom: Not overriding user specified card type\n",
+			btv->c.nr);
+	} else {
+		pr_info("%d: osprey eeprom: Changing card type from %d to %d\n",
+			btv->c.nr, btv->c.type, cardid);
+		btv->c.type = cardid;
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+/* AVermedia specific stuff, from  bktr_card.c                             */
+
+static int tuner_0_table[] = {
+	TUNER_PHILIPS_NTSC,  TUNER_PHILIPS_PAL /* PAL-BG*/,
+	TUNER_PHILIPS_PAL,   TUNER_PHILIPS_PAL /* PAL-I*/,
+	TUNER_PHILIPS_PAL,   TUNER_PHILIPS_PAL,
+	TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM,
+	TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL,
+	TUNER_PHILIPS_FM1216ME_MK3 };
+
+static int tuner_1_table[] = {
+	TUNER_TEMIC_NTSC,  TUNER_TEMIC_PAL,
+	TUNER_TEMIC_PAL,   TUNER_TEMIC_PAL,
+	TUNER_TEMIC_PAL,   TUNER_TEMIC_PAL,
+	TUNER_TEMIC_4012FY5, TUNER_TEMIC_4012FY5, /* TUNER_TEMIC_SECAM */
+	TUNER_TEMIC_4012FY5, TUNER_TEMIC_PAL};
+
+static void __devinit avermedia_eeprom(struct bttv *btv)
+{
+	int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0;
+
+	tuner_make      = (eeprom_data[0x41] & 0x7);
+	tuner_tv_fm     = (eeprom_data[0x41] & 0x18) >> 3;
+	tuner_format    = (eeprom_data[0x42] & 0xf0) >> 4;
+	btv->has_remote = (eeprom_data[0x42] & 0x01);
+
+	if (tuner_make == 0 || tuner_make == 2)
+		if (tuner_format <= 0x0a)
+			tuner_type = tuner_0_table[tuner_format];
+	if (tuner_make == 1)
+		if (tuner_format <= 9)
+			tuner_type = tuner_1_table[tuner_format];
+
+	if (tuner_make == 4)
+		if (tuner_format == 0x09)
+			tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */
+
+	pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=",
+		btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]);
+	if (tuner_type) {
+		btv->tuner_type = tuner_type;
+		pr_cont("%d", tuner_type);
+	} else
+		pr_cont("Unknown type");
+	pr_cont(" radio:%s remote control:%s\n",
+	       tuner_tv_fm     ? "yes" : "no",
+	       btv->has_remote ? "yes" : "no");
+}
+
+/*
+ * For Voodoo TV/FM and Voodoo 200.  These cards' tuners use a TDA9880
+ * analog demod, which is not I2C controlled like the newer and more common
+ * TDA9887 series.  Instead is has two tri-state input pins, S0 and S1,
+ * that control the IF for the video and audio.  Apparently, bttv GPIO
+ * 0x10000 is connected to S0.  S0 low selects a 38.9 MHz VIF for B/G/D/K/I
+ * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC).
+ */
+u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits)
+{
+
+	if (btv->audio == TVAUDIO_INPUT_TUNER) {
+		if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN)
+			gpiobits |= 0x10000;
+		else
+			gpiobits &= ~0x10000;
+	}
+
+	gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits);
+	return gpiobits;
+}
+
+
+/*
+ * reset/enable the MSP on some Hauppauge cards
+ * Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)!
+ *
+ * Hauppauge:  pin  5
+ * Voodoo:     pin 20
+ */
+static void __devinit boot_msp34xx(struct bttv *btv, int pin)
+{
+	int mask = (1 << pin);
+
+	gpio_inout(mask,mask);
+	gpio_bits(mask,0);
+	mdelay(2);
+	udelay(500);
+	gpio_bits(mask,mask);
+
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"msp34xx");
+	if (bttv_verbose)
+		pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n",
+			btv->c.nr, pin);
+}
+
+/* ----------------------------------------------------------------------- */
+/*  Imagenation L-Model PXC200 Framegrabber */
+/*  This is basically the same procedure as
+ *  used by Alessandro Rubini in his pxc200
+ *  driver, but using BTTV functions */
+
+static void __devinit init_PXC200(struct bttv *btv)
+{
+	static int vals[] __devinitdata = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d,
+					    0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+					    0x00 };
+	unsigned int i;
+	int tmp;
+	u32 val;
+
+	/* Initialise GPIO-connevted stuff */
+	gpio_inout(0xffffff, (1<<13));
+	gpio_write(0);
+	udelay(3);
+	gpio_write(1<<13);
+	/* GPIO inputs are pulled up, so no need to drive
+	 * reset pin any longer */
+	gpio_bits(0xffffff, 0);
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"pxc200");
+
+	/*  we could/should try and reset/control the AD pots? but
+	    right now  we simply  turned off the crushing.  Without
+	    this the AGC drifts drifts
+	    remember the EN is reverse logic -->
+	    setting BT848_ADC_AGC_EN disable the AGC
+	    tboult@eecs.lehigh.edu
+	*/
+
+	btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC);
+
+	/*	Initialise MAX517 DAC */
+	pr_info("Setting DAC reference voltage level ...\n");
+	bttv_I2CWrite(btv,0x5E,0,0x80,1);
+
+	/*	Initialise 12C508 PIC */
+	/*	The I2CWrite and I2CRead commmands are actually to the
+	 *	same chips - but the R/W bit is included in the address
+	 *	argument so the numbers are different */
+
+
+	pr_info("Initialising 12C508 PIC chip ...\n");
+
+	/* First of all, enable the clock line. This is used in the PXC200-F */
+	val = btread(BT848_GPIO_DMA_CTL);
+	val |= BT848_GPIO_DMA_CTL_GPCLKMODE;
+	btwrite(val, BT848_GPIO_DMA_CTL);
+
+	/* Then, push to 0 the reset pin long enough to reset the *
+	 * device same as above for the reset line, but not the same
+	 * value sent to the GPIO-connected stuff
+	 * which one is the good one? */
+	gpio_inout(0xffffff,(1<<2));
+	gpio_write(0);
+	udelay(10);
+	gpio_write(1<<2);
+
+	for (i = 0; i < ARRAY_SIZE(vals); i++) {
+		tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
+		if (tmp != -1) {
+			pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
+			       vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL));
+		}
+	}
+
+	pr_info("PXC200 Initialised\n");
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/*
+ *  The Adlink RTV-24 (aka Angelo) has some special initialisation to unlock
+ *  it. This apparently involves the following procedure for each 878 chip:
+ *
+ *  1) write 0x00C3FEFF to the GPIO_OUT_EN register
+ *
+ *  2)  write to GPIO_DATA
+ *      - 0x0E
+ *      - sleep 1ms
+ *      - 0x10 + 0x0E
+ *      - sleep 10ms
+ *      - 0x0E
+ *     read from GPIO_DATA into buf (uint_32)
+ *      - if ( data>>18 & 0x01 != 0) || ( buf>>19 & 0x01 != 1 )
+ *                 error. ERROR_CPLD_Check_Failed stop.
+ *
+ *  3) write to GPIO_DATA
+ *      - write 0x4400 + 0x0E
+ *      - sleep 10ms
+ *      - write 0x4410 + 0x0E
+ *      - sleep 1ms
+ *      - write 0x0E
+ *     read from GPIO_DATA into buf (uint_32)
+ *      - if ( buf>>18 & 0x01 ) || ( buf>>19 & 0x01 != 0 )
+ *                error. ERROR_CPLD_Check_Failed.
+ */
+/* ----------------------------------------------------------------------- */
+static void
+init_RTV24 (struct bttv *btv)
+{
+	uint32_t dataRead = 0;
+	long watchdog_value = 0x0E;
+
+	pr_info("%d: Adlink RTV-24 initialisation in progress ...\n",
+		btv->c.nr);
+
+	btwrite (0x00c3feff, BT848_GPIO_OUT_EN);
+
+	btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+	msleep (1);
+	btwrite (0x10 + watchdog_value, BT848_GPIO_DATA);
+	msleep (10);
+	btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+
+	dataRead = btread (BT848_GPIO_DATA);
+
+	if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) {
+		pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
+			btv->c.nr, dataRead);
+	}
+
+	btwrite (0x4400 + watchdog_value, BT848_GPIO_DATA);
+	msleep (10);
+	btwrite (0x4410 + watchdog_value, BT848_GPIO_DATA);
+	msleep (1);
+	btwrite (watchdog_value, BT848_GPIO_DATA);
+	msleep (1);
+	dataRead = btread (BT848_GPIO_DATA);
+
+	if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) {
+		pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
+			btv->c.nr, dataRead);
+
+		return;
+	}
+
+	pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports     */
+/*
+ * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu>
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Brutally hacked by Dan Sheridan <dan.sheridan@contact.org.uk> djs52 8/3/00
+ */
+
+static void bus_low(struct bttv *btv, int bit)
+{
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+		udelay(5);
+	}
+
+	gpio_bits(bit,0);
+	udelay(5);
+
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+		udelay(5);
+	}
+}
+
+static void bus_high(struct bttv *btv, int bit)
+{
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+		udelay(5);
+	}
+
+	gpio_bits(bit,bit);
+	udelay(5);
+
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+		udelay(5);
+	}
+}
+
+static int bus_in(struct bttv *btv, int bit)
+{
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+		udelay(5);
+
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+		udelay(5);
+	}
+	return gpio_read() & (bit);
+}
+
+/* TEA5757 register bits */
+#define TEA_FREQ		0:14
+#define TEA_BUFFER		15:15
+
+#define TEA_SIGNAL_STRENGTH	16:17
+
+#define TEA_PORT1		18:18
+#define TEA_PORT0		19:19
+
+#define TEA_BAND		20:21
+#define TEA_BAND_FM		0
+#define TEA_BAND_MW		1
+#define TEA_BAND_LW		2
+#define TEA_BAND_SW		3
+
+#define TEA_MONO		22:22
+#define TEA_ALLOW_STEREO	0
+#define TEA_FORCE_MONO		1
+
+#define TEA_SEARCH_DIRECTION	23:23
+#define TEA_SEARCH_DOWN		0
+#define TEA_SEARCH_UP		1
+
+#define TEA_STATUS		24:24
+#define TEA_STATUS_TUNED	0
+#define TEA_STATUS_SEARCHING	1
+
+/* Low-level stuff */
+static int tea5757_read(struct bttv *btv)
+{
+	unsigned long timeout;
+	int value = 0;
+	int i;
+
+	/* better safe than sorry */
+	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we);
+
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+		udelay(5);
+	}
+
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"tea5757 read");
+
+	bus_low(btv,btv->mbox_we);
+	bus_low(btv,btv->mbox_clk);
+
+	udelay(10);
+	timeout= jiffies + msecs_to_jiffies(1000);
+
+	/* wait for DATA line to go low; error if it doesn't */
+	while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
+		schedule();
+	if (bus_in(btv,btv->mbox_data)) {
+		pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
+		return -1;
+	}
+
+	dprintk("%d: tea5757:", btv->c.nr);
+	for (i = 0; i < 24; i++) {
+		udelay(5);
+		bus_high(btv,btv->mbox_clk);
+		udelay(5);
+		dprintk_cont("%c",
+			     bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
+		bus_low(btv,btv->mbox_clk);
+		value <<= 1;
+		value |= (bus_in(btv,btv->mbox_data) == 0)?0:1;  /* MSB first */
+		dprintk_cont("%c",
+			     bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
+	}
+	dprintk_cont("\n");
+	dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
+	return value;
+}
+
+static int tea5757_write(struct bttv *btv, int value)
+{
+	int i;
+	int reg = value;
+
+	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data);
+
+	if (btv->mbox_ior) {
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+		udelay(5);
+	}
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"tea5757 write");
+
+	dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
+	bus_low(btv,btv->mbox_clk);
+	bus_high(btv,btv->mbox_we);
+	for (i = 0; i < 25; i++) {
+		if (reg & 0x1000000)
+			bus_high(btv,btv->mbox_data);
+		else
+			bus_low(btv,btv->mbox_data);
+		reg <<= 1;
+		bus_high(btv,btv->mbox_clk);
+		udelay(10);
+		bus_low(btv,btv->mbox_clk);
+		udelay(10);
+	}
+	bus_low(btv,btv->mbox_we);  /* unmute !!! */
+	return 0;
+}
+
+void tea5757_set_freq(struct bttv *btv, unsigned short freq)
+{
+	dprintk("tea5757_set_freq %d\n",freq);
+	tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
+}
+
+/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
+ *
+ * This is needed because rv605 don't use a normal multiplex, but a crosspoint
+ * switch instead (CD22M3494E). This IC can have multiple active connections
+ * between Xn (input) and Yn (output) pins. We need to clear any existing
+ * connection prior to establish a new one, pulsing the STROBE pin.
+ *
+ * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin.
+ * GPIO pins are wired as:
+ *  GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroller)
+ *  GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroller)
+ *  GPIO[7]   - DATA (xpoint)    - P1[7] (microcontroller)
+ *  GPIO[8]   -                  - P3[5] (microcontroller)
+ *  GPIO[9]   - RESET (xpoint)   - P3[6] (microcontroller)
+ *  GPIO[10]  - STROBE (xpoint)  - P3[7] (microcontroller)
+ *  GPINTR    -                  - P3[4] (microcontroller)
+ *
+ * The microcontroller is a 80C32 like. It should be possible to change xpoint
+ * configuration either directly (as we are doing) or using the microcontroller
+ * which is also wired to I2C interface. I have no further info on the
+ * microcontroller features, one would need to disassembly the firmware.
+ * note: the vendor refused to give any information on this product, all
+ *       that stuff was found using a multimeter! :)
+ */
+static void rv605_muxsel(struct bttv *btv, unsigned int input)
+{
+	static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0,
+				      0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa };
+
+	gpio_bits(0x07f, muxgpio[input]);
+
+	/* reset all conections */
+	gpio_bits(0x200,0x200);
+	mdelay(1);
+	gpio_bits(0x200,0x000);
+	mdelay(1);
+
+	/* create a new connection */
+	gpio_bits(0x480,0x480);
+	mdelay(1);
+	gpio_bits(0x480,0x080);
+	mdelay(1);
+}
+
+/* Tibet Systems 'Progress DVR' CS16 muxsel helper [Chris Fanning]
+ *
+ * The CS16 (available on eBay cheap) is a PCI board with four Fusion
+ * 878A chips, a PCI bridge, an Atmel microcontroller, four sync separator
+ * chips, ten eight input analog multiplexors, a not chip and a few
+ * other components.
+ *
+ * 16 inputs on a secondary bracket are provided and can be selected
+ * from each of the four capture chips.  Two of the eight input
+ * multiplexors are used to select from any of the 16 input signals.
+ *
+ * Unsupported hardware capabilities:
+ *  . A video output monitor on the secondary bracket can be selected from
+ *    one of the 878A chips.
+ *  . Another passthrough but I haven't spent any time investigating it.
+ *  . Digital I/O (logic level connected to GPIO) is available from an
+ *    onboard header.
+ *
+ * The on chip input mux should always be set to 2.
+ * GPIO[16:19] - Video input selection
+ * GPIO[0:3]   - Video output monitor select (only available from one 878A)
+ * GPIO[?:?]   - Digital I/O.
+ *
+ * There is an ATMEL microcontroller with an 8031 core on board.  I have not
+ * determined what function (if any) it provides.  With the microcontroller
+ * and sync separator chips a guess is that it might have to do with video
+ * switching and maybe some digital I/O.
+ */
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input)
+{
+	/* video mux */
+	gpio_bits(0x0f0000, input << 16);
+}
+
+static void tibetCS16_init(struct bttv *btv)
+{
+	/* enable gpio bits, mask obtained via btSpy */
+	gpio_inout(0xffffff, 0x0f7fff);
+	gpio_write(0x0f7fff);
+}
+
+/*
+ * The following routines for the Kodicom-4400r get a little mind-twisting.
+ * There is a "master" controller and three "slave" controllers, together
+ * an analog switch which connects any of 16 cameras to any of the BT87A's.
+ * The analog switch is controlled by the "master", but the detection order
+ * of the four BT878A chips is in an order which I just don't understand.
+ * The "master" is actually the second controller to be detected.  The
+ * logic on the board uses logical numbers for the 4 controllers, but
+ * those numbers are different from the detection sequence.  When working
+ * with the analog switch, we need to "map" from the detection sequence
+ * over to the board's logical controller number.  This mapping sequence
+ * is {3, 0, 2, 1}, i.e. the first controller to be detected is logical
+ * unit 3, the second (which is the master) is logical unit 0, etc.
+ * We need to maintain the status of the analog switch (which of the 16
+ * cameras is connected to which of the 4 controllers).  Rather than
+ * add to the bttv structure for this, we use the data reserved for
+ * the mbox (unused for this card type).
+ */
+
+/*
+ * First a routine to set the analog switch, which controls which camera
+ * is routed to which controller.  The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 7) of '1' enables the switch, and '0' disables
+ * the switch.  A STROBE bit (gpio bit 8) latches the data value into the
+ * specified address.  The idea is to set the address and data, then bring
+ * STROBE high, and finally bring STROBE back to low.
+ */
+static void kodicom4400r_write(struct bttv *btv,
+			       unsigned char xaddr,
+			       unsigned char yaddr,
+			       unsigned char data) {
+	unsigned int udata;
+
+	udata = (data << 7) | ((yaddr&3) << 4) | (xaddr&0xf);
+	gpio_bits(0x1ff, udata);		/* write ADDR and DAT */
+	gpio_bits(0x1ff, udata | (1 << 8));	/* strobe high */
+	gpio_bits(0x1ff, udata);		/* strobe low */
+}
+
+/*
+ * Next the mux select.  Both the "master" and "slave" 'cards' (controllers)
+ * use this routine.  The routine finds the "master" for the card, maps
+ * the controller number from the detected position over to the logical
+ * number, writes the appropriate data to the analog switch, and housekeeps
+ * the local copy of the switch information.  The parameter 'input' is the
+ * requested camera number (0 - 15).
+ */
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
+{
+	char *sw_status;
+	int xaddr, yaddr;
+	struct bttv *mctlr;
+	static unsigned char map[4] = {3, 0, 2, 1};
+
+	mctlr = master[btv->c.nr];
+	if (mctlr == NULL) {	/* ignore if master not yet detected */
+		return;
+	}
+	yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */
+	yaddr = map[yaddr];
+	sw_status = (char *)(&mctlr->mbox_we);
+	xaddr = input & 0xf;
+	/* Check if the controller/camera pair has changed, else ignore */
+	if (sw_status[yaddr] != xaddr)
+	{
+		/* "open" the old switch, "close" the new one, save the new */
+		kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0);
+		sw_status[yaddr] = xaddr;
+		kodicom4400r_write(mctlr, xaddr, yaddr, 1);
+	}
+}
+
+/*
+ * During initialisation, we need to reset the analog switch.  We
+ * also preset the switch to map the 4 connectors on the card to the
+ * *user's* (see above description of kodicom4400r_muxsel) channels
+ * 0 through 3
+ */
+static void kodicom4400r_init(struct bttv *btv)
+{
+	char *sw_status = (char *)(&btv->mbox_we);
+	int ix;
+
+	gpio_inout(0x0003ff, 0x0003ff);
+	gpio_write(1 << 9);	/* reset MUX */
+	gpio_write(0);
+	/* Preset camera 0 to the 4 controllers */
+	for (ix = 0; ix < 4; ix++) {
+		sw_status[ix] = ix;
+		kodicom4400r_write(btv, ix, ix, 1);
+	}
+	/*
+	 * Since this is the "master", we need to set up the
+	 * other three controller chips' pointers to this structure
+	 * for later use in the muxsel routine.
+	 */
+	if ((btv->c.nr<1) || (btv->c.nr>BTTV_MAX-3))
+	    return;
+	master[btv->c.nr-1] = btv;
+	master[btv->c.nr]   = btv;
+	master[btv->c.nr+1] = btv;
+	master[btv->c.nr+2] = btv;
+}
+
+/* The Grandtec X-Guard framegrabber card uses two Dual 4-channel
+ * video multiplexers to provide up to 16 video inputs. These
+ * multiplexers are controlled by the lower 8 GPIO pins of the
+ * bt878. The multiplexers probably Pericom PI5V331Q or similar.
+
+ * xxx0 is pin xxx of multiplexer U5,
+ * yyy1 is pin yyy of multiplexer U2
+ */
+#define ENA0    0x01
+#define ENB0    0x02
+#define ENA1    0x04
+#define ENB1    0x08
+
+#define IN10    0x10
+#define IN00    0x20
+#define IN11    0x40
+#define IN01    0x80
+
+static void xguard_muxsel(struct bttv *btv, unsigned int input)
+{
+	static const int masks[] = {
+		ENB0, ENB0|IN00, ENB0|IN10, ENB0|IN00|IN10,
+		ENA0, ENA0|IN00, ENA0|IN10, ENA0|IN00|IN10,
+		ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11,
+		ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11,
+	};
+	gpio_write(masks[input%16]);
+}
+static void picolo_tetra_init(struct bttv *btv)
+{
+	/*This is the video input redirection fonctionality : I DID NOT USED IT. */
+	btwrite (0x08<<16,BT848_GPIO_DATA);/*GPIO[19] [==> 4053 B+C] set to 1 */
+	btwrite (0x04<<16,BT848_GPIO_DATA);/*GPIO[18] [==> 4053 A]  set to 1*/
+}
+static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
+{
+
+	dprintk("%d : picolo_tetra_muxsel =>  input = %d\n", btv->c.nr, input);
+	/*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/
+	/*GPIO[20]&GPIO[21] used to choose the right input*/
+	btwrite (input<<20,BT848_GPIO_DATA);
+
+}
+
+/*
+ * ivc120_muxsel [Added by Alan Garfield <alan@fromorbit.com>]
+ *
+ * The IVC120G security card has 4 i2c controlled TDA8540 matrix
+ * swichers to provide 16 channels to MUX0. The TDA8540's have
+ * 4 independent outputs and as such the IVC120G also has the
+ * optional "Monitor Out" bus. This allows the card to be looking
+ * at one input while the monitor is looking at another.
+ *
+ * Since I've couldn't be bothered figuring out how to add an
+ * independent muxsel for the monitor bus, I've just set it to
+ * whatever the card is looking at.
+ *
+ *  OUT0 of the TDA8540's is connected to MUX0         (0x03)
+ *  OUT1 of the TDA8540's is connected to "Monitor Out"        (0x0C)
+ *
+ *  TDA8540_ALT3 IN0-3 = Channel 13 - 16       (0x03)
+ *  TDA8540_ALT4 IN0-3 = Channel 1 - 4         (0x03)
+ *  TDA8540_ALT5 IN0-3 = Channel 5 - 8         (0x03)
+ *  TDA8540_ALT6 IN0-3 = Channel 9 - 12                (0x03)
+ *
+ */
+
+/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */
+#define I2C_TDA8540        0x90
+#define I2C_TDA8540_ALT1   0x92
+#define I2C_TDA8540_ALT2   0x94
+#define I2C_TDA8540_ALT3   0x96
+#define I2C_TDA8540_ALT4   0x98
+#define I2C_TDA8540_ALT5   0x9a
+#define I2C_TDA8540_ALT6   0x9c
+
+static void ivc120_muxsel(struct bttv *btv, unsigned int input)
+{
+	/* Simple maths */
+	int key = input % 4;
+	int matrix = input / 4;
+
+	dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+		btv->c.nr, input, matrix, key);
+
+	/* Handles the input selection on the TDA8540's */
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00,
+		      ((matrix == 3) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00,
+		      ((matrix == 0) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00,
+		      ((matrix == 1) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00,
+		      ((matrix == 2) ? (key | key << 2) : 0x00), 1);
+
+	/* Handles the output enables on the TDA8540's */
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02,
+		      ((matrix == 3) ? 0x03 : 0x00), 1);  /* 13 - 16 */
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02,
+		      ((matrix == 0) ? 0x03 : 0x00), 1);  /* 1-4 */
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02,
+		      ((matrix == 1) ? 0x03 : 0x00), 1);  /* 5-8 */
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02,
+		      ((matrix == 2) ? 0x03 : 0x00), 1);  /* 9-12 */
+
+	/* 878's MUX0 is already selected for input via muxsel values */
+}
+
+
+/* PXC200 muxsel helper
+ * luke@syseng.anu.edu.au
+ * another transplant
+ * from Alessandro Rubini (rubini@linux.it)
+ *
+ * There are 4 kinds of cards:
+ * PXC200L which is bt848
+ * PXC200F which is bt848 with PIC controlling mux
+ * PXC200AL which is bt878
+ * PXC200AF which is bt878 with PIC controlling mux
+ */
+#define PX_CFG_PXC200F 0x01
+#define PX_FLAG_PXC200A  0x00001000 /* a pxc200A is bt-878 based */
+#define PX_I2C_PIC       0x0f
+#define PX_PXC200A_CARDID 0x200a1295
+#define PX_I2C_CMD_CFG   0x00
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input)
+{
+	int rc;
+	long mux;
+	int bitmask;
+	unsigned char buf[2];
+
+	/* Read PIC config to determine if this is a PXC200F */
+	/* PX_I2C_CMD_CFG*/
+	buf[0]=0;
+	buf[1]=0;
+	rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
+	if (rc) {
+		pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n",
+			 btv->c.nr, rc);
+	  /* not PXC ? do nothing */
+		return;
+	}
+
+	rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL);
+	if (!(rc & PX_CFG_PXC200F)) {
+		pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n",
+			 btv->c.nr, rc);
+		return;
+	}
+
+
+	/* The multiplexer in the 200F is handled by the GPIO port */
+	/* get correct mapping between inputs  */
+	/*  mux = bttv_tvcards[btv->type].muxsel[input] & 3; */
+	/* ** not needed!?   */
+	mux = input;
+
+	/* make sure output pins are enabled */
+	/* bitmask=0x30f; */
+	bitmask=0x302;
+	/* check whether we have a PXC200A */
+	if (btv->cardid == PX_PXC200A_CARDID)  {
+	   bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */
+	   bitmask |= 7<<4; /* the DAC */
+	}
+	btwrite(bitmask, BT848_GPIO_OUT_EN);
+
+	bitmask = btread(BT848_GPIO_DATA);
+	if (btv->cardid == PX_PXC200A_CARDID)
+	  bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7);
+	else /* older device */
+	  bitmask = (bitmask & ~0x300) | ((mux & 3) << 8);
+	btwrite(bitmask,BT848_GPIO_DATA);
+
+	/*
+	 * Was "to be safe, set the bt848 to input 0"
+	 * Actually, since it's ok at load time, better not messing
+	 * with these bits (on PXC200AF you need to set mux 2 here)
+	 *
+	 * needed because bttv-driver sets mux before calling this function
+	 */
+	if (btv->cardid == PX_PXC200A_CARDID)
+	  btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM);
+	else /* older device */
+	  btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
+
+	pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux);
+}
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input)
+{
+	unsigned int mux = input % 4;
+
+	if (input == btv->svhs)
+		mux = 0;
+
+	gpio_bits(0x3, mux);
+}
+
+/*
+ * GeoVision GV-800(S) functions
+ * Bruno Christo <bchristo@inf.ufsm.br>
+*/
+
+/* This is a function to control the analog switch, which determines which
+ * camera is routed to which controller.  The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 18) of '1' enables the switch, and '0' disables
+ * the switch.  A STROBE bit (gpio bit 17) latches the data value into the
+ * specified address. There is also a chip select (gpio bit 16).
+ * The idea is to set the address and chip select together, bring
+ * STROBE high, write the data, and finally bring STROBE back to low.
+ */
+static void gv800s_write(struct bttv *btv,
+			 unsigned char xaddr,
+			 unsigned char yaddr,
+			 unsigned char data) {
+	/* On the "master" 878A:
+	* GPIO bits 0-9 are used for the analog switch:
+	*   00 - 03:	camera selector
+	*   04 - 06:	878A (controller) selector
+	*   16: 	cselect
+	*   17:		strobe
+	*   18: 	data (1->on, 0->off)
+	*   19:		reset
+	*/
+	const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4);
+	const u32 CSELECT = 1<<16;
+	const u32 STROBE = 1<<17;
+	const u32 DATA = data<<18;
+
+	gpio_bits(0x1007f, ADDRESS | CSELECT);	/* write ADDRESS and CSELECT */
+	gpio_bits(0x20000, STROBE);		/* STROBE high */
+	gpio_bits(0x40000, DATA);		/* write DATA */
+	gpio_bits(0x20000, ~STROBE);		/* STROBE low */
+}
+
+/*
+ * GeoVision GV-800(S) muxsel
+ *
+ * Each of the 4 cards (controllers) use this function.
+ * The controller using this function selects the input through the GPIO pins
+ * of the "master" card. A pointer to this card is stored in master[btv->c.nr].
+ *
+ * The parameter 'input' is the requested camera number (0-4) on the controller.
+ * The map array has the address of each input. Note that the addresses in the
+ * array are in the sequence the original GeoVision driver uses, that is, set
+ * every controller to input 0, then to input 1, 2, 3, repeat. This means that
+ * the physical "camera 1" connector corresponds to controller 0 input 0,
+ * "camera 2" corresponds to controller 1 input 0, and so on.
+ *
+ * After getting the input address, the function then writes the appropriate
+ * data to the analog switch, and housekeeps the local copy of the switch
+ * information.
+ */
+static void gv800s_muxsel(struct bttv *btv, unsigned int input)
+{
+	struct bttv *mctlr;
+	char *sw_status;
+	int xaddr, yaddr;
+	static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 },
+					  { 0x1, 0x5, 0xb, 0x7 },
+					  { 0x2, 0x8, 0xc, 0xe },
+					  { 0x3, 0x9, 0xd, 0xf } };
+	input = input%4;
+	mctlr = master[btv->c.nr];
+	if (mctlr == NULL) {
+		/* do nothing until the "master" is detected */
+		return;
+	}
+	yaddr = (btv->c.nr - mctlr->c.nr) & 3;
+	sw_status = (char *)(&mctlr->mbox_we);
+	xaddr = map[yaddr][input] & 0xf;
+
+	/* Check if the controller/camera pair has changed, ignore otherwise */
+	if (sw_status[yaddr] != xaddr) {
+		/* disable the old switch, enable the new one and save status */
+		gv800s_write(mctlr, sw_status[yaddr], yaddr, 0);
+		sw_status[yaddr] = xaddr;
+		gv800s_write(mctlr, xaddr, yaddr, 1);
+	}
+}
+
+/* GeoVision GV-800(S) "master" chip init */
+static void gv800s_init(struct bttv *btv)
+{
+	char *sw_status = (char *)(&btv->mbox_we);
+	int ix;
+
+	gpio_inout(0xf107f, 0xf107f);
+	gpio_write(1<<19); /* reset the analog MUX */
+	gpio_write(0);
+
+	/* Preset camera 0 to the 4 controllers */
+	for (ix = 0; ix < 4; ix++) {
+		sw_status[ix] = ix;
+		gv800s_write(btv, ix, ix, 1);
+	}
+
+	/* Inputs on the "master" controller need this brightness fix */
+	bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1);
+
+	if (btv->c.nr > BTTV_MAX-4)
+		return;
+	/*
+	 * Store the "master" controller pointer in the master
+	 * array for later use in the muxsel function.
+	 */
+	master[btv->c.nr]   = btv;
+	master[btv->c.nr+1] = btv;
+	master[btv->c.nr+2] = btv;
+	master[btv->c.nr+3] = btv;
+}
+
+/* ----------------------------------------------------------------------- */
+/* motherboard chipset specific stuff                                      */
+
+void __init bttv_check_chipset(void)
+{
+	int pcipci_fail = 0;
+	struct pci_dev *dev = NULL;
+
+	if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) 	/* should check if target is AGP */
+		pcipci_fail = 1;
+	if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF))
+		triton1 = 1;
+	if (pci_pci_problems & PCIPCI_VSFX)
+		vsfx = 1;
+#ifdef PCIPCI_ALIMAGIK
+	if (pci_pci_problems & PCIPCI_ALIMAGIK)
+		latency = 0x0A;
+#endif
+
+
+	/* print warnings about any quirks found */
+	if (triton1)
+		pr_info("Host bridge needs ETBF enabled\n");
+	if (vsfx)
+		pr_info("Host bridge needs VSFX enabled\n");
+	if (pcipci_fail) {
+		pr_info("bttv and your chipset may not work together\n");
+		if (!no_overlay) {
+			pr_info("overlay will be disabled\n");
+			no_overlay = 1;
+		} else {
+			pr_info("overlay forced. Use this option at your own risk.\n");
+		}
+	}
+	if (UNSET != latency)
+		pr_info("pci latency fixup [%d]\n", latency);
+	while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL,
+				      PCI_DEVICE_ID_INTEL_82441, dev))) {
+		unsigned char b;
+		pci_read_config_byte(dev, 0x53, &b);
+		if (bttv_debug)
+			pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n",
+				b);
+	}
+}
+
+int __devinit bttv_handle_chipset(struct bttv *btv)
+{
+	unsigned char command;
+
+	if (!triton1 && !vsfx && UNSET == latency)
+		return 0;
+
+	if (bttv_verbose) {
+		if (triton1)
+			pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n",
+				btv->c.nr);
+		if (vsfx && btv->id >= 878)
+			pr_info("%d: enabling VSFX\n", btv->c.nr);
+		if (UNSET != latency)
+			pr_info("%d: setting pci timer to %d\n",
+				btv->c.nr, latency);
+	}
+
+	if (btv->id < 878) {
+		/* bt848 (mis)uses a bit in the irq mask for etbf */
+		if (triton1)
+			btv->triton1 = BT848_INT_ETBF;
+	} else {
+		/* bt878 has a bit in the pci config space for it */
+		pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command);
+		if (triton1)
+			command |= BT878_EN_TBFX;
+		if (vsfx)
+			command |= BT878_EN_VSFX;
+		pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command);
+	}
+	if (UNSET != latency)
+		pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency);
+	return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
new file mode 100644
index 000000000000..b68918c97f66
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -0,0 +1,4630 @@
+/*
+
+    bttv - Bt848 frame grabber driver
+
+    Copyright (C) 1996,97,98 Ralph  Metzler <rjkm@thp.uni-koeln.de>
+			   & Marcus Metzler <mocm@thp.uni-koeln.de>
+    (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+
+    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
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tvaudio.h>
+#include <media/msp3400.h>
+
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <media/saa6588.h>
+
+#define BTTV_VERSION "0.9.19"
+
+unsigned int bttv_num;			/* number of Bt848s in use */
+struct bttv *bttvs[BTTV_MAX];
+
+unsigned int bttv_debug;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio;
+
+/* config variables */
+#ifdef __BIG_ENDIAN
+static unsigned int bigendian=1;
+#else
+static unsigned int bigendian;
+#endif
+static unsigned int radio[BTTV_MAX];
+static unsigned int irq_debug;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int reset_crop = 1;
+
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int debug_latency;
+static int disable_ir;
+
+static unsigned int fdsr;
+
+/* options */
+static unsigned int combfilter;
+static unsigned int lumafilter;
+static unsigned int automute    = 1;
+static unsigned int chroma_agc;
+static unsigned int adc_crush   = 1;
+static unsigned int whitecrush_upper = 0xCF;
+static unsigned int whitecrush_lower = 0x7F;
+static unsigned int vcr_hack;
+static unsigned int irq_iswitch;
+static unsigned int uv_ratio    = 50;
+static unsigned int full_luma_range;
+static unsigned int coring;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int v4l2        = 1;
+
+/* insmod args */
+module_param(bttv_verbose,      int, 0644);
+module_param(bttv_gpio,         int, 0644);
+module_param(bttv_debug,        int, 0644);
+module_param(irq_debug,         int, 0644);
+module_param(debug_latency,     int, 0644);
+module_param(disable_ir,        int, 0444);
+
+module_param(fdsr,              int, 0444);
+module_param(gbuffers,          int, 0444);
+module_param(gbufsize,          int, 0444);
+module_param(reset_crop,        int, 0444);
+
+module_param(v4l2,              int, 0644);
+module_param(bigendian,         int, 0644);
+module_param(irq_iswitch,       int, 0644);
+module_param(combfilter,        int, 0444);
+module_param(lumafilter,        int, 0444);
+module_param(automute,          int, 0444);
+module_param(chroma_agc,        int, 0444);
+module_param(adc_crush,         int, 0444);
+module_param(whitecrush_upper,  int, 0444);
+module_param(whitecrush_lower,  int, 0444);
+module_param(vcr_hack,          int, 0444);
+module_param(uv_ratio,          int, 0444);
+module_param(full_luma_range,   int, 0444);
+module_param(coring,            int, 0444);
+
+module_param_array(radio,       int, NULL, 0444);
+module_param_array(video_nr,    int, NULL, 0444);
+module_param_array(radio_nr,    int, NULL, 0444);
+module_param_array(vbi_nr,      int, NULL, 0444);
+
+MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+		 "is 1 (yes) for compatibility with older applications");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
+
+/* ----------------------------------------------------------------------- */
+/* sysfs                                                                   */
+
+static ssize_t show_card(struct device *cd,
+			 struct device_attribute *attr, char *buf)
+{
+	struct video_device *vfd = container_of(cd, struct video_device, dev);
+	struct bttv *btv = video_get_drvdata(vfd);
+	return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+}
+static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+
+/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup                                                     */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct bttv *dev)
+{
+	flush_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
+/* static data                                                             */
+
+/* special timing tables from conexant... */
+static u8 SRAM_Table[][60] =
+{
+	/* PAL digital input over GPIO[7:0] */
+	{
+		45, // 45 bytes following
+		0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+		0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+		0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+		0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+		0x37,0x00,0xAF,0x21,0x00
+	},
+	/* NTSC digital input over GPIO[7:0] */
+	{
+		51, // 51 bytes following
+		0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+		0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+		0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+		0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+		0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+		0x00,
+	},
+	// TGB_NTSC392 // quartzsight
+	// This table has been modified to be used for Fusion Rev D
+	{
+		0x2A, // size of table = 42
+		0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+		0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+		0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+		0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+		0x20, 0x00
+	}
+};
+
+/* minhdelayx1	first video pixel we can capture on a line and
+   hdelayx1	start of active video, both relative to rising edge of
+		/HRESET pulse (0H) in 1 / fCLKx1.
+   swidth	width of active video and
+   totalwidth	total line width, both in 1 / fCLKx1.
+   sqwidth	total line width in square pixels.
+   vdelay	start of active video in 2 * field lines relative to
+		trailing edge of /VRESET pulse (VDELAY register).
+   sheight	height of active video in 2 * field lines.
+   videostart0	ITU-R frame line number of the line corresponding
+		to vdelay in the first field. */
+#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,	 \
+		vdelay, sheight, videostart0)				 \
+	.cropcap.bounds.left = minhdelayx1,				 \
+	/* * 2 because vertically we count field lines times two, */	 \
+	/* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */		 \
+	.cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+	/* 4 is a safety margin at the end of the line. */		 \
+	.cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,	 \
+	.cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY,	 \
+	.cropcap.defrect.left = hdelayx1,				 \
+	.cropcap.defrect.top = (videostart0) * 2,			 \
+	.cropcap.defrect.width = swidth,				 \
+	.cropcap.defrect.height = sheight,				 \
+	.cropcap.pixelaspect.numerator = totalwidth,			 \
+	.cropcap.pixelaspect.denominator = sqwidth,
+
+const struct bttv_tvnorm bttv_tvnorms[] = {
+	/* PAL-BDGHI */
+	/* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+	/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+	{
+		.v4l2_id        = V4L2_STD_PAL,
+		.name           = "PAL",
+		.Fsc            = 35468950,
+		.swidth         = 924,
+		.sheight        = 576,
+		.totalwidth     = 1135,
+		.adelay         = 0x7f,
+		.bdelay         = 0x72,
+		.iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+		.scaledtwidth   = 1135,
+		.hdelayx1       = 186,
+		.hactivex1      = 924,
+		.vdelay         = 0x20,
+		.vbipack        = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+		.sram           = 0,
+		/* ITU-R frame line number of the first VBI line
+		   we can capture, of the first and second field.
+		   The last line is determined by cropcap.bounds. */
+		.vbistart       = { 7, 320 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 186,
+			/* Should be (768 * 1135 + 944 / 2) / 944.
+			   cropcap.defrect is used for image width
+			   checks, so we keep the old value 924. */
+			/* swidth */ 924,
+			/* totalwidth */ 1135,
+			/* sqwidth */ 944,
+			/* vdelay */ 0x20,
+			/* sheight */ 576,
+			/* videostart0 */ 23)
+		/* bt878 (and bt848?) can capture another
+		   line below active video. */
+		.cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+	},{
+		.v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+		.name           = "NTSC",
+		.Fsc            = 28636363,
+		.swidth         = 768,
+		.sheight        = 480,
+		.totalwidth     = 910,
+		.adelay         = 0x68,
+		.bdelay         = 0x5d,
+		.iform          = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+		.scaledtwidth   = 910,
+		.hdelayx1       = 128,
+		.hactivex1      = 910,
+		.vdelay         = 0x1a,
+		.vbipack        = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+		.sram           = 1,
+		.vbistart	= { 10, 273 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 128,
+			/* Should be (640 * 910 + 780 / 2) / 780? */
+			/* swidth */ 768,
+			/* totalwidth */ 910,
+			/* sqwidth */ 780,
+			/* vdelay */ 0x1a,
+			/* sheight */ 480,
+			/* videostart0 */ 23)
+	},{
+		.v4l2_id        = V4L2_STD_SECAM,
+		.name           = "SECAM",
+		.Fsc            = 35468950,
+		.swidth         = 924,
+		.sheight        = 576,
+		.totalwidth     = 1135,
+		.adelay         = 0x7f,
+		.bdelay         = 0xb0,
+		.iform          = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+		.scaledtwidth   = 1135,
+		.hdelayx1       = 186,
+		.hactivex1      = 922,
+		.vdelay         = 0x20,
+		.vbipack        = 255,
+		.sram           = 0, /* like PAL, correct? */
+		.vbistart	= { 7, 320 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 186,
+			/* swidth */ 924,
+			/* totalwidth */ 1135,
+			/* sqwidth */ 944,
+			/* vdelay */ 0x20,
+			/* sheight */ 576,
+			/* videostart0 */ 23)
+	},{
+		.v4l2_id        = V4L2_STD_PAL_Nc,
+		.name           = "PAL-Nc",
+		.Fsc            = 28636363,
+		.swidth         = 640,
+		.sheight        = 576,
+		.totalwidth     = 910,
+		.adelay         = 0x68,
+		.bdelay         = 0x5d,
+		.iform          = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+		.scaledtwidth   = 780,
+		.hdelayx1       = 130,
+		.hactivex1      = 734,
+		.vdelay         = 0x1a,
+		.vbipack        = 144,
+		.sram           = -1,
+		.vbistart	= { 7, 320 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 130,
+			/* swidth */ (640 * 910 + 780 / 2) / 780,
+			/* totalwidth */ 910,
+			/* sqwidth */ 780,
+			/* vdelay */ 0x1a,
+			/* sheight */ 576,
+			/* videostart0 */ 23)
+	},{
+		.v4l2_id        = V4L2_STD_PAL_M,
+		.name           = "PAL-M",
+		.Fsc            = 28636363,
+		.swidth         = 640,
+		.sheight        = 480,
+		.totalwidth     = 910,
+		.adelay         = 0x68,
+		.bdelay         = 0x5d,
+		.iform          = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+		.scaledtwidth   = 780,
+		.hdelayx1       = 135,
+		.hactivex1      = 754,
+		.vdelay         = 0x1a,
+		.vbipack        = 144,
+		.sram           = -1,
+		.vbistart	= { 10, 273 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 135,
+			/* swidth */ (640 * 910 + 780 / 2) / 780,
+			/* totalwidth */ 910,
+			/* sqwidth */ 780,
+			/* vdelay */ 0x1a,
+			/* sheight */ 480,
+			/* videostart0 */ 23)
+	},{
+		.v4l2_id        = V4L2_STD_PAL_N,
+		.name           = "PAL-N",
+		.Fsc            = 35468950,
+		.swidth         = 768,
+		.sheight        = 576,
+		.totalwidth     = 1135,
+		.adelay         = 0x7f,
+		.bdelay         = 0x72,
+		.iform          = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+		.scaledtwidth   = 944,
+		.hdelayx1       = 186,
+		.hactivex1      = 922,
+		.vdelay         = 0x20,
+		.vbipack        = 144,
+		.sram           = -1,
+		.vbistart       = { 7, 320 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 186,
+			/* swidth */ (768 * 1135 + 944 / 2) / 944,
+			/* totalwidth */ 1135,
+			/* sqwidth */ 944,
+			/* vdelay */ 0x20,
+			/* sheight */ 576,
+			/* videostart0 */ 23)
+	},{
+		.v4l2_id        = V4L2_STD_NTSC_M_JP,
+		.name           = "NTSC-JP",
+		.Fsc            = 28636363,
+		.swidth         = 640,
+		.sheight        = 480,
+		.totalwidth     = 910,
+		.adelay         = 0x68,
+		.bdelay         = 0x5d,
+		.iform          = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+		.scaledtwidth   = 780,
+		.hdelayx1       = 135,
+		.hactivex1      = 754,
+		.vdelay         = 0x16,
+		.vbipack        = 144,
+		.sram           = -1,
+		.vbistart       = { 10, 273 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 135,
+			/* swidth */ (640 * 910 + 780 / 2) / 780,
+			/* totalwidth */ 910,
+			/* sqwidth */ 780,
+			/* vdelay */ 0x16,
+			/* sheight */ 480,
+			/* videostart0 */ 23)
+	},{
+		/* that one hopefully works with the strange timing
+		 * which video recorders produce when playing a NTSC
+		 * tape on a PAL TV ... */
+		.v4l2_id        = V4L2_STD_PAL_60,
+		.name           = "PAL-60",
+		.Fsc            = 35468950,
+		.swidth         = 924,
+		.sheight        = 480,
+		.totalwidth     = 1135,
+		.adelay         = 0x7f,
+		.bdelay         = 0x72,
+		.iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+		.scaledtwidth   = 1135,
+		.hdelayx1       = 186,
+		.hactivex1      = 924,
+		.vdelay         = 0x1a,
+		.vbipack        = 255,
+		.vtotal         = 524,
+		.sram           = -1,
+		.vbistart	= { 10, 273 },
+		CROPCAP(/* minhdelayx1 */ 68,
+			/* hdelayx1 */ 186,
+			/* swidth */ 924,
+			/* totalwidth */ 1135,
+			/* sqwidth */ 944,
+			/* vdelay */ 0x1a,
+			/* sheight */ 480,
+			/* videostart0 */ 23)
+	}
+};
+static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+   packed pixel formats must come first */
+static const struct bttv_format formats[] = {
+	{
+		.name     = "8 bpp, gray",
+		.fourcc   = V4L2_PIX_FMT_GREY,
+		.btformat = BT848_COLOR_FMT_Y8,
+		.depth    = 8,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "8 bpp, dithered color",
+		.fourcc   = V4L2_PIX_FMT_HI240,
+		.btformat = BT848_COLOR_FMT_RGB8,
+		.depth    = 8,
+		.flags    = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+	},{
+		.name     = "15 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB555,
+		.btformat = BT848_COLOR_FMT_RGB15,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "15 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB555X,
+		.btformat = BT848_COLOR_FMT_RGB15,
+		.btswap   = 0x03, /* byteswap */
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "16 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB565,
+		.btformat = BT848_COLOR_FMT_RGB16,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "16 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB565X,
+		.btformat = BT848_COLOR_FMT_RGB16,
+		.btswap   = 0x03, /* byteswap */
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "24 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR24,
+		.btformat = BT848_COLOR_FMT_RGB24,
+		.depth    = 24,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "32 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR32,
+		.btformat = BT848_COLOR_FMT_RGB32,
+		.depth    = 32,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "32 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB32,
+		.btformat = BT848_COLOR_FMT_RGB32,
+		.btswap   = 0x0f, /* byte+word swap */
+		.depth    = 32,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "4:2:2, packed, YUYV",
+		.fourcc   = V4L2_PIX_FMT_YUYV,
+		.btformat = BT848_COLOR_FMT_YUY2,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "4:2:2, packed, UYVY",
+		.fourcc   = V4L2_PIX_FMT_UYVY,
+		.btformat = BT848_COLOR_FMT_YUY2,
+		.btswap   = 0x03, /* byteswap */
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "4:2:2, planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV422P,
+		.btformat = BT848_COLOR_FMT_YCrCb422,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PLANAR,
+		.hshift   = 1,
+		.vshift   = 0,
+	},{
+		.name     = "4:2:0, planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV420,
+		.btformat = BT848_COLOR_FMT_YCrCb422,
+		.depth    = 12,
+		.flags    = FORMAT_FLAGS_PLANAR,
+		.hshift   = 1,
+		.vshift   = 1,
+	},{
+		.name     = "4:2:0, planar, Y-Cr-Cb",
+		.fourcc   = V4L2_PIX_FMT_YVU420,
+		.btformat = BT848_COLOR_FMT_YCrCb422,
+		.depth    = 12,
+		.flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+		.hshift   = 1,
+		.vshift   = 1,
+	},{
+		.name     = "4:1:1, planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV411P,
+		.btformat = BT848_COLOR_FMT_YCrCb411,
+		.depth    = 12,
+		.flags    = FORMAT_FLAGS_PLANAR,
+		.hshift   = 2,
+		.vshift   = 0,
+	},{
+		.name     = "4:1:0, planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV410,
+		.btformat = BT848_COLOR_FMT_YCrCb411,
+		.depth    = 9,
+		.flags    = FORMAT_FLAGS_PLANAR,
+		.hshift   = 2,
+		.vshift   = 2,
+	},{
+		.name     = "4:1:0, planar, Y-Cr-Cb",
+		.fourcc   = V4L2_PIX_FMT_YVU410,
+		.btformat = BT848_COLOR_FMT_YCrCb411,
+		.depth    = 9,
+		.flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+		.hshift   = 2,
+		.vshift   = 2,
+	},{
+		.name     = "raw scanlines",
+		.fourcc   = -1,
+		.btformat = BT848_COLOR_FMT_RAW,
+		.depth    = 8,
+		.flags    = FORMAT_FLAGS_RAW,
+	}
+};
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
+
+/* ----------------------------------------------------------------------- */
+
+#define V4L2_CID_PRIVATE_CHROMA_AGC  (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER  (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE    (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER  (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH   (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_VCR_HACK    (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER   (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER   (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_UV_RATIO    (V4L2_CID_PRIVATE_BASE + 8)
+#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE    (V4L2_CID_PRIVATE_BASE + 9)
+#define V4L2_CID_PRIVATE_CORING      (V4L2_CID_PRIVATE_BASE + 10)
+#define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 11)
+
+static const struct v4l2_queryctrl no_ctl = {
+	.name  = "42",
+	.flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+	/* --- video --- */
+	{
+		.id            = V4L2_CID_BRIGHTNESS,
+		.name          = "Brightness",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 256,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_CONTRAST,
+		.name          = "Contrast",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 128,
+		.default_value = 27648,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_SATURATION,
+		.name          = "Saturation",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 128,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_HUE,
+		.name          = "Hue",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 256,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},
+	/* --- audio --- */
+	{
+		.id            = V4L2_CID_AUDIO_MUTE,
+		.name          = "Mute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_AUDIO_VOLUME,
+		.name          = "Volume",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 65535,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_BALANCE,
+		.name          = "Balance",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_BASS,
+		.name          = "Bass",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_TREBLE,
+		.name          = "Treble",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},
+	/* --- private --- */
+	{
+		.id            = V4L2_CID_PRIVATE_CHROMA_AGC,
+		.name          = "chroma agc",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_COMBFILTER,
+		.name          = "combfilter",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_AUTOMUTE,
+		.name          = "automute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_LUMAFILTER,
+		.name          = "luma decimation filter",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_AGC_CRUSH,
+		.name          = "agc crush",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_VCR_HACK,
+		.name          = "vcr hack",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+		.name          = "whitecrush upper",
+		.minimum       = 0,
+		.maximum       = 255,
+		.step          = 1,
+		.default_value = 0xCF,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+		.name          = "whitecrush lower",
+		.minimum       = 0,
+		.maximum       = 255,
+		.step          = 1,
+		.default_value = 0x7F,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_UV_RATIO,
+		.name          = "uv ratio",
+		.minimum       = 0,
+		.maximum       = 100,
+		.step          = 1,
+		.default_value = 50,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+		.name          = "full luma range",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_CORING,
+		.name          = "coring",
+		.minimum       = 0,
+		.maximum       = 3,
+		.step          = 1,
+		.default_value = 0,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	}
+
+
+
+};
+
+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                                                     */
+
+/*
+   RESOURCE_    allocated by                freed by
+
+   VIDEO_READ   bttv_read 1)                bttv_read 2)
+
+   VIDEO_STREAM VIDIOC_STREAMON             VIDIOC_STREAMOFF
+		 VIDIOC_QBUF 1)              bttv_release
+		 VIDIOCMCAPTURE 1)
+
+   OVERLAY	 VIDIOCCAPTURE on            VIDIOCCAPTURE off
+		 VIDIOC_OVERLAY on           VIDIOC_OVERLAY off
+		 3)                          bttv_release
+
+   VBI		 VIDIOC_STREAMON             VIDIOC_STREAMOFF
+		 VIDIOC_QBUF 1)              bttv_release
+		 bttv_read, bttv_poll 1) 4)
+
+   1) The resource must be allocated when we enter buffer prepare functions
+      and remain allocated while buffers are in the DMA queue.
+   2) This is a single frame read.
+   3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+      RESOURCE_OVERLAY is allocated.
+   4) This is a continuous read, implies VIDIOC_STREAMON.
+
+   Note this driver permits video input and standard changes regardless if
+   resources are allocated.
+*/
+
+#define VBI_RESOURCES (RESOURCE_VBI)
+#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+			 RESOURCE_VIDEO_STREAM | \
+			 RESOURCE_OVERLAY)
+
+static
+int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+{
+	int xbits; /* mutual exclusive resources */
+
+	if (fh->resources & bit)
+		/* have it already allocated */
+		return 1;
+
+	xbits = bit;
+	if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+		xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+
+	/* is it free? */
+	if (btv->resources & xbits) {
+		/* no, someone else uses it */
+		goto fail;
+	}
+
+	if ((bit & VIDEO_RESOURCES)
+	    && 0 == (btv->resources & VIDEO_RESOURCES)) {
+		/* Do crop - use current, don't - use default parameters. */
+		__s32 top = btv->crop[!!fh->do_crop].rect.top;
+
+		if (btv->vbi_end > top)
+			goto fail;
+
+		/* We cannot capture the same line as video and VBI data.
+		   Claim scan lines crop[].rect.top to bottom. */
+		btv->crop_start = top;
+	} else if (bit & VBI_RESOURCES) {
+		__s32 end = fh->vbi_fmt.end;
+
+		if (end > btv->crop_start)
+			goto fail;
+
+		/* Claim scan lines above fh->vbi_fmt.end. */
+		btv->vbi_end = end;
+	}
+
+	/* it's free, grab it */
+	fh->resources  |= bit;
+	btv->resources |= bit;
+	return 1;
+
+ fail:
+	return 0;
+}
+
+static
+int check_btres(struct bttv_fh *fh, int bit)
+{
+	return (fh->resources & bit);
+}
+
+static
+int locked_btres(struct bttv *btv, int bit)
+{
+	return (btv->resources & bit);
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_vbi_lines(struct bttv *btv)
+{
+	btv->vbi_end = 0;
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_video_lines(struct bttv *btv)
+{
+	const struct bttv_tvnorm *tvnorm;
+	u8 crop;
+
+	tvnorm = &bttv_tvnorms[btv->tvnorm];
+	btv->crop_start = tvnorm->cropcap.bounds.top
+		+ tvnorm->cropcap.bounds.height;
+
+	/* VBI capturing ends at VDELAY, start of video capturing, no
+	   matter how many lines the VBI RISC program expects. When video
+	   capturing is off, it shall no longer "preempt" VBI capturing,
+	   so we set VDELAY to maximum. */
+	crop = btread(BT848_E_CROP) | 0xc0;
+	btwrite(crop, BT848_E_CROP);
+	btwrite(0xfe, BT848_E_VDELAY_LO);
+	btwrite(crop, BT848_O_CROP);
+	btwrite(0xfe, BT848_O_VDELAY_LO);
+}
+
+static
+void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+{
+	if ((fh->resources & bits) != bits) {
+		/* trying to free resources not allocated by us ... */
+		pr_err("BUG! (btres)\n");
+	}
+	fh->resources  &= ~bits;
+	btv->resources &= ~bits;
+
+	bits = btv->resources;
+
+	if (0 == (bits & VIDEO_RESOURCES))
+		disclaim_video_lines(btv);
+
+	if (0 == (bits & VBI_RESOURCES))
+		disclaim_vbi_lines(btv);
+}
+
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
+
+/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+   PLL_X = Reference pre-divider (0=1, 1=2)
+   PLL_C = Post divider (0=6, 1=4)
+   PLL_I = Integer input
+   PLL_F = Fractional input
+
+   F_input = 28.636363 MHz:
+   PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+*/
+
+static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+{
+	unsigned char fl, fh, fi;
+
+	/* prevent overflows */
+	fin/=4;
+	fout/=4;
+
+	fout*=12;
+	fi=fout/fin;
+
+	fout=(fout%fin)*256;
+	fh=fout/fin;
+
+	fout=(fout%fin)*256;
+	fl=fout/fin;
+
+	btwrite(fl, BT848_PLL_F_LO);
+	btwrite(fh, BT848_PLL_F_HI);
+	btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+}
+
+static void set_pll(struct bttv *btv)
+{
+	int i;
+
+	if (!btv->pll.pll_crystal)
+		return;
+
+	if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+		dprintk("%d: PLL: no change required\n", btv->c.nr);
+		return;
+	}
+
+	if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+		/* no PLL needed */
+		if (btv->pll.pll_current == 0)
+			return;
+		if (bttv_verbose)
+			pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+				btv->c.nr, btv->pll.pll_ifreq);
+		btwrite(0x00,BT848_TGCTRL);
+		btwrite(0x00,BT848_PLL_XCI);
+		btv->pll.pll_current = 0;
+		return;
+	}
+
+	if (bttv_verbose)
+		pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+			btv->c.nr,
+			btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+	set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
+	for (i=0; i<10; i++) {
+		/*  Let other people run while the PLL stabilizes */
+		msleep(10);
+
+		if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+			btwrite(0,BT848_DSTATUS);
+		} else {
+			btwrite(0x08,BT848_TGCTRL);
+			btv->pll.pll_current = btv->pll.pll_ofreq;
+			if (bttv_verbose)
+				pr_info("PLL set ok\n");
+			return;
+		}
+	}
+	btv->pll.pll_current = -1;
+	if (bttv_verbose)
+		pr_info("Setting PLL failed\n");
+	return;
+}
+
+/* used to switch between the bt848's analog/digital video capture modes */
+static void bt848A_set_timing(struct bttv *btv)
+{
+	int i, len;
+	int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+	int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
+
+	if (btv->input == btv->dig) {
+		dprintk("%d: load digital timing table (table_idx=%d)\n",
+			btv->c.nr,table_idx);
+
+		/* timing change...reset timing generator address */
+		btwrite(0x00, BT848_TGCTRL);
+		btwrite(0x02, BT848_TGCTRL);
+		btwrite(0x00, BT848_TGCTRL);
+
+		len=SRAM_Table[table_idx][0];
+		for(i = 1; i <= len; i++)
+			btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+		btv->pll.pll_ofreq = 27000000;
+
+		set_pll(btv);
+		btwrite(0x11, BT848_TGCTRL);
+		btwrite(0x41, BT848_DVSIF);
+	} else {
+		btv->pll.pll_ofreq = fsc;
+		set_pll(btv);
+		btwrite(0x0, BT848_DVSIF);
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
+{
+	int value;
+
+	// printk("set bright: %d\n", bright); // DEBUG
+	btv->bright = bright;
+
+	/* We want -128 to 127 we get 0-65535 */
+	value = (bright >> 8) - 128;
+	btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+	int value;
+
+	btv->hue = hue;
+
+	/* -128 to 127 */
+	value = (hue >> 8) - 128;
+	btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+	int value,hibit;
+
+	btv->contrast = cont;
+
+	/* 0-511 */
+	value = (cont  >> 7);
+	hibit = (value >> 6) & 4;
+	btwrite(value & 0xff, BT848_CONTRAST_LO);
+	btaor(hibit, ~4, BT848_E_CONTROL);
+	btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+	int val_u,val_v,hibits;
+
+	btv->saturation = color;
+
+	/* 0-511 for the color */
+	val_u   = ((color * btv->opt_uv_ratio) / 50) >> 7;
+	val_v   = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+	hibits  = (val_u >> 7) & 2;
+	hibits |= (val_v >> 8) & 1;
+	btwrite(val_u & 0xff, BT848_SAT_U_LO);
+	btwrite(val_v & 0xff, BT848_SAT_V_LO);
+	btaor(hibits, ~3, BT848_E_CONTROL);
+	btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+	int mux,mask2;
+
+	if (input >= bttv_tvcards[btv->c.type].video_inputs)
+		return -EINVAL;
+
+	/* needed by RemoteVideo MX */
+	mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+	if (mask2)
+		gpio_inout(mask2,mask2);
+
+	if (input == btv->svhs)  {
+		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+	} else {
+		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+	}
+	mux = bttv_muxsel(btv, input);
+	btaor(mux<<5, ~(3<<5), BT848_IFORM);
+	dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+
+	/* card specific hook */
+	if(bttv_tvcards[btv->c.type].muxsel_hook)
+		bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+	return 0;
+}
+
+static char *audio_modes[] = {
+	"audio: tuner", "audio: radio", "audio: extern",
+	"audio: intern", "audio: mute"
+};
+
+static int
+audio_mux(struct bttv *btv, int input, int mute)
+{
+	int gpio_val, signal;
+	struct v4l2_control ctrl;
+
+	gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+		   bttv_tvcards[btv->c.type].gpiomask);
+	signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+
+	btv->mute = mute;
+	btv->audio = input;
+
+	/* automute */
+	mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+
+	if (mute)
+		gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+	else
+		gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+
+	switch (btv->c.type) {
+	case BTTV_BOARD_VOODOOTV_FM:
+	case BTTV_BOARD_VOODOOTV_200:
+		gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+		break;
+
+	default:
+		gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+	}
+
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+	if (in_interrupt())
+		return 0;
+
+	ctrl.id = V4L2_CID_AUDIO_MUTE;
+	ctrl.value = btv->mute;
+	bttv_call_all(btv, core, s_ctrl, &ctrl);
+	if (btv->sd_msp34xx) {
+		u32 in;
+
+		/* Note: the inputs tuner/radio/extern/intern are translated
+		   to msp routings. This assumes common behavior for all msp3400
+		   based TV cards. When this assumption fails, then the
+		   specific MSP routing must be added to the card table.
+		   For now this is sufficient. */
+		switch (input) {
+		case TVAUDIO_INPUT_RADIO:
+			/* Some boards need the msp do to the radio demod */
+			if (btv->radio_uses_msp_demodulator) {
+				in = MSP_INPUT_DEFAULT;
+				break;
+			}
+			in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+			break;
+		case TVAUDIO_INPUT_EXTERN:
+			in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+			break;
+		case TVAUDIO_INPUT_INTERN:
+			/* Yes, this is the same input as for RADIO. I doubt
+			   if this is ever used. The only board with an INTERN
+			   input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+			   that was tested. My guess is that the whole INTERN
+			   input does not work. */
+			in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+				    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+			break;
+		case TVAUDIO_INPUT_TUNER:
+		default:
+			/* This is the only card that uses TUNER2, and afaik,
+			   is the only difference between the VOODOOTV_FM
+			   and VOODOOTV_200 */
+			if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+				in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+					MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+			else
+				in = MSP_INPUT_DEFAULT;
+			break;
+		}
+		v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+			       in, MSP_OUTPUT_DEFAULT, 0);
+	}
+	if (btv->sd_tvaudio) {
+		v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+				input, 0, 0);
+	}
+	return 0;
+}
+
+static inline int
+audio_mute(struct bttv *btv, int mute)
+{
+	return audio_mux(btv, btv->audio, mute);
+}
+
+static inline int
+audio_input(struct bttv *btv, int input)
+{
+	return audio_mux(btv, input, btv->mute);
+}
+
+static void
+bttv_crop_calc_limits(struct bttv_crop *c)
+{
+	/* Scale factor min. 1:1, max. 16:1. Min. image size
+	   48 x 32. Scaled width must be a multiple of 4. */
+
+	if (1) {
+		/* For bug compatibility with VIDIOCGCAP and image
+		   size checks in earlier driver versions. */
+		c->min_scaled_width = 48;
+		c->min_scaled_height = 32;
+	} else {
+		c->min_scaled_width =
+			(max(48, c->rect.width >> 4) + 3) & ~3;
+		c->min_scaled_height =
+			max(32, c->rect.height >> 4);
+	}
+
+	c->max_scaled_width  = c->rect.width & ~3;
+	c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+{
+	c->rect = bttv_tvnorms[norm].cropcap.defrect;
+	bttv_crop_calc_limits(c);
+}
+
+/* Call with btv->lock down. */
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+	const struct bttv_tvnorm *tvnorm;
+	v4l2_std_id id;
+
+	BUG_ON(norm >= BTTV_TVNORMS);
+	BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+
+	tvnorm = &bttv_tvnorms[norm];
+
+	if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+		    sizeof (tvnorm->cropcap))) {
+		bttv_crop_reset(&btv->crop[0], norm);
+		btv->crop[1] = btv->crop[0]; /* current = default */
+
+		if (0 == (btv->resources & VIDEO_RESOURCES)) {
+			btv->crop_start = tvnorm->cropcap.bounds.top
+				+ tvnorm->cropcap.bounds.height;
+		}
+	}
+
+	btv->tvnorm = norm;
+
+	btwrite(tvnorm->adelay, BT848_ADELAY);
+	btwrite(tvnorm->bdelay, BT848_BDELAY);
+	btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+	      BT848_IFORM);
+	btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+	btwrite(1, BT848_VBI_PACK_DEL);
+	bt848A_set_timing(btv);
+
+	switch (btv->c.type) {
+	case BTTV_BOARD_VOODOOTV_FM:
+	case BTTV_BOARD_VOODOOTV_200:
+		bttv_tda9880_setnorm(btv, gpio_read());
+		break;
+	}
+	id = tvnorm->v4l2_id;
+	bttv_call_all(btv, core, s_std, id);
+
+	return 0;
+}
+
+/* Call with btv->lock down. */
+static void
+set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+{
+	unsigned long flags;
+
+	btv->input = input;
+	if (irq_iswitch) {
+		spin_lock_irqsave(&btv->s_lock,flags);
+		if (btv->curr.frame_irq) {
+			/* active capture -> delayed input switch */
+			btv->new_input = input;
+		} else {
+			video_mux(btv,input);
+		}
+		spin_unlock_irqrestore(&btv->s_lock,flags);
+	} else {
+		video_mux(btv,input);
+	}
+	audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+			 TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+	set_tvnorm(btv, norm);
+}
+
+static void init_irqreg(struct bttv *btv)
+{
+	/* clear status */
+	btwrite(0xfffffUL, BT848_INT_STAT);
+
+	if (bttv_tvcards[btv->c.type].no_video) {
+		/* i2c only */
+		btwrite(BT848_INT_I2CDONE,
+			BT848_INT_MASK);
+	} else {
+		/* full video */
+		btwrite((btv->triton1)  |
+			(btv->gpioirq ? BT848_INT_GPINT : 0) |
+			BT848_INT_SCERR |
+			(fdsr ? BT848_INT_FDSR : 0) |
+			BT848_INT_RISCI | BT848_INT_OCERR |
+			BT848_INT_FMTCHG|BT848_INT_HLOCK|
+			BT848_INT_I2CDONE,
+			BT848_INT_MASK);
+	}
+}
+
+static void init_bt848(struct bttv *btv)
+{
+	int val;
+
+	if (bttv_tvcards[btv->c.type].no_video) {
+		/* very basic init only */
+		init_irqreg(btv);
+		return;
+	}
+
+	btwrite(0x00, BT848_CAP_CTL);
+	btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+	btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+	/* set planar and packed mode trigger points and         */
+	/* set rising edge of inverted GPINTR pin as irq trigger */
+	btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+		BT848_GPIO_DMA_CTL_PLTP1_16|
+		BT848_GPIO_DMA_CTL_PLTP23_16|
+		BT848_GPIO_DMA_CTL_GPINTC|
+		BT848_GPIO_DMA_CTL_GPINTI,
+		BT848_GPIO_DMA_CTL);
+
+	val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+	btwrite(val, BT848_E_SCLOOP);
+	btwrite(val, BT848_O_SCLOOP);
+
+	btwrite(0x20, BT848_E_VSCALE_HI);
+	btwrite(0x20, BT848_O_VSCALE_HI);
+	btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+		BT848_ADC);
+
+	btwrite(whitecrush_upper, BT848_WC_UP);
+	btwrite(whitecrush_lower, BT848_WC_DOWN);
+
+	if (btv->opt_lumafilter) {
+		btwrite(0, BT848_E_CONTROL);
+		btwrite(0, BT848_O_CONTROL);
+	} else {
+		btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+		btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+	}
+
+	bt848_bright(btv,   btv->bright);
+	bt848_hue(btv,      btv->hue);
+	bt848_contrast(btv, btv->contrast);
+	bt848_sat(btv,      btv->saturation);
+
+	/* interrupt */
+	init_irqreg(btv);
+}
+
+static void bttv_reinit_bt848(struct bttv *btv)
+{
+	unsigned long flags;
+
+	if (bttv_verbose)
+		pr_info("%d: reset, reinitialize\n", btv->c.nr);
+	spin_lock_irqsave(&btv->s_lock,flags);
+	btv->errors=0;
+	bttv_set_dma(btv,0);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+
+	init_bt848(btv);
+	btv->pll.pll_current = -1;
+	set_input(btv, btv->input, btv->tvnorm);
+}
+
+static int bttv_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *c)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value = btv->bright;
+		break;
+	case V4L2_CID_HUE:
+		c->value = btv->hue;
+		break;
+	case V4L2_CID_CONTRAST:
+		c->value = btv->contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		c->value = btv->saturation;
+		break;
+
+	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:
+		bttv_call_all(btv, core, g_ctrl, c);
+		break;
+
+	case V4L2_CID_PRIVATE_CHROMA_AGC:
+		c->value = btv->opt_chroma_agc;
+		break;
+	case V4L2_CID_PRIVATE_COMBFILTER:
+		c->value = btv->opt_combfilter;
+		break;
+	case V4L2_CID_PRIVATE_LUMAFILTER:
+		c->value = btv->opt_lumafilter;
+		break;
+	case V4L2_CID_PRIVATE_AUTOMUTE:
+		c->value = btv->opt_automute;
+		break;
+	case V4L2_CID_PRIVATE_AGC_CRUSH:
+		c->value = btv->opt_adc_crush;
+		break;
+	case V4L2_CID_PRIVATE_VCR_HACK:
+		c->value = btv->opt_vcr_hack;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+		c->value = btv->opt_whitecrush_upper;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+		c->value = btv->opt_whitecrush_lower;
+		break;
+	case V4L2_CID_PRIVATE_UV_RATIO:
+		c->value = btv->opt_uv_ratio;
+		break;
+	case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+		c->value = btv->opt_full_luma_range;
+		break;
+	case V4L2_CID_PRIVATE_CORING:
+		c->value = btv->opt_coring;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int bttv_s_ctrl(struct file *file, void *f,
+					struct v4l2_control *c)
+{
+	int err;
+	int val;
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (0 != err)
+		return err;
+
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		bt848_bright(btv, c->value);
+		break;
+	case V4L2_CID_HUE:
+		bt848_hue(btv, c->value);
+		break;
+	case V4L2_CID_CONTRAST:
+		bt848_contrast(btv, c->value);
+		break;
+	case V4L2_CID_SATURATION:
+		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_all(btv, core, s_ctrl, c);
+		break;
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+		bttv_call_all(btv, core, s_ctrl, c);
+		break;
+
+	case V4L2_CID_PRIVATE_CHROMA_AGC:
+		btv->opt_chroma_agc = c->value;
+		val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+		btwrite(val, BT848_E_SCLOOP);
+		btwrite(val, BT848_O_SCLOOP);
+		break;
+	case V4L2_CID_PRIVATE_COMBFILTER:
+		btv->opt_combfilter = c->value;
+		break;
+	case V4L2_CID_PRIVATE_LUMAFILTER:
+		btv->opt_lumafilter = c->value;
+		if (btv->opt_lumafilter) {
+			btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+			btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+		} else {
+			btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+			btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+		}
+		break;
+	case V4L2_CID_PRIVATE_AUTOMUTE:
+		btv->opt_automute = c->value;
+		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);
+		break;
+	case V4L2_CID_PRIVATE_VCR_HACK:
+		btv->opt_vcr_hack = c->value;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+		btv->opt_whitecrush_upper = c->value;
+		btwrite(c->value, BT848_WC_UP);
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+		btv->opt_whitecrush_lower = c->value;
+		btwrite(c->value, BT848_WC_DOWN);
+		break;
+	case V4L2_CID_PRIVATE_UV_RATIO:
+		btv->opt_uv_ratio = c->value;
+		bt848_sat(btv, btv->saturation);
+		break;
+	case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+		btv->opt_full_luma_range = c->value;
+		btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+		break;
+	case V4L2_CID_PRIVATE_CORING:
+		btv->opt_coring = c->value;
+		btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
+{
+	unsigned int outbits, data;
+	outbits = btread(BT848_GPIO_OUT_EN);
+	data    = btread(BT848_GPIO_DATA);
+	pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+		 btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+}
+
+static void bttv_field_count(struct bttv *btv)
+{
+	int need_count = 0;
+
+	if (btv->users)
+		need_count++;
+
+	if (need_count) {
+		/* start field counter */
+		btor(BT848_INT_VSYNC,BT848_INT_MASK);
+	} else {
+		/* stop field counter */
+		btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+		btv->field_count = 0;
+	}
+}
+
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < FORMATS; i++) {
+		if (-1 == formats[i].fourcc)
+			continue;
+		if (formats[i].fourcc == fourcc)
+			return formats+i;
+	}
+	return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers                                                            */
+
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+		    struct bttv_buffer *new)
+{
+	struct bttv_buffer *old;
+	unsigned long flags;
+	int retval = 0;
+
+	dprintk("switch_overlay: enter [new=%p]\n", new);
+	if (new)
+		new->vb.state = VIDEOBUF_DONE;
+	spin_lock_irqsave(&btv->s_lock,flags);
+	old = btv->screen;
+	btv->screen = new;
+	btv->loop_irq |= 1;
+	bttv_set_dma(btv, 0x03);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+	if (NULL != old) {
+		dprintk("switch_overlay: old=%p state is %d\n",
+			old, old->vb.state);
+		bttv_dma_free(&fh->cap,btv, old);
+		kfree(old);
+	}
+	if (NULL == new)
+		free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+	dprintk("switch_overlay: done\n");
+	return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface                                               */
+
+static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+			       struct bttv_buffer *buf,
+			       const struct bttv_format *fmt,
+			       unsigned int width, unsigned int height,
+			       enum v4l2_field field)
+{
+	struct bttv_fh *fh = q->priv_data;
+	int redo_dma_risc = 0;
+	struct bttv_crop c;
+	int norm;
+	int rc;
+
+	/* check settings */
+	if (NULL == fmt)
+		return -EINVAL;
+	if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+		width  = RAW_BPL;
+		height = RAW_LINES*2;
+		if (width*height > buf->vb.bsize)
+			return -EINVAL;
+		buf->vb.size = buf->vb.bsize;
+
+		/* Make sure tvnorm and vbi_end remain consistent
+		   until we're done. */
+
+		norm = btv->tvnorm;
+
+		/* In this mode capturing always starts at defrect.top
+		   (default VDELAY), ignoring cropping parameters. */
+		if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+			return -EINVAL;
+		}
+
+		c.rect = bttv_tvnorms[norm].cropcap.defrect;
+	} else {
+		norm = btv->tvnorm;
+		c = btv->crop[!!fh->do_crop];
+
+		if (width < c.min_scaled_width ||
+		    width > c.max_scaled_width ||
+		    height < c.min_scaled_height)
+			return -EINVAL;
+
+		switch (field) {
+		case V4L2_FIELD_TOP:
+		case V4L2_FIELD_BOTTOM:
+		case V4L2_FIELD_ALTERNATE:
+			/* btv->crop counts frame lines. Max. scale
+			   factor is 16:1 for frames, 8:1 for fields. */
+			if (height * 2 > c.max_scaled_height)
+				return -EINVAL;
+			break;
+
+		default:
+			if (height > c.max_scaled_height)
+				return -EINVAL;
+			break;
+		}
+
+		buf->vb.size = (width * height * fmt->depth) >> 3;
+		if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+			return -EINVAL;
+	}
+
+	/* alloc + fill struct bttv_buffer (if changed) */
+	if (buf->vb.width != width || buf->vb.height != height ||
+	    buf->vb.field != field ||
+	    buf->tvnorm != norm || buf->fmt != fmt ||
+	    buf->crop.top != c.rect.top ||
+	    buf->crop.left != c.rect.left ||
+	    buf->crop.width != c.rect.width ||
+	    buf->crop.height != c.rect.height) {
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = norm;
+		buf->fmt       = fmt;
+		buf->crop      = c.rect;
+		redo_dma_risc = 1;
+	}
+
+	/* alloc risc memory */
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		redo_dma_risc = 1;
+		if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+			goto fail;
+	}
+
+	if (redo_dma_risc)
+		if (0 != (rc = bttv_buffer_risc(btv,buf)))
+			goto fail;
+
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+ fail:
+	bttv_dma_free(q,btv,buf);
+	return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+	struct bttv_fh *fh = q->priv_data;
+
+	*size = fh->fmt->depth*fh->width*fh->height >> 3;
+	if (0 == *count)
+		*count = gbuffers;
+	if (*size * *count > gbuffers * gbufsize)
+		*count = (gbuffers * gbufsize) / *size;
+	return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+	       enum v4l2_field field)
+{
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+	struct bttv_fh *fh = q->priv_data;
+
+	return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+				   fh->width, fh->height, field);
+}
+
+static void
+buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+	struct bttv_fh *fh = q->priv_data;
+	struct bttv    *btv = fh->btv;
+
+	buf->vb.state = VIDEOBUF_QUEUED;
+	list_add_tail(&buf->vb.queue,&btv->capture);
+	if (!btv->curr.frame_irq) {
+		btv->loop_irq |= 1;
+		bttv_set_dma(btv, 0x03);
+	}
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+	struct bttv_fh *fh = q->priv_data;
+
+	bttv_dma_free(q,fh->btv,buf);
+}
+
+static struct videobuf_queue_ops bttv_video_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct bttv_fh *fh  = priv;
+	struct bttv *btv = fh->btv;
+	unsigned int i;
+	int err;
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (err)
+		goto err;
+
+	for (i = 0; i < BTTV_TVNORMS; i++)
+		if (*id & bttv_tvnorms[i].v4l2_id)
+			break;
+	if (i == BTTV_TVNORMS) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	set_tvnorm(btv, i);
+
+err:
+
+	return err;
+}
+
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+		*id = V4L2_STD_625_50;
+	else
+		*id = V4L2_STD_525_60;
+	return 0;
+}
+
+static int bttv_enum_input(struct file *file, void *priv,
+					struct v4l2_input *i)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+	int rc = 0;
+
+	if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	i->type     = V4L2_INPUT_TYPE_CAMERA;
+	i->audioset = 1;
+
+	if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+		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;
+	}
+
+	i->std = BTTV_NORMS;
+
+err:
+
+	return rc;
+}
+
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	*i = btv->input;
+
+	return 0;
+}
+
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct bttv_fh *fh  = priv;
+	struct bttv *btv = fh->btv;
+
+	int err;
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
+	if (i > bttv_tvcards[btv->c.type].video_inputs) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	set_input(btv, i, btv->tvnorm);
+
+err:
+	return 0;
+}
+
+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;
+
+	if (unlikely(0 != t->index))
+		return -EINVAL;
+
+	if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
+	bttv_call_all(btv, tuner, s_tuner, t);
+
+	if (btv->audio_mode_gpio)
+		btv->audio_mode_gpio(btv, t, 1);
+
+err:
+
+	return 0;
+}
+
+static int bttv_g_frequency(struct file *file, void *priv,
+					struct v4l2_frequency *f)
+{
+	struct bttv_fh *fh  = priv;
+	struct bttv *btv = fh->btv;
+
+	f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+	f->frequency = btv->freq;
+
+	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;
+
+	if (unlikely(f->tuner != 0))
+		return -EINVAL;
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
+	if (unlikely(f->type != (btv->radio_user
+		? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+		err = -EINVAL;
+		goto err;
+	}
+	btv->freq = f->frequency;
+	bttv_call_all(btv, tuner, s_frequency, f);
+	if (btv->has_matchbox && btv->radio_user)
+		tea5757_set_freq(btv, btv->freq);
+err:
+
+	return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+	struct bttv_fh *fh  = f;
+	struct bttv *btv = fh->btv;
+
+	bttv_call_all(btv, core, log_status);
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+					struct v4l2_dbg_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))
+		return -EINVAL;
+
+	/* bt848 has a 12-bit register space */
+	reg->reg &= 0xfff;
+	reg->val = btread(reg->reg);
+	reg->size = 1;
+
+	return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+					struct v4l2_dbg_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))
+		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
+   function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust	(struct bttv_crop *             c,
+			 const struct v4l2_rect *	b,
+			 __s32                          width,
+			 __s32                          height,
+			 enum v4l2_field                field)
+{
+	__s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+	__s32 max_left;
+	__s32 max_top;
+
+	if (width < c->min_scaled_width) {
+		/* Max. hor. scale factor 16:1. */
+		c->rect.width = width * 16;
+	} else if (width > c->max_scaled_width) {
+		/* Min. hor. scale factor 1:1. */
+		c->rect.width = width;
+
+		max_left = b->left + b->width - width;
+		max_left = min(max_left, (__s32) MAX_HDELAY);
+		if (c->rect.left > max_left)
+			c->rect.left = max_left;
+	}
+
+	if (height < c->min_scaled_height) {
+		/* Max. vert. scale factor 16:1, single fields 8:1. */
+		c->rect.height = height * 16;
+	} else if (frame_height > c->max_scaled_height) {
+		/* Min. vert. scale factor 1:1.
+		   Top and height count field lines times two. */
+		c->rect.height = (frame_height + 1) & ~1;
+
+		max_top = b->top + b->height - c->rect.height;
+		if (c->rect.top > max_top)
+			c->rect.top = max_top;
+	}
+
+	bttv_crop_calc_limits(c);
+}
+
+/* Returns an error if scaling to a frame or single field with the given
+   width and height is not possible with the current cropping parameters
+   and width aligned according to width_mask. If adjust_size is TRUE the
+   function may adjust the width and/or height instead, rounding width
+   to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+   also adjust the current cropping parameters to get closer to the
+   desired image size. */
+static int
+limit_scaled_size_lock       (struct bttv_fh *               fh,
+			 __s32 *                        width,
+			 __s32 *                        height,
+			 enum v4l2_field                field,
+			 unsigned int			width_mask,
+			 unsigned int			width_bias,
+			 int                            adjust_size,
+			 int                            adjust_crop)
+{
+	struct bttv *btv = fh->btv;
+	const struct v4l2_rect *b;
+	struct bttv_crop *c;
+	__s32 min_width;
+	__s32 min_height;
+	__s32 max_width;
+	__s32 max_height;
+	int rc;
+
+	BUG_ON((int) width_mask >= 0 ||
+	       width_bias >= (unsigned int) -width_mask);
+
+	/* Make sure tvnorm, vbi_end and the current cropping parameters
+	   remain consistent until we're done. */
+
+	b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+	/* Do crop - use current, don't - use default parameters. */
+	c = &btv->crop[!!fh->do_crop];
+
+	if (fh->do_crop
+	    && adjust_size
+	    && adjust_crop
+	    && !locked_btres(btv, VIDEO_RESOURCES)) {
+		min_width = 48;
+		min_height = 32;
+
+		/* We cannot scale up. When the scaled image is larger
+		   than crop.rect we adjust the crop.rect as required
+		   by the V4L2 spec, hence cropcap.bounds are our limit. */
+		max_width = min(b->width, (__s32) MAX_HACTIVE);
+		max_height = b->height;
+
+		/* We cannot capture the same line as video and VBI data.
+		   Note btv->vbi_end is really a minimum, see
+		   bttv_vbi_try_fmt(). */
+		if (btv->vbi_end > b->top) {
+			max_height -= btv->vbi_end - b->top;
+			rc = -EBUSY;
+			if (min_height > max_height)
+				goto fail;
+		}
+	} else {
+		rc = -EBUSY;
+		if (btv->vbi_end > c->rect.top)
+			goto fail;
+
+		min_width  = c->min_scaled_width;
+		min_height = c->min_scaled_height;
+		max_width  = c->max_scaled_width;
+		max_height = c->max_scaled_height;
+
+		adjust_crop = 0;
+	}
+
+	min_width = (min_width - width_mask - 1) & width_mask;
+	max_width = max_width & width_mask;
+
+	/* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+	min_height = min_height;
+	/* Min. scale factor is 1:1. */
+	max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+
+	if (adjust_size) {
+		*width = clamp(*width, min_width, max_width);
+		*height = clamp(*height, min_height, max_height);
+
+		/* Round after clamping to avoid overflow. */
+		*width = (*width + width_bias) & width_mask;
+
+		if (adjust_crop) {
+			bttv_crop_adjust(c, b, *width, *height, field);
+
+			if (btv->vbi_end > c->rect.top) {
+				/* Move the crop window out of the way. */
+				c->rect.top = btv->vbi_end;
+			}
+		}
+	} else {
+		rc = -EINVAL;
+		if (*width  < min_width ||
+		    *height < min_height ||
+		    *width  > max_width ||
+		    *height > max_height ||
+		    0 != (*width & ~width_mask))
+			goto fail;
+	}
+
+	rc = 0; /* success */
+
+ fail:
+
+	return rc;
+}
+
+/* Returns an error if the given overlay window dimensions are not
+   possible with the current cropping parameters. If adjust_size is
+   TRUE the function may adjust the window width and/or height
+   instead, however it always rounds the horizontal position and
+   width as btcx_align() does. If adjust_crop is TRUE the function
+   may also adjust the current cropping parameters to get closer
+   to the desired window size. */
+static int
+verify_window_lock		(struct bttv_fh *               fh,
+			 struct v4l2_window *           win,
+			 int                            adjust_size,
+			 int                            adjust_crop)
+{
+	enum v4l2_field field;
+	unsigned int width_mask;
+	int rc;
+
+	if (win->w.width  < 48 || win->w.height < 32)
+		return -EINVAL;
+	if (win->clipcount > 2048)
+		return -EINVAL;
+
+	field = win->field;
+
+	if (V4L2_FIELD_ANY == field) {
+		__s32 height2;
+
+		height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+		field = (win->w.height > height2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_TOP;
+	}
+	switch (field) {
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+	case V4L2_FIELD_INTERLACED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* 4-byte alignment. */
+	if (NULL == fh->ovfmt)
+		return -EINVAL;
+	width_mask = ~0;
+	switch (fh->ovfmt->depth) {
+	case 8:
+	case 24:
+		width_mask = ~3;
+		break;
+	case 16:
+		width_mask = ~1;
+		break;
+	case 32:
+		break;
+	default:
+		BUG();
+	}
+
+	win->w.width -= win->w.left & ~width_mask;
+	win->w.left = (win->w.left - width_mask - 1) & width_mask;
+
+	rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+			       field, width_mask,
+			       /* width_bias: round down */ 0,
+			       adjust_size, adjust_crop);
+	if (0 != rc)
+		return rc;
+
+	win->field = field;
+	return 0;
+}
+
+static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+			struct v4l2_window *win, int fixup)
+{
+	struct v4l2_clip *clips = NULL;
+	int n,size,retval = 0;
+
+	if (NULL == fh->ovfmt)
+		return -EINVAL;
+	if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+		return -EINVAL;
+	retval = verify_window_lock(fh, win,
+			       /* adjust_size */ fixup,
+			       /* adjust_crop */ fixup);
+	if (0 != retval)
+		return retval;
+
+	/* copy clips  --  luckily v4l1 + v4l2 are binary
+	   compatible here ...*/
+	n = win->clipcount;
+	size = sizeof(*clips)*(n+4);
+	clips = kmalloc(size,GFP_KERNEL);
+	if (NULL == clips)
+		return -ENOMEM;
+	if (n > 0) {
+		if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+			kfree(clips);
+			return -EFAULT;
+		}
+	}
+
+	/* clip against screen */
+	if (NULL != btv->fbuf.base)
+		n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+				      &win->w, clips, n);
+	btcx_sort_clips(clips,n);
+
+	/* 4-byte alignments */
+	switch (fh->ovfmt->depth) {
+	case 8:
+	case 24:
+		btcx_align(&win->w, clips, n, 3);
+		break;
+	case 16:
+		btcx_align(&win->w, clips, n, 1);
+		break;
+	case 32:
+		/* no alignment fixups needed */
+		break;
+	default:
+		BUG();
+	}
+
+	kfree(fh->ov.clips);
+	fh->ov.clips    = clips;
+	fh->ov.nclips   = n;
+
+	fh->ov.w        = win->w;
+	fh->ov.field    = win->field;
+	fh->ov.setup_ok = 1;
+
+	btv->init.ov.w.width   = win->w.width;
+	btv->init.ov.w.height  = win->w.height;
+	btv->init.ov.field     = win->field;
+
+	/* update overlay if needed */
+	retval = 0;
+	if (check_btres(fh, RESOURCE_OVERLAY)) {
+		struct bttv_buffer *new;
+
+		new = videobuf_sg_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);
+	}
+	return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+{
+	struct videobuf_queue* q = NULL;
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		q = &fh->cap;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		q = &fh->vbi;
+		break;
+	default:
+		BUG();
+	}
+	return q;
+}
+
+static int bttv_resource(struct bttv_fh *fh)
+{
+	int res = 0;
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		res = RESOURCE_VIDEO_STREAM;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		res = RESOURCE_VBI;
+		break;
+	default:
+		BUG();
+	}
+	return res;
+}
+
+static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+{
+	struct videobuf_queue *q = bttv_queue(fh);
+	int res = bttv_resource(fh);
+
+	if (check_btres(fh,res))
+		return -EBUSY;
+	if (videobuf_queue_is_busy(q))
+		return -EBUSY;
+	fh->type = type;
+	return 0;
+}
+
+static void
+pix_format_set_size     (struct v4l2_pix_format *       f,
+			 const struct bttv_format *     fmt,
+			 unsigned int                   width,
+			 unsigned int                   height)
+{
+	f->width = width;
+	f->height = height;
+
+	if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+		f->bytesperline = width; /* Y plane */
+		f->sizeimage = (width * height * fmt->depth) >> 3;
+	} else {
+		f->bytesperline = (width * fmt->depth) >> 3;
+		f->sizeimage = height * f->bytesperline;
+	}
+}
+
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	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_g_fmt_vid_overlay(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct bttv_fh *fh  = priv;
+
+	f->fmt.win.w     = fh->ov.w;
+	f->fmt.win.field = fh->ov.field;
+
+	return 0;
+}
+
+static int bttv_try_fmt_vid_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;
+
+	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+	if (NULL == fmt)
+		return -EINVAL;
+
+	field = f->fmt.pix.field;
+
+	if (V4L2_FIELD_ANY == field) {
+		__s32 height2;
+
+		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;
+	}
+
+	width = f->fmt.pix.width;
+	height = f->fmt.pix.height;
+
+	rc = limit_scaled_size_lock(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_try_fmt_vid_overlay(struct file *file, void *priv,
+						struct v4l2_format *f)
+{
+	struct bttv_fh *fh = priv;
+
+	return verify_window_lock(fh, &f->fmt.win,
+			/* adjust_size */ 1,
+			/* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_vid_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;
+
+	retval = bttv_switch_type(fh, f->type);
+	if (0 != retval)
+		return retval;
+
+	retval = bttv_try_fmt_vid_cap(file, priv, f);
+	if (0 != retval)
+		return retval;
+
+	width = f->fmt.pix.width;
+	height = f->fmt.pix.height;
+	field = f->fmt.pix.field;
+
+	retval = limit_scaled_size_lock(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;
+
+	f->fmt.pix.field = field;
+
+	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+	/* update our state informations */
+	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;
+
+	return 0;
+}
+
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (no_overlay > 0) {
+		pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+		return -EINVAL;
+	}
+
+	return setup_window_lock(fh, btv, &f->fmt.win, 1);
+}
+
+static int bttv_querycap(struct file *file, void  *priv,
+				struct v4l2_capability *cap)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (0 == v4l2)
+		return -EINVAL;
+
+	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->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;
+
+	/*
+	 * No need to lock here: those vars are initialized during board
+	 * probe and remains untouched during the rest of the driver lifecycle
+	 */
+	if (btv->has_saa6588)
+		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+	if (btv->tuner_type != TUNER_ABSENT)
+		cap->capabilities |= V4L2_CAP_TUNER;
+	return 0;
+}
+
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+	int index = -1, i;
+
+	for (i = 0; i < FORMATS; i++) {
+		if (formats[i].fourcc != -1)
+			index++;
+		if ((unsigned int)index == f->index)
+			break;
+	}
+	if (FORMATS == i)
+		return -EINVAL;
+
+	f->pixelformat = formats[i].fourcc;
+	strlcpy(f->description, formats[i].name, sizeof(f->description));
+
+	return i;
+}
+
+static int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
+				struct v4l2_fmtdesc *f)
+{
+	int rc = bttv_enum_fmt_cap_ovr(f);
+
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static int bttv_enum_fmt_vid_overlay(struct file *file, void  *priv,
+					struct v4l2_fmtdesc *f)
+{
+	int rc;
+
+	if (no_overlay > 0) {
+		pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+		return -EINVAL;
+	}
+
+	rc = bttv_enum_fmt_cap_ovr(f);
+
+	if (rc < 0)
+		return rc;
+
+	if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int bttv_g_fbuf(struct file *file, void *f,
+				struct v4l2_framebuffer *fb)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	*fb = btv->fbuf;
+	fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+	if (fh->ovfmt)
+		fb->fmt.pixelformat  = fh->ovfmt->fourcc;
+	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 = 0;
+
+	if (on) {
+		/* verify args */
+		if (unlikely(!btv->fbuf.base)) {
+			return -EINVAL;
+		}
+		if (unlikely(!fh->ov.setup_ok)) {
+			dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+			retval = -EINVAL;
+		}
+		if (retval)
+			return retval;
+	}
+
+	if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+		return -EBUSY;
+
+	if (on) {
+		fh->ov.tvnorm = btv->tvnorm;
+		new = videobuf_sg_alloc(sizeof(*new));
+		new->crop = btv->crop[!!fh->do_crop].rect;
+		bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+	} else {
+		new = NULL;
+	}
+
+	/* switch over */
+	retval = bttv_switch_overlay(btv, fh, new);
+	return retval;
+}
+
+static int bttv_s_fbuf(struct file *file, void *f,
+				const 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;
+
+	/* 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_lock(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 */
+	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_sg_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);
+		}
+	}
+	return retval;
+}
+
+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);
+}
+
+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);
+}
+
+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_lock(btv, fh, res))
+		return -EBUSY;
+
+	return videobuf_qbuf(bttv_queue(fh), b);
+}
+
+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);
+}
+
+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);
+
+	if (!check_alloc_btres_lock(btv, fh, res))
+		return -EBUSY;
+	return videobuf_streamon(bttv_queue(fh));
+}
+
+
+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);
+
+
+	retval = videobuf_streamoff(bttv_queue(fh));
+	if (retval < 0)
+		return retval;
+	free_btres_lock(btv, fh, res);
+	return 0;
+}
+
+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;
+
+	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;
+
+	if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+		*c = no_ctl;
+	else {
+		ctrl = ctrl_by_id(c->id);
+
+		*c = (NULL != ctrl) ? *ctrl : no_ctl;
+	}
+
+	return 0;
+}
+
+static int bttv_g_parm(struct file *file, void *f,
+				struct v4l2_streamparm *parm)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+				    &parm->parm.capture.timeperframe);
+
+	return 0;
+}
+
+static int bttv_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (btv->tuner_type == TUNER_ABSENT)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	t->rxsubchans = V4L2_TUNER_SUB_MONO;
+	bttv_call_all(btv, tuner, 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);
+
+	return 0;
+}
+
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	*p = v4l2_prio_max(&btv->prio);
+
+	return 0;
+}
+
+static int bttv_s_priority(struct file *file, void *f,
+					enum v4l2_priority prio)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+	int	rc;
+
+	rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+
+	return rc;
+}
+
+static int bttv_cropcap(struct file *file, void *priv,
+				struct v4l2_cropcap *cap)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+
+	*cap = bttv_tvnorms[btv->tvnorm].cropcap;
+
+	return 0;
+}
+
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+	struct bttv_fh *fh = f;
+	struct bttv *btv = fh->btv;
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+
+	/* 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. */
+
+	crop->c = btv->crop[!!fh->do_crop].rect;
+
+	return 0;
+}
+
+static int bttv_s_crop(struct file *file, void *f, const 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 (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+
+	/* 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_lock(). */
+	retval = v4l2_prio_check(&btv->prio, fh->prio);
+	if (0 != retval) {
+		return retval;
+	}
+
+	retval = -EBUSY;
+
+	if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+		return retval;
+	}
+
+	b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+	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) {
+		return retval;
+	}
+
+	/* Min. scaled size 48 x 32. */
+	c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48);
+	c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+
+	c.rect.width = clamp_t(s32, crop->c.width,
+			     48, b_right - c.rect.left);
+
+	c.rect.top = clamp_t(s32, 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;
+
+	c.rect.height = clamp_t(s32, crop->c.height,
+			      32, b_bottom - c.rect.top);
+	c.rect.height = (c.rect.height + 1) & ~1;
+
+	bttv_crop_calc_limits(&c);
+
+	btv->crop[1] = c;
+
+	fh->do_crop = 1;
+
+	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 (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;
+	}
+
+	return 0;
+}
+
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+	if (unlikely(a->index))
+		return -EINVAL;
+
+	strcpy(a->name, "audio");
+	return 0;
+}
+
+static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+	if (unlikely(a->index))
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t bttv_read(struct file *file, char __user *data,
+			 size_t count, loff_t *ppos)
+{
+	struct bttv_fh *fh = file->private_data;
+	int retval = 0;
+
+	if (fh->btv->errors)
+		bttv_reinit_bt848(fh->btv);
+	dprintk("%d: read count=%d type=%s\n",
+		fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+			/* VIDEO_READ in use by another fh,
+			   or VIDEO_STREAM by any fh. */
+			return -EBUSY;
+		}
+		retval = videobuf_read_one(&fh->cap, data, count, ppos,
+					   file->f_flags & O_NONBLOCK);
+		free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+			return -EBUSY;
+		retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+					      file->f_flags & O_NONBLOCK);
+		break;
+	default:
+		BUG();
+	}
+	return retval;
+}
+
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv_buffer *buf;
+	enum v4l2_field field;
+	unsigned int rc = POLLERR;
+
+	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+		if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+			return POLLERR;
+		return videobuf_poll_stream(file, &fh->vbi, wait);
+	}
+
+	if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+		/* streaming capture */
+		if (list_empty(&fh->cap.stream))
+			goto err;
+		buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+	} else {
+		/* read() capture */
+		if (NULL == fh->cap.read_buf) {
+			/* need to capture a new frame */
+			if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+				goto err;
+			fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+			if (NULL == fh->cap.read_buf)
+				goto err;
+			fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+			field = videobuf_next_field(&fh->cap);
+			if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+				kfree (fh->cap.read_buf);
+				fh->cap.read_buf = NULL;
+				goto err;
+			}
+			fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+			fh->cap.read_off = 0;
+		}
+		buf = (struct bttv_buffer*)fh->cap.read_buf;
+	}
+
+	poll_wait(file, &buf->vb.done, wait);
+	if (buf->vb.state == VIDEOBUF_DONE ||
+	    buf->vb.state == VIDEOBUF_ERROR)
+		rc =  POLLIN|POLLRDNORM;
+	else
+		rc = 0;
+err:
+	return rc;
+}
+
+static int bttv_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct bttv *btv = video_drvdata(file);
+	struct bttv_fh *fh;
+	enum v4l2_buf_type type = 0;
+
+	dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+	if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	} else if (vdev->vfl_type == VFL_TYPE_VBI) {
+		type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	} else {
+		WARN_ON(1);
+		return -ENODEV;
+	}
+
+	dprintk("%d: open called (type=%s)\n",
+		btv->c.nr, v4l2_type_names[type]);
+
+	/* allocate per filehandle data */
+	fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+	if (unlikely(!fh))
+		return -ENOMEM;
+	file->private_data = fh;
+
+	*fh = btv->init;
+
+	fh->type = type;
+	fh->ov.setup_ok = 0;
+
+	v4l2_prio_open(&btv->prio, &fh->prio);
+
+	videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+			    &btv->c.pci->dev, &btv->s_lock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct bttv_buffer),
+			    fh, &btv->lock);
+	videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+			    &btv->c.pci->dev, &btv->s_lock,
+			    V4L2_BUF_TYPE_VBI_CAPTURE,
+			    V4L2_FIELD_SEQ_TB,
+			    sizeof(struct bttv_buffer),
+			    fh, &btv->lock);
+	set_tvnorm(btv,btv->tvnorm);
+	set_input(btv, btv->input, btv->tvnorm);
+
+	btv->users++;
+
+	/* The V4L2 spec requires one global set of cropping parameters
+	   which only change on request. These are stored in btv->crop[1].
+	   However for compatibility with V4L apps and cropping unaware
+	   V4L2 apps we now reset the cropping parameters as seen through
+	   this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+	   will use btv->crop[0], the default cropping parameters for the
+	   current video standard, and VIDIOC_S_FMT will not implicitely
+	   change the cropping parameters until VIDIOC_S_CROP has been
+	   called. */
+	fh->do_crop = !reset_crop; /* module parameter */
+
+	/* Likewise there should be one global set of VBI capture
+	   parameters, but for compatibility with V4L apps and earlier
+	   driver versions each fh has its own parameters. */
+	bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+
+	bttv_field_count(btv);
+	return 0;
+}
+
+static int bttv_release(struct file *file)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+
+	/* turn off overlay */
+	if (check_btres(fh, RESOURCE_OVERLAY))
+		bttv_switch_overlay(btv,fh,NULL);
+
+	/* stop video capture */
+	if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+		videobuf_streamoff(&fh->cap);
+		free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+	}
+	if (fh->cap.read_buf) {
+		buffer_release(&fh->cap,fh->cap.read_buf);
+		kfree(fh->cap.read_buf);
+	}
+	if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+		free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+	}
+
+	/* stop vbi capture */
+	if (check_btres(fh, RESOURCE_VBI)) {
+		videobuf_stop(&fh->vbi);
+		free_btres_lock(btv,fh,RESOURCE_VBI);
+	}
+
+	/* free stuff */
+
+	videobuf_mmap_free(&fh->cap);
+	videobuf_mmap_free(&fh->vbi);
+	v4l2_prio_close(&btv->prio, fh->prio);
+	file->private_data = NULL;
+	kfree(fh);
+
+	btv->users--;
+	bttv_field_count(btv);
+
+	if (!btv->users)
+		audio_mute(btv, 1);
+
+	return 0;
+}
+
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct bttv_fh *fh = file->private_data;
+
+	dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+		fh->btv->c.nr, v4l2_type_names[fh->type],
+		vma->vm_start, vma->vm_end - vma->vm_start);
+	return videobuf_mmap_mapper(bttv_queue(fh),vma);
+}
+
+static const struct v4l2_file_operations bttv_fops =
+{
+	.owner		  = THIS_MODULE,
+	.open		  = bttv_open,
+	.release	  = bttv_release,
+	.unlocked_ioctl	  = video_ioctl2,
+	.read		  = bttv_read,
+	.mmap		  = bttv_mmap,
+	.poll		  = bttv_poll,
+};
+
+static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+	.vidioc_querycap                = bttv_querycap,
+	.vidioc_enum_fmt_vid_cap        = bttv_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap           = bttv_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap         = bttv_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap           = bttv_s_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_overlay    = bttv_enum_fmt_vid_overlay,
+	.vidioc_g_fmt_vid_overlay       = bttv_g_fmt_vid_overlay,
+	.vidioc_try_fmt_vid_overlay     = bttv_try_fmt_vid_overlay,
+	.vidioc_s_fmt_vid_overlay       = bttv_s_fmt_vid_overlay,
+	.vidioc_g_fmt_vbi_cap           = bttv_g_fmt_vbi_cap,
+	.vidioc_try_fmt_vbi_cap         = bttv_try_fmt_vbi_cap,
+	.vidioc_s_fmt_vbi_cap           = bttv_s_fmt_vbi_cap,
+	.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,
+	.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
+};
+
+static struct video_device bttv_video_template = {
+	.fops         = &bttv_fops,
+	.ioctl_ops    = &bttv_ioctl_ops,
+	.tvnorms      = BTTV_NORMS,
+	.current_norm = V4L2_STD_PAL,
+};
+
+/* ----------------------------------------------------------------------- */
+/* radio interface                                                         */
+
+static int radio_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct bttv *btv = video_drvdata(file);
+	struct bttv_fh *fh;
+
+	dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+	dprintk("%d: open called (radio)\n", btv->c.nr);
+
+	/* allocate per filehandle data */
+	fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+	if (unlikely(!fh))
+		return -ENOMEM;
+	file->private_data = fh;
+	*fh = btv->init;
+
+	v4l2_prio_open(&btv->prio, &fh->prio);
+
+	btv->radio_user++;
+
+	bttv_call_all(btv, tuner, s_radio);
+	audio_input(btv,TVAUDIO_INPUT_RADIO);
+
+	return 0;
+}
+
+static int radio_release(struct file *file)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct saa6588_command cmd;
+
+	v4l2_prio_close(&btv->prio, fh->prio);
+	file->private_data = NULL;
+	kfree(fh);
+
+	btv->radio_user--;
+
+	bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+	return 0;
+}
+
+static int radio_querycap(struct file *file, void *priv,
+					struct v4l2_capability *cap)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	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->capabilities = V4L2_CAP_TUNER;
+
+	return 0;
+}
+
+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 (btv->tuner_type == TUNER_ABSENT)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+	strcpy(t->name, "Radio");
+	t->type = V4L2_TUNER_RADIO;
+
+	bttv_call_all(btv, tuner, g_tuner, t);
+
+	if (btv->audio_mode_gpio)
+		btv->audio_mode_gpio(btv, t, 0);
+
+	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 bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	bttv_call_all(btv, tuner, s_tuner, t);
+	return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+					const struct v4l2_audio *a)
+{
+	if (unlikely(a->index))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (unlikely(i))
+		return -EINVAL;
+
+	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;
+
+	return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+			 size_t count, loff_t *ppos)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct saa6588_command cmd;
+	cmd.block_count = count/3;
+	cmd.buffer = data;
+	cmd.instance = file;
+	cmd.result = -ENODEV;
+
+	bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+	return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct saa6588_command cmd;
+	cmd.instance = file;
+	cmd.event_list = wait;
+	cmd.result = -ENODEV;
+	bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+	return cmd.result;
+}
+
+static const struct v4l2_file_operations radio_fops =
+{
+	.owner	  = THIS_MODULE,
+	.open	  = radio_open,
+	.read     = radio_read,
+	.release  = radio_release,
+	.unlocked_ioctl = video_ioctl2,
+	.poll     = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+	.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,
+};
+
+static struct video_device radio_template = {
+	.fops      = &radio_fops,
+	.ioctl_ops = &radio_ioctl_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+/* some debug code                                                         */
+
+static int bttv_risc_decode(u32 risc)
+{
+	static char *instr[16] = {
+		[ BT848_RISC_WRITE     >> 28 ] = "write",
+		[ BT848_RISC_SKIP      >> 28 ] = "skip",
+		[ BT848_RISC_WRITEC    >> 28 ] = "writec",
+		[ BT848_RISC_JUMP      >> 28 ] = "jump",
+		[ BT848_RISC_SYNC      >> 28 ] = "sync",
+		[ BT848_RISC_WRITE123  >> 28 ] = "write123",
+		[ BT848_RISC_SKIP123   >> 28 ] = "skip123",
+		[ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+	};
+	static int incr[16] = {
+		[ BT848_RISC_WRITE     >> 28 ] = 2,
+		[ BT848_RISC_JUMP      >> 28 ] = 2,
+		[ BT848_RISC_SYNC      >> 28 ] = 2,
+		[ BT848_RISC_WRITE123  >> 28 ] = 5,
+		[ BT848_RISC_SKIP123   >> 28 ] = 2,
+		[ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+	};
+	static char *bits[] = {
+		"be0",  "be1",  "be2",  "be3/resync",
+		"set0", "set1", "set2", "set3",
+		"clr0", "clr1", "clr2", "clr3",
+		"irq",  "res",  "eol",  "sol",
+	};
+	int i;
+
+	pr_cont("0x%08x [ %s", risc,
+	       instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+	for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+		if (risc & (1 << (i + 12)))
+			pr_cont(" %s", bits[i]);
+	pr_cont(" count=%d ]\n", risc & 0xfff);
+	return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static void bttv_risc_disasm(struct bttv *btv,
+			     struct btcx_riscmem *risc)
+{
+	unsigned int i,j,n;
+
+	pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+		btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+	for (i = 0; i < (risc->size >> 2); i += n) {
+		pr_info("%s:   0x%lx: ",
+			btv->c.v4l2_dev.name,
+			(unsigned long)(risc->dma + (i<<2)));
+		n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+		for (j = 1; j < n; j++)
+			pr_info("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
+				btv->c.v4l2_dev.name,
+				(unsigned long)(risc->dma + ((i+j)<<2)),
+				risc->cpu[i+j], j);
+		if (0 == risc->cpu[i])
+			break;
+	}
+}
+
+static void bttv_print_riscaddr(struct bttv *btv)
+{
+	pr_info("  main: %08llx\n", (unsigned long long)btv->main.dma);
+	pr_info("  vbi : o=%08llx e=%08llx\n",
+		btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+		btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+	pr_info("  cap : o=%08llx e=%08llx\n",
+		btv->curr.top
+		? (unsigned long long)btv->curr.top->top.dma : 0,
+		btv->curr.bottom
+		? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+	pr_info("  scr : o=%08llx e=%08llx\n",
+		btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+		btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+	bttv_risc_disasm(btv, &btv->main);
+}
+
+/* ----------------------------------------------------------------------- */
+/* irq handler                                                             */
+
+static char *irq_name[] = {
+	"FMTCHG",  // format change detected (525 vs. 625)
+	"VSYNC",   // vertical sync (new field)
+	"HSYNC",   // horizontal sync
+	"OFLOW",   // chroma/luma AGC overflow
+	"HLOCK",   // horizontal lock changed
+	"VPRES",   // video presence changed
+	"6", "7",
+	"I2CDONE", // hw irc operation finished
+	"GPINT",   // gpio port triggered irq
+	"10",
+	"RISCI",   // risc instruction triggered irq
+	"FBUS",    // pixel data fifo dropped data (high pci bus latencies)
+	"FTRGT",   // pixel data fifo overrun
+	"FDSR",    // fifo data stream resyncronisation
+	"PPERR",   // parity error (data transfer)
+	"RIPERR",  // parity error (read risc instructions)
+	"PABORT",  // pci abort
+	"OCERR",   // risc instruction error
+	"SCERR",   // syncronisation error
+};
+
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+	unsigned int i;
+
+	pr_cont("bits:");
+	for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+		if (print & (1 << i))
+			pr_cont(" %s", irq_name[i]);
+		if (mark & (1 << i))
+			pr_cont("*");
+	}
+}
+
+static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+{
+	pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+		btv->c.nr,
+		(unsigned long)btv->main.dma,
+		(unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+		(unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+		(unsigned long)rc);
+
+	if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+		pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+			  "Ok, then this is harmless, don't worry ;)\n",
+			  btv->c.nr);
+		return;
+	}
+	pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+		  btv->c.nr);
+	pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+		  btv->c.nr);
+	dump_stack();
+}
+
+static int
+bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+{
+	struct bttv_buffer *item;
+
+	memset(set,0,sizeof(*set));
+
+	/* capture request ? */
+	if (!list_empty(&btv->capture)) {
+		set->frame_irq = 1;
+		item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+		if (V4L2_FIELD_HAS_TOP(item->vb.field))
+			set->top    = item;
+		if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+			set->bottom = item;
+
+		/* capture request for other field ? */
+		if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+		    (item->vb.queue.next != &btv->capture)) {
+			item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+			/* Mike Isely <isely@pobox.com> - Only check
+			 * and set up the bottom field in the logic
+			 * below.  Don't ever do the top field.  This
+			 * of course means that if we set up the
+			 * bottom field in the above code that we'll
+			 * actually skip a field.  But that's OK.
+			 * Having processed only a single buffer this
+			 * time, then the next time around the first
+			 * available buffer should be for a top field.
+			 * That will then cause us here to set up a
+			 * top then a bottom field in the normal way.
+			 * The alternative to this understanding is
+			 * that we set up the second available buffer
+			 * as a top field, but that's out of order
+			 * since this driver always processes the top
+			 * field first - the effect will be the two
+			 * buffers being returned in the wrong order,
+			 * with the second buffer also being delayed
+			 * by one field time (owing to the fifo nature
+			 * of videobuf).  Worse still, we'll be stuck
+			 * doing fields out of order now every time
+			 * until something else causes a field to be
+			 * dropped.  By effectively forcing a field to
+			 * drop this way then we always get back into
+			 * sync within a single frame time.  (Out of
+			 * order fields can screw up deinterlacing
+			 * algorithms.) */
+			if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+				if (NULL == set->bottom &&
+				    V4L2_FIELD_BOTTOM == item->vb.field) {
+					set->bottom = item;
+				}
+				if (NULL != set->top  &&  NULL != set->bottom)
+					set->top_irq = 2;
+			}
+		}
+	}
+
+	/* screen overlay ? */
+	if (NULL != btv->screen) {
+		if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+			if (NULL == set->top && NULL == set->bottom) {
+				set->top    = btv->screen;
+				set->bottom = btv->screen;
+			}
+		} else {
+			if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+			    NULL == set->top) {
+				set->top = btv->screen;
+			}
+			if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+			    NULL == set->bottom) {
+				set->bottom = btv->screen;
+			}
+		}
+	}
+
+	dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+		btv->c.nr, set->top, set->bottom,
+		btv->screen, set->frame_irq, set->top_irq);
+	return 0;
+}
+
+static void
+bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+		      struct bttv_buffer_set *curr, unsigned int state)
+{
+	struct timeval ts;
+
+	do_gettimeofday(&ts);
+
+	if (wakeup->top == wakeup->bottom) {
+		if (NULL != wakeup->top && curr->top != wakeup->top) {
+			if (irq_debug > 1)
+				pr_debug("%d: wakeup: both=%p\n",
+					 btv->c.nr, wakeup->top);
+			wakeup->top->vb.ts = ts;
+			wakeup->top->vb.field_count = btv->field_count;
+			wakeup->top->vb.state = state;
+			wake_up(&wakeup->top->vb.done);
+		}
+	} else {
+		if (NULL != wakeup->top && curr->top != wakeup->top) {
+			if (irq_debug > 1)
+				pr_debug("%d: wakeup: top=%p\n",
+					 btv->c.nr, wakeup->top);
+			wakeup->top->vb.ts = ts;
+			wakeup->top->vb.field_count = btv->field_count;
+			wakeup->top->vb.state = state;
+			wake_up(&wakeup->top->vb.done);
+		}
+		if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+			if (irq_debug > 1)
+				pr_debug("%d: wakeup: bottom=%p\n",
+					 btv->c.nr, wakeup->bottom);
+			wakeup->bottom->vb.ts = ts;
+			wakeup->bottom->vb.field_count = btv->field_count;
+			wakeup->bottom->vb.state = state;
+			wake_up(&wakeup->bottom->vb.done);
+		}
+	}
+}
+
+static void
+bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+		    unsigned int state)
+{
+	struct timeval ts;
+
+	if (NULL == wakeup)
+		return;
+
+	do_gettimeofday(&ts);
+	wakeup->vb.ts = ts;
+	wakeup->vb.field_count = btv->field_count;
+	wakeup->vb.state = state;
+	wake_up(&wakeup->vb.done);
+}
+
+static void bttv_irq_timeout(unsigned long data)
+{
+	struct bttv *btv = (struct bttv *)data;
+	struct bttv_buffer_set old,new;
+	struct bttv_buffer *ovbi;
+	struct bttv_buffer *item;
+	unsigned long flags;
+
+	if (bttv_verbose) {
+		pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+			btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+			btread(BT848_RISC_COUNT));
+		bttv_print_irqbits(btread(BT848_INT_STAT),0);
+		pr_cont("\n");
+	}
+
+	spin_lock_irqsave(&btv->s_lock,flags);
+
+	/* deactivate stuff */
+	memset(&new,0,sizeof(new));
+	old  = btv->curr;
+	ovbi = btv->cvbi;
+	btv->curr = new;
+	btv->cvbi = NULL;
+	btv->loop_irq = 0;
+	bttv_buffer_activate_video(btv, &new);
+	bttv_buffer_activate_vbi(btv,   NULL);
+	bttv_set_dma(btv, 0);
+
+	/* wake up */
+	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 = 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 = VIDEOBUF_ERROR;
+		wake_up(&item->vb.done);
+	}
+
+	btv->errors++;
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+}
+
+static void
+bttv_irq_wakeup_top(struct bttv *btv)
+{
+	struct bttv_buffer *wakeup = btv->curr.top;
+
+	if (NULL == wakeup)
+		return;
+
+	spin_lock(&btv->s_lock);
+	btv->curr.top_irq = 0;
+	btv->curr.top = NULL;
+	bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+
+	do_gettimeofday(&wakeup->vb.ts);
+	wakeup->vb.field_count = btv->field_count;
+	wakeup->vb.state = VIDEOBUF_DONE;
+	wake_up(&wakeup->vb.done);
+	spin_unlock(&btv->s_lock);
+}
+
+static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+{
+	if (rc < risc->dma)
+		return 0;
+	if (rc > risc->dma + risc->size)
+		return 0;
+	return 1;
+}
+
+static void
+bttv_irq_switch_video(struct bttv *btv)
+{
+	struct bttv_buffer_set new;
+	struct bttv_buffer_set old;
+	dma_addr_t rc;
+
+	spin_lock(&btv->s_lock);
+
+	/* new buffer set */
+	bttv_irq_next_video(btv, &new);
+	rc = btread(BT848_RISC_COUNT);
+	if ((btv->curr.top    && is_active(&btv->curr.top->top,       rc)) ||
+	    (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+		btv->framedrop++;
+		if (debug_latency)
+			bttv_irq_debug_low_latency(btv, rc);
+		spin_unlock(&btv->s_lock);
+		return;
+	}
+
+	/* switch over */
+	old = btv->curr;
+	btv->curr = new;
+	btv->loop_irq &= ~1;
+	bttv_buffer_activate_video(btv, &new);
+	bttv_set_dma(btv, 0);
+
+	/* switch input */
+	if (UNSET != btv->new_input) {
+		video_mux(btv,btv->new_input);
+		btv->new_input = UNSET;
+	}
+
+	/* wake up finished buffers */
+	bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+	spin_unlock(&btv->s_lock);
+}
+
+static void
+bttv_irq_switch_vbi(struct bttv *btv)
+{
+	struct bttv_buffer *new = NULL;
+	struct bttv_buffer *old;
+	u32 rc;
+
+	spin_lock(&btv->s_lock);
+
+	if (!list_empty(&btv->vcapture))
+		new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+	old = btv->cvbi;
+
+	rc = btread(BT848_RISC_COUNT);
+	if (NULL != old && (is_active(&old->top,    rc) ||
+			    is_active(&old->bottom, rc))) {
+		btv->framedrop++;
+		if (debug_latency)
+			bttv_irq_debug_low_latency(btv, rc);
+		spin_unlock(&btv->s_lock);
+		return;
+	}
+
+	/* switch */
+	btv->cvbi = new;
+	btv->loop_irq &= ~4;
+	bttv_buffer_activate_vbi(btv, new);
+	bttv_set_dma(btv, 0);
+
+	bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+	spin_unlock(&btv->s_lock);
+}
+
+static irqreturn_t bttv_irq(int irq, void *dev_id)
+{
+	u32 stat,astat;
+	u32 dstat;
+	int count;
+	struct bttv *btv;
+	int handled = 0;
+
+	btv=(struct bttv *)dev_id;
+
+	count=0;
+	while (1) {
+		/* get/clear interrupt status bits */
+		stat=btread(BT848_INT_STAT);
+		astat=stat&btread(BT848_INT_MASK);
+		if (!astat)
+			break;
+		handled = 1;
+		btwrite(stat,BT848_INT_STAT);
+
+		/* get device status bits */
+		dstat=btread(BT848_DSTATUS);
+
+		if (irq_debug) {
+			pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+				 btv->c.nr, count, btv->field_count,
+				 stat>>28, btread(BT848_RISC_COUNT));
+			bttv_print_irqbits(stat,astat);
+			if (stat & BT848_INT_HLOCK)
+				pr_cont("   HLOC => %s",
+					dstat & BT848_DSTATUS_HLOC
+					? "yes" : "no");
+			if (stat & BT848_INT_VPRES)
+				pr_cont("   PRES => %s",
+					dstat & BT848_DSTATUS_PRES
+					? "yes" : "no");
+			if (stat & BT848_INT_FMTCHG)
+				pr_cont("   NUML => %s",
+					dstat & BT848_DSTATUS_NUML
+					? "625" : "525");
+			pr_cont("\n");
+		}
+
+		if (astat&BT848_INT_VSYNC)
+			btv->field_count++;
+
+		if ((astat & BT848_INT_GPINT) && btv->remote) {
+			bttv_input_irq(btv);
+		}
+
+		if (astat & BT848_INT_I2CDONE) {
+			btv->i2c_done = stat;
+			wake_up(&btv->i2c_queue);
+		}
+
+		if ((astat & BT848_INT_RISCI)  &&  (stat & (4<<28)))
+			bttv_irq_switch_vbi(btv);
+
+		if ((astat & BT848_INT_RISCI)  &&  (stat & (2<<28)))
+			bttv_irq_wakeup_top(btv);
+
+		if ((astat & BT848_INT_RISCI)  &&  (stat & (1<<28)))
+			bttv_irq_switch_video(btv);
+
+		if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute)
+			audio_mute(btv, btv->mute);  /* trigger automute */
+
+		if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+			pr_info("%d: %s%s @ %08x,",
+				btv->c.nr,
+				(astat & BT848_INT_SCERR) ? "SCERR" : "",
+				(astat & BT848_INT_OCERR) ? "OCERR" : "",
+				btread(BT848_RISC_COUNT));
+			bttv_print_irqbits(stat,astat);
+			pr_cont("\n");
+			if (bttv_debug)
+				bttv_print_riscaddr(btv);
+		}
+		if (fdsr && astat & BT848_INT_FDSR) {
+			pr_info("%d: FDSR @ %08x\n",
+				btv->c.nr, btread(BT848_RISC_COUNT));
+			if (bttv_debug)
+				bttv_print_riscaddr(btv);
+		}
+
+		count++;
+		if (count > 4) {
+
+			if (count > 8 || !(astat & BT848_INT_GPINT)) {
+				btwrite(0, BT848_INT_MASK);
+
+				pr_err("%d: IRQ lockup, cleared int mask [",
+				       btv->c.nr);
+			} else {
+				pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+				       btv->c.nr);
+
+				btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+						BT848_INT_MASK);
+			};
+
+			bttv_print_irqbits(stat,astat);
+
+			pr_cont("]\n");
+		}
+	}
+	btv->irq_total++;
+	if (handled)
+		btv->irq_me++;
+	return IRQ_RETVAL(handled);
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* initialitation                                                          */
+
+static struct video_device *vdev_init(struct bttv *btv,
+				      const struct video_device *template,
+				      const char *type_name)
+{
+	struct video_device *vfd;
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->v4l2_dev = &btv->c.v4l2_dev;
+	vfd->release = video_device_release;
+	vfd->debug   = bttv_debug;
+	video_set_drvdata(vfd, btv);
+	snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+		 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+		 type_name, bttv_tvcards[btv->c.type].name);
+	return vfd;
+}
+
+static void bttv_unregister_video(struct bttv *btv)
+{
+	if (btv->video_dev) {
+		if (video_is_registered(btv->video_dev))
+			video_unregister_device(btv->video_dev);
+		else
+			video_device_release(btv->video_dev);
+		btv->video_dev = NULL;
+	}
+	if (btv->vbi_dev) {
+		if (video_is_registered(btv->vbi_dev))
+			video_unregister_device(btv->vbi_dev);
+		else
+			video_device_release(btv->vbi_dev);
+		btv->vbi_dev = NULL;
+	}
+	if (btv->radio_dev) {
+		if (video_is_registered(btv->radio_dev))
+			video_unregister_device(btv->radio_dev);
+		else
+			video_device_release(btv->radio_dev);
+		btv->radio_dev = NULL;
+	}
+}
+
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
+{
+	if (no_overlay > 0)
+		pr_notice("Overlay support disabled\n");
+
+	/* video */
+	btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+
+	if (NULL == btv->video_dev)
+		goto err;
+	if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+				  video_nr[btv->c.nr]) < 0)
+		goto err;
+	pr_info("%d: registered device %s\n",
+		btv->c.nr, video_device_node_name(btv->video_dev));
+	if (device_create_file(&btv->video_dev->dev,
+				     &dev_attr_card)<0) {
+		pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+		goto err;
+	}
+
+	/* vbi */
+	btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+
+	if (NULL == btv->vbi_dev)
+		goto err;
+	if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+				  vbi_nr[btv->c.nr]) < 0)
+		goto err;
+	pr_info("%d: registered device %s\n",
+		btv->c.nr, video_device_node_name(btv->vbi_dev));
+
+	if (!btv->has_radio)
+		return 0;
+	/* radio */
+	btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+	if (NULL == btv->radio_dev)
+		goto err;
+	if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+				  radio_nr[btv->c.nr]) < 0)
+		goto err;
+	pr_info("%d: registered device %s\n",
+		btv->c.nr, video_device_node_name(btv->radio_dev));
+
+	/* all done */
+	return 0;
+
+ err:
+	bttv_unregister_video(btv);
+	return -1;
+}
+
+
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
+{
+#if defined(__powerpc__)
+	unsigned int cmd;
+
+	pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+	cmd = (cmd | PCI_COMMAND_MEMORY );
+	pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+}
+
+static int __devinit bttv_probe(struct pci_dev *dev,
+				const struct pci_device_id *pci_id)
+{
+	int result;
+	unsigned char lat;
+	struct bttv *btv;
+
+	if (bttv_num == BTTV_MAX)
+		return -ENOMEM;
+	pr_info("Bt8xx card found (%d)\n", bttv_num);
+	bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+	if (btv == NULL) {
+		pr_err("out of memory\n");
+		return -ENOMEM;
+	}
+	btv->c.nr  = bttv_num;
+	snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+			"bttv%d", btv->c.nr);
+
+	/* initialize structs / fill in defaults */
+	mutex_init(&btv->lock);
+	spin_lock_init(&btv->s_lock);
+	spin_lock_init(&btv->gpio_lock);
+	init_waitqueue_head(&btv->i2c_queue);
+	INIT_LIST_HEAD(&btv->c.subs);
+	INIT_LIST_HEAD(&btv->capture);
+	INIT_LIST_HEAD(&btv->vcapture);
+	v4l2_prio_init(&btv->prio);
+
+	init_timer(&btv->timeout);
+	btv->timeout.function = bttv_irq_timeout;
+	btv->timeout.data     = (unsigned long)btv;
+
+	btv->i2c_rc = -1;
+	btv->tuner_type  = UNSET;
+	btv->new_input   = UNSET;
+	btv->has_radio=radio[btv->c.nr];
+
+	/* pci stuff (init, get irq/mmio, ... */
+	btv->c.pci = dev;
+	btv->id  = dev->device;
+	if (pci_enable_device(dev)) {
+		pr_warn("%d: Can't enable device\n", btv->c.nr);
+		return -EIO;
+	}
+	if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+		pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+		return -EIO;
+	}
+	if (!request_mem_region(pci_resource_start(dev,0),
+				pci_resource_len(dev,0),
+				btv->c.v4l2_dev.name)) {
+		pr_warn("%d: can't request iomem (0x%llx)\n",
+			btv->c.nr,
+			(unsigned long long)pci_resource_start(dev, 0));
+		return -EBUSY;
+	}
+	pci_set_master(dev);
+	pci_set_command(dev);
+
+	result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+	if (result < 0) {
+		pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+		goto fail0;
+	}
+
+	btv->revision = dev->revision;
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+	pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+		bttv_num, btv->id, btv->revision, pci_name(dev),
+		btv->c.pci->irq, lat,
+		(unsigned long long)pci_resource_start(dev, 0));
+	schedule();
+
+	btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+	if (NULL == btv->bt848_mmio) {
+		pr_err("%d: ioremap() failed\n", btv->c.nr);
+		result = -EIO;
+		goto fail1;
+	}
+
+	/* identify card */
+	bttv_idcard(btv);
+
+	/* disable irqs, register irq handler */
+	btwrite(0, BT848_INT_MASK);
+	result = request_irq(btv->c.pci->irq, bttv_irq,
+	    IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+	if (result < 0) {
+		pr_err("%d: can't get IRQ %d\n",
+		       bttv_num, btv->c.pci->irq);
+		goto fail1;
+	}
+
+	if (0 != bttv_handle_chipset(btv)) {
+		result = -EIO;
+		goto fail2;
+	}
+
+	/* init options from insmod args */
+	btv->opt_combfilter = combfilter;
+	btv->opt_lumafilter = lumafilter;
+	btv->opt_automute   = automute;
+	btv->opt_chroma_agc = chroma_agc;
+	btv->opt_adc_crush  = adc_crush;
+	btv->opt_vcr_hack   = vcr_hack;
+	btv->opt_whitecrush_upper  = whitecrush_upper;
+	btv->opt_whitecrush_lower  = whitecrush_lower;
+	btv->opt_uv_ratio   = uv_ratio;
+	btv->opt_full_luma_range   = full_luma_range;
+	btv->opt_coring     = coring;
+
+	/* fill struct bttv with some useful defaults */
+	btv->init.btv         = btv;
+	btv->init.ov.w.width  = 320;
+	btv->init.ov.w.height = 240;
+	btv->init.fmt         = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+	btv->init.width       = 320;
+	btv->init.height      = 240;
+	btv->input = 0;
+
+	/* initialize hardware */
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"pre-init");
+
+	bttv_risc_init_main(btv);
+	init_bt848(btv);
+
+	/* gpio */
+	btwrite(0x00, BT848_GPIO_REG_INP);
+	btwrite(0x00, BT848_GPIO_OUT_EN);
+	if (bttv_verbose)
+		bttv_gpio_tracking(btv,"init");
+
+	/* needs to be done before i2c is registered */
+	bttv_init_card1(btv);
+
+	/* register i2c + gpio */
+	init_bttv_i2c(btv);
+
+	/* some card-specific stuff (needs working i2c) */
+	bttv_init_card2(btv);
+	bttv_init_tuner(btv);
+	init_irqreg(btv);
+
+	/* register video4linux + input */
+	if (!bttv_tvcards[btv->c.type].no_video) {
+		bttv_register_video(btv);
+		bt848_bright(btv,32768);
+		bt848_contrast(btv, 27648);
+		bt848_hue(btv,32768);
+		bt848_sat(btv,32768);
+		audio_mute(btv, 1);
+		set_input(btv, 0, btv->tvnorm);
+		bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+		btv->crop[1] = btv->crop[0]; /* current = default */
+		disclaim_vbi_lines(btv);
+		disclaim_video_lines(btv);
+	}
+
+	/* add subdevices and autoload dvb-bt8xx if needed */
+	if (bttv_tvcards[btv->c.type].has_dvb) {
+		bttv_sub_add_device(&btv->c, "dvb");
+		request_modules(btv);
+	}
+
+	if (!disable_ir) {
+		init_bttv_i2c_ir(btv);
+		bttv_input_init(btv);
+	}
+
+	/* everything is fine */
+	bttv_num++;
+	return 0;
+
+fail2:
+	free_irq(btv->c.pci->irq,btv);
+
+fail1:
+	v4l2_device_unregister(&btv->c.v4l2_dev);
+
+fail0:
+	if (btv->bt848_mmio)
+		iounmap(btv->bt848_mmio);
+	release_mem_region(pci_resource_start(btv->c.pci,0),
+			   pci_resource_len(btv->c.pci,0));
+	return result;
+}
+
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct bttv *btv = to_bttv(v4l2_dev);
+
+	if (bttv_verbose)
+		pr_info("%d: unloading\n", btv->c.nr);
+
+	if (bttv_tvcards[btv->c.type].has_dvb)
+		flush_request_modules(btv);
+
+	/* shutdown everything (DMA+IRQs) */
+	btand(~15, BT848_GPIO_DMA_CTL);
+	btwrite(0, BT848_INT_MASK);
+	btwrite(~0x0, BT848_INT_STAT);
+	btwrite(0x0, BT848_GPIO_OUT_EN);
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"cleanup");
+
+	/* tell gpio modules we are leaving ... */
+	btv->shutdown=1;
+	bttv_input_fini(btv);
+	bttv_sub_del_devices(&btv->c);
+
+	/* unregister i2c_bus + input */
+	fini_bttv_i2c(btv);
+
+	/* unregister video4linux */
+	bttv_unregister_video(btv);
+
+	/* free allocated memory */
+	btcx_riscmem_free(btv->c.pci,&btv->main);
+
+	/* free ressources */
+	free_irq(btv->c.pci->irq,btv);
+	iounmap(btv->bt848_mmio);
+	release_mem_region(pci_resource_start(btv->c.pci,0),
+			   pci_resource_len(btv->c.pci,0));
+
+	v4l2_device_unregister(&btv->c.v4l2_dev);
+	bttvs[btv->c.nr] = NULL;
+	kfree(btv);
+
+	return;
+}
+
+#ifdef CONFIG_PM
+static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct bttv *btv = to_bttv(v4l2_dev);
+	struct bttv_buffer_set idle;
+	unsigned long flags;
+
+	dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+
+	/* stop dma + irqs */
+	spin_lock_irqsave(&btv->s_lock,flags);
+	memset(&idle, 0, sizeof(idle));
+	btv->state.video = btv->curr;
+	btv->state.vbi   = btv->cvbi;
+	btv->state.loop_irq = btv->loop_irq;
+	btv->curr = idle;
+	btv->loop_irq = 0;
+	bttv_buffer_activate_video(btv, &idle);
+	bttv_buffer_activate_vbi(btv, NULL);
+	bttv_set_dma(btv, 0);
+	btwrite(0, BT848_INT_MASK);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+
+	/* save bt878 state */
+	btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+	btv->state.gpio_data   = gpio_read();
+
+	/* save pci state */
+	pci_save_state(pci_dev);
+	if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+		pci_disable_device(pci_dev);
+		btv->state.disabled = 1;
+	}
+	return 0;
+}
+
+static int bttv_resume(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct bttv *btv = to_bttv(v4l2_dev);
+	unsigned long flags;
+	int err;
+
+	dprintk("%d: resume\n", btv->c.nr);
+
+	/* restore pci state */
+	if (btv->state.disabled) {
+		err=pci_enable_device(pci_dev);
+		if (err) {
+			pr_warn("%d: Can't enable device\n", btv->c.nr);
+			return err;
+		}
+		btv->state.disabled = 0;
+	}
+	err=pci_set_power_state(pci_dev, PCI_D0);
+	if (err) {
+		pci_disable_device(pci_dev);
+		pr_warn("%d: Can't enable device\n", btv->c.nr);
+		btv->state.disabled = 1;
+		return err;
+	}
+
+	pci_restore_state(pci_dev);
+
+	/* restore bt878 state */
+	bttv_reinit_bt848(btv);
+	gpio_inout(0xffffff, btv->state.gpio_enable);
+	gpio_write(btv->state.gpio_data);
+
+	/* restart dma */
+	spin_lock_irqsave(&btv->s_lock,flags);
+	btv->curr = btv->state.video;
+	btv->cvbi = btv->state.vbi;
+	btv->loop_irq = btv->state.loop_irq;
+	bttv_buffer_activate_video(btv, &btv->curr);
+	bttv_buffer_activate_vbi(btv, btv->cvbi);
+	bttv_set_dma(btv, 0);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+	return 0;
+}
+#endif
+
+static struct pci_device_id bttv_pci_tbl[] = {
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+
+static struct pci_driver bttv_pci_driver = {
+	.name     = "bttv",
+	.id_table = bttv_pci_tbl,
+	.probe    = bttv_probe,
+	.remove   = __devexit_p(bttv_remove),
+#ifdef CONFIG_PM
+	.suspend  = bttv_suspend,
+	.resume   = bttv_resume,
+#endif
+};
+
+static int __init bttv_init_module(void)
+{
+	int ret;
+
+	bttv_num = 0;
+
+	pr_info("driver version %s loaded\n", BTTV_VERSION);
+	if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+		gbuffers = 2;
+	if (gbufsize > BTTV_MAX_FBUF)
+		gbufsize = BTTV_MAX_FBUF;
+	gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+	if (bttv_verbose)
+		pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+			gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+
+	bttv_check_chipset();
+
+	ret = bus_register(&bttv_sub_bus_type);
+	if (ret < 0) {
+		pr_warn("bus_register error: %d\n", ret);
+		return ret;
+	}
+	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);
+}
+
+module_init(bttv_init_module);
+module_exit(bttv_cleanup_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c
new file mode 100644
index 000000000000..922e8233fd0b
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-gpio.c
@@ -0,0 +1,189 @@
+/*
+
+    bttv-gpio.c  --  gpio sub drivers
+
+    sysfs-based sub driver interface for bttv
+    mainly intended for gpio access
+
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+			   & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2003 Gerd Knorr <kraxel@bytesex.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.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+/* ----------------------------------------------------------------------- */
+/* internal: the bttv "bus"                                                */
+
+static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
+	int len = strlen(sub->wanted);
+
+	if (0 == strncmp(dev_name(dev), sub->wanted, len))
+		return 1;
+	return 0;
+}
+
+static int bttv_sub_probe(struct device *dev)
+{
+	struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+	struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+	return sub->probe ? sub->probe(sdev) : -ENODEV;
+}
+
+static int bttv_sub_remove(struct device *dev)
+{
+	struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+	struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+	if (sub->remove)
+		sub->remove(sdev);
+	return 0;
+}
+
+struct bus_type bttv_sub_bus_type = {
+	.name   = "bttv-sub",
+	.match  = &bttv_sub_bus_match,
+	.probe  = bttv_sub_probe,
+	.remove = bttv_sub_remove,
+};
+
+static void release_sub_device(struct device *dev)
+{
+	struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+	kfree(sub);
+}
+
+int bttv_sub_add_device(struct bttv_core *core, char *name)
+{
+	struct bttv_sub_device *sub;
+	int err;
+
+	sub = kzalloc(sizeof(*sub),GFP_KERNEL);
+	if (NULL == sub)
+		return -ENOMEM;
+
+	sub->core        = core;
+	sub->dev.parent  = &core->pci->dev;
+	sub->dev.bus     = &bttv_sub_bus_type;
+	sub->dev.release = release_sub_device;
+	dev_set_name(&sub->dev, "%s%d", name, core->nr);
+
+	err = device_register(&sub->dev);
+	if (0 != err) {
+		kfree(sub);
+		return err;
+	}
+	pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
+	list_add_tail(&sub->list,&core->subs);
+	return 0;
+}
+
+int bttv_sub_del_devices(struct bttv_core *core)
+{
+	struct bttv_sub_device *sub, *save;
+
+	list_for_each_entry_safe(sub, save, &core->subs, list) {
+		list_del(&sub->list);
+		device_unregister(&sub->dev);
+	}
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* external: sub-driver register/unregister                                */
+
+int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted)
+{
+	sub->drv.bus = &bttv_sub_bus_type;
+	snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted);
+	return driver_register(&sub->drv);
+}
+EXPORT_SYMBOL(bttv_sub_register);
+
+int bttv_sub_unregister(struct bttv_sub_driver *sub)
+{
+	driver_unregister(&sub->drv);
+	return 0;
+}
+EXPORT_SYMBOL(bttv_sub_unregister);
+
+/* ----------------------------------------------------------------------- */
+/* external: gpio access functions                                         */
+
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	unsigned long flags;
+	u32 data;
+
+	spin_lock_irqsave(&btv->gpio_lock,flags);
+	data = btread(BT848_GPIO_OUT_EN);
+	data = data & ~mask;
+	data = data | (mask & outbits);
+	btwrite(data,BT848_GPIO_OUT_EN);
+	spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+u32 bttv_gpio_read(struct bttv_core *core)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	u32 value;
+
+	value = btread(BT848_GPIO_DATA);
+	return value;
+}
+
+void bttv_gpio_write(struct bttv_core *core, u32 value)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+
+	btwrite(value,BT848_GPIO_DATA);
+}
+
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	unsigned long flags;
+	u32 data;
+
+	spin_lock_irqsave(&btv->gpio_lock,flags);
+	data = btread(BT848_GPIO_DATA);
+	data = data & ~mask;
+	data = data | (mask & bits);
+	btwrite(data,BT848_GPIO_DATA);
+	spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c
new file mode 100644
index 000000000000..580c8e682392
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-i2c.c
@@ -0,0 +1,397 @@
+/*
+
+    bttv-i2c.c  --  all the i2c code is here
+
+    bttv - Bt848 frame grabber driver
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+			   & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+    (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+	- Multituner support and i2c address binding
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+static int i2c_debug;
+static int i2c_hw;
+static int i2c_scan;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "configure i2c debug level");
+module_param(i2c_hw,    int, 0444);
+MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, "
+			"instead of software bitbang");
+module_param(i2c_scan,  int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0444);
+MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs "
+		"(should be 5 or higher). Lower value means higher bus speed.");
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - bitbanging adapter (software i2c)                       */
+
+static void bttv_bit_setscl(void *data, int state)
+{
+	struct bttv *btv = (struct bttv*)data;
+
+	if (state)
+		btv->i2c_state |= 0x02;
+	else
+		btv->i2c_state &= ~0x02;
+	btwrite(btv->i2c_state, BT848_I2C);
+	btread(BT848_I2C);
+}
+
+static void bttv_bit_setsda(void *data, int state)
+{
+	struct bttv *btv = (struct bttv*)data;
+
+	if (state)
+		btv->i2c_state |= 0x01;
+	else
+		btv->i2c_state &= ~0x01;
+	btwrite(btv->i2c_state, BT848_I2C);
+	btread(BT848_I2C);
+}
+
+static int bttv_bit_getscl(void *data)
+{
+	struct bttv *btv = (struct bttv*)data;
+	int state;
+
+	state = btread(BT848_I2C) & 0x02 ? 1 : 0;
+	return state;
+}
+
+static int bttv_bit_getsda(void *data)
+{
+	struct bttv *btv = (struct bttv*)data;
+	int state;
+
+	state = btread(BT848_I2C) & 0x01;
+	return state;
+}
+
+static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = {
+	.setsda  = bttv_bit_setsda,
+	.setscl  = bttv_bit_setscl,
+	.getsda  = bttv_bit_getsda,
+	.getscl  = bttv_bit_getscl,
+	.udelay  = 16,
+	.timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - hardware i2c                                            */
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+bttv_i2c_wait_done(struct bttv *btv)
+{
+	int rc = 0;
+
+	/* timeout */
+	if (wait_event_interruptible_timeout(btv->i2c_queue,
+	    btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS)
+		rc = -EIO;
+
+	if (btv->i2c_done & BT848_INT_RACK)
+		rc = 1;
+	btv->i2c_done = 0;
+	return rc;
+}
+
+#define I2C_HW (BT878_I2C_MODE  | BT848_I2C_SYNC |\
+		BT848_I2C_SCL | BT848_I2C_SDA)
+
+static int
+bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+	u32 xmit;
+	int retval,cnt;
+
+	/* sanity checks */
+	if (0 == msg->len)
+		return -EINVAL;
+
+	/* start, address + first byte */
+	xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW;
+	if (msg->len > 1 || !last)
+		xmit |= BT878_I2C_NOSTOP;
+	btwrite(xmit, BT848_I2C);
+	retval = bttv_i2c_wait_done(btv);
+	if (retval < 0)
+		goto err;
+	if (retval == 0)
+		goto eio;
+	if (i2c_debug) {
+		pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+	}
+
+	for (cnt = 1; cnt < msg->len; cnt++ ) {
+		/* following bytes */
+		xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART;
+		if (cnt < msg->len-1 || !last)
+			xmit |= BT878_I2C_NOSTOP;
+		btwrite(xmit, BT848_I2C);
+		retval = bttv_i2c_wait_done(btv);
+		if (retval < 0)
+			goto err;
+		if (retval == 0)
+			goto eio;
+		if (i2c_debug)
+			pr_cont(" %02x", msg->buf[cnt]);
+	}
+	if (!(xmit & BT878_I2C_NOSTOP))
+		pr_cont(">\n");
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+ err:
+	if (i2c_debug)
+		pr_cont(" ERR: %d\n",retval);
+	return retval;
+}
+
+static int
+bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+	u32 xmit;
+	u32 cnt;
+	int retval;
+
+	for (cnt = 0; cnt < msg->len; cnt++) {
+		xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
+		if (cnt < msg->len-1)
+			xmit |= BT848_I2C_W3B;
+		if (cnt < msg->len-1 || !last)
+			xmit |= BT878_I2C_NOSTOP;
+		if (cnt)
+			xmit |= BT878_I2C_NOSTART;
+
+		if (i2c_debug) {
+			if (!(xmit & BT878_I2C_NOSTART))
+				pr_cont(" <R %02x", (msg->addr << 1) +1);
+		}
+
+		btwrite(xmit, BT848_I2C);
+		retval = bttv_i2c_wait_done(btv);
+		if (retval < 0)
+			goto err;
+		if (retval == 0)
+			goto eio;
+		msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
+		if (i2c_debug) {
+			pr_cont(" =%02x", msg->buf[cnt]);
+		}
+		if (i2c_debug && !(xmit & BT878_I2C_NOSTOP))
+			pr_cont(" >\n");
+	}
+
+
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+ err:
+	if (i2c_debug)
+		pr_cont(" ERR: %d\n",retval);
+	return retval;
+}
+
+static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
+	struct bttv *btv = to_bttv(v4l2_dev);
+	int retval = 0;
+	int i;
+
+	if (i2c_debug)
+		pr_debug("bt-i2c:");
+
+	btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
+	for (i = 0 ; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read */
+			retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num);
+			if (retval < 0)
+				goto err;
+		} else {
+			/* write */
+			retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num);
+			if (retval < 0)
+				goto err;
+		}
+	}
+	return num;
+
+ err:
+	return retval;
+}
+
+static const struct i2c_algorithm bttv_algo = {
+	.master_xfer   = bttv_i2c_xfer,
+	.functionality = functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - common stuff                                            */
+
+/* read I2C */
+int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
+{
+	unsigned char buffer = 0;
+
+	if (0 != btv->i2c_rc)
+		return -1;
+	if (bttv_verbose && NULL != probe_for)
+		pr_info("%d: i2c: checking for %s @ 0x%02x... ",
+			btv->c.nr, probe_for, addr);
+	btv->i2c_client.addr = addr >> 1;
+	if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
+		if (NULL != probe_for) {
+			if (bttv_verbose)
+				pr_cont("not found\n");
+		} else
+			pr_warn("%d: i2c read 0x%x: error\n",
+				btv->c.nr, addr);
+		return -1;
+	}
+	if (bttv_verbose && NULL != probe_for)
+		pr_cont("found\n");
+	return buffer;
+}
+
+/* write I2C */
+int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+		    unsigned char b2, int both)
+{
+	unsigned char buffer[2];
+	int bytes = both ? 2 : 1;
+
+	if (0 != btv->i2c_rc)
+		return -1;
+	btv->i2c_client.addr = addr >> 1;
+	buffer[0] = b1;
+	buffer[1] = b2;
+	if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes))
+		return -1;
+	return 0;
+}
+
+/* read EEPROM content */
+void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr)
+{
+	memset(eedata, 0, 256);
+	if (0 != btv->i2c_rc)
+		return;
+	btv->i2c_client.addr = addr >> 1;
+	tveeprom_read(&btv->i2c_client, eedata, 256);
+}
+
+static char *i2c_devs[128] = {
+	[ 0x1c >> 1 ] = "lgdt330x",
+	[ 0x30 >> 1 ] = "IR (hauppauge)",
+	[ 0x80 >> 1 ] = "msp34xx",
+	[ 0x86 >> 1 ] = "tda9887",
+	[ 0xa0 >> 1 ] = "eeprom",
+	[ 0xc0 >> 1 ] = "tuner (analog)",
+	[ 0xc2 >> 1 ] = "tuner (analog)",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+	unsigned char buf;
+	int i,rc;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+		c->addr = i;
+		rc = i2c_master_recv(c,&buf,0);
+		if (rc < 0)
+			continue;
+		pr_info("%s: i2c scan: found device @ 0x%x  [%s]\n",
+			name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+	}
+}
+
+/* init + register i2c adapter */
+int __devinit init_bttv_i2c(struct bttv *btv)
+{
+	strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE);
+
+	if (i2c_hw)
+		btv->use_i2c_hw = 1;
+	if (btv->use_i2c_hw) {
+		/* bt878 */
+		strlcpy(btv->c.i2c_adap.name, "bt878",
+			sizeof(btv->c.i2c_adap.name));
+		btv->c.i2c_adap.algo = &bttv_algo;
+	} else {
+		/* bt848 */
+	/* Prevents usage of invalid delay values */
+		if (i2c_udelay<5)
+			i2c_udelay=5;
+
+		strlcpy(btv->c.i2c_adap.name, "bttv",
+			sizeof(btv->c.i2c_adap.name));
+		memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
+		       sizeof(bttv_i2c_algo_bit_template));
+		btv->i2c_algo.udelay = i2c_udelay;
+		btv->i2c_algo.data = btv;
+		btv->c.i2c_adap.algo_data = &btv->i2c_algo;
+	}
+	btv->c.i2c_adap.owner = THIS_MODULE;
+
+	btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
+	snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+		 "bt%d #%d [%s]", btv->id, btv->c.nr,
+		 btv->use_i2c_hw ? "hw" : "sw");
+
+	i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev);
+	btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+
+	if (btv->use_i2c_hw) {
+		btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap);
+	} else {
+		bttv_bit_setscl(btv,1);
+		bttv_bit_setsda(btv,1);
+		btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap);
+	}
+	if (0 == btv->i2c_rc && i2c_scan)
+		do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
+
+	return btv->i2c_rc;
+}
diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c
new file mode 100644
index 000000000000..a6a540dc9e4b
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-if.c
@@ -0,0 +1,121 @@
+/*
+
+    bttv-if.c  --  old gpio interface to other kernel modules
+		   don't use in new code, will go away in 2.7
+		   have a look at bttv-gpio.c instead.
+
+    bttv - Bt848 frame grabber driver
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+			   & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2003 Gerd Knorr <kraxel@bytesex.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/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+EXPORT_SYMBOL(bttv_get_pcidev);
+EXPORT_SYMBOL(bttv_gpio_enable);
+EXPORT_SYMBOL(bttv_read_gpio);
+EXPORT_SYMBOL(bttv_write_gpio);
+
+/* ----------------------------------------------------------------------- */
+/* Exported functions - for other modules which want to access the         */
+/*                      gpio ports (IR for example)                        */
+/*                      see bttv.h for comments                            */
+
+struct pci_dev* bttv_get_pcidev(unsigned int card)
+{
+	if (card >= bttv_num)
+		return NULL;
+	if (!bttvs[card])
+		return NULL;
+
+	return bttvs[card]->c.pci;
+}
+
+
+int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
+{
+	struct bttv *btv;
+
+	if (card >= bttv_num) {
+		return -EINVAL;
+	}
+
+	btv = bttvs[card];
+	if (!btv)
+		return -ENODEV;
+
+	gpio_inout(mask,data);
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"extern enable");
+	return 0;
+}
+
+int bttv_read_gpio(unsigned int card, unsigned long *data)
+{
+	struct bttv *btv;
+
+	if (card >= bttv_num) {
+		return -EINVAL;
+	}
+
+	btv = bttvs[card];
+	if (!btv)
+		return -ENODEV;
+
+	if(btv->shutdown) {
+		return -ENODEV;
+	}
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+   because we set direct input on init */
+	*data = gpio_read();
+	return 0;
+}
+
+int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
+{
+	struct bttv *btv;
+
+	if (card >= bttv_num) {
+		return -EINVAL;
+	}
+
+	btv = bttvs[card];
+	if (!btv)
+		return -ENODEV;
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+   because direct input is set on init */
+	gpio_bits(mask,data);
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,"extern write");
+	return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
new file mode 100644
index 000000000000..ef4c7cd41982
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -0,0 +1,589 @@
+/*
+ *
+ * Copyright (c) 2003 Gerd Knorr
+ * Copyright (c) 2003 Pavel Machek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include "bttv.h"
+#include "bttvp.h"
+
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+
+static int ir_rc5_remote_gap = 885;
+module_param(ir_rc5_remote_gap, int, 0644);
+
+#undef dprintk
+#define dprintk(fmt, ...)			\
+do {						\
+	if (ir_debug >= 1)			\
+		pr_info(fmt, ##__VA_ARGS__);	\
+} while (0)
+
+#define DEVNAME "bttv-input"
+
+#define MODULE_NAME "bttv"
+
+/* ---------------------------------------------------------------------- */
+
+static void ir_handle_key(struct bttv *btv)
+{
+	struct bttv_ir *ir = btv->remote;
+	u32 gpio,data;
+
+	/* read gpio value */
+	gpio = bttv_gpio_read(&btv->c);
+	if (ir->polling) {
+		if (ir->last_gpio == gpio)
+			return;
+		ir->last_gpio = gpio;
+	}
+
+	/* extract data */
+	data = ir_extract_bits(gpio, ir->mask_keycode);
+	dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+		gpio, data,
+		ir->polling               ? "poll"  : "irq",
+		(gpio & ir->mask_keydown) ? " down" : "",
+		(gpio & ir->mask_keyup)   ? " up"   : "");
+
+	if ((ir->mask_keydown && (gpio & ir->mask_keydown)) ||
+	    (ir->mask_keyup   && !(gpio & ir->mask_keyup))) {
+		rc_keydown_notimeout(ir->dev, data, 0);
+	} else {
+		/* HACK: Probably, ir->mask_keydown is missing
+		   for this board */
+		if (btv->c.type == BTTV_BOARD_WINFAST2000)
+			rc_keydown_notimeout(ir->dev, data, 0);
+
+		rc_keyup(ir->dev);
+	}
+}
+
+static void ir_enltv_handle_key(struct bttv *btv)
+{
+	struct bttv_ir *ir = btv->remote;
+	u32 gpio, data, keyup;
+
+	/* read gpio value */
+	gpio = bttv_gpio_read(&btv->c);
+
+	/* extract data */
+	data = ir_extract_bits(gpio, ir->mask_keycode);
+
+	/* Check if it is keyup */
+	keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
+
+	if ((ir->last_gpio & 0x7f) != data) {
+		dprintk("gpio=0x%x code=%d | %s\n",
+			gpio, data,
+			(gpio & ir->mask_keyup) ? " up" : "up/down");
+
+		rc_keydown_notimeout(ir->dev, data, 0);
+		if (keyup)
+			rc_keyup(ir->dev);
+	} else {
+		if ((ir->last_gpio & 1 << 31) == keyup)
+			return;
+
+		dprintk("(cnt) gpio=0x%x code=%d | %s\n",
+			gpio, data,
+			(gpio & ir->mask_keyup) ? " up" : "down");
+
+		if (keyup)
+			rc_keyup(ir->dev);
+		else
+			rc_keydown_notimeout(ir->dev, data, 0);
+	}
+
+	ir->last_gpio = data | keyup;
+}
+
+static int bttv_rc5_irq(struct bttv *btv);
+
+void bttv_input_irq(struct bttv *btv)
+{
+	struct bttv_ir *ir = btv->remote;
+
+	if (ir->rc5_gpio)
+		bttv_rc5_irq(btv);
+	else if (!ir->polling)
+		ir_handle_key(btv);
+}
+
+static void bttv_input_timer(unsigned long data)
+{
+	struct bttv *btv = (struct bttv*)data;
+	struct bttv_ir *ir = btv->remote;
+
+	if (btv->c.type == BTTV_BOARD_ENLTV_FM_2)
+		ir_enltv_handle_key(btv);
+	else
+		ir_handle_key(btv);
+	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+/*
+ * FIXME: Nebula digi uses the legacy way to decode RC5, instead of relying
+ * on the rc-core way. As we need to be sure that both IRQ transitions are
+ * properly triggered, Better to touch it only with this hardware for
+ * testing.
+ */
+
+#define RC5_START(x)	(((x) >> 12) & 3)
+#define RC5_TOGGLE(x)	(((x) >> 11) & 1)
+#define RC5_ADDR(x)	(((x) >> 6) & 31)
+#define RC5_INSTR(x)	((x) & 63)
+
+/* decode raw bit pattern to RC5 code */
+static u32 bttv_rc5_decode(unsigned int code)
+{
+	unsigned int org_code = code;
+	unsigned int pair;
+	unsigned int rc5 = 0;
+	int i;
+
+	for (i = 0; i < 14; ++i) {
+		pair = code & 0x3;
+		code >>= 2;
+
+		rc5 <<= 1;
+		switch (pair) {
+		case 0:
+		case 2:
+			break;
+		case 1:
+			rc5 |= 1;
+		break;
+		case 3:
+			dprintk("rc5_decode(%x) bad code\n",
+				org_code);
+			return 0;
+		}
+	}
+	dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+		"instr=%x\n", rc5, org_code, RC5_START(rc5),
+		RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
+	return rc5;
+}
+
+static void bttv_rc5_timer_end(unsigned long data)
+{
+	struct bttv_ir *ir = (struct bttv_ir *)data;
+	struct timeval tv;
+	u32 gap;
+	u32 rc5 = 0;
+
+	/* get time */
+	do_gettimeofday(&tv);
+
+	/* avoid overflow with gap >1s */
+	if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+		gap = 200000;
+	} else {
+		gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+		    tv.tv_usec - ir->base_time.tv_usec;
+	}
+
+	/* signal we're ready to start a new code */
+	ir->active = false;
+
+	/* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
+	if (gap < 28000) {
+		dprintk("spurious timer_end\n");
+		return;
+	}
+
+	if (ir->last_bit < 20) {
+		/* ignore spurious codes (caused by light/other remotes) */
+		dprintk("short code: %x\n", ir->code);
+	} else {
+		ir->code = (ir->code << ir->shift_by) | 1;
+		rc5 = bttv_rc5_decode(ir->code);
+
+		/* two start bits? */
+		if (RC5_START(rc5) != ir->start) {
+			pr_info(DEVNAME ":"
+			       " rc5 start bits invalid: %u\n", RC5_START(rc5));
+
+			/* right address? */
+		} else if (RC5_ADDR(rc5) == ir->addr) {
+			u32 toggle = RC5_TOGGLE(rc5);
+			u32 instr = RC5_INSTR(rc5);
+
+			/* Good code */
+			rc_keydown(ir->dev, instr, toggle);
+			dprintk("instruction %x, toggle %x\n",
+				instr, toggle);
+		}
+	}
+}
+
+static int bttv_rc5_irq(struct bttv *btv)
+{
+	struct bttv_ir *ir = btv->remote;
+	struct timeval tv;
+	u32 gpio;
+	u32 gap;
+	unsigned long current_jiffies;
+
+	/* read gpio port */
+	gpio = bttv_gpio_read(&btv->c);
+
+	/* get time of bit */
+	current_jiffies = jiffies;
+	do_gettimeofday(&tv);
+
+	/* avoid overflow with gap >1s */
+	if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+		gap = 200000;
+	} else {
+		gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+		    tv.tv_usec - ir->base_time.tv_usec;
+	}
+
+	dprintk("RC5 IRQ: gap %d us for %s\n",
+		gap, (gpio & 0x20) ? "mark" : "space");
+
+	/* remote IRQ? */
+	if (!(gpio & 0x20))
+		return 0;
+
+	/* active code => add bit */
+	if (ir->active) {
+		/* only if in the code (otherwise spurious IRQ or timer
+		   late) */
+		if (ir->last_bit < 28) {
+			ir->last_bit = (gap - ir_rc5_remote_gap / 2) /
+			    ir_rc5_remote_gap;
+			ir->code |= 1 << ir->last_bit;
+		}
+		/* starting new code */
+	} else {
+		ir->active = true;
+		ir->code = 0;
+		ir->base_time = tv;
+		ir->last_bit = 0;
+
+		mod_timer(&ir->timer, current_jiffies + msecs_to_jiffies(30));
+	}
+
+	/* toggle GPIO pin 4 to reset the irq */
+	bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+	bttv_gpio_write(&btv->c, gpio | (1 << 4));
+	return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir)
+{
+	if (ir->polling) {
+		setup_timer(&ir->timer, bttv_input_timer, (unsigned long)btv);
+		ir->timer.expires  = jiffies + msecs_to_jiffies(1000);
+		add_timer(&ir->timer);
+	} else if (ir->rc5_gpio) {
+		/* set timer_end for code completion */
+		setup_timer(&ir->timer, bttv_rc5_timer_end, (unsigned long)ir);
+		ir->shift_by = 1;
+		ir->start = 3;
+		ir->addr = 0x0;
+		ir->rc5_remote_gap = ir_rc5_remote_gap;
+	}
+}
+
+static void bttv_ir_stop(struct bttv *btv)
+{
+	if (btv->remote->polling)
+		del_timer_sync(&btv->remote->timer);
+
+	if (btv->remote->rc5_gpio) {
+		u32 gpio;
+
+		del_timer_sync(&btv->remote->timer);
+
+		gpio = bttv_gpio_read(&btv->c);
+		bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+	}
+}
+
+/*
+ * Get_key functions used by I2C remotes
+ */
+
+static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char b;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		dprintk("read error\n");
+		return -EIO;
+	}
+
+	/* ignore 0xaa */
+	if (b==0xaa)
+		return 0;
+	dprintk("key %02x\n", b);
+
+	/*
+	 * NOTE:
+	 * lirc_i2c maps the pv951 code as:
+	 *	addr = 0x61D6
+	 * 	cmd = bit_reverse (b)
+	 * So, it seems that this device uses NEC extended
+	 * I decided to not fix the table, due to two reasons:
+	 * 	1) Without the actual device, this is only a guess;
+	 * 	2) As the addr is not reported via I2C, nor can be changed,
+	 * 	   the device is bound to the vendor-provided RC.
+	 */
+
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
+	const unsigned short addr_list[] = {
+		0x1a, 0x18, 0x64, 0x30, 0x71,
+		I2C_CLIENT_END
+	};
+	struct i2c_board_info info;
+
+	if (0 != btv->i2c_rc)
+		return;
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	memset(&btv->init_data, 0, sizeof(btv->init_data));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+	switch (btv->c.type) {
+	case BTTV_BOARD_PV951:
+		btv->init_data.name = "PV951";
+		btv->init_data.get_key = get_key_pv951;
+		btv->init_data.ir_codes = RC_MAP_PV951;
+		info.addr = 0x4b;
+		break;
+	default:
+		/*
+		 * The external IR receiver is at i2c address 0x34 (0x35 for
+		 * reads).  Future Hauppauge cards will have an internal
+		 * receiver at 0x30 (0x31 for reads).  In theory, both can be
+		 * fitted, and Hauppauge suggest an external overrides an
+		 * internal.
+		 * That's why we probe 0x1a (~0x34) first. CB
+		 */
+
+		i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
+		return;
+	}
+
+	if (btv->init_data.name)
+		info.platform_data = &btv->init_data;
+	i2c_new_device(&btv->c.i2c_adap, &info);
+
+	return;
+}
+
+int __devexit fini_bttv_i2c(struct bttv *btv)
+{
+	if (0 != btv->i2c_rc)
+		return 0;
+
+	return i2c_del_adapter(&btv->c.i2c_adap);
+}
+
+int bttv_input_init(struct bttv *btv)
+{
+	struct bttv_ir *ir;
+	char *ir_codes = NULL;
+	struct rc_dev *rc;
+	int err = -ENOMEM;
+
+	if (!btv->has_remote)
+		return -ENODEV;
+
+	ir = kzalloc(sizeof(*ir),GFP_KERNEL);
+	rc = rc_allocate_device();
+	if (!ir || !rc)
+		goto err_out_free;
+
+	/* detect & configure */
+	switch (btv->c.type) {
+	case BTTV_BOARD_AVERMEDIA:
+	case BTTV_BOARD_AVPHONE98:
+	case BTTV_BOARD_AVERMEDIA98:
+		ir_codes         = RC_MAP_AVERMEDIA;
+		ir->mask_keycode = 0xf88000;
+		ir->mask_keydown = 0x010000;
+		ir->polling      = 50; // ms
+		break;
+
+	case BTTV_BOARD_AVDVBT_761:
+	case BTTV_BOARD_AVDVBT_771:
+		ir_codes         = RC_MAP_AVERMEDIA_DVBT;
+		ir->mask_keycode = 0x0f00c0;
+		ir->mask_keydown = 0x000020;
+		ir->polling      = 50; // ms
+		break;
+
+	case BTTV_BOARD_PXELVWPLTVPAK:
+		ir_codes         = RC_MAP_PIXELVIEW;
+		ir->mask_keycode = 0x003e00;
+		ir->mask_keyup   = 0x010000;
+		ir->polling      = 50; // ms
+		break;
+	case BTTV_BOARD_PV_M4900:
+	case BTTV_BOARD_PV_BT878P_9B:
+	case BTTV_BOARD_PV_BT878P_PLUS:
+		ir_codes         = RC_MAP_PIXELVIEW;
+		ir->mask_keycode = 0x001f00;
+		ir->mask_keyup   = 0x008000;
+		ir->polling      = 50; // ms
+		break;
+
+	case BTTV_BOARD_WINFAST2000:
+		ir_codes         = RC_MAP_WINFAST;
+		ir->mask_keycode = 0x1f8;
+		break;
+	case BTTV_BOARD_MAGICTVIEW061:
+	case BTTV_BOARD_MAGICTVIEW063:
+		ir_codes         = RC_MAP_WINFAST;
+		ir->mask_keycode = 0x0008e000;
+		ir->mask_keydown = 0x00200000;
+		break;
+	case BTTV_BOARD_APAC_VIEWCOMP:
+		ir_codes         = RC_MAP_APAC_VIEWCOMP;
+		ir->mask_keycode = 0x001f00;
+		ir->mask_keyup   = 0x008000;
+		ir->polling      = 50; // ms
+		break;
+	case BTTV_BOARD_ASKEY_CPH03X:
+	case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
+	case BTTV_BOARD_CONTVFMI:
+		ir_codes         = RC_MAP_PIXELVIEW;
+		ir->mask_keycode = 0x001F00;
+		ir->mask_keyup   = 0x006000;
+		ir->polling      = 50; // ms
+		break;
+	case BTTV_BOARD_NEBULA_DIGITV:
+		ir_codes = RC_MAP_NEBULA;
+		ir->rc5_gpio = true;
+		break;
+	case BTTV_BOARD_MACHTV_MAGICTV:
+		ir_codes         = RC_MAP_APAC_VIEWCOMP;
+		ir->mask_keycode = 0x001F00;
+		ir->mask_keyup   = 0x004000;
+		ir->polling      = 50; /* ms */
+		break;
+	case BTTV_BOARD_KOZUMI_KTV_01C:
+		ir_codes         = RC_MAP_PCTV_SEDNA;
+		ir->mask_keycode = 0x001f00;
+		ir->mask_keyup   = 0x006000;
+		ir->polling      = 50; /* ms */
+		break;
+	case BTTV_BOARD_ENLTV_FM_2:
+		ir_codes         = RC_MAP_ENCORE_ENLTV2;
+		ir->mask_keycode = 0x00fd00;
+		ir->mask_keyup   = 0x000080;
+		ir->polling      = 1; /* ms */
+		ir->last_gpio    = ir_extract_bits(bttv_gpio_read(&btv->c),
+						   ir->mask_keycode);
+		break;
+	}
+	if (NULL == ir_codes) {
+		dprintk("Ooops: IR config error [card=%d]\n", btv->c.type);
+		err = -ENODEV;
+		goto err_out_free;
+	}
+
+	if (ir->rc5_gpio) {
+		u32 gpio;
+		/* enable remote irq */
+		bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4);
+		gpio = bttv_gpio_read(&btv->c);
+		bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+		bttv_gpio_write(&btv->c, gpio | (1 << 4));
+	} else {
+		/* init hardware-specific stuff */
+		bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0);
+	}
+
+	/* init input device */
+	ir->dev = rc;
+
+	snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
+		 btv->c.type);
+	snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+		 pci_name(btv->c.pci));
+
+	rc->input_name = ir->name;
+	rc->input_phys = ir->phys;
+	rc->input_id.bustype = BUS_PCI;
+	rc->input_id.version = 1;
+	if (btv->c.pci->subsystem_vendor) {
+		rc->input_id.vendor  = btv->c.pci->subsystem_vendor;
+		rc->input_id.product = btv->c.pci->subsystem_device;
+	} else {
+		rc->input_id.vendor  = btv->c.pci->vendor;
+		rc->input_id.product = btv->c.pci->device;
+	}
+	rc->dev.parent = &btv->c.pci->dev;
+	rc->map_name = ir_codes;
+	rc->driver_name = MODULE_NAME;
+
+	btv->remote = ir;
+	bttv_ir_start(btv, ir);
+
+	/* all done */
+	err = rc_register_device(rc);
+	if (err)
+		goto err_out_stop;
+
+	return 0;
+
+ err_out_stop:
+	bttv_ir_stop(btv);
+	btv->remote = NULL;
+ err_out_free:
+	rc_free_device(rc);
+	kfree(ir);
+	return err;
+}
+
+void bttv_input_fini(struct bttv *btv)
+{
+	if (btv->remote == NULL)
+		return;
+
+	bttv_ir_stop(btv);
+	rc_unregister_device(btv->remote->dev);
+	kfree(btv->remote);
+	btv->remote = NULL;
+}
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
new file mode 100644
index 000000000000..82cc47d2e3fa
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -0,0 +1,909 @@
+/*
+
+    bttv-risc.c  --  interfaces to other kernel modules
+
+    bttv risc code handling
+	- memory management
+	- generation
+
+    (c) 2000-2003 Gerd Knorr <kraxel@bytesex.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.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <media/v4l2-ioctl.h>
+
+#include "bttvp.h"
+
+#define VCR_HACK_LINES 4
+
+/* ---------------------------------------------------------- */
+/* risc code generators                                       */
+
+int
+bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+		 struct scatterlist *sglist,
+		 unsigned int offset, unsigned int bpl,
+		 unsigned int padding, unsigned int skip_lines,
+		 unsigned int store_lines)
+{
+	u32 instructions,line,todo;
+	struct scatterlist *sg;
+	__le32 *rp;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + sync + jump (all 2 dwords).  padding
+	   can cause next bpl to start close to a page border.  First DMA
+	   region may be smaller than PAGE_SIZE */
+	instructions  = skip_lines * 4;
+	instructions += (1 + ((bpl + padding) * store_lines)
+			 / PAGE_SIZE + store_lines) * 8;
+	instructions += 2 * 8;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0)
+		return rc;
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+	*(rp++) = cpu_to_le32(0);
+
+	while (skip_lines-- > 0) {
+		*(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL |
+				      BT848_RISC_EOL | bpl);
+	}
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < store_lines; line++) {
+		if ((btv->opt_vcr_hack) &&
+		    (line >= (store_lines - VCR_HACK_LINES)))
+			continue;
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+		if (bpl <= sg_dma_len(sg)-offset) {
+			/* fits into current chunk */
+			*(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+					    BT848_RISC_EOL|bpl);
+			*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+			offset+=bpl;
+		} else {
+			/* scanline needs to be splitted */
+			todo = bpl;
+			*(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+					    (sg_dma_len(sg)-offset));
+			*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+			todo -= (sg_dma_len(sg)-offset);
+			offset = 0;
+			sg++;
+			while (todo > sg_dma_len(sg)) {
+				*(rp++)=cpu_to_le32(BT848_RISC_WRITE|
+						    sg_dma_len(sg));
+				*(rp++)=cpu_to_le32(sg_dma_address(sg));
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+			*(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
+					    todo);
+			*(rp++)=cpu_to_le32(sg_dma_address(sg));
+			offset += todo;
+		}
+		offset += padding;
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+
+static int
+bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
+		 struct scatterlist *sglist,
+		 unsigned int yoffset,  unsigned int ybpl,
+		 unsigned int ypadding, unsigned int ylines,
+		 unsigned int uoffset,  unsigned int voffset,
+		 unsigned int hshift,   unsigned int vshift,
+		 unsigned int cpadding)
+{
+	unsigned int instructions,line,todo,ylen,chroma;
+	__le32 *rp;
+	u32 ri;
+	struct scatterlist *ysg;
+	struct scatterlist *usg;
+	struct scatterlist *vsg;
+	int topfield = (0 == yoffset);
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line (5 dwords)
+	   plus sync + jump (2 dwords) */
+	instructions  = ((3 + (ybpl + ypadding) * ylines * 2)
+			 / PAGE_SIZE) + ylines;
+	instructions += 2;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
+		return rc;
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
+	*(rp++) = cpu_to_le32(0);
+
+	/* scan lines */
+	ysg = sglist;
+	usg = sglist;
+	vsg = sglist;
+	for (line = 0; line < ylines; line++) {
+		if ((btv->opt_vcr_hack) &&
+		    (line >= (ylines - VCR_HACK_LINES)))
+			continue;
+		switch (vshift) {
+		case 0:
+			chroma = 1;
+			break;
+		case 1:
+			if (topfield)
+				chroma = ((line & 1) == 0);
+			else
+				chroma = ((line & 1) == 1);
+			break;
+		case 2:
+			if (topfield)
+				chroma = ((line & 3) == 0);
+			else
+				chroma = ((line & 3) == 2);
+			break;
+		default:
+			chroma = 0;
+			break;
+		}
+
+		for (todo = ybpl; todo > 0; todo -= ylen) {
+			/* go to next sg entry if needed */
+			while (yoffset && yoffset >= sg_dma_len(ysg)) {
+				yoffset -= sg_dma_len(ysg);
+				ysg++;
+			}
+			while (uoffset && uoffset >= sg_dma_len(usg)) {
+				uoffset -= sg_dma_len(usg);
+				usg++;
+			}
+			while (voffset && voffset >= sg_dma_len(vsg)) {
+				voffset -= sg_dma_len(vsg);
+				vsg++;
+			}
+
+			/* calculate max number of bytes we can write */
+			ylen = todo;
+			if (yoffset + ylen > sg_dma_len(ysg))
+				ylen = sg_dma_len(ysg) - yoffset;
+			if (chroma) {
+				if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
+					ylen = (sg_dma_len(usg) - uoffset) << hshift;
+				if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
+					ylen = (sg_dma_len(vsg) - voffset) << hshift;
+				ri = BT848_RISC_WRITE123;
+			} else {
+				ri = BT848_RISC_WRITE1S23;
+			}
+			if (ybpl == todo)
+				ri |= BT848_RISC_SOL;
+			if (ylen == todo)
+				ri |= BT848_RISC_EOL;
+
+			/* write risc instruction */
+			*(rp++)=cpu_to_le32(ri | ylen);
+			*(rp++)=cpu_to_le32(((ylen >> hshift) << 16) |
+					    (ylen >> hshift));
+			*(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset);
+			yoffset += ylen;
+			if (chroma) {
+				*(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset);
+				uoffset += ylen >> hshift;
+				*(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset);
+				voffset += ylen >> hshift;
+			}
+		}
+		yoffset += ypadding;
+		if (chroma) {
+			uoffset += cpadding;
+			voffset += cpadding;
+		}
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+
+static int
+bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
+		  const struct bttv_format *fmt, struct bttv_overlay *ov,
+		  int skip_even, int skip_odd)
+{
+	int dwords, rc, line, maxy, start, end;
+	unsigned skip, nskips;
+	struct btcx_skiplist *skips;
+	__le32 *rp;
+	u32 ri,ra;
+	u32 addr;
+
+	/* skip list for window clipping */
+	if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+		return -ENOMEM;
+
+	/* estimate risc mem: worst case is (1.5*clip+1) * lines instructions
+	   + sync + jump (all 2 dwords) */
+	dwords  = (3 * ov->nclips + 2) *
+		((skip_even || skip_odd) ? (ov->w.height+1)>>1 :  ov->w.height);
+	dwords += 4;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) {
+		kfree(skips);
+		return rc;
+	}
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+	*(rp++) = cpu_to_le32(0);
+
+	addr  = (unsigned long)btv->fbuf.base;
+	addr += btv->fbuf.fmt.bytesperline * ov->w.top;
+	addr += (fmt->depth >> 3)          * ov->w.left;
+
+	/* scan lines */
+	for (maxy = -1, line = 0; line < ov->w.height;
+	     line++, addr += btv->fbuf.fmt.bytesperline) {
+		if ((btv->opt_vcr_hack) &&
+		     (line >= (ov->w.height - VCR_HACK_LINES)))
+			continue;
+		if ((line%2) == 0  &&  skip_even)
+			continue;
+		if ((line%2) == 1  &&  skip_odd)
+			continue;
+
+		/* calculate clipping */
+		if (line > maxy)
+			btcx_calc_skips(line, ov->w.width, &maxy,
+					skips, &nskips, ov->clips, ov->nclips);
+
+		/* write out risc code */
+		for (start = 0, skip = 0; start < ov->w.width; start = end) {
+			if (skip >= nskips) {
+				ri  = BT848_RISC_WRITE;
+				end = ov->w.width;
+			} else if (start < skips[skip].start) {
+				ri  = BT848_RISC_WRITE;
+				end = skips[skip].start;
+			} else {
+				ri  = BT848_RISC_SKIP;
+				end = skips[skip].end;
+				skip++;
+			}
+			if (BT848_RISC_WRITE == ri)
+				ra = addr + (fmt->depth>>3)*start;
+			else
+				ra = 0;
+
+			if (0 == start)
+				ri |= BT848_RISC_SOL;
+			if (ov->w.width == end)
+				ri |= BT848_RISC_EOL;
+			ri |= (fmt->depth>>3) * (end-start);
+
+			*(rp++)=cpu_to_le32(ri);
+			if (0 != ra)
+				*(rp++)=cpu_to_le32(ra);
+		}
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	kfree(skips);
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+static void
+bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo,
+		  int width, int height, int interleaved,
+		  const struct bttv_tvnorm *tvnorm)
+{
+	u32 xsf, sr;
+	int vdelay;
+
+	int swidth       = tvnorm->swidth;
+	int totalwidth   = tvnorm->totalwidth;
+	int scaledtwidth = tvnorm->scaledtwidth;
+
+	if (btv->input == btv->dig) {
+		swidth       = 720;
+		totalwidth   = 858;
+		scaledtwidth = 858;
+	}
+
+	vdelay = tvnorm->vdelay;
+
+	xsf = (width*scaledtwidth)/swidth;
+	geo->hscale =  ((totalwidth*4096UL)/xsf-4096);
+	geo->hdelay =  tvnorm->hdelayx1;
+	geo->hdelay =  (geo->hdelay*width)/swidth;
+	geo->hdelay &= 0x3fe;
+	sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512;
+	geo->vscale =  (0x10000UL-sr) & 0x1fff;
+	geo->crop   =  ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) |
+		((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0);
+	geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0;
+	geo->vdelay  =  vdelay;
+	geo->width   =  width;
+	geo->sheight =  tvnorm->sheight;
+	geo->vtotal  =  tvnorm->vtotal;
+
+	if (btv->opt_combfilter) {
+		geo->vtc  = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+		geo->comb = (width < 769) ? 1 : 0;
+	} else {
+		geo->vtc  = 0;
+		geo->comb = 0;
+	}
+}
+
+static void
+bttv_calc_geo		(struct bttv *                  btv,
+			 struct bttv_geometry *         geo,
+			 unsigned int                   width,
+			 unsigned int                   height,
+			 int                            both_fields,
+			 const struct bttv_tvnorm *     tvnorm,
+			 const struct v4l2_rect *       crop)
+{
+	unsigned int c_width;
+	unsigned int c_height;
+	u32 sr;
+
+	if ((crop->left == tvnorm->cropcap.defrect.left
+	     && crop->top == tvnorm->cropcap.defrect.top
+	     && crop->width == tvnorm->cropcap.defrect.width
+	     && crop->height == tvnorm->cropcap.defrect.height
+	     && width <= tvnorm->swidth /* see PAL-Nc et al */)
+	    || btv->input == btv->dig) {
+		bttv_calc_geo_old(btv, geo, width, height,
+				  both_fields, tvnorm);
+		return;
+	}
+
+	/* For bug compatibility the image size checks permit scale
+	   factors > 16. See bttv_crop_calc_limits(). */
+	c_width = min((unsigned int) crop->width, width * 16);
+	c_height = min((unsigned int) crop->height, height * 16);
+
+	geo->width = width;
+	geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096;
+	/* Even to store Cb first, odd for Cr. */
+	geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1;
+
+	geo->sheight = c_height;
+	geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY;
+	sr = c_height >> !both_fields;
+	sr = (sr * 512U + (height >> 1)) / height - 512;
+	geo->vscale = (0x10000UL - sr) & 0x1fff;
+	geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0;
+	geo->vtotal = tvnorm->vtotal;
+
+	geo->crop = (((geo->width   >> 8) & 0x03) |
+		     ((geo->hdelay  >> 6) & 0x0c) |
+		     ((geo->sheight >> 4) & 0x30) |
+		     ((geo->vdelay  >> 2) & 0xc0));
+
+	if (btv->opt_combfilter) {
+		geo->vtc  = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+		geo->comb = (width < 769) ? 1 : 0;
+	} else {
+		geo->vtc  = 0;
+		geo->comb = 0;
+	}
+}
+
+static void
+bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
+{
+	int off = odd ? 0x80 : 0x00;
+
+	if (geo->comb)
+		btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+	else
+		btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+
+	btwrite(geo->vtc,             BT848_E_VTC+off);
+	btwrite(geo->hscale >> 8,     BT848_E_HSCALE_HI+off);
+	btwrite(geo->hscale & 0xff,   BT848_E_HSCALE_LO+off);
+	btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
+	btwrite(geo->vscale & 0xff,   BT848_E_VSCALE_LO+off);
+	btwrite(geo->width & 0xff,    BT848_E_HACTIVE_LO+off);
+	btwrite(geo->hdelay & 0xff,   BT848_E_HDELAY_LO+off);
+	btwrite(geo->sheight & 0xff,  BT848_E_VACTIVE_LO+off);
+	btwrite(geo->vdelay & 0xff,   BT848_E_VDELAY_LO+off);
+	btwrite(geo->crop,            BT848_E_CROP+off);
+	btwrite(geo->vtotal>>8,       BT848_VTOTAL_HI);
+	btwrite(geo->vtotal & 0xff,   BT848_VTOTAL_LO);
+}
+
+/* ---------------------------------------------------------- */
+/* risc group / risc main loop / dma management               */
+
+void
+bttv_set_dma(struct bttv *btv, int override)
+{
+	unsigned long cmd;
+	int capctl;
+
+	btv->cap_ctl = 0;
+	if (NULL != btv->curr.top)      btv->cap_ctl |= 0x02;
+	if (NULL != btv->curr.bottom)   btv->cap_ctl |= 0x01;
+	if (NULL != btv->cvbi)          btv->cap_ctl |= 0x0c;
+
+	capctl  = 0;
+	capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00;  /* capture  */
+	capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00;  /* vbi data */
+	capctl |= override;
+
+	d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n",
+		 btv->c.nr,capctl,btv->loop_irq,
+		 btv->cvbi         ? (unsigned long long)btv->cvbi->top.dma            : 0,
+		 btv->curr.top     ? (unsigned long long)btv->curr.top->top.dma        : 0,
+		 btv->cvbi         ? (unsigned long long)btv->cvbi->bottom.dma         : 0,
+		 btv->curr.bottom  ? (unsigned long long)btv->curr.bottom->bottom.dma  : 0);
+
+	cmd = BT848_RISC_JUMP;
+	if (btv->loop_irq) {
+		cmd |= BT848_RISC_IRQ;
+		cmd |= (btv->loop_irq  & 0x0f) << 16;
+		cmd |= (~btv->loop_irq & 0x0f) << 20;
+	}
+	if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) {
+		mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT);
+	} else {
+		del_timer(&btv->timeout);
+	}
+	btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd);
+
+	btaor(capctl, ~0x0f, BT848_CAP_CTL);
+	if (capctl) {
+		if (btv->dma_on)
+			return;
+		btwrite(btv->main.dma, BT848_RISC_STRT_ADD);
+		btor(3, BT848_GPIO_DMA_CTL);
+		btv->dma_on = 1;
+	} else {
+		if (!btv->dma_on)
+			return;
+		btand(~3, BT848_GPIO_DMA_CTL);
+		btv->dma_on = 0;
+	}
+	return;
+}
+
+int
+bttv_risc_init_main(struct bttv *btv)
+{
+	int rc;
+
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
+		return rc;
+	dprintk("%d: risc main @ %08llx\n",
+		btv->c.nr, (unsigned long long)btv->main.dma);
+
+	btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+				       BT848_FIFO_STATUS_VRE);
+	btv->main.cpu[1] = cpu_to_le32(0);
+	btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2));
+
+	/* top field */
+	btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2));
+	btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2));
+
+	btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+				       BT848_FIFO_STATUS_VRO);
+	btv->main.cpu[9] = cpu_to_le32(0);
+
+	/* bottom field */
+	btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2));
+	btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2));
+
+	/* jump back to top field */
+	btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2));
+
+	return 0;
+}
+
+int
+bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+	       int irqflags)
+{
+	unsigned long cmd;
+	unsigned long next = btv->main.dma + ((slot+2) << 2);
+
+	if (NULL == risc) {
+		d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot);
+		btv->main.cpu[slot+1] = cpu_to_le32(next);
+	} else {
+		d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n",
+			 btv->c.nr, risc, slot,
+			 (unsigned long long)risc->dma, irqflags);
+		cmd = BT848_RISC_JUMP;
+		if (irqflags) {
+			cmd |= BT848_RISC_IRQ;
+			cmd |= (irqflags  & 0x0f) << 16;
+			cmd |= (~irqflags & 0x0f) << 20;
+		}
+		risc->jmp[0] = cpu_to_le32(cmd);
+		risc->jmp[1] = cpu_to_le32(next);
+		btv->main.cpu[slot+1] = cpu_to_le32(risc->dma);
+	}
+	return 0;
+}
+
+void
+bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf)
+{
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+	BUG_ON(in_interrupt());
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_dma_unmap(q->dev, dma);
+	videobuf_dma_free(dma);
+	btcx_riscmem_free(btv->c.pci,&buf->bottom);
+	btcx_riscmem_free(btv->c.pci,&buf->top);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+int
+bttv_buffer_activate_vbi(struct bttv *btv,
+			 struct bttv_buffer *vbi)
+{
+	struct btcx_riscmem *top;
+	struct btcx_riscmem *bottom;
+	int top_irq_flags;
+	int bottom_irq_flags;
+
+	top = NULL;
+	bottom = NULL;
+	top_irq_flags = 0;
+	bottom_irq_flags = 0;
+
+	if (vbi) {
+		unsigned int crop, vdelay;
+
+		vbi->vb.state = VIDEOBUF_ACTIVE;
+		list_del(&vbi->vb.queue);
+
+		/* VDELAY is start of video, end of VBI capturing. */
+		crop = btread(BT848_E_CROP);
+		vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2);
+
+		if (vbi->geo.vdelay > vdelay) {
+			vdelay = vbi->geo.vdelay & 0xfe;
+			crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0);
+
+			btwrite(vdelay, BT848_E_VDELAY_LO);
+			btwrite(crop,	BT848_E_CROP);
+			btwrite(vdelay, BT848_O_VDELAY_LO);
+			btwrite(crop,	BT848_O_CROP);
+		}
+
+		if (vbi->vbi_count[0] > 0) {
+			top = &vbi->top;
+			top_irq_flags = 4;
+		}
+
+		if (vbi->vbi_count[1] > 0) {
+			top_irq_flags = 0;
+			bottom = &vbi->bottom;
+			bottom_irq_flags = 4;
+		}
+	}
+
+	bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags);
+	bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags);
+
+	return 0;
+}
+
+int
+bttv_buffer_activate_video(struct bttv *btv,
+			   struct bttv_buffer_set *set)
+{
+	/* video capture */
+	if (NULL != set->top  &&  NULL != set->bottom) {
+		if (set->top == set->bottom) {
+			set->top->vb.state    = VIDEOBUF_ACTIVE;
+			if (set->top->vb.queue.next)
+				list_del(&set->top->vb.queue);
+		} else {
+			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)
+				list_del(&set->bottom->vb.queue);
+		}
+		bttv_apply_geo(btv, &set->top->geo, 1);
+		bttv_apply_geo(btv, &set->bottom->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+			       set->top_irq);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+			       set->frame_irq);
+		btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f),
+		      ~0xff, BT848_COLOR_FMT);
+		btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
+		      ~0x0f, BT848_COLOR_CTL);
+	} else if (NULL != set->top) {
+		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);
+		bttv_apply_geo(btv, &set->top->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+			       set->frame_irq);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL,           0);
+		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 = VIDEOBUF_ACTIVE;
+		if (set->bottom->vb.queue.next)
+			list_del(&set->bottom->vb.queue);
+		bttv_apply_geo(btv, &set->bottom->geo,1);
+		bttv_apply_geo(btv, &set->bottom->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+			       set->frame_irq);
+		btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+		btaor(set->bottom->btswap & 0x0f,   ~0x0f, BT848_COLOR_CTL);
+	} else {
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+	const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+	dprintk("%d: buffer field: %s  format: %s  size: %dx%d\n",
+		btv->c.nr, v4l2_field_names[buf->vb.field],
+		buf->fmt->name, buf->vb.width, buf->vb.height);
+
+	/* packed pixel modes */
+	if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
+		int bpl = (buf->fmt->depth >> 3) * buf->vb.width;
+		int bpf = bpl * (buf->vb.height >> 1);
+
+		bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height,
+			      V4L2_FIELD_HAS_BOTH(buf->vb.field),
+			      tvnorm,&buf->crop);
+
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			bttv_risc_packed(btv,&buf->top,dma->sglist,
+					 /* offset */ 0,bpl,
+					 /* padding */ 0,/* skip_lines */ 0,
+					 buf->vb.height);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+					 0,bpl,0,0,buf->vb.height);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			bttv_risc_packed(btv,&buf->top,dma->sglist,
+					 0,bpl,bpl,0,buf->vb.height >> 1);
+			bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+					 bpl,bpl,bpl,0,buf->vb.height >> 1);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			bttv_risc_packed(btv,&buf->top,dma->sglist,
+					 0,bpl,0,0,buf->vb.height >> 1);
+			bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+					 bpf,bpl,0,0,buf->vb.height >> 1);
+			break;
+		default:
+			BUG();
+		}
+	}
+
+	/* planar modes */
+	if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
+		int uoffset, voffset;
+		int ypadding, cpadding, lines;
+
+		/* calculate chroma offsets */
+		uoffset = buf->vb.width * buf->vb.height;
+		voffset = buf->vb.width * buf->vb.height;
+		if (buf->fmt->flags & FORMAT_FLAGS_CrCb) {
+			/* Y-Cr-Cb plane order */
+			uoffset >>= buf->fmt->hshift;
+			uoffset >>= buf->fmt->vshift;
+			uoffset  += voffset;
+		} else {
+			/* Y-Cb-Cr plane order */
+			voffset >>= buf->fmt->hshift;
+			voffset >>= buf->fmt->vshift;
+			voffset  += uoffset;
+		}
+
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,/* both_fields */ 0,
+				      tvnorm,&buf->crop);
+			bttv_risc_planar(btv, &buf->top, dma->sglist,
+					 0,buf->vb.width,0,buf->vb.height,
+					 uoffset,voffset,buf->fmt->hshift,
+					 buf->fmt->vshift,0);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,0,
+				      tvnorm,&buf->crop);
+			bttv_risc_planar(btv, &buf->bottom, dma->sglist,
+					 0,buf->vb.width,0,buf->vb.height,
+					 uoffset,voffset,buf->fmt->hshift,
+					 buf->fmt->vshift,0);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,1,
+				      tvnorm,&buf->crop);
+			lines    = buf->vb.height >> 1;
+			ypadding = buf->vb.width;
+			cpadding = buf->vb.width >> buf->fmt->hshift;
+			bttv_risc_planar(btv,&buf->top,
+					 dma->sglist,
+					 0,buf->vb.width,ypadding,lines,
+					 uoffset,voffset,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 cpadding);
+			bttv_risc_planar(btv,&buf->bottom,
+					 dma->sglist,
+					 ypadding,buf->vb.width,ypadding,lines,
+					 uoffset+cpadding,
+					 voffset+cpadding,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 cpadding);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,1,
+				      tvnorm,&buf->crop);
+			lines    = buf->vb.height >> 1;
+			ypadding = buf->vb.width;
+			cpadding = buf->vb.width >> buf->fmt->hshift;
+			bttv_risc_planar(btv,&buf->top,
+					 dma->sglist,
+					 0,buf->vb.width,0,lines,
+					 uoffset >> 1,
+					 voffset >> 1,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 0);
+			bttv_risc_planar(btv,&buf->bottom,
+					 dma->sglist,
+					 lines * ypadding,buf->vb.width,0,lines,
+					 lines * ypadding + (uoffset >> 1),
+					 lines * ypadding + (voffset >> 1),
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 0);
+			break;
+		default:
+			BUG();
+		}
+	}
+
+	/* raw data */
+	if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
+		/* build risc code */
+		buf->vb.field = V4L2_FIELD_SEQ_TB;
+		bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
+			      1,tvnorm,&buf->crop);
+		bttv_risc_packed(btv, &buf->top,  dma->sglist,
+				 /* offset */ 0, RAW_BPL, /* padding */ 0,
+				 /* skip_lines */ 0, RAW_LINES);
+		bttv_risc_packed(btv, &buf->bottom, dma->sglist,
+				 buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES);
+	}
+
+	/* copy format info */
+	buf->btformat = buf->fmt->btformat;
+	buf->btswap   = buf->fmt->btswap;
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_overlay_risc(struct bttv *btv,
+		  struct bttv_overlay *ov,
+		  const struct bttv_format *fmt,
+		  struct bttv_buffer *buf)
+{
+	/* check interleave, bottom+top fields */
+	dprintk("%d: overlay fields: %s format: %s  size: %dx%d\n",
+		btv->c.nr, v4l2_field_names[buf->vb.field],
+		fmt->name, ov->w.width, ov->w.height);
+
+	/* calculate geometry */
+	bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
+		      V4L2_FIELD_HAS_BOTH(ov->field),
+		      &bttv_tvnorms[ov->tvnorm],&buf->crop);
+
+	/* build risc code */
+	switch (ov->field) {
+	case V4L2_FIELD_TOP:
+		bttv_risc_overlay(btv, &buf->top,    fmt, ov, 0, 0);
+		break;
+	case V4L2_FIELD_BOTTOM:
+		bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0);
+		break;
+	case V4L2_FIELD_INTERLACED:
+		bttv_risc_overlay(btv, &buf->top,    fmt, ov, 0, 1);
+		bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0);
+		break;
+	default:
+		BUG();
+	}
+
+	/* copy format info */
+	buf->btformat = fmt->btformat;
+	buf->btswap   = fmt->btswap;
+	buf->vb.field = ov->field;
+	return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c
new file mode 100644
index 000000000000..b433267d9aa9
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-vbi.c
@@ -0,0 +1,459 @@
+/*
+
+    bttv - Bt848 frame grabber driver
+    vbi interface
+
+    (c) 2002 Gerd Knorr <kraxel@bytesex.org>
+
+    Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+    Sponsored by OPQ Systems AB
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/io.h>
+#include "bttvp.h"
+
+/* Offset from line sync pulse leading edge (0H) to start of VBI capture,
+   in fCLKx2 pixels.  According to the datasheet, VBI capture starts
+   VBI_HDELAY fCLKx1 pixels from the tailing edgeof /HRESET, and /HRESET
+   is 64 fCLKx1 pixels wide.  VBI_HDELAY is set to 0, so this should be
+   (64 + 0) * 2 = 128 fCLKx2 pixels.  But it's not!  The datasheet is
+   Just Plain Wrong.  The real value appears to be different for
+   different revisions of the bt8x8 chips, and to be affected by the
+   horizontal scaling factor.  Experimentally, the value is measured
+   to be about 244.  */
+#define VBI_OFFSET 244
+
+/* 2048 for compatibility with earlier driver versions. The driver
+   really stores 1024 + tvnorm->vbipack * 4 samples per line in the
+   buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI
+   is 0x1FF DWORDs) and VBI read()s store a frame counter in the last
+   four bytes of the VBI image. */
+#define VBI_BPL 2048
+
+/* Compatibility. */
+#define VBI_DEFLINES 16
+
+static unsigned int vbibufs = 4;
+static unsigned int vbi_debug;
+
+module_param(vbibufs,   int, 0444);
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4");
+MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
+
+#ifdef dprintk
+# undef dprintk
+#endif
+#define dprintk(fmt, ...)						\
+do {									\
+	if (vbi_debug)							\
+		pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__);		\
+} while (0)
+
+#define IMAGE_SIZE(fmt) \
+	(((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
+
+/* ----------------------------------------------------------------------- */
+/* vbi risc code + mm                                                      */
+
+static int vbi_buffer_setup(struct videobuf_queue *q,
+			    unsigned int *count, unsigned int *size)
+{
+	struct bttv_fh *fh = q->priv_data;
+	struct bttv *btv = fh->btv;
+
+	if (0 == *count)
+		*count = vbibufs;
+
+	*size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+
+	dprintk("setup: samples=%u start=%d,%d count=%u,%u\n",
+		fh->vbi_fmt.fmt.samples_per_line,
+		fh->vbi_fmt.fmt.start[0],
+		fh->vbi_fmt.fmt.start[1],
+		fh->vbi_fmt.fmt.count[0],
+		fh->vbi_fmt.fmt.count[1]);
+
+	return 0;
+}
+
+static int vbi_buffer_prepare(struct videobuf_queue *q,
+			      struct videobuf_buffer *vb,
+			      enum v4l2_field field)
+{
+	struct bttv_fh *fh = q->priv_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+	const struct bttv_tvnorm *tvnorm;
+	unsigned int skip_lines0, skip_lines1, min_vdelay;
+	int redo_dma_risc;
+	int rc;
+
+	buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+		return -EINVAL;
+
+	tvnorm = fh->vbi_fmt.tvnorm;
+
+	/* There's no VBI_VDELAY register, RISC must skip the lines
+	   we don't want. With default parameters we skip zero lines
+	   as earlier driver versions did. The driver permits video
+	   standard changes while capturing, so we use vbi_fmt.tvnorm
+	   instead of btv->tvnorm to skip zero lines after video
+	   standard changes as well. */
+
+	skip_lines0 = 0;
+	skip_lines1 = 0;
+
+	if (fh->vbi_fmt.fmt.count[0] > 0)
+		skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0]
+				      - tvnorm->vbistart[0]));
+	if (fh->vbi_fmt.fmt.count[1] > 0)
+		skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1]
+				      - tvnorm->vbistart[1]));
+
+	redo_dma_risc = 0;
+
+	if (buf->vbi_skip[0] != skip_lines0 ||
+	    buf->vbi_skip[1] != skip_lines1 ||
+	    buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] ||
+	    buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) {
+		buf->vbi_skip[0] = skip_lines0;
+		buf->vbi_skip[1] = skip_lines1;
+		buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0];
+		buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1];
+		redo_dma_risc = 1;
+	}
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		redo_dma_risc = 1;
+		if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
+			goto fail;
+	}
+
+	if (redo_dma_risc) {
+		unsigned int bpl, padding, offset;
+		struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+		bpl = 2044; /* max. vbipack */
+		padding = VBI_BPL - bpl;
+
+		if (fh->vbi_fmt.fmt.count[0] > 0) {
+			rc = bttv_risc_packed(btv, &buf->top,
+					      dma->sglist,
+					      /* offset */ 0, bpl,
+					      padding, skip_lines0,
+					      fh->vbi_fmt.fmt.count[0]);
+			if (0 != rc)
+				goto fail;
+		}
+
+		if (fh->vbi_fmt.fmt.count[1] > 0) {
+			offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL;
+
+			rc = bttv_risc_packed(btv, &buf->bottom,
+					      dma->sglist,
+					      offset, bpl,
+					      padding, skip_lines1,
+					      fh->vbi_fmt.fmt.count[1]);
+			if (0 != rc)
+				goto fail;
+		}
+	}
+
+	/* VBI capturing ends at VDELAY, start of video capturing,
+	   no matter where the RISC program ends. VDELAY minimum is 2,
+	   bounds.top is the corresponding first field line number
+	   times two. VDELAY counts half field lines. */
+	min_vdelay = MIN_VDELAY;
+	if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top)
+		min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top;
+
+	/* For bttv_buffer_activate_vbi(). */
+	buf->geo.vdelay = min_vdelay;
+
+	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,
+		v4l2_field_names[buf->vb.field]);
+	return 0;
+
+ fail:
+	bttv_dma_free(q,btv,buf);
+	return rc;
+}
+
+static void
+vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct bttv_fh *fh = q->priv_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+	dprintk("queue %p\n",vb);
+	buf->vb.state = VIDEOBUF_QUEUED;
+	list_add_tail(&buf->vb.queue,&btv->vcapture);
+	if (NULL == btv->cvbi) {
+		fh->btv->loop_irq |= 4;
+		bttv_set_dma(btv,0x0c);
+	}
+}
+
+static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct bttv_fh *fh = q->priv_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+	dprintk("free %p\n",vb);
+	bttv_dma_free(q,fh->btv,buf);
+}
+
+struct videobuf_queue_ops bttv_vbi_qops = {
+	.buf_setup    = vbi_buffer_setup,
+	.buf_prepare  = vbi_buffer_prepare,
+	.buf_queue    = vbi_buffer_queue,
+	.buf_release  = vbi_buffer_release,
+};
+
+/* ----------------------------------------------------------------------- */
+
+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;
+
+	/* For compatibility with earlier driver versions we must pretend
+	   the VBI and video capture window may overlap. In reality RISC
+	   magic aborts VBI capturing at the first line of video capturing,
+	   leaving the rest of the buffer unchanged, usually all zero.
+	   VBI capturing must always start before video capturing. >> 1
+	   because cropping counts field lines times two. */
+	min_start = tvnorm->vbistart[0];
+	max_start = (crop_start >> 1) - 1;
+	max_end = (tvnorm->cropcap.bounds.top
+		   + tvnorm->cropcap.bounds.height) >> 1;
+
+	if (min_start > max_start)
+		return -EBUSY;
+
+	BUG_ON(max_start >= max_end);
+
+	f->sampling_rate    = tvnorm->Fsc;
+	f->samples_per_line = VBI_BPL;
+	f->sample_format    = V4L2_PIX_FMT_GREY;
+	f->offset           = VBI_OFFSET;
+
+	f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0];
+
+	for (i = 0; i < 2; ++i) {
+		if (0 == f->count[i]) {
+			/* No data from this field. We leave f->start[i]
+			   alone because VIDIOCSVBIFMT is w/o and EINVALs
+			   when a driver does not support exactly the
+			   requested parameters. */
+		} else {
+			s64 start, count;
+
+			start = clamp(f->start[i], min_start, max_start);
+			/* s64 to prevent overflow. */
+			count = (s64) f->start[i] + f->count[i] - start;
+			f->start[i] = start;
+			f->count[i] = clamp(count, (s64) 1,
+					    max_end - start);
+		}
+
+		min_start += f2_offset;
+		max_start += f2_offset;
+		max_end += f2_offset;
+	}
+
+	if (0 == (f->count[0] | f->count[1])) {
+		/* As in earlier driver versions. */
+		f->start[0] = tvnorm->vbistart[0];
+		f->start[1] = tvnorm->vbistart[1];
+		f->count[0] = 1;
+		f->count[1] = 1;
+	}
+
+	f->flags = 0;
+
+	f->reserved[0] = 0;
+	f->reserved[1] = 0;
+
+	return 0;
+}
+
+int bttv_try_fmt_vbi_cap(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;
+
+	mutex_lock(&btv->lock);
+
+	tvnorm = &bttv_tvnorms[btv->tvnorm];
+	crop_start = btv->crop_start;
+
+	mutex_unlock(&btv->lock);
+
+	return try_fmt(&frt->fmt.vbi, tvnorm, crop_start);
+}
+
+
+int bttv_s_fmt_vbi_cap(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;
+	int rc;
+
+	mutex_lock(&btv->lock);
+
+	rc = -EBUSY;
+	if (fh->resources & RESOURCE_VBI)
+		goto fail;
+
+	tvnorm = &bttv_tvnorms[btv->tvnorm];
+
+	rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start);
+	if (0 != rc)
+		goto fail;
+
+	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
+	   when capturing both fields. But for compatibility we must
+	   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(frt->fmt.vbi.start[0], start1) * 2 + 2;
+
+	mutex_lock(&fh->vbi.vb_lock);
+
+	fh->vbi_fmt.fmt    = frt->fmt.vbi;
+	fh->vbi_fmt.tvnorm = tvnorm;
+	fh->vbi_fmt.end    = end;
+
+	mutex_unlock(&fh->vbi.vb_lock);
+
+	rc = 0;
+
+ fail:
+	mutex_unlock(&btv->lock);
+
+	return rc;
+}
+
+
+int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+	struct bttv_fh *fh = f;
+	const struct bttv_tvnorm *tvnorm;
+
+	frt->fmt.vbi = fh->vbi_fmt.fmt;
+
+	tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
+
+	if (tvnorm != fh->vbi_fmt.tvnorm) {
+		__s32 max_end;
+		unsigned int i;
+
+		/* As in vbi_buffer_prepare() this imitates the
+		   behaviour of earlier driver versions after video
+		   standard changes, with default parameters anyway. */
+
+		max_end = (tvnorm->cropcap.bounds.top
+			   + tvnorm->cropcap.bounds.height) >> 1;
+
+		frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
+
+		for (i = 0; i < 2; ++i) {
+			__s32 new_start;
+
+			new_start = frt->fmt.vbi.start[i]
+				+ tvnorm->vbistart[i]
+				- fh->vbi_fmt.tvnorm->vbistart[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, unsigned int norm)
+{
+	const struct bttv_tvnorm *tvnorm;
+	unsigned int real_samples_per_line;
+	unsigned int real_count;
+
+	tvnorm = &bttv_tvnorms[norm];
+
+	f->fmt.sampling_rate    = tvnorm->Fsc;
+	f->fmt.samples_per_line = VBI_BPL;
+	f->fmt.sample_format    = V4L2_PIX_FMT_GREY;
+	f->fmt.offset           = VBI_OFFSET;
+	f->fmt.start[0]		= tvnorm->vbistart[0];
+	f->fmt.start[1]		= tvnorm->vbistart[1];
+	f->fmt.count[0]		= VBI_DEFLINES;
+	f->fmt.count[1]		= VBI_DEFLINES;
+	f->fmt.flags            = 0;
+	f->fmt.reserved[0]      = 0;
+	f->fmt.reserved[1]      = 0;
+
+	/* For compatibility the buffer size must be 2 * VBI_DEFLINES *
+	   VBI_BPL regardless of the current video standard. */
+	real_samples_per_line   = 1024 + tvnorm->vbipack * 4;
+	real_count              = ((tvnorm->cropcap.defrect.top >> 1)
+				   - tvnorm->vbistart[0]);
+
+	BUG_ON(real_samples_per_line > VBI_BPL);
+	BUG_ON(real_count > VBI_DEFLINES);
+
+	f->tvnorm               = tvnorm;
+
+	/* See bttv_vbi_fmt_set(). */
+	f->end                  = tvnorm->vbistart[0] * 2 + 2;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
new file mode 100644
index 000000000000..79a11240a590
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -0,0 +1,376 @@
+/*
+ *
+ *  bttv - Bt848 frame grabber driver
+ *
+ *  card ID's and external interfaces of the bttv driver
+ *  basically stuff needed by other drivers (i2c, lirc, ...)
+ *  and is supported not to change much over time.
+ *
+ *  Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ *  (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef _BTTV_H_
+#define _BTTV_H_
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/i2c-addr.h>
+#include <media/tuner.h>
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-cards.c                                   */
+
+#define BTTV_BOARD_UNKNOWN                 0x00
+#define BTTV_BOARD_MIRO                    0x01
+#define BTTV_BOARD_HAUPPAUGE               0x02
+#define BTTV_BOARD_STB                     0x03
+#define BTTV_BOARD_INTEL                   0x04
+#define BTTV_BOARD_DIAMOND                 0x05
+#define BTTV_BOARD_AVERMEDIA               0x06
+#define BTTV_BOARD_MATRIX_VISION           0x07
+#define BTTV_BOARD_FLYVIDEO                0x08
+#define BTTV_BOARD_TURBOTV                 0x09
+#define BTTV_BOARD_HAUPPAUGE878            0x0a
+#define BTTV_BOARD_MIROPRO                 0x0b
+#define BTTV_BOARD_ADSTECH_TV              0x0c
+#define BTTV_BOARD_AVERMEDIA98             0x0d
+#define BTTV_BOARD_VHX                     0x0e
+#define BTTV_BOARD_ZOLTRIX                 0x0f
+#define BTTV_BOARD_PIXVIEWPLAYTV           0x10
+#define BTTV_BOARD_WINVIEW_601             0x11
+#define BTTV_BOARD_AVEC_INTERCAP           0x12
+#define BTTV_BOARD_LIFE_FLYKIT             0x13
+#define BTTV_BOARD_CEI_RAFFLES             0x14
+#define BTTV_BOARD_CONFERENCETV            0x15
+#define BTTV_BOARD_PHOEBE_TVMAS            0x16
+#define BTTV_BOARD_MODTEC_205              0x17
+#define BTTV_BOARD_MAGICTVIEW061           0x18
+#define BTTV_BOARD_VOBIS_BOOSTAR           0x19
+#define BTTV_BOARD_HAUPPAUG_WCAM           0x1a
+#define BTTV_BOARD_MAXI                    0x1b
+#define BTTV_BOARD_TERRATV                 0x1c
+#define BTTV_BOARD_PXC200                  0x1d
+#define BTTV_BOARD_FLYVIDEO_98             0x1e
+#define BTTV_BOARD_IPROTV                  0x1f
+#define BTTV_BOARD_INTEL_C_S_PCI           0x20
+#define BTTV_BOARD_TERRATVALUE             0x21
+#define BTTV_BOARD_WINFAST2000             0x22
+#define BTTV_BOARD_CHRONOS_VS2             0x23
+#define BTTV_BOARD_TYPHOON_TVIEW           0x24
+#define BTTV_BOARD_PXELVWPLTVPRO           0x25
+#define BTTV_BOARD_MAGICTVIEW063           0x26
+#define BTTV_BOARD_PINNACLE                0x27
+#define BTTV_BOARD_STB2                    0x28
+#define BTTV_BOARD_AVPHONE98               0x29
+#define BTTV_BOARD_PV951                   0x2a
+#define BTTV_BOARD_ONAIR_TV                0x2b
+#define BTTV_BOARD_SIGMA_TVII_FM           0x2c
+#define BTTV_BOARD_MATRIX_VISION2          0x2d
+#define BTTV_BOARD_ZOLTRIX_GENIE           0x2e
+#define BTTV_BOARD_TERRATVRADIO            0x2f
+#define BTTV_BOARD_DYNALINK                0x30
+#define BTTV_BOARD_GVBCTV3PCI              0x31
+#define BTTV_BOARD_PXELVWPLTVPAK           0x32
+#define BTTV_BOARD_EAGLE                   0x33
+#define BTTV_BOARD_PINNACLEPRO             0x34
+#define BTTV_BOARD_TVIEW_RDS_FM            0x35
+#define BTTV_BOARD_LIFETEC_9415            0x36
+#define BTTV_BOARD_BESTBUY_EASYTV          0x37
+#define BTTV_BOARD_FLYVIDEO_98FM           0x38
+#define BTTV_BOARD_GRANDTEC                0x39
+#define BTTV_BOARD_ASKEY_CPH060            0x3a
+#define BTTV_BOARD_ASKEY_CPH03X            0x3b
+#define BTTV_BOARD_MM100PCTV               0x3c
+#define BTTV_BOARD_GMV1                    0x3d
+#define BTTV_BOARD_BESTBUY_EASYTV2         0x3e
+#define BTTV_BOARD_ATI_TVWONDER            0x3f
+#define BTTV_BOARD_ATI_TVWONDERVE          0x40
+#define BTTV_BOARD_FLYVIDEO2000            0x41
+#define BTTV_BOARD_TERRATVALUER            0x42
+#define BTTV_BOARD_GVBCTV4PCI              0x43
+#define BTTV_BOARD_VOODOOTV_FM             0x44
+#define BTTV_BOARD_AIMMS                   0x45
+#define BTTV_BOARD_PV_BT878P_PLUS          0x46
+#define BTTV_BOARD_FLYVIDEO98EZ            0x47
+#define BTTV_BOARD_PV_BT878P_9B            0x48
+#define BTTV_BOARD_SENSORAY311_611         0x49
+#define BTTV_BOARD_RV605                   0x4a
+#define BTTV_BOARD_POWERCLR_MTV878         0x4b
+#define BTTV_BOARD_WINDVR                  0x4c
+#define BTTV_BOARD_GRANDTEC_MULTI          0x4d
+#define BTTV_BOARD_KWORLD                  0x4e
+#define BTTV_BOARD_DSP_TCVIDEO             0x4f
+#define BTTV_BOARD_HAUPPAUGEPVR            0x50
+#define BTTV_BOARD_GVBCTV5PCI              0x51
+#define BTTV_BOARD_OSPREY1x0               0x52
+#define BTTV_BOARD_OSPREY1x0_848           0x53
+#define BTTV_BOARD_OSPREY101_848           0x54
+#define BTTV_BOARD_OSPREY1x1               0x55
+#define BTTV_BOARD_OSPREY1x1_SVID          0x56
+#define BTTV_BOARD_OSPREY2xx               0x57
+#define BTTV_BOARD_OSPREY2x0_SVID          0x58
+#define BTTV_BOARD_OSPREY2x0               0x59
+#define BTTV_BOARD_OSPREY500               0x5a
+#define BTTV_BOARD_OSPREY540               0x5b
+#define BTTV_BOARD_OSPREY2000              0x5c
+#define BTTV_BOARD_IDS_EAGLE               0x5d
+#define BTTV_BOARD_PINNACLESAT             0x5e
+#define BTTV_BOARD_FORMAC_PROTV            0x5f
+#define BTTV_BOARD_MACHTV                  0x60
+#define BTTV_BOARD_EURESYS_PICOLO          0x61
+#define BTTV_BOARD_PV150                   0x62
+#define BTTV_BOARD_AD_TVK503               0x63
+#define BTTV_BOARD_HERCULES_SM_TV          0x64
+#define BTTV_BOARD_PACETV                  0x65
+#define BTTV_BOARD_IVC200                  0x66
+#define BTTV_BOARD_XGUARD                  0x67
+#define BTTV_BOARD_NEBULA_DIGITV           0x68
+#define BTTV_BOARD_PV143                   0x69
+#define BTTV_BOARD_VD009X1_VD011_MINIDIN   0x6a
+#define BTTV_BOARD_VD009X1_VD011_COMBI     0x6b
+#define BTTV_BOARD_VD009_MINIDIN           0x6c
+#define BTTV_BOARD_VD009_COMBI             0x6d
+#define BTTV_BOARD_IVC100                  0x6e
+#define BTTV_BOARD_IVC120                  0x6f
+#define BTTV_BOARD_PC_HDTV                 0x70
+#define BTTV_BOARD_TWINHAN_DST             0x71
+#define BTTV_BOARD_WINFASTVC100            0x72
+#define BTTV_BOARD_TEV560                  0x73
+#define BTTV_BOARD_SIMUS_GVC1100           0x74
+#define BTTV_BOARD_NGSTV_PLUS              0x75
+#define BTTV_BOARD_LMLBT4                  0x76
+#define BTTV_BOARD_TEKRAM_M205             0x77
+#define BTTV_BOARD_CONTVFMI                0x78
+#define BTTV_BOARD_PICOLO_TETRA_CHIP       0x79
+#define BTTV_BOARD_SPIRIT_TV               0x7a
+#define BTTV_BOARD_AVDVBT_771              0x7b
+#define BTTV_BOARD_AVDVBT_761              0x7c
+#define BTTV_BOARD_MATRIX_VISIONSQ         0x7d
+#define BTTV_BOARD_MATRIX_VISIONSLC        0x7e
+#define BTTV_BOARD_APAC_VIEWCOMP           0x7f
+#define BTTV_BOARD_DVICO_DVBT_LITE         0x80
+#define BTTV_BOARD_VGEAR_MYVCD             0x81
+#define BTTV_BOARD_SUPER_TV                0x82
+#define BTTV_BOARD_TIBET_CS16              0x83
+#define BTTV_BOARD_KODICOM_4400R           0x84
+#define BTTV_BOARD_KODICOM_4400R_SL        0x85
+#define BTTV_BOARD_ADLINK_RTV24            0x86
+#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
+#define BTTV_BOARD_ACORP_Y878F             0x88
+#define BTTV_BOARD_CONCEPTRONIC_CTVFMI2    0x89
+#define BTTV_BOARD_PV_BT878P_2E            0x8a
+#define BTTV_BOARD_PV_M4900                0x8b
+#define BTTV_BOARD_OSPREY440               0x8c
+#define BTTV_BOARD_ASOUND_SKYEYE	   0x8d
+#define BTTV_BOARD_SABRENT_TVFM   	   0x8e
+#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB     0x8f
+#define BTTV_BOARD_MACHTV_MAGICTV          0x90
+#define BTTV_BOARD_SSAI_SECURITY	   0x91
+#define BTTV_BOARD_SSAI_ULTRASOUND	   0x92
+#define BTTV_BOARD_VOODOOTV_200		   0x93
+#define BTTV_BOARD_DVICO_FUSIONHDTV_2	   0x94
+#define BTTV_BOARD_TYPHOON_TVTUNERPCI	   0x95
+#define BTTV_BOARD_GEOVISION_GV600	   0x96
+#define BTTV_BOARD_KOZUMI_KTV_01C          0x97
+#define BTTV_BOARD_ENLTV_FM_2		   0x98
+#define BTTV_BOARD_VD012		   0x99
+#define BTTV_BOARD_VD012_X1		   0x9a
+#define BTTV_BOARD_VD012_X2		   0x9b
+#define BTTV_BOARD_IVCE8784		   0x9c
+#define BTTV_BOARD_GEOVISION_GV800S	   0x9d
+#define BTTV_BOARD_GEOVISION_GV800S_SL	   0x9e
+#define BTTV_BOARD_PV183                   0x9f
+#define BTTV_BOARD_TVT_TD3116		   0xa0
+#define BTTV_BOARD_APOSONIC_WDVR           0xa1
+
+/* more card-specific defines */
+#define PT2254_L_CHANNEL 0x10
+#define PT2254_R_CHANNEL 0x08
+#define PT2254_DBS_IN_2 0x400
+#define PT2254_DBS_IN_10 0x20000
+#define WINVIEW_PT2254_CLK  0x40
+#define WINVIEW_PT2254_DATA 0x20
+#define WINVIEW_PT2254_STROBE 0x80
+
+struct bttv_core {
+	/* device structs */
+	struct v4l2_device   v4l2_dev;
+	struct pci_dev       *pci;
+	struct i2c_adapter   i2c_adap;
+	struct list_head     subs;     /* struct bttv_sub_device */
+
+	/* device config */
+	unsigned int         nr;       /* dev nr (for printk("bttv%d: ...");  */
+	unsigned int         type;     /* card type (pointer into tvcards[])  */
+};
+
+struct bttv;
+
+struct tvcard {
+	char *name;
+	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);
+
+	/* MUX bits for each input, two bits per input starting with the LSB */
+	u32 muxsel; /* Use MUXSEL() to set */
+
+	u32 gpiomask;
+	u32 gpiomux[4];  /* Tuner, Radio, external, internal */
+	u32 gpiomute;    /* GPIO mute setting */
+	u32 gpiomask2;   /* GPIO MUX mask */
+
+	unsigned int tuner_type;
+	u8 tuner_addr;
+	u8 video_inputs;	/* Number of inputs */
+	unsigned int svhs:4;	/* Which input is s-video */
+#define NO_SVHS	15
+	unsigned int pll:2;
+#define PLL_NONE 0
+#define PLL_28   1
+#define PLL_35   2
+
+	/* i2c audio flags */
+	unsigned int no_msp34xx:1;
+	unsigned int no_tda7432:1;
+	unsigned int msp34xx_alt:1;
+	/* Note: currently no card definition needs to mark the presence
+	   of a RDS saa6588 chip. If this is ever needed, then add a new
+	   'has_saa6588' bit here. */
+
+	unsigned int no_video:1; /* video pci function is unused */
+	unsigned int has_dvb:1;
+	unsigned int has_remote:1;
+	unsigned int has_radio:1;
+	unsigned int has_dig_in:1; /* Has digital input (always last input) */
+	unsigned int no_gpioirq:1;
+};
+
+extern struct tvcard bttv_tvcards[];
+
+/*
+ * This bit of cpp voodoo is used to create a macro with a variable number of
+ * arguments (1 to 16).  It will pack each argument into a word two bits at a
+ * time.  It can't be a function because it needs to be compile time constant to
+ * initialize structures.  Since each argument must fit in two bits, it's ok
+ * that they are changed to octal.  One should not use hex number, macros, or
+ * anything else with this macro.  Just use plain integers from 0 to 3.
+ */
+#define _MUXSELf(a)      	0##a << 30
+#define _MUXSELe(a, b...)	0##a << 28 | _MUXSELf(b)
+#define _MUXSELd(a, b...)	0##a << 26 | _MUXSELe(b)
+#define _MUXSELc(a, b...)	0##a << 24 | _MUXSELd(b)
+#define _MUXSELb(a, b...)	0##a << 22 | _MUXSELc(b)
+#define _MUXSELa(a, b...)	0##a << 20 | _MUXSELb(b)
+#define _MUXSEL9(a, b...)	0##a << 18 | _MUXSELa(b)
+#define _MUXSEL8(a, b...)	0##a << 16 | _MUXSEL9(b)
+#define _MUXSEL7(a, b...)	0##a << 14 | _MUXSEL8(b)
+#define _MUXSEL6(a, b...)	0##a << 12 | _MUXSEL7(b)
+#define _MUXSEL5(a, b...)	0##a << 10 | _MUXSEL6(b)
+#define _MUXSEL4(a, b...)	0##a << 8  | _MUXSEL5(b)
+#define _MUXSEL3(a, b...)	0##a << 6  | _MUXSEL4(b)
+#define _MUXSEL2(a, b...)	0##a << 4  | _MUXSEL3(b)
+#define _MUXSEL1(a, b...)	0##a << 2  | _MUXSEL2(b)
+#define MUXSEL(a, b...)		(a | _MUXSEL1(b))
+
+/* identification / initialization of the card */
+extern void bttv_idcard(struct bttv *btv);
+extern void bttv_init_card1(struct bttv *btv);
+extern void bttv_init_card2(struct bttv *btv);
+extern void bttv_init_tuner(struct bttv *btv);
+
+/* card-specific funtions */
+extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
+extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits);
+
+/* extra tweaks for some chipsets */
+extern void bttv_check_chipset(void);
+extern int bttv_handle_chipset(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-if.c                                      */
+
+/* this obsolete -- please use the sysfs-based
+   interface below for new code */
+
+extern struct pci_dev* bttv_get_pcidev(unsigned int card);
+
+/* sets GPOE register (BT848_GPIO_OUT_EN) to new value:
+   data | (current_GPOE_value & ~mask)
+   returns negative value if error occurred
+*/
+extern int bttv_gpio_enable(unsigned int card,
+			    unsigned long mask, unsigned long data);
+
+/* fills data with GPDATA register contents
+   returns negative value if error occurred
+*/
+extern int bttv_read_gpio(unsigned int card, unsigned long *data);
+
+/* sets GPDATA register to new value:
+  (data & mask) | (current_GPDATA_value & ~mask)
+  returns negative value if error occurred
+*/
+extern int bttv_write_gpio(unsigned int card,
+			   unsigned long mask, unsigned long data);
+
+
+
+
+/* ---------------------------------------------------------- */
+/* sysfs/driver-moded based gpio access interface             */
+
+struct bttv_sub_device {
+	struct device    dev;
+	struct bttv_core *core;
+	struct list_head list;
+};
+#define to_bttv_sub_dev(x) container_of((x), struct bttv_sub_device, dev)
+
+struct bttv_sub_driver {
+	struct device_driver   drv;
+	char                   wanted[20];
+	int                    (*probe)(struct bttv_sub_device *sub);
+	void                   (*remove)(struct bttv_sub_device *sub);
+};
+#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
+
+int bttv_sub_register(struct bttv_sub_driver *drv, char *wanted);
+int bttv_sub_unregister(struct bttv_sub_driver *drv);
+
+/* gpio access functions */
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits);
+u32 bttv_gpio_read(struct bttv_core *core);
+void bttv_gpio_write(struct bttv_core *core, u32 value);
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits);
+
+#define gpio_inout(mask,bits)  bttv_gpio_inout(&btv->c, mask, bits)
+#define gpio_read()            bttv_gpio_read(&btv->c)
+#define gpio_write(value)      bttv_gpio_write(&btv->c, value)
+#define gpio_bits(mask,bits)   bttv_gpio_bits(&btv->c, mask, bits)
+
+
+/* ---------------------------------------------------------- */
+/* i2c                                                        */
+
+#define bttv_call_all(btv, o, f, args...) \
+	v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args)
+
+extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for);
+extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+			 unsigned char b2, int both);
+extern void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr);
+
+extern int bttv_input_init(struct bttv *dev);
+extern void bttv_input_fini(struct bttv *dev);
+extern void bttv_input_irq(struct bttv *dev);
+
+#endif /* _BTTV_H_ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h
new file mode 100644
index 000000000000..70fd4f23f605
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttvp.h
@@ -0,0 +1,535 @@
+/*
+
+    bttv - Bt848 frame grabber driver
+
+    bttv's *private* header file  --  nobody other than bttv itself
+    should ever include this file.
+
+    (c) 2000-2002 Gerd Knorr <kraxel@bytesex.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 _BTTVP_H_
+#define _BTTVP_H_
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/tveeprom.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+
+#include "bt848.h"
+#include "bttv.h"
+#include "btcx-risc.h"
+
+#ifdef __KERNEL__
+
+#define FORMAT_FLAGS_DITHER       0x01
+#define FORMAT_FLAGS_PACKED       0x02
+#define FORMAT_FLAGS_PLANAR       0x04
+#define FORMAT_FLAGS_RAW          0x08
+#define FORMAT_FLAGS_CrCb         0x10
+
+#define RISC_SLOT_O_VBI        4
+#define RISC_SLOT_O_FIELD      6
+#define RISC_SLOT_E_VBI       10
+#define RISC_SLOT_E_FIELD     12
+#define RISC_SLOT_LOOP        14
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_VIDEO_STREAM  2
+#define RESOURCE_VBI           4
+#define RESOURCE_VIDEO_READ    8
+
+#define RAW_LINES            640
+#define RAW_BPL             1024
+
+#define UNSET (-1U)
+
+/* Min. value in VDELAY register. */
+#define MIN_VDELAY 2
+/* Even to get Cb first, odd for Cr. */
+#define MAX_HDELAY (0x3FF & -2)
+/* Limits scaled width, which must be a multiple of 4. */
+#define MAX_HACTIVE (0x3FF & -4)
+
+#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 {
+	int   v4l2_id;
+	char  *name;
+	u32   Fsc;
+	u16   swidth, sheight; /* scaled standard width, height */
+	u16   totalwidth;
+	u8    adelay, bdelay, iform;
+	u32   scaledtwidth;
+	u16   hdelayx1, hactivex1;
+	u16   vdelay;
+	u8    vbipack;
+	u16   vtotal;
+	int   sram;
+	/* ITU-R frame line number of the first VBI line we can
+	   capture, of the first and second field. The last possible line
+	   is determined by cropcap.bounds. */
+	u16   vbistart[2];
+	/* Horizontally this counts fCLKx1 samples following the leading
+	   edge of the horizontal sync pulse, vertically ITU-R frame line
+	   numbers of the first field times two (2, 4, 6, ... 524 or 624). */
+	struct v4l2_cropcap cropcap;
+};
+extern const struct bttv_tvnorm bttv_tvnorms[];
+
+struct bttv_format {
+	char *name;
+	int  fourcc;          /* video4linux 2      */
+	int  btformat;        /* BT848_COLOR_FMT_*  */
+	int  btswap;          /* BT848_COLOR_CTL_*  */
+	int  depth;           /* bit/pixel          */
+	int  flags;
+	int  hshift,vshift;   /* for planar modes   */
+};
+
+struct bttv_ir {
+	struct rc_dev           *dev;
+	struct timer_list       timer;
+
+	char                    name[32];
+	char                    phys[32];
+
+	/* Usual gpio signalling */
+	u32                     mask_keycode;
+	u32                     mask_keydown;
+	u32                     mask_keyup;
+	u32                     polling;
+	u32                     last_gpio;
+	int                     shift_by;
+	int                     start; // What should RC5_START() be
+	int                     addr; // What RC5_ADDR() should be.
+	int                     rc5_remote_gap;
+
+	/* RC5 gpio */
+	bool			rc5_gpio;   /* Is RC5 legacy GPIO enabled? */
+	u32                     last_bit;   /* last raw bit seen */
+	u32                     code;       /* raw code under construction */
+	struct timeval          base_time;  /* time of last seen code */
+	bool                    active;     /* building raw code */
+};
+
+
+/* ---------------------------------------------------------- */
+
+struct bttv_geometry {
+	u8  vtc,crop,comb;
+	u16 width,hscale,hdelay;
+	u16 sheight,vscale,vdelay,vtotal;
+};
+
+struct bttv_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer     vb;
+
+	/* bttv specific */
+	const struct bttv_format   *fmt;
+	unsigned int               tvnorm;
+	int                        btformat;
+	int                        btswap;
+	struct bttv_geometry       geo;
+	struct btcx_riscmem        top;
+	struct btcx_riscmem        bottom;
+	struct v4l2_rect           crop;
+	unsigned int               vbi_skip[2];
+	unsigned int               vbi_count[2];
+};
+
+struct bttv_buffer_set {
+	struct bttv_buffer     *top;       /* top field buffer    */
+	struct bttv_buffer     *bottom;    /* bottom field buffer */
+	unsigned int           top_irq;
+	unsigned int           frame_irq;
+};
+
+struct bttv_overlay {
+	unsigned int           tvnorm;
+	struct v4l2_rect       w;
+	enum v4l2_field        field;
+	struct v4l2_clip       *clips;
+	int                    nclips;
+	int                    setup_ok;
+};
+
+struct bttv_vbi_fmt {
+	struct v4l2_vbi_format fmt;
+
+	/* fmt.start[] and count[] refer to this video standard. */
+	const struct bttv_tvnorm *tvnorm;
+
+	/* Earliest possible start of video capturing with this
+	   v4l2_vbi_format, in struct bttv_crop.rect units. */
+	__s32                  end;
+};
+
+/* bttv-vbi.c */
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm);
+
+struct bttv_crop {
+	/* A cropping rectangle in struct bttv_tvnorm.cropcap units. */
+	struct v4l2_rect       rect;
+
+	/* Scaled image size limits with this crop rect. Divide
+	   max_height, but not min_height, by two when capturing
+	   single fields. See also bttv_crop_reset() and
+	   bttv_crop_adjust() in bttv-driver.c. */
+	__s32                  min_scaled_width;
+	__s32                  min_scaled_height;
+	__s32                  max_scaled_width;
+	__s32                  max_scaled_height;
+};
+
+struct bttv_fh {
+	struct bttv              *btv;
+	int resources;
+#ifdef VIDIOC_G_PRIORITY
+	enum v4l2_priority       prio;
+#endif
+	enum v4l2_buf_type       type;
+
+	/* video capture */
+	struct videobuf_queue    cap;
+	const struct bttv_format *fmt;
+	int                      width;
+	int                      height;
+
+	/* video overlay */
+	const struct bttv_format *ovfmt;
+	struct bttv_overlay      ov;
+
+	/* Application called VIDIOC_S_CROP. */
+	int                      do_crop;
+
+	/* vbi capture */
+	struct videobuf_queue    vbi;
+	/* Current VBI capture window as seen through this fh (cannot
+	   be global for compatibility with earlier drivers). Protected
+	   by struct bttv.lock and struct bttv_fh.vbi.lock. */
+	struct bttv_vbi_fmt      vbi_fmt;
+};
+
+/* ---------------------------------------------------------- */
+/* bttv-risc.c                                                */
+
+/* risc code generators - capture */
+int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+		     struct scatterlist *sglist,
+		     unsigned int offset, unsigned int bpl,
+		     unsigned int pitch, unsigned int skip_lines,
+		     unsigned int store_lines);
+
+/* control dma register + risc main loop */
+void bttv_set_dma(struct bttv *btv, int override);
+int bttv_risc_init_main(struct bttv *btv);
+int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+		   int irqflags);
+
+/* capture buffer handling */
+int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
+int bttv_buffer_activate_video(struct bttv *btv,
+			       struct bttv_buffer_set *set);
+int bttv_buffer_activate_vbi(struct bttv *btv,
+			     struct bttv_buffer *vbi);
+void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv,
+		   struct bttv_buffer *buf);
+
+/* overlay handling */
+int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
+		      const struct bttv_format *fmt,
+		      struct bttv_buffer *buf);
+
+
+/* ---------------------------------------------------------- */
+/* bttv-vbi.c                                                 */
+
+int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+
+extern struct videobuf_queue_ops bttv_vbi_qops;
+
+/* ---------------------------------------------------------- */
+/* bttv-gpio.c */
+
+extern struct bus_type bttv_sub_bus_type;
+int bttv_sub_add_device(struct bttv_core *core, char *name);
+int bttv_sub_del_devices(struct bttv_core *core);
+
+/* ---------------------------------------------------------- */
+/* bttv-cards.c                                               */
+
+extern int no_overlay;
+
+/* ---------------------------------------------------------- */
+/* bttv-input.c                                               */
+
+extern void init_bttv_i2c_ir(struct bttv *btv);
+extern int fini_bttv_i2c(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* bttv-driver.c                                              */
+
+/* insmod options */
+extern unsigned int bttv_verbose;
+extern unsigned int bttv_debug;
+extern unsigned int bttv_gpio;
+extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
+extern int init_bttv_i2c(struct bttv *btv);
+
+#define dprintk(fmt, ...)			\
+do {						\
+	if (bttv_debug >= 1)			\
+		pr_debug(fmt, ##__VA_ARGS__);	\
+} while (0)
+#define dprintk_cont(fmt, ...)			\
+do {						\
+	if (bttv_debug >= 1)			\
+		pr_cont(fmt, ##__VA_ARGS__);	\
+} while (0)
+#define d2printk(fmt, ...)			\
+do {						\
+	if (bttv_debug >= 2)			\
+		printk(fmt, ##__VA_ARGS__);	\
+} while (0)
+
+#define BTTV_MAX_FBUF   0x208000
+#define BTTV_TIMEOUT    msecs_to_jiffies(500)    /* 0.5 seconds */
+#define BTTV_FREE_IDLE  msecs_to_jiffies(1000)   /* one second */
+
+
+struct bttv_pll_info {
+	unsigned int pll_ifreq;    /* PLL input frequency        */
+	unsigned int pll_ofreq;    /* PLL output frequency       */
+	unsigned int pll_crystal;  /* Crystal used for input     */
+	unsigned int pll_current;  /* Currently programmed ofreq */
+};
+
+/* for gpio-connected remote control */
+struct bttv_input {
+	struct input_dev      *dev;
+	char                  name[32];
+	char                  phys[32];
+	u32                   mask_keycode;
+	u32                   mask_keydown;
+};
+
+struct bttv_suspend_state {
+	u32  gpio_enable;
+	u32  gpio_data;
+	int  disabled;
+	int  loop_irq;
+	struct bttv_buffer_set video;
+	struct bttv_buffer     *vbi;
+};
+
+struct bttv {
+	struct bttv_core c;
+
+	/* pci device config */
+	unsigned short id;
+	unsigned char revision;
+	unsigned char __iomem *bt848_mmio;   /* pointer to mmio */
+
+	/* card configuration info */
+	unsigned int cardid;   /* pci subsystem id (bt878 based ones) */
+	unsigned int tuner_type;  /* tuner chip type */
+	unsigned int tda9887_conf;
+	unsigned int svhs, dig;
+	unsigned int has_saa6588:1;
+	struct bttv_pll_info pll;
+	int triton1;
+	int gpioirq;
+
+	int use_i2c_hw;
+
+	/* old gpio interface */
+	int shutdown;
+
+	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;
+
+	/* i2c layer */
+	struct i2c_algo_bit_data   i2c_algo;
+	struct i2c_client          i2c_client;
+	int                        i2c_state, i2c_rc;
+	int                        i2c_done;
+	wait_queue_head_t          i2c_queue;
+	struct v4l2_subdev 	  *sd_msp34xx;
+	struct v4l2_subdev 	  *sd_tvaudio;
+
+	/* video4linux (1) */
+	struct video_device *video_dev;
+	struct video_device *radio_dev;
+	struct video_device *vbi_dev;
+
+	/* infrared remote */
+	int has_remote;
+	struct bttv_ir *remote;
+
+	/* I2C remote data */
+	struct IR_i2c_init_data    init_data;
+
+	/* locking */
+	spinlock_t s_lock;
+	struct mutex lock;
+	int resources;
+#ifdef VIDIOC_G_PRIORITY
+	struct v4l2_prio_state prio;
+#endif
+
+	/* video state */
+	unsigned int input;
+	unsigned int audio;
+	unsigned int mute;
+	unsigned long freq;
+	unsigned int tvnorm;
+	int hue, contrast, bright, saturation;
+	struct v4l2_framebuffer fbuf;
+	unsigned int field_count;
+
+	/* various options */
+	int opt_combfilter;
+	int opt_lumafilter;
+	int opt_automute;
+	int opt_chroma_agc;
+	int opt_adc_crush;
+	int opt_vcr_hack;
+	int opt_whitecrush_upper;
+	int opt_whitecrush_lower;
+	int opt_uv_ratio;
+	int opt_full_luma_range;
+	int opt_coring;
+
+	/* radio data/state */
+	int has_radio;
+	int radio_user;
+	int radio_uses_msp_demodulator;
+
+	/* miro/pinnacle + Aimslab VHX
+	   philips matchbox (tea5757 radio tuner) support */
+	int has_matchbox;
+	int mbox_we;
+	int mbox_data;
+	int mbox_clk;
+	int mbox_most;
+	int mbox_mask;
+
+	/* ISA stuff (Terratec Active Radio Upgrade) */
+	int mbox_ior;
+	int mbox_iow;
+	int mbox_csel;
+
+	/* risc memory management data
+	   - must acquire s_lock before changing these
+	   - only the irq handler is supported to touch top + bottom + vcurr */
+	struct btcx_riscmem     main;
+	struct bttv_buffer      *screen;    /* overlay             */
+	struct list_head        capture;    /* video capture queue */
+	struct list_head        vcapture;   /* vbi capture queue   */
+	struct bttv_buffer_set  curr;       /* active buffers      */
+	struct bttv_buffer      *cvbi;      /* active vbi buffer   */
+	int                     loop_irq;
+	int                     new_input;
+
+	unsigned long cap_ctl;
+	unsigned long dma_on;
+	struct timer_list timeout;
+	struct bttv_suspend_state state;
+
+	/* stats */
+	unsigned int errors;
+	unsigned int framedrop;
+	unsigned int irq_total;
+	unsigned int irq_me;
+
+	unsigned int users;
+	struct bttv_fh init;
+
+	/* used to make dvb-bt8xx autoloadable */
+	struct work_struct request_module_wk;
+
+	/* Default (0) and current (1) video capturing and overlay
+	   cropping parameters in bttv_tvnorm.cropcap units. Protected
+	   by bttv.lock. */
+	struct bttv_crop crop[2];
+
+	/* Earliest possible start of video capturing in
+	   bttv_tvnorm.cropcap line units. Set by check_alloc_btres()
+	   and free_btres(). Protected by bttv.lock. */
+	__s32			vbi_end;
+
+	/* Latest possible end of VBI capturing (= crop[x].rect.top when
+	   VIDEO_RESOURCES are locked). Set by check_alloc_btres()
+	   and free_btres(). Protected by bttv.lock. */
+	__s32			crop_start;
+};
+
+static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct bttv, c.v4l2_dev);
+}
+
+/* our devices */
+#define BTTV_MAX 32
+extern unsigned int bttv_num;
+extern struct bttv *bttvs[BTTV_MAX];
+
+static inline unsigned int bttv_muxsel(const struct bttv *btv,
+				       unsigned int input)
+{
+	return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3;
+}
+
+#endif
+
+#define btwrite(dat,adr)    writel((dat), btv->bt848_mmio+(adr))
+#define btread(adr)         readl(btv->bt848_mmio+(adr))
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif /* _BTTVP_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
new file mode 100644
index 000000000000..430b3eb11815
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst.c
@@ -0,0 +1,1873 @@
+/*
+	Frontend/Card driver for TwinHan DST Frontend
+	Copyright (C) 2003 Jamie Honan
+	Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+#include "dst_priv.h"
+#include "dst_common.h"
+
+static unsigned int verbose = 1;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+static unsigned int dst_addons;
+module_param(dst_addons, int, 0644);
+MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)");
+
+static unsigned int dst_algo;
+module_param(dst_algo, int, 0644);
+MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)");
+
+#define HAS_LOCK		1
+#define ATTEMPT_TUNE		2
+#define HAS_POWER		4
+
+#define DST_ERROR		0
+#define DST_NOTICE		1
+#define DST_INFO		2
+#define DST_DEBUG		3
+
+#define dprintk(x, y, z, format, arg...) do {				\
+	if (z) {							\
+		if	((x > DST_ERROR) && (x > y))			\
+			printk(KERN_ERR "dst(%d) %s: " format "\n",	\
+				state->bt->nr, __func__ , ##arg);	\
+		else if	((x > DST_NOTICE) && (x > y))			\
+			printk(KERN_NOTICE "dst(%d) %s: " format "\n",  \
+				state->bt->nr, __func__ , ##arg);	\
+		else if ((x > DST_INFO) && (x > y))			\
+			printk(KERN_INFO "dst(%d) %s: " format "\n",	\
+				state->bt->nr, __func__ , ##arg);	\
+		else if ((x > DST_DEBUG) && (x > y))			\
+			printk(KERN_DEBUG "dst(%d) %s: " format "\n",	\
+				state->bt->nr,  __func__ , ##arg);	\
+	} else {							\
+		if (x > y)						\
+			printk(format, ##arg);				\
+	}								\
+} while(0)
+
+static int dst_command(struct dst_state *state, u8 *data, u8 len);
+
+static void dst_packsize(struct dst_state *state, int psize)
+{
+	union dst_gpio_packet bits;
+
+	bits.psize = psize;
+	bt878_device_control(state->bt, DST_IG_TS, &bits);
+}
+
+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;
+	int err;
+
+	enb.enb.mask = mask;
+	enb.enb.enable = enbb;
+
+	dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh);
+	if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) {
+		dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb);
+		return -EREMOTEIO;
+	}
+	udelay(1000);
+	/* because complete disabling means no output, no need to do output packet */
+	if (enbb == 0)
+		return 0;
+	if (delay)
+		msleep(10);
+	bits.outp.mask = enbb;
+	bits.outp.highvals = outhigh;
+	if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) {
+		dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int dst_gpio_inb(struct dst_state *state, u8 *result)
+{
+	union dst_gpio_packet rd_packet;
+	int err;
+
+	*result = 0;
+	if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err);
+		return -EREMOTEIO;
+	}
+	*result = (u8) rd_packet.rd.value;
+
+	return 0;
+}
+
+int rdc_reset_state(struct dst_state *state)
+{
+	dprintk(verbose, DST_INFO, 1, "Resetting state machine");
+	if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+		return -1;
+	}
+	msleep(10);
+	if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+		msleep(10);
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rdc_reset_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) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+		return -1;
+	}
+	udelay(1000);
+	if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+		return -1;
+	}
+
+	return 0;
+}
+
+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 !");
+		return -1;
+	}
+	udelay(1000);
+
+	return 0;
+}
+
+int dst_pio_disable(struct dst_state *state)
+{
+	if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+		return -1;
+	}
+	if (state->type_flags & DST_TYPE_HAS_FW_1)
+		udelay(1000);
+
+	return 0;
+}
+EXPORT_SYMBOL(dst_pio_disable);
+
+int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode)
+{
+	u8 reply;
+	int i;
+
+	for (i = 0; i < 200; i++) {
+		if (dst_gpio_inb(state, &reply) < 0) {
+			dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !");
+			return -1;
+		}
+		if ((reply & RDC_8820_PIO_0_ENABLE) == 0) {
+			dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i);
+			return 1;
+		}
+		msleep(10);
+	}
+	dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i);
+
+	return 0;
+}
+EXPORT_SYMBOL(dst_wait_dst_ready);
+
+int dst_error_recovery(struct dst_state *state)
+{
+	dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors.");
+	dst_pio_disable(state);
+	msleep(10);
+	dst_pio_enable(state);
+	msleep(10);
+
+	return 0;
+}
+EXPORT_SYMBOL(dst_error_recovery);
+
+int dst_error_bailout(struct dst_state *state)
+{
+	dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error.");
+	rdc_8820_reset(state);
+	dst_pio_disable(state);
+	msleep(10);
+
+	return 0;
+}
+EXPORT_SYMBOL(dst_error_bailout);
+
+int dst_comm_init(struct dst_state *state)
+{
+	dprintk(verbose, DST_INFO, 1, "Initializing DST.");
+	if ((dst_pio_enable(state)) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed");
+		return -1;
+	}
+	if ((rdc_reset_state(state)) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed.");
+		return -1;
+	}
+	if (state->type_flags & DST_TYPE_HAS_FW_1)
+		msleep(100);
+	else
+		msleep(5);
+
+	return 0;
+}
+EXPORT_SYMBOL(dst_comm_init);
+
+int write_dst(struct dst_state *state, u8 *data, u8 len)
+{
+	struct i2c_msg msg = {
+		.addr = state->config->demod_address,
+		.flags = 0,
+		.buf = data,
+		.len = len
+	};
+
+	int err;
+	u8 cnt, i;
+
+	dprintk(verbose, DST_NOTICE, 0, "writing [ ");
+	for (i = 0; i < len; i++)
+		dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]);
+	dprintk(verbose, DST_NOTICE, 0, "]\n");
+
+	for (cnt = 0; cnt < 2; cnt++) {
+		if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+			dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]);
+			dst_error_recovery(state);
+			continue;
+		} else
+			break;
+	}
+	if (cnt >= 2) {
+		dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET");
+		dst_error_bailout(state);
+
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(write_dst);
+
+int read_dst(struct dst_state *state, u8 *ret, u8 len)
+{
+	struct i2c_msg msg = {
+		.addr = state->config->demod_address,
+		.flags = I2C_M_RD,
+		.buf = ret,
+		.len = len
+	};
+
+	int err;
+	int cnt;
+
+	for (cnt = 0; cnt < 2; cnt++) {
+		if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+			dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]);
+			dst_error_recovery(state);
+			continue;
+		} else
+			break;
+	}
+	if (cnt >= 2) {
+		dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET");
+		dst_error_bailout(state);
+
+		return -1;
+	}
+	dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]);
+	for (err = 1; err < len; err++)
+		dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]);
+	if (err > 1)
+		dprintk(verbose, DST_DEBUG, 0, "\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(read_dst);
+
+static int dst_set_polarization(struct dst_state *state)
+{
+	switch (state->voltage) {
+	case SEC_VOLTAGE_13:	/*	Vertical	*/
+		dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]");
+		state->tx_tuna[8] &= ~0x40;
+		break;
+	case SEC_VOLTAGE_18:	/*	Horizontal	*/
+		dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]");
+		state->tx_tuna[8] |= 0x40;
+		break;
+	case SEC_VOLTAGE_OFF:
+		break;
+	}
+
+	return 0;
+}
+
+static int dst_set_freq(struct dst_state *state, u32 freq)
+{
+	state->frequency = freq;
+	dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq);
+
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		freq = freq / 1000;
+		if (freq < 950 || freq > 2150)
+			return -EINVAL;
+		state->tx_tuna[2] = (freq >> 8);
+		state->tx_tuna[3] = (u8) freq;
+		state->tx_tuna[4] = 0x01;
+		state->tx_tuna[8] &= ~0x04;
+		if (state->type_flags & DST_TYPE_HAS_OBS_REGS) {
+			if (freq < 1531)
+				state->tx_tuna[8] |= 0x04;
+		}
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		freq = freq / 1000;
+		if (freq < 137000 || freq > 858000)
+			return -EINVAL;
+		state->tx_tuna[2] = (freq >> 16) & 0xff;
+		state->tx_tuna[3] = (freq >> 8) & 0xff;
+		state->tx_tuna[4] = (u8) freq;
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		freq = freq / 1000;
+		state->tx_tuna[2] = (freq >> 16) & 0xff;
+		state->tx_tuna[3] = (freq >> 8) & 0xff;
+		state->tx_tuna[4] = (u8) freq;
+	} else if (state->dst_type == DST_TYPE_IS_ATSC) {
+		freq = freq / 1000;
+		if (freq < 51000 || freq > 858000)
+			return -EINVAL;
+		state->tx_tuna[2] = (freq >> 16) & 0xff;
+		state->tx_tuna[3] = (freq >>  8) & 0xff;
+		state->tx_tuna[4] = (u8) freq;
+		state->tx_tuna[5] = 0x00;		/*	ATSC	*/
+		state->tx_tuna[6] = 0x00;
+		if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG)
+			state->tx_tuna[7] = 0x00;	/*	Digital	*/
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth)
+{
+	state->bandwidth = bandwidth;
+
+	if (state->dst_type != DST_TYPE_IS_TERR)
+		return -EOPNOTSUPP;
+
+	switch (bandwidth) {
+	case 6000000:
+		if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+			state->tx_tuna[7] = 0x06;
+		else {
+			state->tx_tuna[6] = 0x06;
+			state->tx_tuna[7] = 0x00;
+		}
+		break;
+	case 7000000:
+		if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+			state->tx_tuna[7] = 0x07;
+		else {
+			state->tx_tuna[6] = 0x07;
+			state->tx_tuna[7] = 0x00;
+		}
+		break;
+	case 8000000:
+		if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+			state->tx_tuna[7] = 0x08;
+		else {
+			state->tx_tuna[6] = 0x08;
+			state->tx_tuna[7] = 0x00;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion)
+{
+	state->inversion = inversion;
+	switch (inversion) {
+	case INVERSION_OFF:	/*	Inversion = Normal	*/
+		state->tx_tuna[8] &= ~0x80;
+		break;
+	case INVERSION_ON:
+		state->tx_tuna[8] |= 0x80;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec)
+{
+	state->fec = fec;
+	return 0;
+}
+
+static fe_code_rate_t dst_get_fec(struct dst_state *state)
+{
+	return state->fec;
+}
+
+static int dst_set_symbolrate(struct dst_state *state, u32 srate)
+{
+	u32 symcalc;
+	u64 sval;
+
+	state->symbol_rate = srate;
+	if (state->dst_type == DST_TYPE_IS_TERR) {
+		return -EOPNOTSUPP;
+	}
+	dprintk(verbose, DST_INFO, 1, "set symrate %u", srate);
+	srate /= 1000;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		if (state->type_flags & DST_TYPE_HAS_SYMDIV) {
+			sval = srate;
+			sval <<= 20;
+			do_div(sval, 88000);
+			symcalc = (u32) sval;
+			dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc);
+			state->tx_tuna[5] = (u8) (symcalc >> 12);
+			state->tx_tuna[6] = (u8) (symcalc >> 4);
+			state->tx_tuna[7] = (u8) (symcalc << 4);
+		} else {
+			state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f;
+			state->tx_tuna[6] = (u8) (srate >> 8);
+			state->tx_tuna[7] = (u8) srate;
+		}
+		state->tx_tuna[8] &= ~0x20;
+		if (state->type_flags & DST_TYPE_HAS_OBS_REGS) {
+			if (srate > 8000)
+				state->tx_tuna[8] |= 0x20;
+		}
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name);
+		if (!strncmp(state->fw_name, "DCTNEW", 6)) {
+			state->tx_tuna[5] = (u8) (srate >> 8);
+			state->tx_tuna[6] = (u8) srate;
+			state->tx_tuna[7] = 0x00;
+		} else if (!strncmp(state->fw_name, "DCT-CI", 6)) {
+			state->tx_tuna[5] = 0x00;
+			state->tx_tuna[6] = (u8) (srate >> 8);
+			state->tx_tuna[7] = (u8) srate;
+		}
+	}
+	return 0;
+}
+
+static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation)
+{
+	if (state->dst_type != DST_TYPE_IS_CABLE)
+		return -EOPNOTSUPP;
+
+	state->modulation = modulation;
+	switch (modulation) {
+	case QAM_16:
+		state->tx_tuna[8] = 0x10;
+		break;
+	case QAM_32:
+		state->tx_tuna[8] = 0x20;
+		break;
+	case QAM_64:
+		state->tx_tuna[8] = 0x40;
+		break;
+	case QAM_128:
+		state->tx_tuna[8] = 0x80;
+		break;
+	case QAM_256:
+		if (!strncmp(state->fw_name, "DCTNEW", 6))
+			state->tx_tuna[8] = 0xff;
+		else if (!strncmp(state->fw_name, "DCT-CI", 6))
+			state->tx_tuna[8] = 0x00;
+		break;
+	case QPSK:
+	case QAM_AUTO:
+	case VSB_8:
+	case VSB_16:
+	default:
+		return -EINVAL;
+
+	}
+
+	return 0;
+}
+
+static fe_modulation_t dst_get_modulation(struct dst_state *state)
+{
+	return state->modulation;
+}
+
+
+u8 dst_check_sum(u8 *buf, u32 len)
+{
+	u32 i;
+	u8 val = 0;
+	if (!len)
+		return 0;
+	for (i = 0; i < len; i++) {
+		val += buf[i];
+	}
+	return ((~val) + 1);
+}
+EXPORT_SYMBOL(dst_check_sum);
+
+static void dst_type_flags_print(struct dst_state *state)
+{
+	u32 type_flags = state->type_flags;
+
+	dprintk(verbose, DST_ERROR, 0, "DST type flags :");
+	if (type_flags & DST_TYPE_HAS_TS188)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188);
+	if (type_flags & DST_TYPE_HAS_NEWTUNE_2)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2);
+	if (type_flags & DST_TYPE_HAS_TS204)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204);
+	if (type_flags & DST_TYPE_HAS_VLF)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF);
+	if (type_flags & DST_TYPE_HAS_SYMDIV)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV);
+	if (type_flags & DST_TYPE_HAS_FW_1)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1);
+	if (type_flags & DST_TYPE_HAS_FW_2)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2);
+	if (type_flags & DST_TYPE_HAS_FW_3)
+		dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3);
+	dprintk(verbose, DST_ERROR, 0, "\n");
+}
+
+
+static int dst_type_print(struct dst_state *state, u8 type)
+{
+	char *otype;
+	switch (type) {
+	case DST_TYPE_IS_SAT:
+		otype = "satellite";
+		break;
+
+	case DST_TYPE_IS_TERR:
+		otype = "terrestrial";
+		break;
+
+	case DST_TYPE_IS_CABLE:
+		otype = "cable";
+		break;
+
+	case DST_TYPE_IS_ATSC:
+		otype = "atsc";
+		break;
+
+	default:
+		dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type);
+		return -EINVAL;
+	}
+	dprintk(verbose, DST_INFO, 1, "DST type: %s", otype);
+
+	return 0;
+}
+
+static struct tuner_types tuner_list[] = {
+	{
+		.tuner_type = TUNER_TYPE_L64724,
+		.tuner_name = "L 64724",
+		.board_name = "UNKNOWN",
+		.fw_name    = "UNKNOWN"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_STV0299,
+		.tuner_name = "STV 0299",
+		.board_name = "VP1020",
+		.fw_name    = "DST-MOT"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_STV0299,
+		.tuner_name = "STV 0299",
+		.board_name = "VP1020",
+		.fw_name    = "DST-03T"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_MB86A15,
+		.tuner_name = "MB 86A15",
+		.board_name = "VP1022",
+		.fw_name    = "DST-03T"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_MB86A15,
+		.tuner_name = "MB 86A15",
+		.board_name = "VP1025",
+		.fw_name    = "DST-03T"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_STV0299,
+		.tuner_name = "STV 0299",
+		.board_name = "VP1030",
+		.fw_name    = "DST-CI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_STV0299,
+		.tuner_name = "STV 0299",
+		.board_name = "VP1030",
+		.fw_name    = "DSTMCI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP2021",
+		.fw_name    = "DCTNEW"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP2030",
+		.fw_name    = "DCT-CI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP2031",
+		.fw_name    = "DCT-CI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP2040",
+		.fw_name    = "DCT-CI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP3020",
+		.fw_name    = "DTTFTA"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP3021",
+		.fw_name    = "DTTFTA"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_TDA10046,
+		.tuner_name = "TDA10046",
+		.board_name = "VP3040",
+		.fw_name    = "DTT-CI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_UNKNOWN,
+		.tuner_name = "UNKNOWN",
+		.board_name = "VP3051",
+		.fw_name    = "DTTNXT"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_NXT200x,
+		.tuner_name = "NXT200x",
+		.board_name = "VP3220",
+		.fw_name    = "ATSCDI"
+	},
+
+	{
+		.tuner_type = TUNER_TYPE_NXT200x,
+		.tuner_name = "NXT200x",
+		.board_name = "VP3250",
+		.fw_name    = "ATSCAD"
+	},
+};
+
+/*
+	Known cards list
+	Satellite
+	-------------------
+		  200103A
+	VP-1020   DST-MOT	LG(old), TS=188
+
+	VP-1020   DST-03T	LG(new), TS=204
+	VP-1022   DST-03T	LG(new), TS=204
+	VP-1025   DST-03T	LG(new), TS=204
+
+	VP-1030   DSTMCI,	LG(new), TS=188
+	VP-1032   DSTMCI,	LG(new), TS=188
+
+	Cable
+	-------------------
+	VP-2030   DCT-CI,	Samsung, TS=204
+	VP-2021   DCT-CI,	Unknown, TS=204
+	VP-2031   DCT-CI,	Philips, TS=188
+	VP-2040   DCT-CI,	Philips, TS=188, with CA daughter board
+	VP-2040   DCT-CI,	Philips, TS=204, without CA daughter board
+
+	Terrestrial
+	-------------------
+	VP-3050  DTTNXT			 TS=188
+	VP-3040  DTT-CI,	Philips, TS=188
+	VP-3040  DTT-CI,	Philips, TS=204
+
+	ATSC
+	-------------------
+	VP-3220  ATSCDI,		 TS=188
+	VP-3250  ATSCAD,		 TS=188
+
+*/
+
+static struct dst_types dst_tlist[] = {
+	{
+		.device_id = "200103A",
+		.offset = 0,
+		.dst_type =  DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},	/*	obsolete	*/
+
+	{
+		.device_id = "DST-020",
+		.offset = 0,
+		.dst_type =  DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},	/*	obsolete	*/
+
+	{
+		.device_id = "DST-030",
+		.offset =  0,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},	/*	obsolete	*/
+
+	{
+		.device_id = "DST-03T",
+		.offset = 0,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2,
+		.dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5
+							 | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO,
+		.tuner_type = TUNER_TYPE_MULTI
+	 },
+
+	{
+		.device_id = "DST-MOT",
+		.offset =  0,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},	/*	obsolete	*/
+
+	{
+		.device_id = "DST-CI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1,
+		.dst_feature = DST_TYPE_HAS_CA,
+		.tuner_type = 0
+	},	/*	An OEM board	*/
+
+	{
+		.device_id = "DSTMCI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF,
+		.dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4
+							| DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC,
+		.tuner_type = TUNER_TYPE_MULTI
+	},
+
+	{
+		.device_id = "DSTFCI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_SAT,
+		.type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},	/* unknown to vendor	*/
+
+	{
+		.device_id = "DCT-CI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_CABLE,
+		.type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1	| DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF,
+		.dst_feature = DST_TYPE_HAS_CA,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "DCTNEW",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_CABLE,
+		.type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "DTT-CI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_TERR,
+		.type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF,
+		.dst_feature = DST_TYPE_HAS_CA,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "DTTDIG",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_TERR,
+		.type_flags = DST_TYPE_HAS_FW_2,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "DTTNXT",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_TERR,
+		.type_flags = DST_TYPE_HAS_FW_2,
+		.dst_feature = DST_TYPE_HAS_ANALOG,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "ATSCDI",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_ATSC,
+		.type_flags = DST_TYPE_HAS_FW_2,
+		.dst_feature = 0,
+		.tuner_type = 0
+	},
+
+	{
+		.device_id = "ATSCAD",
+		.offset = 1,
+		.dst_type = DST_TYPE_IS_ATSC,
+		.type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD,
+		.dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG,
+		.tuner_type = 0
+	},
+
+	{ }
+
+};
+
+static int dst_get_mac(struct dst_state *state)
+{
+	u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	get_mac[7] = dst_check_sum(get_mac, 7);
+	if (dst_command(state, get_mac, 8) < 0) {
+		dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+		return -1;
+	}
+	memset(&state->mac_address, '\0', 8);
+	memcpy(&state->mac_address, &state->rxbuffer, 6);
+	dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address);
+
+	return 0;
+}
+
+static int dst_fw_ver(struct dst_state *state)
+{
+	u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	get_ver[7] = dst_check_sum(get_ver, 7);
+	if (dst_command(state, get_ver, 8) < 0) {
+		dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+		return -1;
+	}
+	memcpy(&state->fw_version, &state->rxbuffer, 8);
+	dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x",
+		state->fw_version[0] >> 4, state->fw_version[0] & 0x0f,
+		state->fw_version[1],
+		state->fw_version[5], state->fw_version[6],
+		state->fw_version[4], state->fw_version[3], state->fw_version[2]);
+
+	return 0;
+}
+
+static int dst_card_type(struct dst_state *state)
+{
+	int j;
+	struct tuner_types *p_tuner_list = NULL;
+
+	u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	get_type[7] = dst_check_sum(get_type, 7);
+	if (dst_command(state, get_type, 8) < 0) {
+		dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+		return -1;
+	}
+	memset(&state->card_info, '\0', 8);
+	memcpy(&state->card_info, &state->rxbuffer, 7);
+	dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]);
+
+	for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) {
+		if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) {
+			state->tuner_type = p_tuner_list->tuner_type;
+			dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]",
+				p_tuner_list->tuner_name, p_tuner_list->tuner_type);
+		}
+	}
+
+	return 0;
+}
+
+static int dst_get_vendor(struct dst_state *state)
+{
+	u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	get_vendor[7] = dst_check_sum(get_vendor, 7);
+	if (dst_command(state, get_vendor, 8) < 0) {
+		dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+		return -1;
+	}
+	memset(&state->vendor, '\0', 8);
+	memcpy(&state->vendor, &state->rxbuffer, 7);
+	dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]);
+
+	return 0;
+}
+
+static void debug_dst_buffer(struct dst_state *state)
+{
+	int i;
+
+	if (verbose > 2) {
+		printk("%s: [", __func__);
+		for (i = 0; i < 8; i++)
+			printk(" %02x", state->rxbuffer[i]);
+		printk("]\n");
+	}
+}
+
+static int dst_check_stv0299(struct dst_state *state)
+{
+	u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	check_stv0299[7] = dst_check_sum(check_stv0299, 7);
+	if (dst_command(state, check_stv0299, 8) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed");
+		return -1;
+	}
+	debug_dst_buffer(state);
+
+	if (memcmp(&check_stv0299, &state->rxbuffer, 8)) {
+		dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM");
+		state->tuner_type = TUNER_TYPE_STV0299;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int dst_check_mb86a15(struct dst_state *state)
+{
+	u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	check_mb86a15[7] = dst_check_sum(check_mb86a15, 7);
+	if (dst_command(state, check_mb86a15, 8) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed");
+		return -1;
+	}
+	debug_dst_buffer(state);
+
+	if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM");
+		state->tuner_type = TUNER_TYPE_MB86A15;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int dst_get_tuner_info(struct dst_state *state)
+{
+	u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	get_tuner_1[7] = dst_check_sum(get_tuner_1, 7);
+	get_tuner_2[7] = dst_check_sum(get_tuner_2, 7);
+	dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE");
+	if (state->type_flags & DST_TYPE_HAS_MULTI_FE) {
+		if (dst_command(state, get_tuner_1, 8) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported");
+			goto force;
+		}
+	} else {
+		if (dst_command(state, get_tuner_2, 8) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported");
+			goto force;
+		}
+	}
+	memcpy(&state->board_info, &state->rxbuffer, 8);
+	if (state->type_flags & DST_TYPE_HAS_MULTI_FE) {
+		dprintk(verbose, DST_ERROR, 1, "DST type has TS=188");
+	}
+	if (state->board_info[0] == 0xbc) {
+		if (state->dst_type != DST_TYPE_IS_ATSC)
+			state->type_flags |= DST_TYPE_HAS_TS188;
+		else
+			state->type_flags |= DST_TYPE_HAS_NEWTUNE_2;
+
+		if (state->board_info[1] == 0x01) {
+			state->dst_hw_cap |= DST_TYPE_HAS_DBOARD;
+			dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard");
+		}
+	}
+
+	return 0;
+force:
+	if (!strncmp(state->fw_name, "DCT-CI", 6)) {
+		state->type_flags |= DST_TYPE_HAS_TS204;
+		dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name);
+	}
+
+	return -1;
+}
+
+static int dst_get_device_id(struct dst_state *state)
+{
+	u8 reply;
+
+	int i, j;
+	struct dst_types *p_dst_type = NULL;
+	struct tuner_types *p_tuner_list = NULL;
+
+	u8 use_dst_type = 0;
+	u32 use_type_flags = 0;
+
+	static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+	state->tuner_type = 0;
+	device_type[7] = dst_check_sum(device_type, 7);
+
+	if (write_dst(state, device_type, FIXED_COMM))
+		return -1;		/*	Write failed		*/
+	if ((dst_pio_disable(state)) < 0)
+		return -1;
+	if (read_dst(state, &reply, GET_ACK))
+		return -1;		/*	Read failure		*/
+	if (reply != ACK) {
+		dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply);
+		return -1;		/*	Unack'd write		*/
+	}
+	if (!dst_wait_dst_ready(state, DEVICE_INIT))
+		return -1;		/*	DST not ready yet	*/
+	if (read_dst(state, state->rxbuffer, FIXED_COMM))
+		return -1;
+
+	dst_pio_disable(state);
+	if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+		dprintk(verbose, DST_INFO, 1, "Checksum failure!");
+		return -1;		/*	Checksum failure	*/
+	}
+	state->rxbuffer[7] = '\0';
+
+	for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) {
+		if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) {
+			use_type_flags = p_dst_type->type_flags;
+			use_dst_type = p_dst_type->dst_type;
+
+			/*	Card capabilities	*/
+			state->dst_hw_cap = p_dst_type->dst_feature;
+			dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id);
+			strncpy(&state->fw_name[0], p_dst_type->device_id, 6);
+			/*	Multiple tuners		*/
+			if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) {
+				switch (use_dst_type) {
+				case DST_TYPE_IS_SAT:
+					/*	STV0299 check	*/
+					if (dst_check_stv0299(state) < 0) {
+						dprintk(verbose, DST_ERROR, 1, "Unsupported");
+						state->tuner_type = TUNER_TYPE_MB86A15;
+					}
+					break;
+				default:
+					break;
+				}
+				if (dst_check_mb86a15(state) < 0)
+					dprintk(verbose, DST_ERROR, 1, "Unsupported");
+			/*	Single tuner		*/
+			} else {
+				state->tuner_type = p_dst_type->tuner_type;
+			}
+			for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) {
+				if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) &&
+					p_tuner_list->tuner_type == state->tuner_type) {
+					dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]",
+						p_dst_type->device_id, p_tuner_list->tuner_name);
+				}
+			}
+			break;
+		}
+	}
+
+	if (i >= ARRAY_SIZE(dst_tlist)) {
+		dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]);
+		dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in");
+		use_dst_type = DST_TYPE_IS_SAT;
+		use_type_flags = DST_TYPE_HAS_SYMDIV;
+	}
+	dst_type_print(state, use_dst_type);
+	state->type_flags = use_type_flags;
+	state->dst_type = use_dst_type;
+	dst_type_flags_print(state);
+
+	return 0;
+}
+
+static int dst_probe(struct dst_state *state)
+{
+	mutex_init(&state->dst_mutex);
+	if (dst_addons & DST_TYPE_HAS_CA) {
+		if ((rdc_8820_reset(state)) < 0) {
+			dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed.");
+			return -1;
+		}
+		msleep(4000);
+	} else {
+		msleep(100);
+	}
+	if ((dst_comm_init(state)) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed.");
+		return -1;
+	}
+	msleep(100);
+	if (dst_get_device_id(state) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "unknown device.");
+		return -1;
+	}
+	if (dst_get_mac(state) < 0) {
+		dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command");
+	}
+	if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) {
+		if (dst_get_tuner_info(state) < 0)
+			dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command");
+	}
+	if (state->type_flags & DST_TYPE_HAS_TS204) {
+		dst_packsize(state, 204);
+	}
+	if (state->type_flags & DST_TYPE_HAS_FW_BUILD) {
+		if (dst_fw_ver(state) < 0) {
+			dprintk(verbose, DST_INFO, 1, "FW: Unsupported command");
+			return 0;
+		}
+		if (dst_card_type(state) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Card: Unsupported command");
+			return 0;
+		}
+		if (dst_get_vendor(state) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command");
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static int dst_command(struct dst_state *state, u8 *data, u8 len)
+{
+	u8 reply;
+
+	mutex_lock(&state->dst_mutex);
+	if ((dst_comm_init(state)) < 0) {
+		dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed.");
+		goto error;
+	}
+	if (write_dst(state, data, len)) {
+		dprintk(verbose, DST_INFO, 1, "Trying to recover.. ");
+		if ((dst_error_recovery(state)) < 0) {
+			dprintk(verbose, DST_ERROR, 1, "Recovery Failed.");
+			goto error;
+		}
+		goto error;
+	}
+	if ((dst_pio_disable(state)) < 0) {
+		dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed.");
+		goto error;
+	}
+	if (state->type_flags & DST_TYPE_HAS_FW_1)
+		mdelay(3);
+	if (read_dst(state, &reply, GET_ACK)) {
+		dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. ");
+		if ((dst_error_recovery(state)) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Recovery Failed.");
+			goto error;
+		}
+		goto error;
+	}
+	if (reply != ACK) {
+		dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply);
+		goto error;
+	}
+	if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3))
+		goto error;
+	if (state->type_flags & DST_TYPE_HAS_FW_1)
+		mdelay(3);
+	else
+		udelay(2000);
+	if (!dst_wait_dst_ready(state, NO_DELAY))
+		goto error;
+	if (read_dst(state, state->rxbuffer, FIXED_COMM)) {
+		dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. ");
+		if ((dst_error_recovery(state)) < 0) {
+			dprintk(verbose, DST_INFO, 1, "Recovery failed.");
+			goto error;
+		}
+		goto error;
+	}
+	if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+		dprintk(verbose, DST_INFO, 1, "checksum failure");
+		goto error;
+	}
+	mutex_unlock(&state->dst_mutex);
+	return 0;
+
+error:
+	mutex_unlock(&state->dst_mutex);
+	return -EIO;
+
+}
+
+static int dst_get_signal(struct dst_state *state)
+{
+	int retval;
+	u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb };
+	//dprintk("%s: Getting Signal strength and other parameters\n", __func__);
+	if ((state->diseq_flags & ATTEMPT_TUNE) == 0) {
+		state->decode_lock = state->decode_strength = state->decode_snr = 0;
+		return 0;
+	}
+	if (0 == (state->diseq_flags & HAS_LOCK)) {
+		state->decode_lock = state->decode_strength = state->decode_snr = 0;
+		return 0;
+	}
+	if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) {
+		retval = dst_command(state, get_signal, 8);
+		if (retval < 0)
+			return retval;
+		if (state->dst_type == DST_TYPE_IS_SAT) {
+			state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0;
+			state->decode_strength = state->rxbuffer[5] << 8;
+			state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+		} else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) {
+			state->decode_lock = (state->rxbuffer[1]) ? 1 : 0;
+			state->decode_strength = state->rxbuffer[4] << 8;
+			state->decode_snr = state->rxbuffer[3] << 8;
+		} else if (state->dst_type == DST_TYPE_IS_ATSC) {
+			state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0;
+			state->decode_strength = state->rxbuffer[4] << 8;
+			state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+		}
+		state->cur_jiff = jiffies;
+	}
+	return 0;
+}
+
+static int dst_tone_power_cmd(struct dst_state *state)
+{
+	u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
+
+	if (state->dst_type != DST_TYPE_IS_SAT)
+		return -EOPNOTSUPP;
+	paket[4] = state->tx_tuna[4];
+	paket[2] = state->tx_tuna[2];
+	paket[3] = state->tx_tuna[3];
+	paket[7] = dst_check_sum (paket, 7);
+	return dst_command(state, paket, 8);
+}
+
+static int dst_get_tuna(struct dst_state *state)
+{
+	int retval;
+
+	if ((state->diseq_flags & ATTEMPT_TUNE) == 0)
+		return 0;
+	state->diseq_flags &= ~(HAS_LOCK);
+	if (!dst_wait_dst_ready(state, NO_DELAY))
+		return -EIO;
+	if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+		!(state->dst_type == DST_TYPE_IS_ATSC))
+
+		retval = read_dst(state, state->rx_tuna, 10);
+	else
+		retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM);
+	if (retval < 0) {
+		dprintk(verbose, DST_DEBUG, 1, "read not successful");
+		return retval;
+	}
+	if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+	   !(state->dst_type == DST_TYPE_IS_ATSC)) {
+
+		if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
+			dprintk(verbose, DST_INFO, 1, "checksum failure ? ");
+			return -EIO;
+		}
+	} else {
+		if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) {
+			dprintk(verbose, DST_INFO, 1, "checksum failure? ");
+			return -EIO;
+		}
+	}
+	if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0)
+		return 0;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3];
+	} else {
+		state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4];
+	}
+	state->decode_freq = state->decode_freq * 1000;
+	state->decode_lock = 1;
+	state->diseq_flags |= HAS_LOCK;
+
+	return 1;
+}
+
+static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+
+static int dst_write_tuna(struct dvb_frontend *fe)
+{
+	struct dst_state *state = fe->demodulator_priv;
+	int retval;
+	u8 reply;
+
+	dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags);
+	state->decode_freq = 0;
+	state->decode_lock = state->decode_strength = state->decode_snr = 0;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		if (!(state->diseq_flags & HAS_POWER))
+			dst_set_voltage(fe, SEC_VOLTAGE_13);
+	}
+	state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE);
+	mutex_lock(&state->dst_mutex);
+	if ((dst_comm_init(state)) < 0) {
+		dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed.");
+		goto error;
+	}
+//	if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+	if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+		(!(state->dst_type == DST_TYPE_IS_ATSC))) {
+
+		state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9);
+		retval = write_dst(state, &state->tx_tuna[0], 10);
+	} else {
+		state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7);
+		retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM);
+	}
+	if (retval < 0) {
+		dst_pio_disable(state);
+		dprintk(verbose, DST_DEBUG, 1, "write not successful");
+		goto werr;
+	}
+	if ((dst_pio_disable(state)) < 0) {
+		dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !");
+		goto error;
+	}
+	if ((read_dst(state, &reply, GET_ACK) < 0)) {
+		dprintk(verbose, DST_DEBUG, 1, "read verify not successful.");
+		goto error;
+	}
+	if (reply != ACK) {
+		dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply);
+		goto error;
+	}
+	state->diseq_flags |= ATTEMPT_TUNE;
+	retval = dst_get_tuna(state);
+werr:
+	mutex_unlock(&state->dst_mutex);
+	return retval;
+
+error:
+	mutex_unlock(&state->dst_mutex);
+	return -EIO;
+}
+
+/*
+ * line22k0    0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k1    0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k2    0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00
+ * tone        0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00
+ * data        0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00
+ * power_off   0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+ * power_on    0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00
+ * Diseqc 1    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec
+ * Diseqc 2    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8
+ * Diseqc 3    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4
+ * Diseqc 4    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0
+ */
+
+static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+	struct dst_state *state = fe->demodulator_priv;
+	u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+
+	if (state->dst_type != DST_TYPE_IS_SAT)
+		return -EOPNOTSUPP;
+	if (cmd->msg_len > 0 && cmd->msg_len < 5)
+		memcpy(&paket[3], cmd->msg, cmd->msg_len);
+	else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5)
+		memcpy(&paket[2], cmd->msg, cmd->msg_len);
+	else
+		return -EINVAL;
+	paket[7] = dst_check_sum(&paket[0], 7);
+	return dst_command(state, paket, 8);
+}
+
+static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	int need_cmd, retval = 0;
+	struct dst_state *state = fe->demodulator_priv;
+
+	state->voltage = voltage;
+	if (state->dst_type != DST_TYPE_IS_SAT)
+		return -EOPNOTSUPP;
+
+	need_cmd = 0;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+	case SEC_VOLTAGE_18:
+		if ((state->diseq_flags & HAS_POWER) == 0)
+			need_cmd = 1;
+		state->diseq_flags |= HAS_POWER;
+		state->tx_tuna[4] = 0x01;
+		break;
+	case SEC_VOLTAGE_OFF:
+		need_cmd = 1;
+		state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE);
+		state->tx_tuna[4] = 0x00;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (need_cmd)
+		retval = dst_tone_power_cmd(state);
+
+	return retval;
+}
+
+static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	state->tone = tone;
+	if (state->dst_type != DST_TYPE_IS_SAT)
+		return -EOPNOTSUPP;
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+		    state->tx_tuna[2] = 0x00;
+		else
+		    state->tx_tuna[2] = 0xff;
+		break;
+
+	case SEC_TONE_ON:
+		state->tx_tuna[2] = 0x02;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return dst_tone_power_cmd(state);
+}
+
+static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	if (state->dst_type != DST_TYPE_IS_SAT)
+		return -EOPNOTSUPP;
+	state->minicmd = minicmd;
+	switch (minicmd) {
+	case SEC_MINI_A:
+		state->tx_tuna[3] = 0x02;
+		break;
+	case SEC_MINI_B:
+		state->tx_tuna[3] = 0xff;
+		break;
+	}
+	return dst_tone_power_cmd(state);
+}
+
+
+static int dst_init(struct dvb_frontend *fe)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 };
+	static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 };
+	static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+	static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+	static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+	static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+	static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+
+	state->inversion = INVERSION_OFF;
+	state->voltage = SEC_VOLTAGE_13;
+	state->tone = SEC_TONE_OFF;
+	state->diseq_flags = 0;
+	state->k22 = 0x02;
+	state->bandwidth = 7000000;
+	state->cur_jiff = jiffies;
+	if (state->dst_type == DST_TYPE_IS_SAT)
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204));
+	else if (state->dst_type == DST_TYPE_IS_TERR)
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204));
+	else if (state->dst_type == DST_TYPE_IS_CABLE)
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204));
+	else if (state->dst_type == DST_TYPE_IS_ATSC)
+		memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner));
+
+	return 0;
+}
+
+static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	*status = 0;
+	if (state->diseq_flags & HAS_LOCK) {
+//		dst_get_signal(state);	// don't require(?) to ask MCU
+		if (state->decode_lock)
+			*status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI;
+	}
+
+	return 0;
+}
+
+static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	int retval = dst_get_signal(state);
+	*strength = state->decode_strength;
+
+	return retval;
+}
+
+static int dst_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct dst_state *state = fe->demodulator_priv;
+
+	int retval = dst_get_signal(state);
+	*snr = state->decode_snr;
+
+	return retval;
+}
+
+static int dst_set_frontend(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	int retval = -EINVAL;
+	struct dst_state *state = fe->demodulator_priv;
+
+	if (p != NULL) {
+		retval = dst_set_freq(state, p->frequency);
+		if(retval != 0)
+			return retval;
+		dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency);
+
+		if (state->dst_type == DST_TYPE_IS_SAT) {
+			if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+				dst_set_inversion(state, p->inversion);
+			dst_set_fec(state, p->fec_inner);
+			dst_set_symbolrate(state, p->symbol_rate);
+			dst_set_polarization(state);
+			dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate);
+
+		} else if (state->dst_type == DST_TYPE_IS_TERR)
+			dst_set_bandwidth(state, p->bandwidth_hz);
+		else if (state->dst_type == DST_TYPE_IS_CABLE) {
+			dst_set_fec(state, p->fec_inner);
+			dst_set_symbolrate(state, p->symbol_rate);
+			dst_set_modulation(state, p->modulation);
+		}
+		retval = dst_write_tuna(fe);
+	}
+
+	return retval;
+}
+
+static int dst_tune_frontend(struct dvb_frontend* fe,
+			    bool re_tune,
+			    unsigned int mode_flags,
+			    unsigned int *delay,
+			    fe_status_t *status)
+{
+	struct dst_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	if (re_tune) {
+		dst_set_freq(state, p->frequency);
+		dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency);
+
+		if (state->dst_type == DST_TYPE_IS_SAT) {
+			if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+				dst_set_inversion(state, p->inversion);
+			dst_set_fec(state, p->fec_inner);
+			dst_set_symbolrate(state, p->symbol_rate);
+			dst_set_polarization(state);
+			dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate);
+
+		} else if (state->dst_type == DST_TYPE_IS_TERR)
+			dst_set_bandwidth(state, p->bandwidth_hz);
+		else if (state->dst_type == DST_TYPE_IS_CABLE) {
+			dst_set_fec(state, p->fec_inner);
+			dst_set_symbolrate(state, p->symbol_rate);
+			dst_set_modulation(state, p->modulation);
+		}
+		dst_write_tuna(fe);
+	}
+
+	if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
+		dst_read_status(fe, status);
+
+	*delay = HZ/10;
+	return 0;
+}
+
+static int dst_get_tuning_algo(struct dvb_frontend *fe)
+{
+	return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW;
+}
+
+static int dst_get_frontend(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct dst_state *state = fe->demodulator_priv;
+
+	p->frequency = state->decode_freq;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+			p->inversion = state->inversion;
+		p->symbol_rate = state->symbol_rate;
+		p->fec_inner = dst_get_fec(state);
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		p->bandwidth_hz = state->bandwidth;
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		p->symbol_rate = state->symbol_rate;
+		p->fec_inner = dst_get_fec(state);
+		p->modulation = dst_get_modulation(state);
+	}
+
+	return 0;
+}
+
+static void dst_release(struct dvb_frontend *fe)
+{
+	struct dst_state *state = fe->demodulator_priv;
+	if (state->dst_ca) {
+		dvb_unregister_device(state->dst_ca);
+#ifdef CONFIG_MEDIA_ATTACH
+		symbol_put(dst_ca_attach);
+#endif
+	}
+	kfree(state);
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops;
+static struct dvb_frontend_ops dst_dvbs_ops;
+static struct dvb_frontend_ops dst_dvbc_ops;
+static struct dvb_frontend_ops dst_atsc_ops;
+
+struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter)
+{
+	/* check if the ASIC is there */
+	if (dst_probe(state) < 0) {
+		kfree(state);
+		return NULL;
+	}
+	/* determine settings based on type */
+	/* create dvb_frontend */
+	switch (state->dst_type) {
+	case DST_TYPE_IS_TERR:
+		memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	case DST_TYPE_IS_CABLE:
+		memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	case DST_TYPE_IS_SAT:
+		memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	case DST_TYPE_IS_ATSC:
+		memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	default:
+		dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist.");
+		kfree(state);
+		return NULL;
+	}
+	state->frontend.demodulator_priv = state;
+
+	return state;				/*	Manu (DST is a card not a frontend)	*/
+}
+
+EXPORT_SYMBOL(dst_attach);
+
+static struct dvb_frontend_ops dst_dvbt_ops = {
+	.delsys = { SYS_DVBT },
+	.info = {
+		.name = "DST DVB-T",
+		.frequency_min = 137000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps = FE_CAN_FEC_AUTO			|
+			FE_CAN_QAM_AUTO			|
+			FE_CAN_QAM_16			|
+			FE_CAN_QAM_32			|
+			FE_CAN_QAM_64			|
+			FE_CAN_QAM_128			|
+			FE_CAN_QAM_256			|
+			FE_CAN_TRANSMISSION_MODE_AUTO	|
+			FE_CAN_GUARD_INTERVAL_AUTO
+	},
+
+	.release = dst_release,
+	.init = dst_init,
+	.tune = dst_tune_frontend,
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+	.get_frontend_algo = dst_get_tuning_algo,
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_dvbs_ops = {
+	.delsys = { SYS_DVBS },
+	.info = {
+		.name = "DST DVB-S",
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1000,	/* kHz for QPSK frontends */
+		.frequency_tolerance = 29500,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+	/*     . symbol_rate_tolerance	=	???,*/
+		.caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK
+	},
+
+	.release = dst_release,
+	.init = dst_init,
+	.tune = dst_tune_frontend,
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+	.get_frontend_algo = dst_get_tuning_algo,
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+	.diseqc_send_burst = dst_send_burst,
+	.diseqc_send_master_cmd = dst_set_diseqc,
+	.set_voltage = dst_set_voltage,
+	.set_tone = dst_set_tone,
+};
+
+static struct dvb_frontend_ops dst_dvbc_ops = {
+	.delsys = { SYS_DVBC_ANNEX_A },
+	.info = {
+		.name = "DST DVB-C",
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_FEC_AUTO |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_QAM_16	|
+			FE_CAN_QAM_32	|
+			FE_CAN_QAM_64	|
+			FE_CAN_QAM_128	|
+			FE_CAN_QAM_256
+	},
+
+	.release = dst_release,
+	.init = dst_init,
+	.tune = dst_tune_frontend,
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+	.get_frontend_algo = dst_get_tuning_algo,
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_atsc_ops = {
+	.delsys = { SYS_ATSC },
+	.info = {
+		.name = "DST ATSC",
+		.frequency_stepsize = 62500,
+		.frequency_min = 510000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+	},
+
+	.release = dst_release,
+	.init = dst_init,
+	.tune = dst_tune_frontend,
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+	.get_frontend_algo = dst_get_tuning_algo,
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+};
+
+MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver");
+MODULE_AUTHOR("Jamie Honan, Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
new file mode 100644
index 000000000000..ee3884fbc9ce
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_ca.c
@@ -0,0 +1,726 @@
+/*
+	CA-driver for TwinHan DST Frontend/Card
+
+	Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/slab.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/dvb/ca.h>
+#include "dvbdev.h"
+#include "dvb_frontend.h"
+#include "dst_ca.h"
+#include "dst_common.h"
+
+#define DST_CA_ERROR		0
+#define DST_CA_NOTICE		1
+#define DST_CA_INFO		2
+#define DST_CA_DEBUG		3
+
+#define dprintk(x, y, z, format, arg...) do {						\
+	if (z) {									\
+		if	((x > DST_CA_ERROR) && (x > y))					\
+			printk(KERN_ERR "%s: " format "\n", __func__ , ##arg);	\
+		else if	((x > DST_CA_NOTICE) && (x > y))				\
+			printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg);	\
+		else if ((x > DST_CA_INFO) && (x > y))					\
+			printk(KERN_INFO "%s: " format "\n", __func__ , ##arg);	\
+		else if ((x > DST_CA_DEBUG) && (x > y))					\
+			printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg);	\
+	} else {									\
+		if (x > y)								\
+			printk(format, ## arg);						\
+	}										\
+} while(0)
+
+
+static DEFINE_MUTEX(dst_ca_mutex);
+static unsigned int verbose = 5;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+/*	Need some more work	*/
+static int ca_set_slot_descr(void)
+{
+	/*	We could make this more graceful ?	*/
+	return -EOPNOTSUPP;
+}
+
+/*	Need some more work	*/
+static int ca_set_pid(void)
+{
+	/*	We could make this more graceful ?	*/
+	return -EOPNOTSUPP;
+}
+
+static void put_command_and_length(u8 *data, int command, int length)
+{
+	data[0] = (command >> 16) & 0xff;
+	data[1] = (command >> 8) & 0xff;
+	data[2] = command & 0xff;
+	data[3] = length;
+}
+
+static void put_checksum(u8 *check_string, int length)
+{
+	dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum.");
+	dprintk(verbose, DST_CA_DEBUG, 1, "  -> string length : 0x%02x", length);
+	check_string[length] = dst_check_sum (check_string, length);
+	dprintk(verbose, DST_CA_DEBUG, 1, "  -> checksum      : 0x%02x", check_string[length]);
+}
+
+static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
+{
+	u8 reply;
+
+	mutex_lock(&state->dst_mutex);
+	dst_comm_init(state);
+	msleep(65);
+
+	if (write_dst(state, data, len)) {
+		dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover");
+		dst_error_recovery(state);
+		goto error;
+	}
+	if ((dst_pio_disable(state)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed.");
+		goto error;
+	}
+	if (read_dst(state, &reply, GET_ACK) < 0) {
+		dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+		dst_error_recovery(state);
+		goto error;
+	}
+	if (read) {
+		if (! dst_wait_dst_ready(state, LONG_DELAY)) {
+			dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready");
+			goto error;
+		}
+		if (read_dst(state, ca_string, 128) < 0) {	/*	Try to make this dynamic	*/
+			dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+			dst_error_recovery(state);
+			goto error;
+		}
+	}
+	mutex_unlock(&state->dst_mutex);
+	return 0;
+
+error:
+	mutex_unlock(&state->dst_mutex);
+	return -EIO;
+}
+
+
+static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
+{
+	u8 dst_ca_comm_err = 0;
+
+	while (dst_ca_comm_err < RETRIES) {
+		dprintk(verbose, DST_CA_NOTICE, 1, " Put Command");
+		if (dst_ci_command(state, data, ca_string, len, read)) {	// If error
+			dst_error_recovery(state);
+			dst_ca_comm_err++; // work required here.
+		} else {
+			break;
+		}
+	}
+
+	if(dst_ca_comm_err == RETRIES)
+		return -1;
+
+	return 0;
+}
+
+
+
+static int ca_get_app_info(struct dst_state *state)
+{
+	int length, str_length;
+	static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
+
+	put_checksum(&command[0], command[0]);
+	if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+		return -1;
+	}
+	dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+	dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================");
+	dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]",
+		state->messages[7], (state->messages[8] << 8) | state->messages[9],
+		(state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12]));
+	dprintk(verbose, DST_CA_INFO, 1, " ==================================================================================================");
+
+	// Transform dst message to correct application_info message
+	length = state->messages[5];
+	str_length = length - 6;
+	if (str_length < 0) {
+		str_length = 0;
+		dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering.");
+	}
+
+	// First, the command and length fields
+	put_command_and_length(&state->messages[0], CA_APP_INFO, length);
+
+	// Copy application_type, application_manufacturer and manufacturer_code
+	memcpy(&state->messages[4], &state->messages[7], 5);
+
+	// Set string length and copy string
+	state->messages[9] = str_length;
+	memcpy(&state->messages[10], &state->messages[12], str_length);
+
+	return 0;
+}
+
+static int ca_get_ca_info(struct dst_state *state)
+{
+	int srcPtr, dstPtr, i, num_ids;
+	static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff};
+	const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7;
+
+	put_checksum(&slot_command[0], slot_command[0]);
+	if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+		return -1;
+	}
+	dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+	// Print raw data
+	dprintk(verbose, DST_CA_INFO, 0, " DST data = [");
+	for (i = 0; i < state->messages[0] + 1; i++) {
+		dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]);
+	}
+	dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+	// Set the command and length of the output
+	num_ids = state->messages[in_num_ids_pos];
+	if (num_ids >= 100) {
+		num_ids = 100;
+		dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering.");
+	}
+	put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2);
+
+	dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = [");
+	srcPtr = in_system_id_pos;
+	dstPtr = out_system_id_pos;
+	for(i = 0; i < num_ids; i++) {
+		dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]);
+		// Append to output
+		state->messages[dstPtr + 0] = state->messages[srcPtr + 0];
+		state->messages[dstPtr + 1] = state->messages[srcPtr + 1];
+		srcPtr += 2;
+		dstPtr += 2;
+	}
+	dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+	return 0;
+}
+
+static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg)
+{
+	int i;
+	u8 slot_cap[256];
+	static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
+
+	put_checksum(&slot_command[0], slot_command[0]);
+	if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+		return -1;
+	}
+	dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !");
+
+	/*	Will implement the rest soon		*/
+
+	dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]);
+	dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+	for (i = 0; i < slot_cap[0] + 1; i++)
+		dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]);
+	dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+	p_ca_caps->slot_num = 1;
+	p_ca_caps->slot_type = 1;
+	p_ca_caps->descr_num = slot_cap[7];
+	p_ca_caps->descr_type = 1;
+
+	if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*	Need some more work	*/
+static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+	return -EOPNOTSUPP;
+}
+
+
+static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg)
+{
+	int i;
+	static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+	u8 *slot_info = state->messages;
+
+	put_checksum(&slot_command[0], 7);
+	if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+		return -1;
+	}
+	dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+	/*	Will implement the rest soon		*/
+
+	dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]);
+	dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+	for (i = 0; i < 8; i++)
+		dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]);
+	dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+	if (slot_info[4] & 0x80) {
+		p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
+		p_ca_slot_info->num = 1;
+		p_ca_slot_info->type = CA_CI;
+	} else if (slot_info[4] & 0x40) {
+		p_ca_slot_info->flags = CA_CI_MODULE_READY;
+		p_ca_slot_info->num = 1;
+		p_ca_slot_info->type = CA_CI;
+	} else
+		p_ca_slot_info->flags = 0;
+
+	if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+
+static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+	u8 i = 0;
+	u32 command = 0;
+
+	if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg)))
+		return -EFAULT;
+
+	if (p_ca_message->msg) {
+		dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]",
+			3, p_ca_message->msg);
+
+		for (i = 0; i < 3; i++) {
+			command = command | p_ca_message->msg[i];
+			if (i < 2)
+				command = command << 8;
+		}
+		dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command);
+
+		switch (command) {
+		case CA_APP_INFO:
+			memcpy(p_ca_message->msg, state->messages, 128);
+			if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+				return -EFAULT;
+			break;
+		case CA_INFO:
+			memcpy(p_ca_message->msg, state->messages, 128);
+			if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+				return -EFAULT;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length)
+{
+	if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
+		hw_buffer->msg[2] = p_ca_message->msg[1];	/*	MSB	*/
+		hw_buffer->msg[3] = p_ca_message->msg[2];	/*	LSB	*/
+	} else {
+		if (length > 247) {
+			dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !");
+			return -1;
+		}
+		hw_buffer->msg[0] = (length & 0xff) + 7;
+		hw_buffer->msg[1] = 0x40;
+		hw_buffer->msg[2] = 0x03;
+		hw_buffer->msg[3] = 0x00;
+		hw_buffer->msg[4] = 0x03;
+		hw_buffer->msg[5] = length & 0xff;
+		hw_buffer->msg[6] = 0x00;
+
+		/*
+		 *	Need to compute length for EN50221 section 8.3.2, for the time being
+		 *	assuming 8.3.2 is not applicable
+		 */
+		memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length);
+	}
+
+	return 0;
+}
+
+static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply)
+{
+	if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) {
+		dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed.");
+		dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST.");
+		rdc_reset_state(state);
+		return -1;
+	}
+	dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success.");
+
+	return 0;
+}
+
+static u32 asn_1_decode(u8 *asn_1_array)
+{
+	u8 length_field = 0, word_count = 0, count = 0;
+	u32 length = 0;
+
+	length_field = asn_1_array[0];
+	dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field);
+	if (length_field < 0x80) {
+		length = length_field & 0x7f;
+		dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length);
+	} else {
+		word_count = length_field & 0x7f;
+		for (count = 0; count < word_count; count++) {
+			length = length  << 8;
+			length += asn_1_array[count + 1];
+			dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length);
+		}
+	}
+	return length;
+}
+
+static int debug_string(u8 *msg, u32 length, u32 offset)
+{
+	u32 i;
+
+	dprintk(verbose, DST_CA_DEBUG, 0, " String=[ ");
+	for (i = offset; i < length; i++)
+		dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]);
+	dprintk(verbose, DST_CA_DEBUG, 0, "]\n");
+
+	return 0;
+}
+
+
+static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
+{
+	u32 length = 0;
+	u8 tag_length = 8;
+
+	length = asn_1_decode(&p_ca_message->msg[3]);
+	dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length);
+	debug_string(&p_ca_message->msg[4], length, 0); /*	length is excluding tag & length	*/
+
+	memset(hw_buffer->msg, '\0', length);
+	handle_dst_tag(state, p_ca_message, hw_buffer, length);
+	put_checksum(hw_buffer->msg, hw_buffer->msg[0]);
+
+	debug_string(hw_buffer->msg, (length + tag_length), 0); /*	tags too	*/
+	write_to_8820(state, hw_buffer, (length + tag_length), reply);
+
+	return 0;
+}
+
+
+/*	Board supports CA PMT reply ?		*/
+static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
+{
+	int ca_pmt_reply_test = 0;
+
+	/*	Do test board			*/
+	/*	Not there yet but soon		*/
+
+	/*	CA PMT Reply capable		*/
+	if (ca_pmt_reply_test) {
+		if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+			return -1;
+		}
+
+	/*	Process CA PMT Reply		*/
+	/*	will implement soon		*/
+		dprintk(verbose, DST_CA_ERROR, 1, " Not there yet");
+	}
+	/*	CA PMT Reply not capable	*/
+	if (!ca_pmt_reply_test) {
+		if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+			return -1;
+		}
+		dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !");
+	/*	put a dummy message		*/
+
+	}
+	return 0;
+}
+
+static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+	int i = 0;
+
+	u32 command = 0;
+	struct ca_msg *hw_buffer;
+	int result = 0;
+
+	if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+		dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+		return -ENOMEM;
+	}
+	dprintk(verbose, DST_CA_DEBUG, 1, " ");
+
+	if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) {
+		result = -EFAULT;
+		goto free_mem_and_exit;
+	}
+
+
+	if (p_ca_message->msg) {
+		/*	EN50221 tag	*/
+		command = 0;
+
+		for (i = 0; i < 3; i++) {
+			command = command | p_ca_message->msg[i];
+			if (i < 2)
+				command = command << 8;
+		}
+		dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command);
+
+		switch (command) {
+		case CA_PMT:
+			dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT");
+			if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) {	// code simplification started
+				dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !");
+				result = -1;
+				goto free_mem_and_exit;
+			}
+			dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !");
+			break;
+		case CA_PMT_REPLY:
+			dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY");
+			/*      Have to handle the 2 basic types of cards here  */
+			if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
+				dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !");
+				result = -1;
+				goto free_mem_and_exit;
+			}
+			dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !");
+			break;
+		case CA_APP_INFO_ENQUIRY:		// only for debugging
+			dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information");
+
+			if ((ca_get_app_info(state)) < 0) {
+				dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !");
+				result = -1;
+				goto free_mem_and_exit;
+			}
+			dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !");
+			break;
+		case CA_INFO_ENQUIRY:
+			dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information");
+
+			if ((ca_get_ca_info(state)) < 0) {
+				dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !");
+				result = -1;
+				goto free_mem_and_exit;
+			}
+			dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !");
+			break;
+		}
+	}
+free_mem_and_exit:
+	kfree (hw_buffer);
+
+	return result;
+}
+
+static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg)
+{
+	struct dvb_device *dvbdev;
+	struct dst_state *state;
+	struct ca_slot_info *p_ca_slot_info;
+	struct ca_caps *p_ca_caps;
+	struct ca_msg *p_ca_message;
+	void __user *arg = (void __user *)ioctl_arg;
+	int result = 0;
+
+	mutex_lock(&dst_ca_mutex);
+	dvbdev = file->private_data;
+	state = (struct dst_state *)dvbdev->priv;
+	p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
+	p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
+	p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
+	if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) {
+		dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+		result = -ENOMEM;
+		goto free_mem_and_exit;
+	}
+
+	/*	We have now only the standard ioctl's, the driver is upposed to handle internals.	*/
+	switch (cmd) {
+	case CA_SEND_MSG:
+		dprintk(verbose, DST_CA_INFO, 1, " Sending message");
+		if ((ca_send_message(state, p_ca_message, arg)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		break;
+	case CA_GET_MSG:
+		dprintk(verbose, DST_CA_INFO, 1, " Getting message");
+		if ((ca_get_message(state, p_ca_message, arg)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !");
+		break;
+	case CA_RESET:
+		dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST");
+		dst_error_bailout(state);
+		msleep(4000);
+		break;
+	case CA_GET_SLOT_INFO:
+		dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info");
+		if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !");
+		break;
+	case CA_GET_CAP:
+		dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities");
+		if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !");
+		break;
+	case CA_GET_DESCR_INFO:
+		dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description");
+		if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !");
+		break;
+	case CA_SET_DESCR:
+		dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler");
+		if ((ca_set_slot_descr()) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !");
+		break;
+	case CA_SET_PID:
+		dprintk(verbose, DST_CA_INFO, 1, " Setting PID");
+		if ((ca_set_pid()) < 0) {
+			dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !");
+			result = -1;
+			goto free_mem_and_exit;
+		}
+		dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
+	default:
+		result = -EOPNOTSUPP;
+	};
+ free_mem_and_exit:
+	kfree (p_ca_message);
+	kfree (p_ca_slot_info);
+	kfree (p_ca_caps);
+
+	mutex_unlock(&dst_ca_mutex);
+	return result;
+}
+
+static int dst_ca_open(struct inode *inode, struct file *file)
+{
+	dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
+	try_module_get(THIS_MODULE);
+
+	return 0;
+}
+
+static int dst_ca_release(struct inode *inode, struct file *file)
+{
+	dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
+	module_put(THIS_MODULE);
+
+	return 0;
+}
+
+static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
+{
+	ssize_t bytes_read = 0;
+
+	dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
+
+	return bytes_read;
+}
+
+static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
+{
+	dprintk(verbose, DST_CA_DEBUG, 1, " Device write.");
+
+	return 0;
+}
+
+static const struct file_operations dst_ca_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = dst_ca_ioctl,
+	.open = dst_ca_open,
+	.release = dst_ca_release,
+	.read = dst_ca_read,
+	.write = dst_ca_write,
+	.llseek = noop_llseek,
+};
+
+static struct dvb_device dvbdev_ca = {
+	.priv = NULL,
+	.users = 1,
+	.readers = 1,
+	.writers = 1,
+	.fops = &dst_ca_fops
+};
+
+struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
+{
+	struct dvb_device *dvbdev;
+
+	dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
+	if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
+		dst->dst_ca = dvbdev;
+		return dst->dst_ca;
+	}
+
+	return NULL;
+}
+
+EXPORT_SYMBOL(dst_ca_attach);
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dst_ca.h b/drivers/media/pci/bt8xx/dst_ca.h
new file mode 100644
index 000000000000..59cd0ddd6d8e
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_ca.h
@@ -0,0 +1,58 @@
+/*
+	CA-driver for TwinHan DST Frontend/Card
+
+	Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 _DST_CA_H_
+#define _DST_CA_H_
+
+#define RETRIES			5
+
+
+#define	CA_APP_INFO_ENQUIRY	0x9f8020
+#define	CA_APP_INFO		0x9f8021
+#define	CA_ENTER_MENU		0x9f8022
+#define CA_INFO_ENQUIRY		0x9f8030
+#define	CA_INFO			0x9f8031
+#define CA_PMT			0x9f8032
+#define CA_PMT_REPLY		0x9f8033
+
+#define CA_CLOSE_MMI		0x9f8800
+#define CA_DISPLAY_CONTROL	0x9f8801
+#define CA_DISPLAY_REPLY	0x9f8802
+#define CA_TEXT_LAST		0x9f8803
+#define CA_TEXT_MORE		0x9f8804
+#define CA_KEYPAD_CONTROL	0x9f8805
+#define CA_KEYPRESS		0x9f8806
+
+#define CA_ENQUIRY		0x9f8807
+#define CA_ANSWER		0x9f8808
+#define CA_MENU_LAST		0x9f8809
+#define CA_MENU_MORE		0x9f880a
+#define CA_MENU_ANSWER		0x9f880b
+#define CA_LIST_LAST		0x9f880c
+#define CA_LIST_MORE		0x9f880d
+
+
+struct dst_ca_private {
+	struct dst_state *dst;
+	struct dvb_device *dvbdev;
+};
+
+
+#endif
diff --git a/drivers/media/pci/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h
new file mode 100644
index 000000000000..d70d98f1a571
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_common.h
@@ -0,0 +1,182 @@
+/*
+	Frontend-driver for TwinHan DST Frontend
+
+	Copyright (C) 2003 Jamie Honan
+	Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 DST_COMMON_H
+#define DST_COMMON_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "bt878.h"
+
+#include "dst_ca.h"
+
+
+#define NO_DELAY		0
+#define LONG_DELAY		1
+#define DEVICE_INIT		2
+
+#define DELAY			1
+
+#define DST_TYPE_IS_SAT		0
+#define DST_TYPE_IS_TERR	1
+#define DST_TYPE_IS_CABLE	2
+#define DST_TYPE_IS_ATSC	3
+
+#define DST_TYPE_HAS_TS188	1
+#define DST_TYPE_HAS_TS204	2
+#define DST_TYPE_HAS_SYMDIV	4
+#define DST_TYPE_HAS_FW_1	8
+#define DST_TYPE_HAS_FW_2	16
+#define DST_TYPE_HAS_FW_3	32
+#define DST_TYPE_HAS_FW_BUILD	64
+#define DST_TYPE_HAS_OBS_REGS	128
+#define DST_TYPE_HAS_INC_COUNT	256
+#define DST_TYPE_HAS_MULTI_FE	512
+#define DST_TYPE_HAS_NEWTUNE_2	1024
+#define DST_TYPE_HAS_DBOARD	2048
+#define DST_TYPE_HAS_VLF	4096
+
+/*	Card capability list	*/
+
+#define DST_TYPE_HAS_MAC	1
+#define DST_TYPE_HAS_DISEQC3	2
+#define DST_TYPE_HAS_DISEQC4	4
+#define DST_TYPE_HAS_DISEQC5	8
+#define DST_TYPE_HAS_MOTO	16
+#define DST_TYPE_HAS_CA		32
+#define	DST_TYPE_HAS_ANALOG	64	/*	Analog inputs	*/
+#define DST_TYPE_HAS_SESSION	128
+
+#define TUNER_TYPE_MULTI	1
+#define TUNER_TYPE_UNKNOWN	2
+/*	DVB-S		*/
+#define TUNER_TYPE_L64724	4
+#define TUNER_TYPE_STV0299	8
+#define TUNER_TYPE_MB86A15	16
+
+/*	DVB-T		*/
+#define TUNER_TYPE_TDA10046	32
+
+/*	ATSC		*/
+#define TUNER_TYPE_NXT200x	64
+
+
+#define RDC_8820_PIO_0_DISABLE	0
+#define RDC_8820_PIO_0_ENABLE	1
+#define RDC_8820_INT		2
+#define RDC_8820_RESET		4
+
+/*	DST Communication	*/
+#define GET_REPLY		1
+#define NO_REPLY		0
+
+#define GET_ACK			1
+#define FIXED_COMM		8
+
+#define ACK			0xff
+
+struct dst_state {
+
+	struct i2c_adapter* i2c;
+
+	struct bt878* bt;
+
+	/* configuration settings */
+	const struct dst_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* private ASIC data */
+	u8 tx_tuna[10];
+	u8 rx_tuna[10];
+	u8 rxbuffer[10];
+	u8 diseq_flags;
+	u8 dst_type;
+	u32 type_flags;
+	u32 frequency;		/* intermediate frequency in kHz for QPSK */
+	fe_spectral_inversion_t inversion;
+	u32 symbol_rate;	/* symbol rate in Symbols per second */
+	fe_code_rate_t fec;
+	fe_sec_voltage_t voltage;
+	fe_sec_tone_mode_t tone;
+	u32 decode_freq;
+	u8 decode_lock;
+	u16 decode_strength;
+	u16 decode_snr;
+	unsigned long cur_jiff;
+	u8 k22;
+	u32 bandwidth;
+	u32 dst_hw_cap;
+	u8 dst_fw_version;
+	fe_sec_mini_cmd_t minicmd;
+	fe_modulation_t modulation;
+	u8 messages[256];
+	u8 mac_address[8];
+	u8 fw_version[8];
+	u8 card_info[8];
+	u8 vendor[8];
+	u8 board_info[8];
+	u32 tuner_type;
+	char *tuner_name;
+	struct mutex dst_mutex;
+	u8 fw_name[8];
+	struct dvb_device *dst_ca;
+};
+
+struct tuner_types {
+	u32 tuner_type;
+	char *tuner_name;
+	char *board_name;
+	char *fw_name;
+};
+
+struct dst_types {
+	char *device_id;
+	int offset;
+	u8 dst_type;
+	u32 type_flags;
+	u32 dst_feature;
+	u32 tuner_type;
+};
+
+struct dst_config
+{
+	/* the ASIC i2c address */
+	u8 demod_address;
+};
+
+int rdc_reset_state(struct dst_state *state);
+
+int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
+int dst_pio_disable(struct dst_state *state);
+int dst_error_recovery(struct dst_state* state);
+int dst_error_bailout(struct dst_state *state);
+int dst_comm_init(struct dst_state* state);
+
+int write_dst(struct dst_state *state, u8 * data, u8 len);
+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);
+
+
+#endif // DST_COMMON_H
diff --git a/drivers/media/pci/bt8xx/dst_priv.h b/drivers/media/pci/bt8xx/dst_priv.h
new file mode 100644
index 000000000000..3974a4c6ebe7
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_priv.h
@@ -0,0 +1,35 @@
+/*
+ * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend
+ *
+ * Copyright (C) 2003 Jamie Honan
+ */
+
+struct dst_gpio_enable {
+	u32	mask;
+	u32	enable;
+};
+
+struct dst_gpio_output {
+	u32	mask;
+	u32	highvals;
+};
+
+struct dst_gpio_read {
+	unsigned long value;
+};
+
+union dst_gpio_packet {
+	struct dst_gpio_enable enb;
+	struct dst_gpio_output outp;
+	struct dst_gpio_read rd;
+	int    psize;
+};
+
+#define DST_IG_ENABLE	0
+#define DST_IG_WRITE	1
+#define DST_IG_READ	2
+#define DST_IG_TS       3
+
+struct bt878;
+
+int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c
new file mode 100644
index 000000000000..81fab9adc1ca
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c
@@ -0,0 +1,975 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.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.
+ *
+ */
+
+#define pr_fmt(fmt) "dvb_bt8xx: " fmt
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb-bt8xx.h"
+#include "bt878.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk( args... ) \
+	do { \
+		if (debug) printk(KERN_DEBUG args); \
+	} while (0)
+
+#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+
+static void dvb_bt8xx_task(unsigned long data)
+{
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data;
+
+	//printk("%d ", card->bt->finished_block);
+
+	while (card->bt->last_block != card->bt->finished_block) {
+		(card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
+			(&card->demux,
+			 &card->bt->buf_cpu[card->bt->last_block *
+					    card->bt->block_bytes],
+			 card->bt->block_bytes);
+		card->bt->last_block = (card->bt->last_block + 1) %
+					card->bt->block_count;
+	}
+}
+
+static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux*dvbdmx = dvbdmxfeed->demux;
+	struct dvb_bt8xx_card *card = dvbdmx->priv;
+	int rc;
+
+	dprintk("dvb_bt8xx: start_feed\n");
+
+	if (!dvbdmx->dmx.frontend)
+		return -EINVAL;
+
+	mutex_lock(&card->lock);
+	card->nfeeds++;
+	rc = card->nfeeds;
+	if (card->nfeeds == 1)
+		bt878_start(card->bt, card->gpio_mode,
+			    card->op_sync_orin, card->irq_err_ignore);
+	mutex_unlock(&card->lock);
+	return rc;
+}
+
+static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct dvb_bt8xx_card *card = dvbdmx->priv;
+
+	dprintk("dvb_bt8xx: stop_feed\n");
+
+	if (!dvbdmx->dmx.frontend)
+		return -EINVAL;
+
+	mutex_lock(&card->lock);
+	card->nfeeds--;
+	if (card->nfeeds == 0)
+		bt878_stop(card->bt);
+	mutex_unlock(&card->lock);
+
+	return 0;
+}
+
+static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev)
+{
+	if ((adev->subsystem_vendor == bdev->subsystem_vendor) &&
+		(adev->subsystem_device == bdev->subsystem_device) &&
+		(adev->bus->number == bdev->bus->number) &&
+		(PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn)))
+		return 1;
+	return 0;
+}
+
+static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev)
+{
+	unsigned int card_nr;
+
+	/* Hmm, n squared. Hope n is small */
+	for (card_nr = 0; card_nr < bt878_num; card_nr++)
+		if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev))
+			return &bt878[card_nr];
+	return NULL;
+}
+
+static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 };
+	static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+	mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u32 div;
+	unsigned char bs = 0;
+	unsigned char cp = 0;
+
+	if (buf_len < 5)
+		return -EINVAL;
+
+	div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (c->frequency < 542000000)
+		cp = 0xb4;
+	else if (c->frequency < 771000000)
+		cp = 0xbc;
+	else
+		cp = 0xf4;
+
+	if (c->frequency == 0)
+		bs = 0x03;
+	else if (c->frequency < 443250000)
+		bs = 0x02;
+	else
+		bs = 0x08;
+
+	pllbuf[0] = 0x60;
+	pllbuf[1] = div >> 8;
+	pllbuf[2] = div & 0xff;
+	pllbuf[3] = cp;
+	pllbuf[4] = bs;
+
+	return 5;
+}
+
+static struct mt352_config thomson_dtt7579_config = {
+	.demod_address = 0x0f,
+	.demod_init = thomson_dtt7579_demod_init,
+};
+
+static struct zl10353_config thomson_dtt7579_zl10353_config = {
+	.demod_address = 0x0f,
+};
+
+static int cx24108_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u32 freq = c->frequency;
+	int i, a, n, pump;
+	u32 band, pll;
+	u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
+		1576000,1718000,1856000,2036000,2150000};
+	u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
+		0x00102000,0x00104000,0x00108000,0x00110000,
+		0x00120000,0x00140000};
+
+	#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
+	dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq);
+
+	/* This is really the bit driving the tuner chip cx24108 */
+
+	if (freq<950000)
+		freq = 950000; /* kHz */
+	else if (freq>2150000)
+		freq = 2150000; /* satellite IF is 950..2150MHz */
+
+	/* decide which VCO to use for the input frequency */
+	for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++);
+	dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq);
+	band=bandsel[i];
+	/* the gain values must be set by SetSymbolrate */
+	/* compute the pll divider needed, from Conexant data sheet,
+	   resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
+	   depending on the divider bit. It is set to /4 on the 2 lowest
+	   bands  */
+	n=((i<=2?2:1)*freq*10L)/(XTAL/100);
+	a=n%32; n/=32; if(a==0) n--;
+	pump=(freq<(osci[i-1]+osci[i])/2);
+	pll=0xf8000000|
+	    ((pump?1:2)<<(14+11))|
+	    ((n&0x1ff)<<(5+11))|
+	    ((a&0x1f)<<11);
+	/* everything is shifted left 11 bits to left-align the bits in the
+	   32bit word. Output to the tuner goes MSB-aligned, after all */
+	dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a);
+	cx24110_pll_write(fe,band);
+	/* set vga and vca to their widest-band settings, as a precaution.
+	   SetSymbolrate might not be called to set this up */
+	cx24110_pll_write(fe,0x500c0000);
+	cx24110_pll_write(fe,0x83f1f800);
+	cx24110_pll_write(fe,pll);
+	//writereg(client,0x56,0x7f);
+
+	return 0;
+}
+
+static int pinnsat_tuner_init(struct dvb_frontend* fe)
+{
+	struct dvb_bt8xx_card *card = fe->dvb->priv;
+
+	bttv_gpio_enable(card->bttv_nr, 1, 1);  /* output */
+	bttv_write_gpio(card->bttv_nr, 1, 1);   /* relay on */
+
+	return 0;
+}
+
+static int pinnsat_tuner_sleep(struct dvb_frontend* fe)
+{
+	struct dvb_bt8xx_card *card = fe->dvb->priv;
+
+	bttv_write_gpio(card->bttv_nr, 1, 0);   /* relay off */
+
+	return 0;
+}
+
+static struct cx24110_config pctvsat_config = {
+	.demod_address = 0x55,
+};
+
+static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (36000000 + c->frequency + 83333) / 166666;
+	cfg = 0x88;
+
+	if (c->frequency < 175000000)
+		cpump = 2;
+	else if (c->frequency < 390000000)
+		cpump = 1;
+	else if (c->frequency < 470000000)
+		cpump = 2;
+	else if (c->frequency < 750000000)
+		cpump = 2;
+	else
+		cpump = 3;
+
+	if (c->frequency < 175000000)
+		band_select = 0x0e;
+	else if (c->frequency < 470000000)
+		band_select = 0x05;
+	else
+		band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = (cpump << 6) | band_select;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(card->i2c_adapter, &msg, 1);
+	return (div * 166666 - 36000000);
+}
+
+static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static struct sp887x_config microtune_mt7202dtf_config = {
+	.demod_address = 0x70,
+	.request_firmware = microtune_mt7202dtf_request_firmware,
+};
+
+static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+				       0x00, 0xFF, 0x00, 0x40, 0x40 };
+	static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
+	udelay(2000);
+	mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u32 div;
+	unsigned char bs = 0;
+	unsigned char cp = 0;
+
+	if (buf_len < 5) return -EINVAL;
+
+	div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (c->frequency < 150000000)
+		cp = 0xB4;
+	else if (c->frequency < 173000000)
+		cp = 0xBC;
+	else if (c->frequency < 250000000)
+		cp = 0xB4;
+	else if (c->frequency < 400000000)
+		cp = 0xBC;
+	else if (c->frequency < 420000000)
+		cp = 0xF4;
+	else if (c->frequency < 470000000)
+		cp = 0xFC;
+	else if (c->frequency < 600000000)
+		cp = 0xBC;
+	else if (c->frequency < 730000000)
+		cp = 0xF4;
+	else
+		cp = 0xFC;
+
+	if (c->frequency < 150000000)
+		bs = 0x01;
+	else if (c->frequency < 173000000)
+		bs = 0x01;
+	else if (c->frequency < 250000000)
+		bs = 0x02;
+	else if (c->frequency < 400000000)
+		bs = 0x02;
+	else if (c->frequency < 420000000)
+		bs = 0x02;
+	else if (c->frequency < 470000000)
+		bs = 0x02;
+	else if (c->frequency < 600000000)
+		bs = 0x08;
+	else if (c->frequency < 730000000)
+		bs = 0x08;
+	else
+		bs = 0x08;
+
+	pllbuf[0] = 0x61;
+	pllbuf[1] = div >> 8;
+	pllbuf[2] = div & 0xff;
+	pllbuf[3] = cp;
+	pllbuf[4] = bs;
+
+	return 5;
+}
+
+static struct mt352_config advbt771_samsung_tdtc9251dh0_config = {
+	.demod_address = 0x0f,
+	.demod_init = advbt771_samsung_tdtc9251dh0_demod_init,
+};
+
+static struct dst_config dst_config = {
+	.demod_address = 0x55,
+};
+
+static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static void or51211_setmode(struct dvb_frontend * fe, int mode)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+	bttv_write_gpio(bt->bttv_nr, 0x0002, mode);   /* Reset */
+	msleep(20);
+}
+
+static void or51211_reset(struct dvb_frontend * fe)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+
+	/* RESET DEVICE
+	 * reset is controlled by GPIO-0
+	 * when set to 0 causes reset and when to 1 for normal op
+	 * must remain reset for 128 clock cycles on a 50Mhz clock
+	 * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4
+	 * We assume that the reset has be held low long enough or we
+	 * have been reset by a power on.  When the driver is unloaded
+	 * reset set to 0 so if reloaded we have been reset.
+	 */
+	/* reset & PRM1,2&4 are outputs */
+	int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F);
+	if (ret != 0)
+		printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret);
+	bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000);   /* Reset */
+	msleep(20);
+	/* Now set for normal operation */
+	bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001);
+	/* wait for operation to begin */
+	msleep(500);
+}
+
+static void or51211_sleep(struct dvb_frontend * fe)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+	bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000);
+}
+
+static struct or51211_config or51211_config = {
+	.demod_address = 0x15,
+	.request_firmware = or51211_request_firmware,
+	.setmode = or51211_setmode,
+	.reset = or51211_reset,
+	.sleep = or51211_sleep,
+};
+
+static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
+
+	div = (c->frequency + 36166667) / 166667;
+
+	buf[0] = (div >> 8) & 0x7F;
+	buf[1] = div & 0xFF;
+	buf[2] = 0x85;
+	if ((c->frequency >= 47000000) && (c->frequency < 153000000))
+		buf[3] = 0x01;
+	else if ((c->frequency >= 153000000) && (c->frequency < 430000000))
+		buf[3] = 0x02;
+	else if ((c->frequency >= 430000000) && (c->frequency < 824000000))
+		buf[3] = 0x0C;
+	else if ((c->frequency >= 824000000) && (c->frequency < 863000000))
+		buf[3] = 0x8C;
+	else
+		return -EINVAL;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(card->i2c_adapter, &msg, 1);
+	return 0;
+}
+
+static struct nxt6000_config vp3021_alps_tded4_config = {
+	.demod_address = 0x0a,
+	.clock_inversion = 1,
+};
+
+static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+	mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe,  u8 *pllbuf, int buf_len)
+{
+	u32 div;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	if (buf_len < 5)
+		return -EINVAL;
+
+	div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	pllbuf[0] = 0x61;
+	pllbuf[1] = (div >> 8) & 0x7F;
+	pllbuf[2] = div & 0xFF;
+	pllbuf[3] = 0x85;
+
+	dprintk("frequency %u, div %u\n", c->frequency, div);
+
+	if (c->frequency < 470000000)
+		pllbuf[4] = 0x02;
+	else if (c->frequency > 823000000)
+		pllbuf[4] = 0x88;
+	else
+		pllbuf[4] = 0x08;
+
+	if (c->bandwidth_hz == 8000000)
+		pllbuf[4] |= 0x04;
+
+	return 5;
+}
+
+static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt)
+{
+	/*
+	 * Reset the frontend, must be called before trying
+	 * to initialise the MT352 or mt352_attach
+	 * will fail. Same goes for the nxt6000 frontend.
+	 *
+	 */
+
+	int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08);
+	if (ret != 0)
+		printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret);
+
+	/* Pulse the reset line */
+	bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
+	bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low  */
+	msleep(100);
+
+	bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
+}
+
+static struct mt352_config digitv_alps_tded4_config = {
+	.demod_address = 0x0a,
+	.demod_init = digitv_alps_tded4_demod_init,
+};
+
+static struct lgdt330x_config tdvs_tua6034_config = {
+	.demod_address    = 0x0e,
+	.demod_chip       = LGDT3303,
+	.serial_mpeg      = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+};
+
+static void lgdt330x_reset(struct dvb_bt8xx_card *bt)
+{
+	/* Set pin 27 of the lgdt3303 chip high to reset the frontend */
+
+	/* Pulse the reset line */
+	bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
+	bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low  */
+	msleep(100);
+
+	bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
+	msleep(100);
+}
+
+static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
+{
+	struct dst_state* state = NULL;
+
+	switch(type) {
+	case BTTV_BOARD_DVICO_DVBT_LITE:
+		card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter);
+
+		if (card->fe == NULL)
+			card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config,
+						  card->i2c_adapter);
+
+		if (card->fe != NULL) {
+			card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs;
+			card->fe->ops.info.frequency_min = 174000000;
+			card->fe->ops.info.frequency_max = 862000000;
+		}
+		break;
+
+	case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
+		lgdt330x_reset(card);
+		card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			dvb_attach(simple_tuner_attach, card->fe,
+				   card->i2c_adapter, 0x61,
+				   TUNER_LG_TDVS_H06XF);
+			dprintk ("dvb_bt8xx: lgdt330x detected\n");
+		}
+		break;
+
+	case BTTV_BOARD_NEBULA_DIGITV:
+		/*
+		 * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK);
+		 * this would be a cleaner solution than trying each frontend in turn.
+		 */
+
+		/* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */
+		digitv_alps_tded4_reset(card);
+		card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params;
+			dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n");
+			break;
+		}
+
+		/* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */
+		digitv_alps_tded4_reset(card);
+		card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter);
+
+		if (card->fe != NULL) {
+			card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs;
+			dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n");
+		}
+		break;
+
+	case BTTV_BOARD_AVDVBT_761:
+		card->fe = dvb_attach(sp887x_attach, &microtune_mt7202dtf_config, card->i2c_adapter);
+		if (card->fe) {
+			card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params;
+		}
+		break;
+
+	case BTTV_BOARD_AVDVBT_771:
+		card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs;
+			card->fe->ops.info.frequency_min = 174000000;
+			card->fe->ops.info.frequency_max = 862000000;
+		}
+		break;
+
+	case BTTV_BOARD_TWINHAN_DST:
+		/*	DST is not a frontend driver !!!		*/
+		state = kmalloc(sizeof (struct dst_state), GFP_KERNEL);
+		if (!state) {
+			pr_err("No memory\n");
+			break;
+		}
+		/*	Setup the Card					*/
+		state->config = &dst_config;
+		state->i2c = card->i2c_adapter;
+		state->bt = card->bt;
+		state->dst_ca = NULL;
+		/*	DST is not a frontend, attaching the ASIC	*/
+		if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) {
+			pr_err("%s: Could not find a Twinhan DST\n", __func__);
+			break;
+		}
+		/*	Attach other DST peripherals if any		*/
+		/*	Conditional Access device			*/
+		card->fe = &state->frontend;
+		if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+			dvb_attach(dst_ca_attach, state, &card->dvb_adapter);
+		break;
+
+	case BTTV_BOARD_PINNACLESAT:
+		card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter);
+		if (card->fe) {
+			card->fe->ops.tuner_ops.init = pinnsat_tuner_init;
+			card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep;
+			card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params;
+		}
+		break;
+
+	case BTTV_BOARD_PC_HDTV:
+		card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter);
+		if (card->fe != NULL)
+			dvb_attach(simple_tuner_attach, card->fe,
+				   card->i2c_adapter, 0x61,
+				   TUNER_PHILIPS_FCV1236D);
+		break;
+	}
+
+	if (card->fe == NULL)
+		pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       card->bt->dev->vendor,
+		       card->bt->dev->device,
+		       card->bt->dev->subsystem_vendor,
+		       card->bt->dev->subsystem_device);
+	else
+		if (dvb_register_frontend(&card->dvb_adapter, card->fe)) {
+			pr_err("Frontend registration failed!\n");
+			dvb_frontend_detach(card->fe);
+			card->fe = NULL;
+		}
+}
+
+static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
+{
+	int result;
+
+	result = dvb_register_adapter(&card->dvb_adapter, card->card_name,
+				      THIS_MODULE, &card->bt->dev->dev,
+				      adapter_nr);
+	if (result < 0) {
+		pr_err("dvb_register_adapter failed (errno = %d)\n", result);
+		return result;
+	}
+	card->dvb_adapter.priv = card;
+
+	card->bt->adapter = card->i2c_adapter;
+
+	memset(&card->demux, 0, sizeof(struct dvb_demux));
+
+	card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING;
+
+	card->demux.priv = card;
+	card->demux.filternum = 256;
+	card->demux.feednum = 256;
+	card->demux.start_feed = dvb_bt8xx_start_feed;
+	card->demux.stop_feed = dvb_bt8xx_stop_feed;
+	card->demux.write_to_decoder = NULL;
+
+	result = dvb_dmx_init(&card->demux);
+	if (result < 0) {
+		pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+		goto err_unregister_adaptor;
+	}
+
+	card->dmxdev.filternum = 256;
+	card->dmxdev.demux = &card->demux.dmx;
+	card->dmxdev.capabilities = 0;
+
+	result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter);
+	if (result < 0) {
+		pr_err("dvb_dmxdev_init failed (errno = %d)\n", result);
+		goto err_dmx_release;
+	}
+
+	card->fe_hw.source = DMX_FRONTEND_0;
+
+	result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw);
+	if (result < 0) {
+		pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+		goto err_dmxdev_release;
+	}
+
+	card->fe_mem.source = DMX_MEMORY_FE;
+
+	result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem);
+	if (result < 0) {
+		pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+		goto err_remove_hw_frontend;
+	}
+
+	result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw);
+	if (result < 0) {
+		pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+		goto err_remove_mem_frontend;
+	}
+
+	result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
+	if (result < 0) {
+		pr_err("dvb_net_init failed (errno = %d)\n", result);
+		goto err_disconnect_frontend;
+	}
+
+	tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
+
+	frontend_init(card, type);
+
+	return 0;
+
+err_disconnect_frontend:
+	card->demux.dmx.disconnect_frontend(&card->demux.dmx);
+err_remove_mem_frontend:
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+err_remove_hw_frontend:
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+err_dmxdev_release:
+	dvb_dmxdev_release(&card->dmxdev);
+err_dmx_release:
+	dvb_dmx_release(&card->demux);
+err_unregister_adaptor:
+	dvb_unregister_adapter(&card->dvb_adapter);
+	return result;
+}
+
+static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
+{
+	struct dvb_bt8xx_card *card;
+	struct pci_dev* bttv_pci_dev;
+	int ret;
+
+	if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL)))
+		return -ENOMEM;
+
+	mutex_init(&card->lock);
+	card->bttv_nr = sub->core->nr;
+	strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name));
+	card->i2c_adapter = &sub->core->i2c_adap;
+
+	switch(sub->core->type) {
+	case BTTV_BOARD_PINNACLESAT:
+		card->gpio_mode = 0x0400c060;
+		/* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
+			      BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		break;
+
+	case BTTV_BOARD_DVICO_DVBT_LITE:
+		card->gpio_mode = 0x0400C060;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		/* 26, 15, 14, 6, 5
+		 * A_PWRDN  DA_DPM DA_SBR DA_IOM_DA
+		 * DA_APP(parallel) */
+		break;
+
+	case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
+		card->gpio_mode = 0x0400c060;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		break;
+
+	case BTTV_BOARD_NEBULA_DIGITV:
+	case BTTV_BOARD_AVDVBT_761:
+		card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5);
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		/* A_PWRDN DA_SBR DA_APP (high speed serial) */
+		break;
+
+	case BTTV_BOARD_AVDVBT_771: //case 0x07711461:
+		card->gpio_mode = 0x0400402B;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		/* A_PWRDN DA_SBR  DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/
+		break;
+
+	case BTTV_BOARD_TWINHAN_DST:
+		card->gpio_mode = 0x2204f2c;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR |
+				       BT878_APPERR | BT878_AFBUS;
+		/* 25,21,14,11,10,9,8,3,2 then
+		 * 0x33 = 5,4,1,0
+		 * A_SEL=SML, DA_MLB, DA_SBR,
+		 * DA_SDR=f, fifo trigger = 32 DWORDS
+		 * IOM = 0 == audio A/D
+		 * DPM = 0 == digital audio mode
+		 * == async data parallel port
+		 * then 0x33 (13 is set by start_capture)
+		 * DA_APP = async data parallel port,
+		 * ACAP_EN = 1,
+		 * RISC+FIFO ENABLE */
+		break;
+
+	case BTTV_BOARD_PC_HDTV:
+		card->gpio_mode = 0x0100EC7B;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+		break;
+
+	default:
+		pr_err("Unknown bttv card type: %d\n", sub->core->type);
+		kfree(card);
+		return -ENODEV;
+	}
+
+	dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name);
+
+	if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
+		pr_err("no pci device for card %d\n", card->bttv_nr);
+		kfree(card);
+		return -ENODEV;
+	}
+
+	if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
+		pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr);
+		pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n");
+
+		kfree(card);
+		return -ENODEV;
+	}
+
+	mutex_init(&card->bt->gpio_lock);
+	card->bt->bttv_nr = sub->core->nr;
+
+	if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) {
+		kfree(card);
+		return ret;
+	}
+
+	dev_set_drvdata(&sub->dev, card);
+	return 0;
+}
+
+static void dvb_bt8xx_remove(struct bttv_sub_device *sub)
+{
+	struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev);
+
+	dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr);
+
+	bt878_stop(card->bt);
+	tasklet_kill(&card->bt->tasklet);
+	dvb_net_release(&card->dvbnet);
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+	dvb_dmxdev_release(&card->dmxdev);
+	dvb_dmx_release(&card->demux);
+	if (card->fe) {
+		dvb_unregister_frontend(card->fe);
+		dvb_frontend_detach(card->fe);
+	}
+	dvb_unregister_adapter(&card->dvb_adapter);
+
+	kfree(card);
+}
+
+static struct bttv_sub_driver driver = {
+	.drv = {
+		.name		= "dvb-bt8xx",
+	},
+	.probe		= dvb_bt8xx_probe,
+	.remove		= dvb_bt8xx_remove,
+	/* FIXME:
+	 * .shutdown	= dvb_bt8xx_shutdown,
+	 * .suspend	= dvb_bt8xx_suspend,
+	 * .resume	= dvb_bt8xx_resume,
+	 */
+};
+
+static int __init dvb_bt8xx_init(void)
+{
+	return bttv_sub_register(&driver, "dvb");
+}
+
+static void __exit dvb_bt8xx_exit(void)
+{
+	bttv_sub_unregister(&driver);
+}
+
+module_init(dvb_bt8xx_init);
+module_exit(dvb_bt8xx_exit);
+
+MODULE_DESCRIPTION("Bt8xx based DVB adapter driver");
+MODULE_AUTHOR("Florian Schirmer <jolt@tuxbox.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.h b/drivers/media/pci/bt8xx/dvb-bt8xx.h
new file mode 100644
index 000000000000..4499ed2ac0ed
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.h
@@ -0,0 +1,63 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
+ * Copyright (C) 1999-2001 Ralph  Metzler & Marcus Metzler for convergence integrated media GmbH
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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 DVB_BT8XX_H
+#define DVB_BT8XX_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include "dvbdev.h"
+#include "dvb_net.h"
+#include "bttv.h"
+#include "mt352.h"
+#include "sp887x.h"
+#include "dst_common.h"
+#include "nxt6000.h"
+#include "cx24110.h"
+#include "or51211.h"
+#include "lgdt330x.h"
+#include "zl10353.h"
+#include "tuner-simple.h"
+
+struct dvb_bt8xx_card {
+	struct mutex lock;
+	int nfeeds;
+	char card_name[32];
+	struct dvb_adapter dvb_adapter;
+	struct bt878 *bt;
+	unsigned int bttv_nr;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend fe_hw;
+	struct dmx_frontend fe_mem;
+	u32 gpio_mode;
+	u32 op_sync_orin;
+	u32 irq_err_ignore;
+	struct i2c_adapter *i2c_adapter;
+	struct dvb_net dvbnet;
+
+	struct dvb_frontend* fe;
+};
+
+#endif /* DVB_BT8XX_H */
diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig
new file mode 100644
index 000000000000..c675b83c43a9
--- /dev/null
+++ b/drivers/media/pci/cx18/Kconfig
@@ -0,0 +1,35 @@
+config VIDEO_CX18
+	tristate "Conexant cx23418 MPEG encoder support"
+	depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C
+	select I2C_ALGOBIT
+	select VIDEOBUF_VMALLOC
+	depends on RC_CORE
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_CX2341X
+	select VIDEO_CS5345
+	select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for Conexant cx23418 based
+	  PCI combo video recorder devices.
+
+	  This is used in devices such as the Hauppauge HVR-1600
+	  cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx18.
+
+config VIDEO_CX18_ALSA
+	tristate "Conexant 23418 DMA audio support"
+	depends on VIDEO_CX18 && SND
+	select SND_PCM
+	---help---
+	  This is a video4linux driver for direct (DMA) audio on
+	  Conexant 23418 based TV cards using ALSA.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx18-alsa.
diff --git a/drivers/media/pci/cx18/Makefile b/drivers/media/pci/cx18/Makefile
new file mode 100644
index 000000000000..d3ff1545c2c5
--- /dev/null
+++ b/drivers/media/pci/cx18/Makefile
@@ -0,0 +1,13 @@
+cx18-objs    := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
+	cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
+	cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
+	cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
+	cx18-dvb.o cx18-io.o
+cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
+
+obj-$(CONFIG_VIDEO_CX18) += cx18.o
+obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c
new file mode 100644
index 000000000000..6d2a98246b6d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa-main.c
@@ -0,0 +1,295 @@
+/*
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
+ *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ *  Portions of this work were sponsored by ONELAN Limited.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-alsa.h"
+#include "cx18-alsa-mixer.h"
+#include "cx18-alsa-pcm.h"
+
+int cx18_alsa_debug;
+
+#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \
+	do { \
+		if (cx18_alsa_debug & 2) \
+			printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \
+	} while (0);
+
+module_param_named(debug, cx18_alsa_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: 0\n"
+		 "\t\t\t  1/0x0001: warning\n"
+		 "\t\t\t  2/0x0002: info\n");
+
+MODULE_AUTHOR("Andy Walls");
+MODULE_DESCRIPTION("CX23418 ALSA Interface");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+static inline
+struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)
+{
+	return to_cx18(v4l2_dev)->alsa;
+}
+
+static inline
+struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)
+{
+	return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);
+}
+
+static void snd_cx18_card_free(struct snd_cx18_card *cxsc)
+{
+	if (cxsc == NULL)
+		return;
+
+	if (cxsc->v4l2_dev != NULL)
+		to_cx18(cxsc->v4l2_dev)->alsa = NULL;
+
+	/* FIXME - take any other stopping actions needed */
+
+	kfree(cxsc);
+}
+
+static void snd_cx18_card_private_free(struct snd_card *sc)
+{
+	if (sc == NULL)
+		return;
+	snd_cx18_card_free(sc->private_data);
+	sc->private_data = NULL;
+	sc->private_free = NULL;
+}
+
+static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,
+				       struct snd_card *sc,
+				       struct snd_cx18_card **cxsc)
+{
+	*cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);
+	if (*cxsc == NULL)
+		return -ENOMEM;
+
+	(*cxsc)->v4l2_dev = v4l2_dev;
+	(*cxsc)->sc = sc;
+
+	sc->private_data = *cxsc;
+	sc->private_free = snd_cx18_card_private_free;
+
+	return 0;
+}
+
+static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)
+{
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+	struct snd_card *sc = cxsc->sc;
+
+	/* sc->driver is used by alsa-lib's configurator: simple, unique */
+	strlcpy(sc->driver, "CX23418", sizeof(sc->driver));
+
+	/* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */
+	snprintf(sc->shortname,  sizeof(sc->shortname), "CX18-%d",
+		 cx->instance);
+
+	/* sc->longname is read from /proc/asound/cards */
+	snprintf(sc->longname, sizeof(sc->longname),
+		 "CX23418 #%d %s TV/FM Radio/Line-In Capture",
+		 cx->instance, cx->card_name);
+
+	return 0;
+}
+
+static int snd_cx18_init(struct v4l2_device *v4l2_dev)
+{
+	struct cx18 *cx = to_cx18(v4l2_dev);
+	struct snd_card *sc = NULL;
+	struct snd_cx18_card *cxsc;
+	int ret;
+
+	/* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
+
+	/* (1) Check and increment the device index */
+	/* This is a no-op for us.  We'll use the cx->instance */
+
+	/* (2) Create a card instance */
+	ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
+			      SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
+			      THIS_MODULE, 0, &sc);
+	if (ret) {
+		CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit;
+	}
+
+	/* (3) Create a main component */
+	ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);
+	if (ret) {
+		CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+
+	/* (4) Set the driver ID and name strings */
+	snd_cx18_card_set_names(cxsc);
+
+
+	ret = snd_cx18_pcm_create(cxsc);
+	if (ret) {
+		CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+	/* FIXME - proc files */
+
+	/* (7) Set the driver data and return 0 */
+	/* We do this out of normal order for PCI drivers to avoid races */
+	cx->alsa = cxsc;
+
+	/* (6) Register the card instance */
+	ret = snd_card_register(sc);
+	if (ret) {
+		cx->alsa = NULL;
+		CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+
+	return 0;
+
+err_exit_free:
+	if (sc != NULL)
+		snd_card_free(sc);
+	kfree(cxsc);
+err_exit:
+	return ret;
+}
+
+int cx18_alsa_load(struct cx18 *cx)
+{
+	struct v4l2_device *v4l2_dev = &cx->v4l2_dev;
+	struct cx18_stream *s;
+
+	if (v4l2_dev == NULL) {
+		printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+		       __func__);
+		return 0;
+	}
+
+	cx = to_cx18(v4l2_dev);
+	if (cx == NULL) {
+		printk(KERN_ERR "cx18-alsa cx is NULL\n");
+		return 0;
+	}
+
+	s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+	if (s->video_dev == NULL) {
+		CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
+				     "skipping\n", __func__);
+		return 0;
+	}
+
+	if (cx->alsa != NULL) {
+		CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",
+			      __func__);
+		return 0;
+	}
+
+	if (snd_cx18_init(v4l2_dev)) {
+		CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",
+			      __func__);
+	} else {
+		CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "
+				     "\n", __func__);
+	}
+	return 0;
+}
+
+static int __init cx18_alsa_init(void)
+{
+	printk(KERN_INFO "cx18-alsa: module loading...\n");
+	cx18_ext_init = &cx18_alsa_load;
+	return 0;
+}
+
+static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)
+{
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+
+	/* FIXME - pointer checks & shutdown cxsc */
+
+	snd_card_free(cxsc->sc);
+	cx->alsa = NULL;
+}
+
+static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+	struct snd_cx18_card *cxsc;
+
+	if (v4l2_dev == NULL) {
+		printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+		       __func__);
+		return 0;
+	}
+
+	cxsc = to_snd_cx18_card(v4l2_dev);
+	if (cxsc == NULL) {
+		CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",
+			       __func__);
+		return 0;
+	}
+
+	snd_cx18_exit(cxsc);
+	return 0;
+}
+
+static void __exit cx18_alsa_exit(void)
+{
+	struct device_driver *drv;
+	int ret;
+
+	printk(KERN_INFO "cx18-alsa: module unloading...\n");
+
+	drv = driver_find("cx18", &pci_bus_type);
+	ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
+	(void)ret;	/* suppress compiler warning */
+
+	cx18_ext_init = NULL;
+	printk(KERN_INFO "cx18-alsa: module unload complete\n");
+}
+
+module_init(cx18_alsa_init);
+module_exit(cx18_alsa_exit);
diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c
new file mode 100644
index 000000000000..341bddc00b77
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa-mixer.c
@@ -0,0 +1,175 @@
+/*
+ *  ALSA mixer controls for the
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "cx18-alsa.h"
+#include "cx18-driver.h"
+
+/*
+ * Note the cx18-av-core volume scale is funny, due to the alignment of the
+ * scale with another chip's range:
+ *
+ * v4l2_control value	/512	indicated dB	actual dB	reg 0x8d4
+ * 0x0000 - 0x01ff	  0	-119		-96		228
+ * 0x0200 - 0x02ff	  1	-118		-96		228
+ * ...
+ * 0x2c00 - 0x2dff	 22	 -97		-96		228
+ * 0x2e00 - 0x2fff	 23	 -96		-96		228
+ * 0x3000 - 0x31ff	 24	 -95		-95		226
+ * ...
+ * 0xee00 - 0xefff	119	   0		  0		 36
+ * ...
+ * 0xfe00 - 0xffff	127	  +8		 +8		 20
+ */
+static inline int dB_to_cx18_av_vol(int dB)
+{
+	if (dB < -96)
+		dB = -96;
+	else if (dB > 8)
+		dB = 8;
+	return (dB + 119) << 9;
+}
+
+static inline int cx18_av_vol_to_dB(int v)
+{
+	if (v < (23 << 9))
+		v = (23 << 9);
+	else if (v > (127 << 9))
+		v = (127 << 9);
+	return (v >> 9) - 119;
+}
+
+static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	/* We're already translating values, just keep this control in dB */
+	uinfo->value.integer.min  = -96;
+	uinfo->value.integer.max  =   8;
+	uinfo->value.integer.step =   1;
+	return 0;
+}
+
+static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+	struct v4l2_control vctrl;
+	int ret;
+
+	vctrl.id = V4L2_CID_AUDIO_VOLUME;
+	vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+	snd_cx18_lock(cxsc);
+	ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+	snd_cx18_unlock(cxsc);
+
+	if (!ret)
+		uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
+	return ret;
+}
+
+static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+	struct v4l2_control vctrl;
+	int ret;
+
+	vctrl.id = V4L2_CID_AUDIO_VOLUME;
+	vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+	snd_cx18_lock(cxsc);
+
+	/* Fetch current state */
+	ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+
+	if (ret ||
+	    (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
+
+		/* Set, if needed */
+		vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+		ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
+		if (!ret)
+			ret = 1; /* Indicate control was changed w/o error */
+	}
+	snd_cx18_unlock(cxsc);
+
+	return ret;
+}
+
+
+/* This is a bit of overkill, the slider is already in dB internally */
+static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
+
+static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog TV Capture Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	.info = snd_cx18_mixer_tv_volume_info,
+	.get = snd_cx18_mixer_tv_volume_get,
+	.put = snd_cx18_mixer_tv_volume_put,
+	.tlv.p = snd_cx18_mixer_tv_vol_db_scale
+};
+
+/* FIXME - add mute switch and balance, bass, treble sliders:
+	V4L2_CID_AUDIO_MUTE
+
+	V4L2_CID_AUDIO_BALANCE
+
+	V4L2_CID_AUDIO_BASS
+	V4L2_CID_AUDIO_TREBLE
+*/
+
+/* FIXME - add stereo, lang1, lang2, mono menu */
+/* FIXME - add CS5345 I2S volume for HVR-1600 */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
+{
+	struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+	struct snd_card *sc = cxsc->sc;
+	int ret;
+
+	strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
+
+	ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc));
+	if (ret) {
+		CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
+				__func__, snd_cx18_mixer_tv_vol.name, ret);
+	}
+	return ret;
+}
diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.h b/drivers/media/pci/cx18/cx18-alsa-mixer.h
new file mode 100644
index 000000000000..ec9238793f6f
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa-mixer.h
@@ -0,0 +1,23 @@
+/*
+ *  ALSA mixer controls for the
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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
+ */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc);
diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c
new file mode 100644
index 000000000000..7a5b84a86bb3
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c
@@ -0,0 +1,356 @@
+/*
+ *  ALSA PCM device for the
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
+ *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ *  Portions of this work were sponsored by ONELAN Limited.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "cx18-driver.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-fileops.h"
+#include "cx18-alsa.h"
+
+static unsigned int pcm_debug;
+module_param(pcm_debug, int, 0644);
+MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
+
+#define dprintk(fmt, arg...) do {					\
+	    if (pcm_debug)						\
+		printk(KERN_INFO "cx18-alsa-pcm %s: " fmt,		\
+				  __func__, ##arg); 			\
+	} while (0)
+
+static struct snd_pcm_hardware snd_cx18_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_48000,
+
+	.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, */
+};
+
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
+				 size_t num_bytes)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int oldptr;
+	unsigned int stride;
+	int period_elapsed = 0;
+	int length;
+
+	dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc,
+		pcm_data, num_bytes);
+
+	substream = cxsc->capture_pcm_substream;
+	if (substream == NULL) {
+		dprintk("substream was NULL\n");
+		return;
+	}
+
+	runtime = substream->runtime;
+	if (runtime == NULL) {
+		dprintk("runtime was NULL\n");
+		return;
+	}
+
+	stride = runtime->frame_bits >> 3;
+	if (stride == 0) {
+		dprintk("stride is zero\n");
+		return;
+	}
+
+	length = num_bytes / stride;
+	if (length == 0) {
+		dprintk("%s: length was zero\n", __func__);
+		return;
+	}
+
+	if (runtime->dma_area == NULL) {
+		dprintk("dma area was NULL - ignoring\n");
+		return;
+	}
+
+	oldptr = cxsc->hwptr_done_capture;
+	if (oldptr + length >= runtime->buffer_size) {
+		unsigned int cnt =
+			runtime->buffer_size - oldptr;
+		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+		       cnt * stride);
+		memcpy(runtime->dma_area, pcm_data + cnt * stride,
+		       length * stride - cnt * stride);
+	} else {
+		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+		       length * stride);
+	}
+	snd_pcm_stream_lock(substream);
+
+	cxsc->hwptr_done_capture += length;
+	if (cxsc->hwptr_done_capture >=
+	    runtime->buffer_size)
+		cxsc->hwptr_done_capture -=
+			runtime->buffer_size;
+
+	cxsc->capture_transfer_done += length;
+	if (cxsc->capture_transfer_done >=
+	    runtime->period_size) {
+		cxsc->capture_transfer_done -=
+			runtime->period_size;
+		period_elapsed = 1;
+	}
+
+	snd_pcm_stream_unlock(substream);
+
+	if (period_elapsed)
+		snd_pcm_period_elapsed(substream);
+}
+
+static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+	struct cx18 *cx = to_cx18(v4l2_dev);
+	struct cx18_stream *s;
+	struct cx18_open_id item;
+	int ret;
+
+	/* Instruct the cx18 to start sending packets */
+	snd_cx18_lock(cxsc);
+	s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+
+	item.cx = cx;
+	item.type = s->type;
+	item.open_id = cx->open_id++;
+
+	/* See if the stream is available */
+	if (cx18_claim_stream(&item, item.type)) {
+		/* No, it's already in use */
+		snd_cx18_unlock(cxsc);
+		return -EBUSY;
+	}
+
+	if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+	    test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		/* We're already streaming.  No additional action required */
+		snd_cx18_unlock(cxsc);
+		return 0;
+	}
+
+
+	runtime->hw = snd_cx18_hw_capture;
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	cxsc->capture_pcm_substream = substream;
+	runtime->private_data = cx;
+
+	cx->pcm_announce_callback = cx18_alsa_announce_pcm_data;
+
+	/* Not currently streaming, so start it up */
+	set_bit(CX18_F_S_STREAMING, &s->s_flags);
+	ret = cx18_start_v4l2_encode_stream(s);
+	snd_cx18_unlock(cxsc);
+
+	return ret;
+}
+
+static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+	struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+	struct cx18 *cx = to_cx18(v4l2_dev);
+	struct cx18_stream *s;
+
+	/* Instruct the cx18 to stop sending packets */
+	snd_cx18_lock(cxsc);
+	s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+	cx18_stop_v4l2_encode_stream(s, 0);
+	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+	cx18_release_stream(s);
+
+	cx->pcm_announce_callback = NULL;
+	snd_cx18_unlock(cxsc);
+
+	return 0;
+}
+
+static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream,
+		     unsigned int cmd, void *arg)
+{
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+	int ret;
+
+	snd_cx18_lock(cxsc);
+	ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+	snd_cx18_unlock(cxsc);
+	return ret;
+}
+
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+					size_t size)
+{
+	struct snd_pcm_runtime *runtime = subs->runtime;
+
+	dprintk("Allocating vbuffer\n");
+	if (runtime->dma_area) {
+		if (runtime->dma_bytes > size)
+			return 0;
+
+		vfree(runtime->dma_area);
+	}
+	runtime->dma_area = vmalloc(size);
+	if (!runtime->dma_area)
+		return -ENOMEM;
+
+	runtime->dma_bytes = size;
+
+	return 0;
+}
+
+static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	dprintk("%s called\n", __func__);
+
+	return snd_pcm_alloc_vmalloc_buffer(substream,
+					   params_buffer_bytes(params));
+}
+
+static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&cxsc->slock, flags);
+	if (substream->runtime->dma_area) {
+		dprintk("freeing pcm capture region\n");
+		vfree(substream->runtime->dma_area);
+		substream->runtime->dma_area = NULL;
+	}
+	spin_unlock_irqrestore(&cxsc->slock, flags);
+
+	return 0;
+}
+
+static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+	cxsc->hwptr_done_capture = 0;
+	cxsc->capture_transfer_done = 0;
+
+	return 0;
+}
+
+static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	unsigned long flags;
+	snd_pcm_uframes_t hwptr_done;
+	struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+	spin_lock_irqsave(&cxsc->slock, flags);
+	hwptr_done = cxsc->hwptr_done_capture;
+	spin_unlock_irqrestore(&cxsc->slock, flags);
+
+	return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+					     unsigned long offset)
+{
+	void *pageptr = subs->runtime->dma_area + offset;
+
+	return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
+	.open		= snd_cx18_pcm_capture_open,
+	.close		= snd_cx18_pcm_capture_close,
+	.ioctl		= snd_cx18_pcm_ioctl,
+	.hw_params	= snd_cx18_pcm_hw_params,
+	.hw_free	= snd_cx18_pcm_hw_free,
+	.prepare	= snd_cx18_pcm_prepare,
+	.trigger	= snd_cx18_pcm_trigger,
+	.pointer	= snd_cx18_pcm_pointer,
+	.page		= snd_pcm_get_vmalloc_page,
+};
+
+int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
+{
+	struct snd_pcm *sp;
+	struct snd_card *sc = cxsc->sc;
+	struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+	struct cx18 *cx = to_cx18(v4l2_dev);
+	int ret;
+
+	ret = snd_pcm_new(sc, "CX23418 PCM",
+			  0, /* PCM device 0, the only one for this card */
+			  0, /* 0 playback substreams */
+			  1, /* 1 capture substream */
+			  &sp);
+	if (ret) {
+		CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit;
+	}
+
+	spin_lock_init(&cxsc->slock);
+
+	snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_cx18_pcm_capture_ops);
+	sp->info_flags = 0;
+	sp->private_data = cxsc;
+	strlcpy(sp->name, cx->card_name, sizeof(sp->name));
+
+	return 0;
+
+err_exit:
+	return ret;
+}
diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.h b/drivers/media/pci/cx18/cx18-alsa-pcm.h
new file mode 100644
index 000000000000..d26e51f94577
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.h
@@ -0,0 +1,27 @@
+/*
+ *  ALSA PCM device for the
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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
+ */
+
+int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc);
+
+/* Used by cx18-mailbox to announce the PCM data to the module */
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data,
+				 size_t num_bytes);
diff --git a/drivers/media/pci/cx18/cx18-alsa.h b/drivers/media/pci/cx18/cx18-alsa.h
new file mode 100644
index 000000000000..447da374c9e8
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-alsa.h
@@ -0,0 +1,75 @@
+/*
+ *  ALSA interface to cx18 PCM capture streams
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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
+ */
+
+struct snd_card;
+
+struct snd_cx18_card {
+	struct v4l2_device *v4l2_dev;
+	struct snd_card *sc;
+	unsigned int capture_transfer_done;
+	unsigned int hwptr_done_capture;
+	struct snd_pcm_substream *capture_pcm_substream;
+	spinlock_t slock;
+};
+
+extern int cx18_alsa_debug;
+
+/*
+ * File operations that manipulate the encoder or video or audio subdevices
+ * need to be serialized.  Use the same lock we use for v4l2 file ops.
+ */
+static inline void snd_cx18_lock(struct snd_cx18_card *cxsc)
+{
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+	mutex_lock(&cx->serialize_lock);
+}
+
+static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc)
+{
+	struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+	mutex_unlock(&cx->serialize_lock);
+}
+
+#define CX18_ALSA_DBGFLG_WARN  (1 << 0)
+#define CX18_ALSA_DBGFLG_WARN  (1 << 0)
+#define CX18_ALSA_DBGFLG_INFO  (1 << 1)
+
+#define CX18_ALSA_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & cx18_alsa_debug) \
+			printk(KERN_INFO "%s-alsa: " type ": " fmt, \
+				v4l2_dev->name , ## args); \
+	} while (0)
+
+#define CX18_ALSA_DEBUG_WARN(fmt, args...) \
+	CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args)
+
+#define CX18_ALSA_DEBUG_INFO(fmt, args...) \
+	CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args)
+
+#define CX18_ALSA_ERR(fmt, args...) \
+	printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_WARN(fmt, args...) \
+	printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_INFO(fmt, args...) \
+	printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args)
diff --git a/drivers/media/pci/cx18/cx18-audio.c b/drivers/media/pci/cx18/cx18-audio.c
new file mode 100644
index 000000000000..35268923911c
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-audio.c
@@ -0,0 +1,92 @@
+/*
+ *  cx18 audio-related functions
+ *
+ *  Derived from ivtv-audio.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-audio.h"
+
+#define CX18_AUDIO_ENABLE    0xc72014
+#define CX18_AI1_MUX_MASK    0x30
+#define CX18_AI1_MUX_I2S1    0x00
+#define CX18_AI1_MUX_I2S2    0x10
+#define CX18_AI1_MUX_843_I2S 0x20
+
+/* Selects the audio input and output according to the current
+   settings. */
+int cx18_audio_set_io(struct cx18 *cx)
+{
+	const struct cx18_card_audio_input *in;
+	u32 u, v;
+	int err;
+
+	/* Determine which input to use */
+	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+		in = &cx->card->radio_input;
+	else
+		in = &cx->card->audio_inputs[cx->audio_input];
+
+	/* handle muxer chips */
+	v4l2_subdev_call(cx->sd_extmux, audio, s_routing,
+			 (u32) in->muxer_input, 0, 0);
+
+	err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
+			       audio, s_routing, in->audio_input, 0, 0);
+	if (err)
+		return err;
+
+	/* FIXME - this internal mux should be abstracted to a subdev */
+	u = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+	v = u & ~CX18_AI1_MUX_MASK;
+	switch (in->audio_input) {
+	case CX18_AV_AUDIO_SERIAL1:
+		v |= CX18_AI1_MUX_I2S1;
+		break;
+	case CX18_AV_AUDIO_SERIAL2:
+		v |= CX18_AI1_MUX_I2S2;
+		break;
+	default:
+		v |= CX18_AI1_MUX_843_I2S;
+		break;
+	}
+	if (v == u) {
+		/* force a toggle of some AI1 MUX control bits */
+		u &= ~CX18_AI1_MUX_MASK;
+		switch (in->audio_input) {
+		case CX18_AV_AUDIO_SERIAL1:
+			u |= CX18_AI1_MUX_843_I2S;
+			break;
+		case CX18_AV_AUDIO_SERIAL2:
+			u |= CX18_AI1_MUX_843_I2S;
+			break;
+		default:
+			u |= CX18_AI1_MUX_I2S1;
+			break;
+		}
+		cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE,
+				      u, CX18_AI1_MUX_MASK);
+	}
+	cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+			      v, CX18_AI1_MUX_MASK);
+	return 0;
+}
diff --git a/drivers/media/pci/cx18/cx18-audio.h b/drivers/media/pci/cx18/cx18-audio.h
new file mode 100644
index 000000000000..2731d29b0ab9
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-audio.h
@@ -0,0 +1,24 @@
+/*
+ *  cx18 audio-related functions
+ *
+ *  Derived from ivtv-audio.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+int cx18_audio_set_io(struct cx18 *cx);
diff --git a/drivers/media/pci/cx18/cx18-av-audio.c b/drivers/media/pci/cx18/cx18-av-audio.c
new file mode 100644
index 000000000000..4a24ffb17a7d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-audio.c
@@ -0,0 +1,471 @@
+/*
+ *  cx18 ADEC audio functions
+ *
+ *  Derived from cx25840-audio.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+static int set_audclk_freq(struct cx18 *cx, u32 freq)
+{
+	struct cx18_av_state *state = &cx->av_state;
+
+	if (freq != 32000 && freq != 44100 && freq != 48000)
+		return -EINVAL;
+
+	/*
+	 * The PLL parameters are based on the external crystal frequency that
+	 * would ideally be:
+	 *
+	 * NTSC Color subcarrier freq * 8 =
+	 * 	4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+	 *
+	 * The accidents of history and rationale that explain from where this
+	 * combination of magic numbers originate can be found in:
+	 *
+	 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+	 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+	 *
+	 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+	 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+	 *
+	 * As Mike Bradley has rightly pointed out, it's not the exact crystal
+	 * frequency that matters, only that all parts of the driver and
+	 * firmware are using the same value (close to the ideal value).
+	 *
+	 * Since I have a strong suspicion that, if the firmware ever assumes a
+	 * crystal value at all, it will assume 28.636360 MHz, the crystal
+	 * freq used in calculations in this driver will be:
+	 *
+	 *	xtal_freq = 28.636360 MHz
+	 *
+	 * an error of less than 0.13 ppm which is way, way better than any off
+	 * the shelf crystal will have for accuracy anyway.
+	 *
+	 * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
+	 *
+	 * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+	 * investigation, experimentation, testing, and suggested solutions of
+	 * of audio/video sync problems with SVideo and CVBS captures.
+	 */
+
+	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+		switch (freq) {
+		case 32000:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+			 */
+			cx18_av_write4(cx, 0x108, 0x200d040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x176740c */
+			/* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x0176740c);
+
+			/* src3/4/6_ctl */
+			/* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
+			cx18_av_write4(cx, 0x900, 0x0801f77f);
+			cx18_av_write4(cx, 0x904, 0x0801f77f);
+			cx18_av_write4(cx, 0x90c, 0x0801f77f);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+			cx18_av_write(cx, 0x127, 0x60);
+
+			/* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x11202fff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
+			 *  ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa00d2ef8);
+			break;
+
+		case 44100:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
+			 */
+			cx18_av_write4(cx, 0x108, 0x180e040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x062a1f2 */
+			/* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x0062a1f2);
+
+			/* src3/4/6_ctl */
+			/* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
+			cx18_av_write4(cx, 0x900, 0x08016d59);
+			cx18_av_write4(cx, 0x904, 0x08016d59);
+			cx18_av_write4(cx, 0x90c, 0x08016d59);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
+			cx18_av_write(cx, 0x127, 0x58);
+
+			/* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x112092ff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
+			 *  ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa01d4bf8);
+			break;
+
+		case 48000:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
+			 */
+			cx18_av_write4(cx, 0x108, 0x160e040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x05227ad */
+			/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x005227ad);
+
+			/* src3/4/6_ctl */
+			/* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
+			cx18_av_write4(cx, 0x900, 0x08014faa);
+			cx18_av_write4(cx, 0x904, 0x08014faa);
+			cx18_av_write4(cx, 0x90c, 0x08014faa);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+			cx18_av_write(cx, 0x127, 0x56);
+
+			/* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x11205fff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
+			 *  ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa01193f8);
+			break;
+		}
+	} else {
+		switch (freq) {
+		case 32000:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
+			 */
+			cx18_av_write4(cx, 0x108, 0x300d040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x176740c */
+			/* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x0176740c);
+
+			/* src1_ctl */
+			/* 0x1.0000 = 32000/32000 */
+			cx18_av_write4(cx, 0x8f8, 0x08010000);
+
+			/* src3/4/6_ctl */
+			/* 0x2.0000 = 2 * (32000/32000) */
+			cx18_av_write4(cx, 0x900, 0x08020000);
+			cx18_av_write4(cx, 0x904, 0x08020000);
+			cx18_av_write4(cx, 0x90c, 0x08020000);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
+			cx18_av_write(cx, 0x127, 0x70);
+
+			/* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x11201fff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
+			 *  ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa00d2ef8);
+			break;
+
+		case 44100:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
+			 */
+			cx18_av_write4(cx, 0x108, 0x240e040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x062a1f2 */
+			/* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x0062a1f2);
+
+			/* src1_ctl */
+			/* 0x1.60cd = 44100/32000 */
+			cx18_av_write4(cx, 0x8f8, 0x080160cd);
+
+			/* src3/4/6_ctl */
+			/* 0x1.7385 = 2 * (32000/44100) */
+			cx18_av_write4(cx, 0x900, 0x08017385);
+			cx18_av_write4(cx, 0x904, 0x08017385);
+			cx18_av_write4(cx, 0x90c, 0x08017385);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
+			cx18_av_write(cx, 0x127, 0x64);
+
+			/* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x112061ff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
+			 *  ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa01d4bf8);
+			break;
+
+		case 48000:
+			/*
+			 * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+			 * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+			 */
+			cx18_av_write4(cx, 0x108, 0x200d040f);
+
+			/* VID_PLL Fraction = 0x2be2fe */
+			/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+			cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+			/* AUX_PLL Fraction = 0x176740c */
+			/* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+			cx18_av_write4(cx, 0x110, 0x0176740c);
+
+			/* src1_ctl */
+			/* 0x1.8000 = 48000/32000 */
+			cx18_av_write4(cx, 0x8f8, 0x08018000);
+
+			/* src3/4/6_ctl */
+			/* 0x1.5555 = 2 * (32000/48000) */
+			cx18_av_write4(cx, 0x900, 0x08015555);
+			cx18_av_write4(cx, 0x904, 0x08015555);
+			cx18_av_write4(cx, 0x90c, 0x08015555);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+			cx18_av_write(cx, 0x127, 0x60);
+
+			/* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
+			cx18_av_write4(cx, 0x12c, 0x11203fff);
+
+			/*
+			 * EN_AV_LOCK = 0
+			 * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
+			 *  ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
+			 */
+			cx18_av_write4(cx, 0x128, 0xa01193f8);
+			break;
+		}
+	}
+
+	state->audclk_freq = freq;
+
+	return 0;
+}
+
+void cx18_av_audio_set_path(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	u8 v;
+
+	/* stop microcontroller */
+	v = cx18_av_read(cx, 0x803) & ~0x10;
+	cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+
+	/* assert soft reset */
+	v = cx18_av_read(cx, 0x810) | 0x01;
+	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+	/* Mute everything to prevent the PFFT! */
+	cx18_av_write(cx, 0x8d3, 0x1f);
+
+	if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) {
+		/* Set Path1 to Serial Audio Input */
+		cx18_av_write4(cx, 0x8d0, 0x01011012);
+
+		/* The microcontroller should not be started for the
+		 * non-tuner inputs: autodetection is specific for
+		 * TV audio. */
+	} else {
+		/* Set Path1 to Analog Demod Main Channel */
+		cx18_av_write4(cx, 0x8d0, 0x1f063870);
+	}
+
+	set_audclk_freq(cx, state->audclk_freq);
+
+	/* deassert soft reset */
+	v = cx18_av_read(cx, 0x810) & ~0x01;
+	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+		/* When the microcontroller detects the
+		 * audio format, it will unmute the lines */
+		v = cx18_av_read(cx, 0x803) | 0x10;
+		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+	}
+}
+
+static void set_volume(struct cx18 *cx, int volume)
+{
+	/* First convert the volume to msp3400 values (0-127) */
+	int vol = volume >> 9;
+	/* now scale it up to cx18_av values
+	 * -114dB to -96dB maps to 0
+	 * this should be 19, but in my testing that was 4dB too loud */
+	if (vol <= 23)
+		vol = 0;
+	else
+		vol -= 23;
+
+	/* PATH1_VOLUME */
+	cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
+}
+
+static void set_bass(struct cx18 *cx, int bass)
+{
+	/* PATH1_EQ_BASS_VOL */
+	cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+static void set_treble(struct cx18 *cx, int treble)
+{
+	/* PATH1_EQ_TREBLE_VOL */
+	cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+static void set_balance(struct cx18 *cx, int balance)
+{
+	int bal = balance >> 8;
+	if (bal > 0x80) {
+		/* PATH1_BAL_LEFT */
+		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
+		/* PATH1_BAL_LEVEL */
+		cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
+	} else {
+		/* PATH1_BAL_LEFT */
+		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
+		/* PATH1_BAL_LEVEL */
+		cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
+	}
+}
+
+static void set_mute(struct cx18 *cx, int mute)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	u8 v;
+
+	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+		/* Must turn off microcontroller in order to mute sound.
+		 * Not sure if this is the best method, but it does work.
+		 * If the microcontroller is running, then it will undo any
+		 * changes to the mute register. */
+		v = cx18_av_read(cx, 0x803);
+		if (mute) {
+			/* disable microcontroller */
+			v &= ~0x10;
+			cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+			cx18_av_write(cx, 0x8d3, 0x1f);
+		} else {
+			/* enable microcontroller */
+			v |= 0x10;
+			cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+		}
+	} else {
+		/* SRC1_MUTE_EN */
+		cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+	}
+}
+
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	struct cx18_av_state *state = &cx->av_state;
+	int retval;
+	u8 v;
+
+	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+		v = cx18_av_read(cx, 0x803) & ~0x10;
+		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+		cx18_av_write(cx, 0x8d3, 0x1f);
+	}
+	v = cx18_av_read(cx, 0x810) | 0x1;
+	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+	retval = set_audclk_freq(cx, freq);
+
+	v = cx18_av_read(cx, 0x810) & ~0x1;
+	cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+	if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+		v = cx18_av_read(cx, 0x803) | 0x10;
+		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+	}
+	return retval;
+}
+
+static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		set_volume(cx, ctrl->val);
+		break;
+	case V4L2_CID_AUDIO_BASS:
+		set_bass(cx, ctrl->val);
+		break;
+	case V4L2_CID_AUDIO_TREBLE:
+		set_treble(cx, ctrl->val);
+		break;
+	case V4L2_CID_AUDIO_BALANCE:
+		set_balance(cx, ctrl->val);
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		set_mute(cx, ctrl->val);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = {
+	.s_ctrl = cx18_av_audio_s_ctrl,
+};
diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
new file mode 100644
index 000000000000..f164b7f610a5
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-core.c
@@ -0,0 +1,1401 @@
+/*
+ *  cx18 ADEC audio functions
+ *
+ *  Derived from cx25840-core.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <media/v4l2-chip-ident.h>
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
+{
+	u32 reg = 0xc40000 + (addr & ~3);
+	u32 mask = 0xff;
+	int shift = (addr & 3) * 8;
+	u32 x = cx18_read_reg(cx, reg);
+
+	x = (x & ~(mask << shift)) | ((u32)value << shift);
+	cx18_write_reg(cx, x, reg);
+	return 0;
+}
+
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
+{
+	u32 reg = 0xc40000 + (addr & ~3);
+	int shift = (addr & 3) * 8;
+	u32 x = cx18_read_reg(cx, reg);
+
+	x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
+	cx18_write_reg_expect(cx, x, reg,
+				((u32)eval << shift), ((u32)mask << shift));
+	return 0;
+}
+
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
+{
+	cx18_write_reg(cx, value, 0xc40000 + addr);
+	return 0;
+}
+
+int
+cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
+{
+	cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
+	return 0;
+}
+
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
+{
+	cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
+	return 0;
+}
+
+u8 cx18_av_read(struct cx18 *cx, u16 addr)
+{
+	u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3));
+	int shift = (addr & 3) * 8;
+
+	return (x >> shift) & 0xff;
+}
+
+u32 cx18_av_read4(struct cx18 *cx, u16 addr)
+{
+	return cx18_read_reg(cx, 0xc40000 + addr);
+}
+
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
+		   u8 or_value)
+{
+	return cx18_av_write(cx, addr,
+			     (cx18_av_read(cx, addr) & and_mask) |
+			     or_value);
+}
+
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
+		   u32 or_value)
+{
+	return cx18_av_write4(cx, addr,
+			     (cx18_av_read4(cx, addr) & and_mask) |
+			     or_value);
+}
+
+static void cx18_av_init(struct cx18 *cx)
+{
+	/*
+	 * The crystal freq used in calculations in this driver will be
+	 * 28.636360 MHz.
+	 * Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+	 */
+
+	/*
+	 * VDCLK  Integer = 0x0f, Post Divider = 0x04
+	 * AIMCLK Integer = 0x0e, Post Divider = 0x16
+	 */
+	cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+	/* VDCLK Fraction = 0x2be2fe */
+	/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+	cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+	/* AIMCLK Fraction = 0x05227ad */
+	/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
+	cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+	/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+	cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+}
+
+static void cx18_av_initialize(struct v4l2_subdev *sd)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	int default_volume;
+	u32 v;
+
+	cx18_av_loadfw(cx);
+	/* Stop 8051 code execution */
+	cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
+						 0x03000000, 0x13000000);
+
+	/* initallize the PLL by toggling sleep bit */
+	v = cx18_av_read4(cx, CXADEC_HOST_REG1);
+	/* enable sleep mode - register appears to be read only... */
+	cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
+	/* disable sleep mode */
+	cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
+						    v & 0xfffe, 0xffff);
+
+	/* initialize DLLs */
+	v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
+	/* disable FLD */
+	cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v);
+	/* enable FLD */
+	cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100);
+
+	v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF;
+	/* disable FLD */
+	cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v);
+	/* enable FLD */
+	cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100);
+
+	/* set analog bias currents. Set Vreg to 1.20V. */
+	cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802);
+
+	v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
+	/* enable TUNE_FIL_RST */
+	cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
+	/* disable TUNE_FIL_RST */
+	cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
+			      v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);
+
+	/* enable 656 output */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
+
+	/* video output drive strength */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2);
+
+	/* reset video */
+	cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000);
+	cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0);
+
+	/*
+	 * Disable Video Auto-config of the Analog Front End and Video PLL.
+	 *
+	 * Since we only use BT.656 pixel mode, which works for both 525 and 625
+	 * line systems, it's just easier for us to set registers
+	 * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL),
+	 * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC)
+	 * ourselves, than to run around cleaning up after the auto-config.
+	 *
+	 * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit
+	 * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL
+	 * autoconfig either.)
+	 *
+	 * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3.
+	 */
+	cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000);
+
+	/* Setup the Video and and Aux/Audio PLLs */
+	cx18_av_init(cx);
+
+	/* set video to auto-detect */
+	/* Clear bits 11-12 to enable slow locking mode.  Set autodetect mode */
+	/* set the comb notch = 1 */
+	cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800);
+
+	/* Enable wtw_en in CRUSH_CTRL (Set bit 22) */
+	/* Enable maj_sel in CRUSH_CTRL (Set bit 20) */
+	cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000);
+
+	/* Set VGA_TRACK_RANGE to 0x20 */
+	cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
+
+	/*
+	 * Initial VBI setup
+	 * VIP-1.1, 10 bit mode, enable Raw, disable sliced,
+	 * don't clamp raw samples when codes are in use, 1 byte user D-words,
+	 * IDID0 has line #, RP code V bit transition on VBLANK, data during
+	 * blanking intervals
+	 */
+	cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e);
+
+	/* Set the video input.
+	   The setting in MODE_CTRL gets lost when we do the above setup */
+	/* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */
+	/* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */
+
+	/*
+	 * Analog Front End (AFE)
+	 * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2
+	 *  bypass_ch[1-3]     use filter
+	 *  droop_comp_ch[1-3] disable
+	 *  clamp_en_ch[1-3]   disable
+	 *  aud_in_sel         ADC2
+	 *  luma_in_sel        ADC1
+	 *  chroma_in_sel      ADC2
+	 *  clamp_sel_ch[2-3]  midcode
+	 *  clamp_sel_ch1      video decoder
+	 *  vga_sel_ch3        audio decoder
+	 *  vga_sel_ch[1-2]    video decoder
+	 *  half_bw_ch[1-3]    disable
+	 *  +12db_ch[1-3]      disable
+	 */
+	cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00);
+
+/* 	if(dwEnable && dw3DCombAvailable) { */
+/*      	CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */
+/*    } else { */
+/*      	CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
+/*    } */
+	cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
+	default_volume = cx18_av_read(cx, 0x8d4);
+	/*
+	 * Enforce the legacy volume scale mapping limits to avoid
+	 * -ERANGE errors when initializing the volume control
+	 */
+	if (default_volume > 228) {
+		/* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */
+		default_volume = 228;
+		cx18_av_write(cx, 0x8d4, 228);
+	} else if (default_volume < 20) {
+		/* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */
+		default_volume = 20;
+		cx18_av_write(cx, 0x8d4, 20);
+	}
+	default_volume = (((228 - default_volume) >> 1) + 23) << 9;
+	state->volume->cur.val = state->volume->default_value = default_volume;
+	v4l2_ctrl_handler_setup(&state->hdl);
+}
+
+static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
+{
+	cx18_av_initialize(sd);
+	return 0;
+}
+
+static int cx18_av_load_fw(struct v4l2_subdev *sd)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+
+	if (!state->is_initialized) {
+		/* initialize on first use */
+		state->is_initialized = 1;
+		cx18_av_initialize(sd);
+	}
+	return 0;
+}
+
+void cx18_av_std_setup(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd = &state->sd;
+	v4l2_std_id std = state->std;
+
+	/*
+	 * Video ADC crystal clock to pixel clock SRC decimation ratio
+	 * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b
+	 */
+	const int src_decimation = 0x21f;
+
+	int hblank, hactive, burst, vblank, vactive, sc;
+	int vblank656;
+	int luma_lpf, uv_lpf, comb;
+	u32 pll_int, pll_frac, pll_post;
+
+	/* datasheet startup, step 8d */
+	if (std & ~V4L2_STD_NTSC)
+		cx18_av_write(cx, 0x49f, 0x11);
+	else
+		cx18_av_write(cx, 0x49f, 0x14);
+
+	/*
+	 * Note: At the end of a field, there are 3 sets of half line duration
+	 * (double horizontal rate) pulses:
+	 *
+	 * 5 (625) or 6 (525) half-lines to blank for the vertical retrace
+	 * 5 (625) or 6 (525) vertical sync pulses of half line duration
+	 * 5 (625) or 6 (525) half-lines of equalization pulses
+	 */
+	if (std & V4L2_STD_625_50) {
+		/*
+		 * The following relationships of half line counts should hold:
+		 * 625 = vblank656 + vactive
+		 * 10 = vblank656 - vblank = vsync pulses + equalization pulses
+		 *
+		 * vblank656: half lines after line 625/mid-313 of blanked video
+		 * vblank:    half lines, after line 5/317, of blanked video
+		 * vactive:   half lines of active video +
+		 * 		5 half lines after the end of active video
+		 *
+		 * As far as I can tell:
+		 * vblank656 starts counting from the falling edge of the first
+		 * 	vsync pulse (start of line 1 or mid-313)
+		 * vblank starts counting from the after the 5 vsync pulses and
+		 * 	5 or 4 equalization pulses (start of line 6 or 318)
+		 *
+		 * For 625 line systems the driver will extract VBI information
+		 * from lines 6-23 and lines 318-335 (but the slicer can only
+		 * handle 17 lines, not the 18 in the vblank region).
+		 * In addition, we need vblank656 and vblank to be one whole
+		 * line longer, to cover line 24 and 336, so the SAV/EAV RP
+		 * codes get generated such that the encoder can actually
+		 * extract line 23 & 335 (WSS).  We'll lose 1 line in each field
+		 * at the top of the screen.
+		 *
+		 * It appears the 5 half lines that happen after active
+		 * video must be included in vactive (579 instead of 574),
+		 * otherwise the colors get badly displayed in various regions
+		 * of the screen.  I guess the chroma comb filter gets confused
+		 * without them (at least when a PVR-350 is the PAL source).
+		 */
+		vblank656 = 48; /* lines  1 -  24  &  313 - 336 */
+		vblank = 38;    /* lines  6 -  24  &  318 - 336 */
+		vactive = 579;  /* lines 24 - 313  &  337 - 626 */
+
+		/*
+		 * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is
+		 * is 864 pixels = 720 active + 144 blanking.  ITU-R BT.601
+		 * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after
+		 * the end of active video to start a horizontal line, so that
+		 * leaves 132 pixels of hblank to ignore.
+		 */
+		hblank = 132;
+		hactive = 720;
+
+		/*
+		 * Burst gate delay (for 625 line systems)
+		 * Hsync leading edge to color burst rise = 5.6 us
+		 * Color burst width = 2.25 us
+		 * Gate width = 4 pixel clocks
+		 * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks
+		 */
+		burst = 93;
+		luma_lpf = 2;
+		if (std & V4L2_STD_PAL) {
+			uv_lpf = 1;
+			comb = 0x20;
+			/* sc = 4433618.75 * src_decimation/28636360 * 2^13 */
+			sc = 688700;
+		} else if (std == V4L2_STD_PAL_Nc) {
+			uv_lpf = 1;
+			comb = 0x20;
+			/* sc = 3582056.25 * src_decimation/28636360 * 2^13 */
+			sc = 556422;
+		} else { /* SECAM */
+			uv_lpf = 0;
+			comb = 0;
+			/* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */
+			/* sc = 4328130 * src_decimation/28636360 * 2^13 */
+			sc = 672314;
+		}
+	} else {
+		/*
+		 * The following relationships of half line counts should hold:
+		 * 525 = prevsync + vblank656 + vactive
+		 * 12 = vblank656 - vblank = vsync pulses + equalization pulses
+		 *
+		 * prevsync:  6 half-lines before the vsync pulses
+		 * vblank656: half lines, after line 3/mid-266, of blanked video
+		 * vblank:    half lines, after line 9/272, of blanked video
+		 * vactive:   half lines of active video
+		 *
+		 * As far as I can tell:
+		 * vblank656 starts counting from the falling edge of the first
+		 * 	vsync pulse (start of line 4 or mid-266)
+		 * vblank starts counting from the after the 6 vsync pulses and
+		 * 	6 or 5 equalization pulses (start of line 10 or 272)
+		 *
+		 * For 525 line systems the driver will extract VBI information
+		 * from lines 10-21 and lines 273-284.
+		 */
+		vblank656 = 38; /* lines  4 -  22  &  266 - 284 */
+		vblank = 26;	/* lines 10 -  22  &  272 - 284 */
+		vactive = 481;  /* lines 23 - 263  &  285 - 525 */
+
+		/*
+		 * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
+		 * is 858 pixels = 720 active + 138 blanking.  The Hsync leading
+		 * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
+		 * end of active video, leaving 122 pixels of hblank to ignore
+		 * before active video starts.
+		 */
+		hactive = 720;
+		hblank = 122;
+		luma_lpf = 1;
+		uv_lpf = 1;
+
+		/*
+		 * Burst gate delay (for 525 line systems)
+		 * Hsync leading edge to color burst rise = 5.3 us
+		 * Color burst width = 2.5 us
+		 * Gate width = 4 pixel clocks
+		 * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks
+		 */
+		if (std == V4L2_STD_PAL_60) {
+			burst = 90;
+			luma_lpf = 2;
+			comb = 0x20;
+			/* sc = 4433618.75 * src_decimation/28636360 * 2^13 */
+			sc = 688700;
+		} else if (std == V4L2_STD_PAL_M) {
+			/* The 97 needs to be verified against PAL-M timings */
+			burst = 97;
+			comb = 0x20;
+			/* sc = 3575611.49 * src_decimation/28636360 * 2^13 */
+			sc = 555421;
+		} else {
+			burst = 90;
+			comb = 0x66;
+			/* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */
+			sc = 556032;
+		}
+	}
+
+	/* DEBUG: Displays configured PLL frequency */
+	pll_int = cx18_av_read(cx, 0x108);
+	pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+	pll_post = cx18_av_read(cx, 0x109);
+	CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n",
+			    pll_int, pll_frac, pll_post);
+
+	if (pll_post) {
+		int fsc, pll;
+		u64 tmp;
+
+		pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
+		pll /= pll_post;
+		CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n",
+				    pll / 1000000, pll % 1000000);
+		CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n",
+				    pll / 8000000, (pll / 8) % 1000000);
+
+		CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio "
+				    "= %d.%03d\n", src_decimation / 256,
+				    ((src_decimation % 256) * 1000) / 256);
+
+		tmp = 28636360 * (u64) sc;
+		do_div(tmp, src_decimation);
+		fsc = tmp >> 13;
+		CX18_DEBUG_INFO_DEV(sd,
+				    "Chroma sub-carrier initial freq = %d.%06d "
+				    "MHz\n", fsc / 1000000, fsc % 1000000);
+
+		CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, "
+				    "vactive %i, vblank656 %i, src_dec %i, "
+				    "burst 0x%02x, luma_lpf %i, uv_lpf %i, "
+				    "comb 0x%02x, sc 0x%06x\n",
+				    hblank, hactive, vblank, vactive, vblank656,
+				    src_decimation, burst, luma_lpf, uv_lpf,
+				    comb, sc);
+	}
+
+	/* Sets horizontal blanking delay and active lines */
+	cx18_av_write(cx, 0x470, hblank);
+	cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
+						(hactive << 4)));
+	cx18_av_write(cx, 0x472, hactive >> 4);
+
+	/* Sets burst gate delay */
+	cx18_av_write(cx, 0x473, burst);
+
+	/* Sets vertical blanking delay and active duration */
+	cx18_av_write(cx, 0x474, vblank);
+	cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
+						(vactive << 4)));
+	cx18_av_write(cx, 0x476, vactive >> 4);
+	cx18_av_write(cx, 0x477, vblank656);
+
+	/* Sets src decimation rate */
+	cx18_av_write(cx, 0x478, 0xff & src_decimation);
+	cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+
+	/* Sets Luma and UV Low pass filters */
+	cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+	/* Enables comb filters */
+	cx18_av_write(cx, 0x47b, comb);
+
+	/* Sets SC Step*/
+	cx18_av_write(cx, 0x47c, sc);
+	cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+	cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+	if (std & V4L2_STD_625_50) {
+		state->slicer_line_delay = 1;
+		state->slicer_line_offset = (6 + state->slicer_line_delay - 2);
+	} else {
+		state->slicer_line_delay = 0;
+		state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+	}
+	cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+}
+
+static void input_change(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	v4l2_std_id std = state->std;
+	u8 v;
+
+	/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
+	cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+	cx18_av_and_or(cx, 0x401, ~0x60, 0);
+	cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
+
+	if (std & V4L2_STD_525_60) {
+		if (std == V4L2_STD_NTSC_M_JP) {
+			/* Japan uses EIAJ audio standard */
+			cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
+			cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
+		} else if (std == V4L2_STD_NTSC_M_KR) {
+			/* South Korea uses A2 audio standard */
+			cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
+			cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+		} else {
+			/* Others use the BTSC audio standard */
+			cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
+			cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
+		}
+	} else if (std & V4L2_STD_PAL) {
+		/* Follow tuner change procedure for PAL */
+		cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+		cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+	} else if (std & V4L2_STD_SECAM) {
+		/* Select autodetect for SECAM */
+		cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+		cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+	}
+
+	v = cx18_av_read(cx, 0x803);
+	if (v & 0x10) {
+		/* restart audio decoder microcontroller */
+		v &= ~0x10;
+		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+		v |= 0x10;
+		cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+	}
+}
+
+static int cx18_av_s_frequency(struct v4l2_subdev *sd,
+			       struct v4l2_frequency *freq)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	input_change(cx);
+	return 0;
+}
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+					enum cx18_av_audio_input aud_input)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd = &state->sd;
+
+	enum analog_signal_type {
+		NONE, CVBS, Y, C, SIF, Pb, Pr
+	} ch[3] = {NONE, NONE, NONE};
+
+	u8 afe_mux_cfg;
+	u8 adc2_cfg;
+	u8 input_mode;
+	u32 afe_cfg;
+	int i;
+
+	CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n",
+			    vid_input, aud_input);
+
+	if (vid_input >= CX18_AV_COMPOSITE1 &&
+	    vid_input <= CX18_AV_COMPOSITE8) {
+		afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
+		ch[0] = CVBS;
+		input_mode = 0x0;
+	} else if (vid_input >= CX18_AV_COMPONENT_LUMA1) {
+		int luma = vid_input & 0xf000;
+		int r_chroma = vid_input & 0xf0000;
+		int b_chroma = vid_input & 0xf00000;
+
+		if ((vid_input & ~0xfff000) ||
+		    luma < CX18_AV_COMPONENT_LUMA1 ||
+		    luma > CX18_AV_COMPONENT_LUMA8 ||
+		    r_chroma < CX18_AV_COMPONENT_R_CHROMA4 ||
+		    r_chroma > CX18_AV_COMPONENT_R_CHROMA6 ||
+		    b_chroma < CX18_AV_COMPONENT_B_CHROMA7 ||
+		    b_chroma > CX18_AV_COMPONENT_B_CHROMA8) {
+			CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n",
+				     vid_input);
+			return -EINVAL;
+		}
+		afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12;
+		ch[0] = Y;
+		afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12;
+		ch[1] = Pr;
+		afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14;
+		ch[2] = Pb;
+		input_mode = 0x6;
+	} else {
+		int luma = vid_input & 0xf0;
+		int chroma = vid_input & 0xf00;
+
+		if ((vid_input & ~0xff0) ||
+		    luma < CX18_AV_SVIDEO_LUMA1 ||
+		    luma > CX18_AV_SVIDEO_LUMA8 ||
+		    chroma < CX18_AV_SVIDEO_CHROMA4 ||
+		    chroma > CX18_AV_SVIDEO_CHROMA8) {
+			CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n",
+				     vid_input);
+			return -EINVAL;
+		}
+		afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
+		ch[0] = Y;
+		if (chroma >= CX18_AV_SVIDEO_CHROMA7) {
+			afe_mux_cfg &= 0x3f;
+			afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2;
+			ch[2] = C;
+		} else {
+			afe_mux_cfg &= 0xcf;
+			afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4;
+			ch[1] = C;
+		}
+		input_mode = 0x2;
+	}
+
+	switch (aud_input) {
+	case CX18_AV_AUDIO_SERIAL1:
+	case CX18_AV_AUDIO_SERIAL2:
+		/* do nothing, use serial audio input */
+		break;
+	case CX18_AV_AUDIO4:
+		afe_mux_cfg &= ~0x30;
+		ch[1] = SIF;
+		break;
+	case CX18_AV_AUDIO5:
+		afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10;
+		ch[1] = SIF;
+		break;
+	case CX18_AV_AUDIO6:
+		afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20;
+		ch[1] = SIF;
+		break;
+	case CX18_AV_AUDIO7:
+		afe_mux_cfg &= ~0xc0;
+		ch[2] = SIF;
+		break;
+	case CX18_AV_AUDIO8:
+		afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40;
+		ch[2] = SIF;
+		break;
+
+	default:
+		CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n",
+			     aud_input);
+		return -EINVAL;
+	}
+
+	/* Set up analog front end multiplexers */
+	cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7);
+	/* Set INPUT_MODE to Composite, S-Video, or Component */
+	cx18_av_and_or(cx, 0x401, ~0x6, input_mode);
+
+	/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+	adc2_cfg = cx18_av_read(cx, 0x102);
+	if (ch[2] == NONE)
+		adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */
+	else
+		adc2_cfg |= 0x2;  /* Signal on CH3, set ADC2 to CH3 for input */
+
+	/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
+	if (ch[1] != NONE && ch[2] != NONE)
+		adc2_cfg |= 0x4; /* Set dual mode */
+	else
+		adc2_cfg &= ~0x4; /* Clear dual mode */
+	cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17);
+
+	/* Configure the analog front end */
+	afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL);
+	afe_cfg &= 0xff000000;
+	afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */
+	if (ch[1] != NONE && ch[2] != NONE)
+		afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */
+
+	for (i = 0; i < 3; i++) {
+		switch (ch[i]) {
+		default:
+		case NONE:
+			/* CLAMP_SEL = Fixed to midcode clamp level */
+			afe_cfg |= (0x00000200 << i);
+			break;
+		case CVBS:
+		case Y:
+			if (i > 0)
+				afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */
+			break;
+		case C:
+		case Pb:
+		case Pr:
+			/* CLAMP_SEL = Fixed to midcode clamp level */
+			afe_cfg |= (0x00000200 << i);
+			if (i == 0 && ch[i] == C)
+				afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */
+			break;
+		case SIF:
+			/*
+			 * VGA_GAIN_SEL = Audio Decoder
+			 * CLAMP_SEL = Fixed to midcode clamp level
+			 */
+			afe_cfg |= (0x00000240 << i);
+			if (i == 0)
+				afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */
+			break;
+		}
+	}
+
+	cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg);
+
+	state->vid_input = vid_input;
+	state->aud_input = aud_input;
+	cx18_av_audio_set_path(cx);
+	input_change(cx);
+	return 0;
+}
+
+static int cx18_av_s_video_routing(struct v4l2_subdev *sd,
+				   u32 input, u32 output, u32 config)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	return set_input(cx, input, state->aud_input);
+}
+
+static int cx18_av_s_audio_routing(struct v4l2_subdev *sd,
+				   u32 input, u32 output, u32 config)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	return set_input(cx, state->vid_input, input);
+}
+
+static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u8 vpres;
+	u8 mode;
+	int val = 0;
+
+	if (state->radio)
+		return 0;
+
+	vpres = cx18_av_read(cx, 0x40e) & 0x20;
+	vt->signal = vpres ? 0xffff : 0x0;
+
+	vt->capability |=
+		    V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+		    V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+	mode = cx18_av_read(cx, 0x804);
+
+	/* get rxsubchans and audmode */
+	if ((mode & 0xf) == 1)
+		val |= V4L2_TUNER_SUB_STEREO;
+	else
+		val |= V4L2_TUNER_SUB_MONO;
+
+	if (mode == 2 || mode == 4)
+		val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+	if (mode & 0x10)
+		val |= V4L2_TUNER_SUB_SAP;
+
+	vt->rxsubchans = val;
+	vt->audmode = state->audmode;
+	return 0;
+}
+
+static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u8 v;
+
+	if (state->radio)
+		return 0;
+
+	v = cx18_av_read(cx, 0x809);
+	v &= ~0xf;
+
+	switch (vt->audmode) {
+	case V4L2_TUNER_MODE_MONO:
+		/* mono      -> mono
+		   stereo    -> mono
+		   bilingual -> lang1 */
+		break;
+	case V4L2_TUNER_MODE_STEREO:
+	case V4L2_TUNER_MODE_LANG1:
+		/* mono      -> mono
+		   stereo    -> stereo
+		   bilingual -> lang1 */
+		v |= 0x4;
+		break;
+	case V4L2_TUNER_MODE_LANG1_LANG2:
+		/* mono      -> mono
+		   stereo    -> stereo
+		   bilingual -> lang1/lang2 */
+		v |= 0x7;
+		break;
+	case V4L2_TUNER_MODE_LANG2:
+		/* mono      -> mono
+		   stereo    -> stereo
+		   bilingual -> lang2 */
+		v |= 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	cx18_av_write_expect(cx, 0x809, v, v, 0xff);
+	state->audmode = vt->audmode;
+	return 0;
+}
+
+static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	u8 fmt = 0; 	/* zero is autodetect */
+	u8 pal_m = 0;
+
+	if (state->radio == 0 && state->std == norm)
+		return 0;
+
+	state->radio = 0;
+	state->std = norm;
+
+	/* First tests should be against specific std */
+	if (state->std == V4L2_STD_NTSC_M_JP) {
+		fmt = 0x2;
+	} else if (state->std == V4L2_STD_NTSC_443) {
+		fmt = 0x3;
+	} else if (state->std == V4L2_STD_PAL_M) {
+		pal_m = 1;
+		fmt = 0x5;
+	} else if (state->std == V4L2_STD_PAL_N) {
+		fmt = 0x6;
+	} else if (state->std == V4L2_STD_PAL_Nc) {
+		fmt = 0x7;
+	} else if (state->std == V4L2_STD_PAL_60) {
+		fmt = 0x8;
+	} else {
+		/* Then, test against generic ones */
+		if (state->std & V4L2_STD_NTSC)
+			fmt = 0x1;
+		else if (state->std & V4L2_STD_PAL)
+			fmt = 0x4;
+		else if (state->std & V4L2_STD_SECAM)
+			fmt = 0xc;
+	}
+
+	CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt);
+
+	/* Follow step 9 of section 3.16 in the cx18_av datasheet.
+	   Without this PAL may display a vertical ghosting effect.
+	   This happens for example with the Yuan MPC622. */
+	if (fmt >= 4 && fmt < 8) {
+		/* Set format to NTSC-M */
+		cx18_av_and_or(cx, 0x400, ~0xf, 1);
+		/* Turn off LCOMB */
+		cx18_av_and_or(cx, 0x47b, ~6, 0);
+	}
+	cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20);
+	cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
+	cx18_av_std_setup(cx);
+	input_change(cx);
+	return 0;
+}
+
+static int cx18_av_s_radio(struct v4l2_subdev *sd)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	state->radio = 1;
+	return 0;
+}
+
+static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		cx18_av_write(cx, 0x414, ctrl->val - 128);
+		break;
+
+	case V4L2_CID_CONTRAST:
+		cx18_av_write(cx, 0x415, ctrl->val << 1);
+		break;
+
+	case V4L2_CID_SATURATION:
+		cx18_av_write(cx, 0x420, ctrl->val << 1);
+		cx18_av_write(cx, 0x421, ctrl->val << 1);
+		break;
+
+	case V4L2_CID_HUE:
+		cx18_av_write(cx, 0x422, ctrl->val);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+	int is_50Hz = !(state->std & V4L2_STD_525_60);
+
+	if (fmt->code != V4L2_MBUS_FMT_FIXED)
+		return -EINVAL;
+
+	fmt->field = V4L2_FIELD_INTERLACED;
+	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+	Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
+	Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
+
+	Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+	Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+
+	/*
+	 * This adjustment reflects the excess of vactive, set in
+	 * cx18_av_std_setup(), above standard values:
+	 *
+	 * 480 + 1 for 60 Hz systems
+	 * 576 + 3 for 50 Hz systems
+	 */
+	Vlines = fmt->height + (is_50Hz ? 3 : 1);
+
+	/*
+	 * Invalid height and width scaling requests are:
+	 * 1. width less than 1/16 of the source width
+	 * 2. width greater than the source width
+	 * 3. height less than 1/8 of the source height
+	 * 4. height greater than the source height
+	 */
+	if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
+	    (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+		CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
+			     fmt->width, fmt->height);
+		return -ERANGE;
+	}
+
+	HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20);
+	VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+	VSC &= 0x1fff;
+
+	if (fmt->width >= 385)
+		filter = 0;
+	else if (fmt->width > 192)
+		filter = 1;
+	else if (fmt->width > 96)
+		filter = 2;
+	else
+		filter = 3;
+
+	CX18_DEBUG_INFO_DEV(sd,
+			    "decoder set size %dx%d -> scale  %ux%u\n",
+			    fmt->width, fmt->height, HSC, VSC);
+
+	/* HSCALE=HSC */
+	cx18_av_write(cx, 0x418, HSC & 0xff);
+	cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
+	cx18_av_write(cx, 0x41a, HSC >> 16);
+	/* VSCALE=VSC */
+	cx18_av_write(cx, 0x41c, VSC & 0xff);
+	cx18_av_write(cx, 0x41d, VSC >> 8);
+	/* VS_INTRLACE=1 VFILT=filter */
+	cx18_av_write(cx, 0x41e, 0x8 | filter);
+	return 0;
+}
+
+static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable");
+	if (enable) {
+		cx18_av_write(cx, 0x115, 0x8c);
+		cx18_av_write(cx, 0x116, 0x07);
+	} else {
+		cx18_av_write(cx, 0x115, 0x00);
+		cx18_av_write(cx, 0x116, 0x00);
+	}
+	return 0;
+}
+
+static void log_video_status(struct cx18 *cx)
+{
+	static const char *const fmt_strs[] = {
+		"0x0",
+		"NTSC-M", "NTSC-J", "NTSC-4.43",
+		"PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+		"0x9", "0xA", "0xB",
+		"SECAM",
+		"0xD", "0xE", "0xF"
+	};
+
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd = &state->sd;
+	u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
+	u8 gen_stat1 = cx18_av_read(cx, 0x40d);
+	u8 gen_stat2 = cx18_av_read(cx, 0x40e);
+	int vid_input = state->vid_input;
+
+	CX18_INFO_DEV(sd, "Video signal:              %spresent\n",
+		      (gen_stat2 & 0x20) ? "" : "not ");
+	CX18_INFO_DEV(sd, "Detected format:           %s\n",
+		      fmt_strs[gen_stat1 & 0xf]);
+
+	CX18_INFO_DEV(sd, "Specified standard:        %s\n",
+		      vidfmt_sel ? fmt_strs[vidfmt_sel]
+				 : "automatic detection");
+
+	if (vid_input >= CX18_AV_COMPOSITE1 &&
+	    vid_input <= CX18_AV_COMPOSITE8) {
+		CX18_INFO_DEV(sd, "Specified video input:     Composite %d\n",
+			      vid_input - CX18_AV_COMPOSITE1 + 1);
+	} else {
+		CX18_INFO_DEV(sd, "Specified video input:     "
+			      "S-Video (Luma In%d, Chroma In%d)\n",
+			      (vid_input & 0xf0) >> 4,
+			      (vid_input & 0xf00) >> 8);
+	}
+
+	CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n",
+		      state->audclk_freq);
+}
+
+static void log_audio_status(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd = &state->sd;
+	u8 download_ctl = cx18_av_read(cx, 0x803);
+	u8 mod_det_stat0 = cx18_av_read(cx, 0x804);
+	u8 mod_det_stat1 = cx18_av_read(cx, 0x805);
+	u8 audio_config = cx18_av_read(cx, 0x808);
+	u8 pref_mode = cx18_av_read(cx, 0x809);
+	u8 afc0 = cx18_av_read(cx, 0x80b);
+	u8 mute_ctl = cx18_av_read(cx, 0x8d3);
+	int aud_input = state->aud_input;
+	char *p;
+
+	switch (mod_det_stat0) {
+	case 0x00: p = "mono"; break;
+	case 0x01: p = "stereo"; break;
+	case 0x02: p = "dual"; break;
+	case 0x04: p = "tri"; break;
+	case 0x10: p = "mono with SAP"; break;
+	case 0x11: p = "stereo with SAP"; break;
+	case 0x12: p = "dual with SAP"; break;
+	case 0x14: p = "tri with SAP"; break;
+	case 0xfe: p = "forced mode"; break;
+	default: p = "not defined"; break;
+	}
+	CX18_INFO_DEV(sd, "Detected audio mode:       %s\n", p);
+
+	switch (mod_det_stat1) {
+	case 0x00: p = "not defined"; break;
+	case 0x01: p = "EIAJ"; break;
+	case 0x02: p = "A2-M"; break;
+	case 0x03: p = "A2-BG"; break;
+	case 0x04: p = "A2-DK1"; break;
+	case 0x05: p = "A2-DK2"; break;
+	case 0x06: p = "A2-DK3"; break;
+	case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+	case 0x08: p = "AM-L"; break;
+	case 0x09: p = "NICAM-BG"; break;
+	case 0x0a: p = "NICAM-DK"; break;
+	case 0x0b: p = "NICAM-I"; break;
+	case 0x0c: p = "NICAM-L"; break;
+	case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+	case 0x0e: p = "IF FM Radio"; break;
+	case 0x0f: p = "BTSC"; break;
+	case 0x10: p = "detected chrominance"; break;
+	case 0xfd: p = "unknown audio standard"; break;
+	case 0xfe: p = "forced audio standard"; break;
+	case 0xff: p = "no detected audio standard"; break;
+	default: p = "not defined"; break;
+	}
+	CX18_INFO_DEV(sd, "Detected audio standard:   %s\n", p);
+	CX18_INFO_DEV(sd, "Audio muted:               %s\n",
+		      (mute_ctl & 0x2) ? "yes" : "no");
+	CX18_INFO_DEV(sd, "Audio microcontroller:     %s\n",
+		      (download_ctl & 0x10) ? "running" : "stopped");
+
+	switch (audio_config >> 4) {
+	case 0x00: p = "undefined"; break;
+	case 0x01: p = "BTSC"; break;
+	case 0x02: p = "EIAJ"; break;
+	case 0x03: p = "A2-M"; break;
+	case 0x04: p = "A2-BG"; break;
+	case 0x05: p = "A2-DK1"; break;
+	case 0x06: p = "A2-DK2"; break;
+	case 0x07: p = "A2-DK3"; break;
+	case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+	case 0x09: p = "AM-L"; break;
+	case 0x0a: p = "NICAM-BG"; break;
+	case 0x0b: p = "NICAM-DK"; break;
+	case 0x0c: p = "NICAM-I"; break;
+	case 0x0d: p = "NICAM-L"; break;
+	case 0x0e: p = "FM radio"; break;
+	case 0x0f: p = "automatic detection"; break;
+	default: p = "undefined"; break;
+	}
+	CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p);
+
+	if ((audio_config >> 4) < 0xF) {
+		switch (audio_config & 0xF) {
+		case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+		case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+		case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+		case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+		case 0x04: p = "STEREO"; break;
+		case 0x05: p = "DUAL1 (AC)"; break;
+		case 0x06: p = "DUAL2 (BC)"; break;
+		case 0x07: p = "DUAL3 (AB)"; break;
+		default: p = "undefined";
+		}
+		CX18_INFO_DEV(sd, "Configured audio mode:     %s\n", p);
+	} else {
+		switch (audio_config & 0xF) {
+		case 0x00: p = "BG"; break;
+		case 0x01: p = "DK1"; break;
+		case 0x02: p = "DK2"; break;
+		case 0x03: p = "DK3"; break;
+		case 0x04: p = "I"; break;
+		case 0x05: p = "L"; break;
+		case 0x06: p = "BTSC"; break;
+		case 0x07: p = "EIAJ"; break;
+		case 0x08: p = "A2-M"; break;
+		case 0x09: p = "FM Radio (4.5 MHz)"; break;
+		case 0x0a: p = "FM Radio (5.5 MHz)"; break;
+		case 0x0b: p = "S-Video"; break;
+		case 0x0f: p = "automatic standard and mode detection"; break;
+		default: p = "undefined"; break;
+		}
+		CX18_INFO_DEV(sd, "Configured audio system:   %s\n", p);
+	}
+
+	if (aud_input)
+		CX18_INFO_DEV(sd, "Specified audio input:     Tuner (In%d)\n",
+			      aud_input);
+	else
+		CX18_INFO_DEV(sd, "Specified audio input:     External\n");
+
+	switch (pref_mode & 0xf) {
+	case 0: p = "mono/language A"; break;
+	case 1: p = "language B"; break;
+	case 2: p = "language C"; break;
+	case 3: p = "analog fallback"; break;
+	case 4: p = "stereo"; break;
+	case 5: p = "language AC"; break;
+	case 6: p = "language BC"; break;
+	case 7: p = "language AB"; break;
+	default: p = "undefined"; break;
+	}
+	CX18_INFO_DEV(sd, "Preferred audio mode:      %s\n", p);
+
+	if ((audio_config & 0xf) == 0xf) {
+		switch ((afc0 >> 3) & 0x1) {
+		case 0: p = "system DK"; break;
+		case 1: p = "system L"; break;
+		}
+		CX18_INFO_DEV(sd, "Selected 65 MHz format:    %s\n", p);
+
+		switch (afc0 & 0x7) {
+		case 0: p = "Chroma"; break;
+		case 1: p = "BTSC"; break;
+		case 2: p = "EIAJ"; break;
+		case 3: p = "A2-M"; break;
+		case 4: p = "autodetect"; break;
+		default: p = "undefined"; break;
+		}
+		CX18_INFO_DEV(sd, "Selected 45 MHz format:    %s\n", p);
+	}
+}
+
+static int cx18_av_log_status(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	log_video_status(cx);
+	log_audio_status(cx);
+	return 0;
+}
+
+static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match)
+{
+	return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1;
+}
+
+static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
+				struct v4l2_dbg_chip_ident *chip)
+{
+	struct cx18_av_state *state = to_cx18_av_state(sd);
+
+	if (cx18_av_dbg_match(&chip->match)) {
+		chip->ident = state->id;
+		chip->revision = state->rev;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_av_g_register(struct v4l2_subdev *sd,
+			      struct v4l2_dbg_register *reg)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	if (!cx18_av_dbg_match(&reg->match))
+		return -EINVAL;
+	if ((reg->reg & 0x3) != 0)
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	reg->size = 4;
+	reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc);
+	return 0;
+}
+
+static int cx18_av_s_register(struct v4l2_subdev *sd,
+			      struct v4l2_dbg_register *reg)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	if (!cx18_av_dbg_match(&reg->match))
+		return -EINVAL;
+	if ((reg->reg & 0x3) != 0)
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val);
+	return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = {
+	.s_ctrl = cx18_av_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
+	.g_chip_ident = cx18_av_g_chip_ident,
+	.log_status = cx18_av_log_status,
+	.load_fw = cx18_av_load_fw,
+	.reset = cx18_av_reset,
+	.g_ctrl = v4l2_subdev_g_ctrl,
+	.s_ctrl = v4l2_subdev_s_ctrl,
+	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+	.queryctrl = v4l2_subdev_queryctrl,
+	.querymenu = v4l2_subdev_querymenu,
+	.s_std = cx18_av_s_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = cx18_av_g_register,
+	.s_register = cx18_av_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = {
+	.s_radio = cx18_av_s_radio,
+	.s_frequency = cx18_av_s_frequency,
+	.g_tuner = cx18_av_g_tuner,
+	.s_tuner = cx18_av_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
+	.s_clock_freq = cx18_av_s_clock_freq,
+	.s_routing = cx18_av_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
+	.s_routing = cx18_av_s_video_routing,
+	.s_stream = cx18_av_s_stream,
+	.s_mbus_fmt = cx18_av_s_mbus_fmt,
+};
+
+static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = {
+	.decode_vbi_line = cx18_av_decode_vbi_line,
+	.g_sliced_fmt = cx18_av_g_sliced_fmt,
+	.s_sliced_fmt = cx18_av_s_sliced_fmt,
+	.s_raw_fmt = cx18_av_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops cx18_av_ops = {
+	.core = &cx18_av_general_ops,
+	.tuner = &cx18_av_tuner_ops,
+	.audio = &cx18_av_audio_ops,
+	.video = &cx18_av_video_ops,
+	.vbi = &cx18_av_vbi_ops,
+};
+
+int cx18_av_probe(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd;
+	int err;
+
+	state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
+	state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
+		    ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
+
+	state->vid_input = CX18_AV_COMPOSITE7;
+	state->aud_input = CX18_AV_AUDIO8;
+	state->audclk_freq = 48000;
+	state->audmode = V4L2_TUNER_MODE_LANG1;
+	state->slicer_line_delay = 0;
+	state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+
+	sd = &state->sd;
+	v4l2_subdev_init(sd, &cx18_av_ops);
+	v4l2_set_subdevdata(sd, cx);
+	snprintf(sd->name, sizeof(sd->name),
+		 "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
+	sd->grp_id = CX18_HW_418_AV;
+	v4l2_ctrl_handler_init(&state->hdl, 9);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 127, 1, 64);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 127, 1, 64);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+			V4L2_CID_HUE, -128, 127, 1, 0);
+
+	state->volume = v4l2_ctrl_new_std(&state->hdl,
+			&cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+			0, 65535, 65535 / 100, 0);
+	v4l2_ctrl_new_std(&state->hdl,
+			&cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+			0, 1, 1, 0);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+			V4L2_CID_AUDIO_BALANCE,
+			0, 65535, 65535 / 100, 32768);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+			V4L2_CID_AUDIO_BASS,
+			0, 65535, 65535 / 100, 32768);
+	v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+			V4L2_CID_AUDIO_TREBLE,
+			0, 65535, 65535 / 100, 32768);
+	sd->ctrl_handler = &state->hdl;
+	if (state->hdl.error) {
+		int err = state->hdl.error;
+
+		v4l2_ctrl_handler_free(&state->hdl);
+		return err;
+	}
+	err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+	if (err)
+		v4l2_ctrl_handler_free(&state->hdl);
+	else
+		cx18_av_init(cx);
+	return err;
+}
diff --git a/drivers/media/pci/cx18/cx18-av-core.h b/drivers/media/pci/cx18/cx18-av-core.h
new file mode 100644
index 000000000000..e9c69d9c9e4a
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-core.h
@@ -0,0 +1,391 @@
+/*
+ *  cx18 ADEC header
+ *
+ *  Derived from cx25840-core.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX18_AV_CORE_H_
+#define _CX18_AV_CORE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+struct cx18;
+
+enum cx18_av_video_input {
+	/* Composite video inputs In1-In8 */
+	CX18_AV_COMPOSITE1 = 1,
+	CX18_AV_COMPOSITE2,
+	CX18_AV_COMPOSITE3,
+	CX18_AV_COMPOSITE4,
+	CX18_AV_COMPOSITE5,
+	CX18_AV_COMPOSITE6,
+	CX18_AV_COMPOSITE7,
+	CX18_AV_COMPOSITE8,
+
+	/* S-Video inputs consist of one luma input (In1-In8) ORed with one
+	   chroma input (In5-In8) */
+	CX18_AV_SVIDEO_LUMA1 = 0x10,
+	CX18_AV_SVIDEO_LUMA2 = 0x20,
+	CX18_AV_SVIDEO_LUMA3 = 0x30,
+	CX18_AV_SVIDEO_LUMA4 = 0x40,
+	CX18_AV_SVIDEO_LUMA5 = 0x50,
+	CX18_AV_SVIDEO_LUMA6 = 0x60,
+	CX18_AV_SVIDEO_LUMA7 = 0x70,
+	CX18_AV_SVIDEO_LUMA8 = 0x80,
+	CX18_AV_SVIDEO_CHROMA4 = 0x400,
+	CX18_AV_SVIDEO_CHROMA5 = 0x500,
+	CX18_AV_SVIDEO_CHROMA6 = 0x600,
+	CX18_AV_SVIDEO_CHROMA7 = 0x700,
+	CX18_AV_SVIDEO_CHROMA8 = 0x800,
+
+	/* S-Video aliases for common luma/chroma combinations */
+	CX18_AV_SVIDEO1 = 0x510,
+	CX18_AV_SVIDEO2 = 0x620,
+	CX18_AV_SVIDEO3 = 0x730,
+	CX18_AV_SVIDEO4 = 0x840,
+
+	/* Component Video inputs consist of one luma input (In1-In8) ORed
+	   with a red chroma (In4-In6) and blue chroma input (In7-In8) */
+	CX18_AV_COMPONENT_LUMA1 = 0x1000,
+	CX18_AV_COMPONENT_LUMA2 = 0x2000,
+	CX18_AV_COMPONENT_LUMA3 = 0x3000,
+	CX18_AV_COMPONENT_LUMA4 = 0x4000,
+	CX18_AV_COMPONENT_LUMA5 = 0x5000,
+	CX18_AV_COMPONENT_LUMA6 = 0x6000,
+	CX18_AV_COMPONENT_LUMA7 = 0x7000,
+	CX18_AV_COMPONENT_LUMA8 = 0x8000,
+	CX18_AV_COMPONENT_R_CHROMA4 = 0x40000,
+	CX18_AV_COMPONENT_R_CHROMA5 = 0x50000,
+	CX18_AV_COMPONENT_R_CHROMA6 = 0x60000,
+	CX18_AV_COMPONENT_B_CHROMA7 = 0x700000,
+	CX18_AV_COMPONENT_B_CHROMA8 = 0x800000,
+
+	/* Component Video aliases for common combinations */
+	CX18_AV_COMPONENT1 = 0x861000,
+};
+
+enum cx18_av_audio_input {
+	/* Audio inputs: serial or In4-In8 */
+	CX18_AV_AUDIO_SERIAL1,
+	CX18_AV_AUDIO_SERIAL2,
+	CX18_AV_AUDIO4 = 4,
+	CX18_AV_AUDIO5,
+	CX18_AV_AUDIO6,
+	CX18_AV_AUDIO7,
+	CX18_AV_AUDIO8,
+};
+
+struct cx18_av_state {
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *volume;
+	int radio;
+	v4l2_std_id std;
+	enum cx18_av_video_input vid_input;
+	enum cx18_av_audio_input aud_input;
+	u32 audclk_freq;
+	int audmode;
+	u32 id;
+	u32 rev;
+	int is_initialized;
+
+	/*
+	 * The VBI slicer starts operating and counting lines, beginning at
+	 * slicer line count of 1, at D lines after the deassertion of VRESET.
+	 * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525
+	 * line systems respectively.  Sliced ancillary data captured on VBI
+	 * slicer line M is inserted after the VBI slicer is done with line M,
+	 * when VBI slicer line count is N = M+1.  Thus when the VBI slicer
+	 * reports a VBI slicer line number with ancillary data, the IDID0 byte
+	 * indicates VBI slicer line N.  The actual field line that the captured
+	 * data comes from is
+	 *
+	 * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2).
+	 *
+	 * L is the line in the field, not frame, from which the VBI data came.
+	 * N is the line reported by the slicer in the ancillary data.
+	 * D is the slicer_line_delay value programmed into register 0x47f.
+	 * S is 6 for 625 line systems or 10 for 525 line systems
+	 * (S+D-2) is the slicer_line_offset used to convert slicer reported
+	 * line counts to actual field lines.
+	 */
+	int slicer_line_delay;
+	int slicer_line_offset;
+};
+
+
+/* Registers */
+#define CXADEC_CHIP_TYPE_TIGER     0x837
+#define CXADEC_CHIP_TYPE_MAKO      0x843
+
+#define CXADEC_HOST_REG1           0x000
+#define CXADEC_HOST_REG2           0x001
+
+#define CXADEC_CHIP_CTRL           0x100
+#define CXADEC_AFE_CTRL            0x104
+#define CXADEC_PLL_CTRL1           0x108
+#define CXADEC_VID_PLL_FRAC        0x10C
+#define CXADEC_AUX_PLL_FRAC        0x110
+#define CXADEC_PIN_CTRL1           0x114
+#define CXADEC_PIN_CTRL2           0x118
+#define CXADEC_PIN_CFG1            0x11C
+#define CXADEC_PIN_CFG2            0x120
+
+#define CXADEC_PIN_CFG3            0x124
+#define CXADEC_I2S_MCLK            0x127
+
+#define CXADEC_AUD_LOCK1           0x128
+#define CXADEC_AUD_LOCK2           0x12C
+#define CXADEC_POWER_CTRL          0x130
+#define CXADEC_AFE_DIAG_CTRL1      0x134
+#define CXADEC_AFE_DIAG_CTRL2      0x138
+#define CXADEC_AFE_DIAG_CTRL3      0x13C
+#define CXADEC_PLL_DIAG_CTRL       0x140
+#define CXADEC_TEST_CTRL1          0x144
+#define CXADEC_TEST_CTRL2          0x148
+#define CXADEC_BIST_STAT           0x14C
+#define CXADEC_DLL1_DIAG_CTRL      0x158
+#define CXADEC_DLL2_DIAG_CTRL      0x15C
+
+/* IR registers */
+#define CXADEC_IR_CTRL_REG         0x200
+#define CXADEC_IR_TXCLK_REG        0x204
+#define CXADEC_IR_RXCLK_REG        0x208
+#define CXADEC_IR_CDUTY_REG        0x20C
+#define CXADEC_IR_STAT_REG         0x210
+#define CXADEC_IR_IRQEN_REG        0x214
+#define CXADEC_IR_FILTER_REG       0x218
+#define CXADEC_IR_FIFO_REG         0x21C
+
+/* Video Registers */
+#define CXADEC_MODE_CTRL           0x400
+#define CXADEC_OUT_CTRL1           0x404
+#define CXADEC_OUT_CTRL2           0x408
+#define CXADEC_GEN_STAT            0x40C
+#define CXADEC_INT_STAT_MASK       0x410
+#define CXADEC_LUMA_CTRL           0x414
+
+#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414
+#define CXADEC_CONTRAST_CTRL_BYTE  0x415
+#define CXADEC_LUMA_CTRL_BYTE_3    0x416
+
+#define CXADEC_HSCALE_CTRL         0x418
+#define CXADEC_VSCALE_CTRL         0x41C
+
+#define CXADEC_CHROMA_CTRL         0x420
+
+#define CXADEC_USAT_CTRL_BYTE      0x420
+#define CXADEC_VSAT_CTRL_BYTE      0x421
+#define CXADEC_HUE_CTRL_BYTE       0x422
+
+#define CXADEC_VBI_LINE_CTRL1      0x424
+#define CXADEC_VBI_LINE_CTRL2      0x428
+#define CXADEC_VBI_LINE_CTRL3      0x42C
+#define CXADEC_VBI_LINE_CTRL4      0x430
+#define CXADEC_VBI_LINE_CTRL5      0x434
+#define CXADEC_VBI_FC_CFG          0x438
+#define CXADEC_VBI_MISC_CFG1       0x43C
+#define CXADEC_VBI_MISC_CFG2       0x440
+#define CXADEC_VBI_PAY1            0x444
+#define CXADEC_VBI_PAY2            0x448
+#define CXADEC_VBI_CUST1_CFG1      0x44C
+#define CXADEC_VBI_CUST1_CFG2      0x450
+#define CXADEC_VBI_CUST1_CFG3      0x454
+#define CXADEC_VBI_CUST2_CFG1      0x458
+#define CXADEC_VBI_CUST2_CFG2      0x45C
+#define CXADEC_VBI_CUST2_CFG3      0x460
+#define CXADEC_VBI_CUST3_CFG1      0x464
+#define CXADEC_VBI_CUST3_CFG2      0x468
+#define CXADEC_VBI_CUST3_CFG3      0x46C
+#define CXADEC_HORIZ_TIM_CTRL      0x470
+#define CXADEC_VERT_TIM_CTRL       0x474
+#define CXADEC_SRC_COMB_CFG        0x478
+#define CXADEC_CHROMA_VBIOFF_CFG   0x47C
+#define CXADEC_FIELD_COUNT         0x480
+#define CXADEC_MISC_TIM_CTRL       0x484
+#define CXADEC_DFE_CTRL1           0x488
+#define CXADEC_DFE_CTRL2           0x48C
+#define CXADEC_DFE_CTRL3           0x490
+#define CXADEC_PLL_CTRL2           0x494
+#define CXADEC_HTL_CTRL            0x498
+#define CXADEC_COMB_CTRL           0x49C
+#define CXADEC_CRUSH_CTRL          0x4A0
+#define CXADEC_SOFT_RST_CTRL       0x4A4
+#define CXADEC_MV_DT_CTRL2         0x4A8
+#define CXADEC_MV_DT_CTRL3         0x4AC
+#define CXADEC_MISC_DIAG_CTRL      0x4B8
+
+#define CXADEC_DL_CTL              0x800
+#define CXADEC_DL_CTL_ADDRESS_LOW  0x800   /* Byte 1 in DL_CTL */
+#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801   /* Byte 2 in DL_CTL */
+#define CXADEC_DL_CTL_DATA         0x802   /* Byte 3 in DL_CTL */
+#define CXADEC_DL_CTL_CONTROL      0x803   /* Byte 4 in DL_CTL */
+
+#define CXADEC_STD_DET_STATUS      0x804
+
+#define CXADEC_STD_DET_CTL         0x808
+#define CXADEC_STD_DET_CTL_AUD_CTL   0x808 /* Byte 1 in STD_DET_CTL */
+#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
+
+#define CXADEC_DW8051_INT          0x80C
+#define CXADEC_GENERAL_CTL         0x810
+#define CXADEC_AAGC_CTL            0x814
+#define CXADEC_IF_SRC_CTL          0x818
+#define CXADEC_ANLOG_DEMOD_CTL     0x81C
+#define CXADEC_ROT_FREQ_CTL        0x820
+#define CXADEC_FM1_CTL             0x824
+#define CXADEC_PDF_CTL             0x828
+#define CXADEC_DFT1_CTL1           0x82C
+#define CXADEC_DFT1_CTL2           0x830
+#define CXADEC_DFT_STATUS          0x834
+#define CXADEC_DFT2_CTL1           0x838
+#define CXADEC_DFT2_CTL2           0x83C
+#define CXADEC_DFT2_STATUS         0x840
+#define CXADEC_DFT3_CTL1           0x844
+#define CXADEC_DFT3_CTL2           0x848
+#define CXADEC_DFT3_STATUS         0x84C
+#define CXADEC_DFT4_CTL1           0x850
+#define CXADEC_DFT4_CTL2           0x854
+#define CXADEC_DFT4_STATUS         0x858
+#define CXADEC_AM_MTS_DET          0x85C
+#define CXADEC_ANALOG_MUX_CTL      0x860
+#define CXADEC_DIG_PLL_CTL1        0x864
+#define CXADEC_DIG_PLL_CTL2        0x868
+#define CXADEC_DIG_PLL_CTL3        0x86C
+#define CXADEC_DIG_PLL_CTL4        0x870
+#define CXADEC_DIG_PLL_CTL5        0x874
+#define CXADEC_DEEMPH_GAIN_CTL     0x878
+#define CXADEC_DEEMPH_COEF1        0x87C
+#define CXADEC_DEEMPH_COEF2        0x880
+#define CXADEC_DBX1_CTL1           0x884
+#define CXADEC_DBX1_CTL2           0x888
+#define CXADEC_DBX1_STATUS         0x88C
+#define CXADEC_DBX2_CTL1           0x890
+#define CXADEC_DBX2_CTL2           0x894
+#define CXADEC_DBX2_STATUS         0x898
+#define CXADEC_AM_FM_DIFF          0x89C
+
+/* NICAM registers go here */
+#define CXADEC_NICAM_STATUS        0x8C8
+#define CXADEC_DEMATRIX_CTL        0x8CC
+
+#define CXADEC_PATH1_CTL1          0x8D0
+#define CXADEC_PATH1_VOL_CTL       0x8D4
+#define CXADEC_PATH1_EQ_CTL        0x8D8
+#define CXADEC_PATH1_SC_CTL        0x8DC
+
+#define CXADEC_PATH2_CTL1          0x8E0
+#define CXADEC_PATH2_VOL_CTL       0x8E4
+#define CXADEC_PATH2_EQ_CTL        0x8E8
+#define CXADEC_PATH2_SC_CTL        0x8EC
+
+#define CXADEC_SRC_CTL             0x8F0
+#define CXADEC_SRC_LF_COEF         0x8F4
+#define CXADEC_SRC1_CTL            0x8F8
+#define CXADEC_SRC2_CTL            0x8FC
+#define CXADEC_SRC3_CTL            0x900
+#define CXADEC_SRC4_CTL            0x904
+#define CXADEC_SRC5_CTL            0x908
+#define CXADEC_SRC6_CTL            0x90C
+
+#define CXADEC_BASEBAND_OUT_SEL    0x910
+#define CXADEC_I2S_IN_CTL          0x914
+#define CXADEC_I2S_OUT_CTL         0x918
+#define CXADEC_AC97_CTL            0x91C
+#define CXADEC_QAM_PDF             0x920
+#define CXADEC_QAM_CONST_DEC       0x924
+#define CXADEC_QAM_ROTATOR_FREQ    0x948
+
+/* Bit definitions / settings used in Mako Audio */
+#define CXADEC_PREF_MODE_MONO_LANGA        0
+#define CXADEC_PREF_MODE_MONO_LANGB        1
+#define CXADEC_PREF_MODE_MONO_LANGC        2
+#define CXADEC_PREF_MODE_FALLBACK          3
+#define CXADEC_PREF_MODE_STEREO            4
+#define CXADEC_PREF_MODE_DUAL_LANG_AC      5
+#define CXADEC_PREF_MODE_DUAL_LANG_BC      6
+#define CXADEC_PREF_MODE_DUAL_LANG_AB      7
+
+
+#define CXADEC_DETECT_STEREO               1
+#define CXADEC_DETECT_DUAL                 2
+#define CXADEC_DETECT_TRI                  4
+#define CXADEC_DETECT_SAP                  0x10
+#define CXADEC_DETECT_NO_SIGNAL            0xFF
+
+#define CXADEC_SELECT_AUDIO_STANDARD_BG    0xF0  /* NICAM BG and A2 BG */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK1   0xF1  /* NICAM DK and A2 DK */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK2   0xF2
+#define CXADEC_SELECT_AUDIO_STANDARD_DK3   0xF3
+#define CXADEC_SELECT_AUDIO_STANDARD_I     0xF4  /* NICAM I and A1 */
+#define CXADEC_SELECT_AUDIO_STANDARD_L     0xF5  /* NICAM L and System L AM */
+#define CXADEC_SELECT_AUDIO_STANDARD_BTSC  0xF6
+#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ  0xF7
+#define CXADEC_SELECT_AUDIO_STANDARD_A2_M  0xF8  /* A2 M */
+#define CXADEC_SELECT_AUDIO_STANDARD_FM    0xF9  /* FM radio */
+#define CXADEC_SELECT_AUDIO_STANDARD_AUTO  0xFF  /* Auto detect */
+
+static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct cx18_av_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-core.c 							   */
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
+int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
+			  u32 mask);
+u8 cx18_av_read(struct cx18 *cx, u16 addr);
+u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
+void cx18_av_std_setup(struct cx18 *cx);
+
+int cx18_av_probe(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-firmware.c                                                      */
+int cx18_av_loadfw(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-audio.c                                                         */
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+void cx18_av_audio_set_path(struct cx18 *cx);
+extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops;
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-vbi.c                                                           */
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+			   struct v4l2_decode_vbi_line *vbi);
+int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
+int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+
+#endif
diff --git a/drivers/media/pci/cx18/cx18-av-firmware.c b/drivers/media/pci/cx18/cx18-av-firmware.c
new file mode 100644
index 000000000000..a34fd082b76e
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-firmware.c
@@ -0,0 +1,225 @@
+/*
+ *  cx18 ADEC firmware functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include <linux/firmware.h>
+
+#define CX18_AUDIO_ENABLE    0xc72014
+#define CX18_AI1_MUX_MASK    0x30
+#define CX18_AI1_MUX_I2S1    0x00
+#define CX18_AI1_MUX_I2S2    0x10
+#define CX18_AI1_MUX_843_I2S 0x20
+#define CX18_AI1_MUX_INVALID 0x30
+
+#define FWFILE "v4l-cx23418-dig.fw"
+
+static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw)
+{
+	struct v4l2_subdev *sd = &cx->av_state.sd;
+	int ret = 0;
+	const u8 *data;
+	u32 size;
+	int addr;
+	u32 expected, dl_control;
+
+	/* Ensure we put the 8051 in reset and enable firmware upload mode */
+	dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+	do {
+		dl_control &= 0x00ffffff;
+		dl_control |= 0x0f000000;
+		cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control);
+		dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+	} while ((dl_control & 0xff000000) != 0x0f000000);
+
+	/* Read and auto increment until at address 0x0000 */
+	while (dl_control & 0x3fff)
+		dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+
+	data = fw->data;
+	size = fw->size;
+	for (addr = 0; addr < size; addr++) {
+		dl_control &= 0xffff3fff; /* ignore top 2 bits of address */
+		expected = 0x0f000000 | ((u32)data[addr] << 16) | addr;
+		if (expected != dl_control) {
+			CX18_ERR_DEV(sd, "verification of %s firmware load "
+				     "failed: expected %#010x got %#010x\n",
+				     FWFILE, expected, dl_control);
+			ret = -EIO;
+			break;
+		}
+		dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+	}
+	if (ret == 0)
+		CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n",
+			      FWFILE, size);
+	return ret;
+}
+
+int cx18_av_loadfw(struct cx18 *cx)
+{
+	struct v4l2_subdev *sd = &cx->av_state.sd;
+	const struct firmware *fw = NULL;
+	u32 size;
+	u32 u, v;
+	const u8 *ptr;
+	int i;
+	int retries1 = 0;
+
+	if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
+		CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
+		return -EINVAL;
+	}
+
+	/* The firmware load often has byte errors, so allow for several
+	   retries, both at byte level and at the firmware load level. */
+	while (retries1 < 5) {
+		cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+					  0x00008430, 0xffffffff); /* cx25843 */
+		cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
+
+		/* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+		cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+					  0x00008430, 0xffffffff); /* cx25843 */
+
+		/* Put the 8051 in reset and enable firmware upload */
+		cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
+
+		ptr = fw->data;
+		size = fw->size;
+
+		for (i = 0; i < size; i++) {
+			u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
+			u32 value = 0;
+			int retries2;
+			int unrec_err = 0;
+
+			for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
+			     retries2++) {
+				cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
+						       dl_control);
+				udelay(10);
+				value = cx18_av_read4(cx, CXADEC_DL_CTL);
+				if (value == dl_control)
+					break;
+				/* Check if we can correct the byte by changing
+				   the address.  We can only write the lower
+				   address byte of the address. */
+				if ((value & 0x3F00) != (dl_control & 0x3F00)) {
+					unrec_err = 1;
+					break;
+				}
+			}
+			if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
+				break;
+		}
+		if (i == size)
+			break;
+		retries1++;
+	}
+	if (retries1 >= 5) {
+		CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
+		release_firmware(fw);
+		return -EIO;
+	}
+
+	cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+				0x03000000 | fw->size, 0x03000000, 0x13000000);
+
+	CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
+
+	if (cx18_av_verifyfw(cx, fw) == 0)
+		cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+				0x13000000 | fw->size, 0x13000000, 0x13000000);
+
+	/* Output to the 416 */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
+
+	/* Audio input control 1 set to Sony mode */
+	/* Audio output input 2 is 0 for slave operation input */
+	/* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+	/* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+	   after WS transition for first bit of audio word. */
+	cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
+
+	/* Audio output control 1 is set to Sony mode */
+	/* Audio output control 2 is set to 1 for master mode */
+	/* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+	/* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+	   after WS transition for first bit of audio word. */
+	/* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
+	   are generated) */
+	cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+	/* set alt I2s master clock to /0x16 and enable alt divider i2s
+	   passthrough */
+	cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
+
+	cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+								  0x3F00FFFF);
+	/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
+
+	/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
+	/* Register 0x09CC is defined by the Merlin firmware, and doesn't
+	   have a name in the spec. */
+	cx18_av_write4(cx, 0x09CC, 1);
+
+	v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+	/* If bit 11 is 1, clear bit 10 */
+	if (v & 0x800)
+		cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
+				      0, 0x400);
+
+	/* Toggle the AI1 MUX */
+	v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+	u = v & CX18_AI1_MUX_MASK;
+	v &= ~CX18_AI1_MUX_MASK;
+	if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) {
+		/* Switch to I2S1 */
+		v |= CX18_AI1_MUX_I2S1;
+		cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+				      v, CX18_AI1_MUX_MASK);
+		/* Switch back to the A/V decoder core I2S output */
+		v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S;
+	} else {
+		/* Switch to the A/V decoder core I2S output */
+		v |= CX18_AI1_MUX_843_I2S;
+		cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+				      v, CX18_AI1_MUX_MASK);
+		/* Switch back to I2S1 or I2S2 */
+		v = (v & ~CX18_AI1_MUX_MASK) | u;
+	}
+	cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+			      v, CX18_AI1_MUX_MASK);
+
+	/* Enable WW auto audio standard detection */
+	v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+	v |= 0xFF;   /* Auto by default */
+	v |= 0x400;  /* Stereo by default */
+	v |= 0x14000000;
+	cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
+
+	release_firmware(fw);
+	return 0;
+}
+
+MODULE_FIRMWARE(FWFILE);
diff --git a/drivers/media/pci/cx18/cx18-av-vbi.c b/drivers/media/pci/cx18/cx18-av-vbi.c
new file mode 100644
index 000000000000..246982841fec
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-vbi.c
@@ -0,0 +1,313 @@
+/*
+ *  cx18 ADEC VBI functions
+ *
+ *  Derived from cx25840-vbi.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+
+#include "cx18-driver.h"
+
+/*
+ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
+ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
+ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
+ * (should!) look like:
+ *	4 byte EAV code:          0xff 0x00 0x00 0xRP
+ *	unknown number of possible idle bytes
+ *	3 byte Anc data preamble: 0x00 0xff 0xff
+ *	1 byte data identifier:   ne010iii (parity bits, 010, DID bits)
+ *	1 byte secondary data id: nessssss (parity bits, SDID bits)
+ *	1 byte data word count:   necccccc (parity bits, NN Dword count)
+ *	2 byte Internal DID:	  VBI-line-# 0x80
+ *	NN data bytes
+ *	1 byte checksum
+ *	Fill bytes needed to fil out to 4*NN bytes of payload
+ *
+ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
+ * in the vertical blanking interval are:
+ *	0xb0 (Task         0 VerticalBlank HorizontalBlank 0 0 0 0)
+ *	0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code, just
+ * before the first active region line and for active lines, they are:
+ *	0x90 (Task         0 0 HorizontalBlank 0 0 0 0)
+ *	0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
+ *
+ * The user application DID bytes we care about are:
+ *	0x91 (1 0 010        0 !ActiveLine AncDataPresent)
+ *	0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
+ *
+ */
+static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
+
+struct vbi_anc_data {
+	/* u8 eav[4]; */
+	/* u8 idle[]; Variable number of idle bytes */
+	u8 preamble[3];
+	u8 did;
+	u8 sdid;
+	u8 data_count;
+	u8 idid[2];
+	u8 payload[1]; /* data_count of payload */
+	/* u8 checksum; */
+	/* u8 fill[]; Variable number of fill bytes */
+};
+
+static int odd_parity(u8 c)
+{
+	c ^= (c >> 4);
+	c ^= (c >> 2);
+	c ^= (c >> 1);
+
+	return c & 1;
+}
+
+static int decode_vps(u8 *dst, u8 *p)
+{
+	static const u8 biphase_tbl[] = {
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+		0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+		0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+		0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+		0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+		0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+		0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+		0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+		0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+	};
+
+	u8 c, err = 0;
+	int i;
+
+	for (i = 0; i < 2 * 13; i += 2) {
+		err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+		c = (biphase_tbl[p[i + 1]] & 0xf) |
+		    ((biphase_tbl[p[i]] & 0xf) << 4);
+		dst[i / 2] = c;
+	}
+
+	return err & 0xf0;
+}
+
+int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	struct cx18_av_state *state = &cx->av_state;
+	static const u16 lcr2vbi[] = {
+		0, V4L2_SLICED_TELETEXT_B, 0,	/* 1 */
+		0, V4L2_SLICED_WSS_625, 0,	/* 4 */
+		V4L2_SLICED_CAPTION_525,	/* 6 */
+		0, 0, V4L2_SLICED_VPS, 0, 0,	/* 9 */
+		0, 0, 0, 0
+	};
+	int is_pal = !(state->std & V4L2_STD_525_60);
+	int i;
+
+	memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
+	svbi->service_set = 0;
+
+	/* we're done if raw VBI is active */
+	if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+		return 0;
+
+	if (is_pal) {
+		for (i = 7; i <= 23; i++) {
+			u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+			svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+			svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+			svbi->service_set |= svbi->service_lines[0][i] |
+				svbi->service_lines[1][i];
+		}
+	} else {
+		for (i = 10; i <= 21; i++) {
+			u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+			svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+			svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+			svbi->service_set |= svbi->service_lines[0][i] |
+				svbi->service_lines[1][i];
+		}
+	}
+	return 0;
+}
+
+int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	struct cx18_av_state *state = &cx->av_state;
+
+	/* Setup standard */
+	cx18_av_std_setup(cx);
+
+	/* VBI Offset */
+	cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+	cx18_av_write(cx, 0x404, 0x2e);
+	return 0;
+}
+
+int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	struct cx18_av_state *state = &cx->av_state;
+	int is_pal = !(state->std & V4L2_STD_525_60);
+	int i, x;
+	u8 lcr[24];
+
+	for (x = 0; x <= 23; x++)
+		lcr[x] = 0x00;
+
+	/* Setup standard */
+	cx18_av_std_setup(cx);
+
+	/* Sliced VBI */
+	cx18_av_write(cx, 0x404, 0x32);	/* Ancillary data */
+	cx18_av_write(cx, 0x406, 0x13);
+	cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+
+	/* Force impossible lines to 0 */
+	if (is_pal) {
+		for (i = 0; i <= 6; i++)
+			svbi->service_lines[0][i] =
+				svbi->service_lines[1][i] = 0;
+	} else {
+		for (i = 0; i <= 9; i++)
+			svbi->service_lines[0][i] =
+				svbi->service_lines[1][i] = 0;
+
+		for (i = 22; i <= 23; i++)
+			svbi->service_lines[0][i] =
+				svbi->service_lines[1][i] = 0;
+	}
+
+	/* Build register values for requested service lines */
+	for (i = 7; i <= 23; i++) {
+		for (x = 0; x <= 1; x++) {
+			switch (svbi->service_lines[1-x][i]) {
+			case V4L2_SLICED_TELETEXT_B:
+				lcr[i] |= 1 << (4 * x);
+				break;
+			case V4L2_SLICED_WSS_625:
+				lcr[i] |= 4 << (4 * x);
+				break;
+			case V4L2_SLICED_CAPTION_525:
+				lcr[i] |= 6 << (4 * x);
+				break;
+			case V4L2_SLICED_VPS:
+				lcr[i] |= 9 << (4 * x);
+				break;
+			}
+		}
+	}
+
+	if (is_pal) {
+		for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+			cx18_av_write(cx, i, lcr[6 + x]);
+	} else {
+		for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+			cx18_av_write(cx, i, lcr[9 + x]);
+		for (i = 0x431; i <= 0x434; i++)
+			cx18_av_write(cx, i, 0);
+	}
+
+	cx18_av_write(cx, 0x43c, 0x16);
+	/* Should match vblank set in cx18_av_std_setup() */
+	cx18_av_write(cx, 0x474, is_pal ? 38 : 26);
+	return 0;
+}
+
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+				   struct v4l2_decode_vbi_line *vbi)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	struct cx18_av_state *state = &cx->av_state;
+	struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
+	u8 *p;
+	int did, sdid, l, err = 0;
+
+	/*
+	 * Check for the ancillary data header for sliced VBI
+	 */
+	if (anc->preamble[0] ||
+			anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
+			(anc->did != sliced_vbi_did[0] &&
+			 anc->did != sliced_vbi_did[1])) {
+		vbi->line = vbi->type = 0;
+		return 0;
+	}
+
+	did = anc->did;
+	sdid = anc->sdid & 0xf;
+	l = anc->idid[0] & 0x3f;
+	l += state->slicer_line_offset;
+	p = anc->payload;
+
+	/* Decode the SDID set by the slicer */
+	switch (sdid) {
+	case 1:
+		sdid = V4L2_SLICED_TELETEXT_B;
+		break;
+	case 4:
+		sdid = V4L2_SLICED_WSS_625;
+		break;
+	case 6:
+		sdid = V4L2_SLICED_CAPTION_525;
+		err = !odd_parity(p[0]) || !odd_parity(p[1]);
+		break;
+	case 9:
+		sdid = V4L2_SLICED_VPS;
+		if (decode_vps(p, p) != 0)
+			err = 1;
+		break;
+	default:
+		sdid = 0;
+		err = 1;
+		break;
+	}
+
+	vbi->type = err ? 0 : sdid;
+	vbi->line = err ? 0 : l;
+	vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
+	vbi->p = p;
+	return 0;
+}
diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c
new file mode 100644
index 000000000000..c07c849b1aaf
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-cards.c
@@ -0,0 +1,638 @@
+/*
+ *  cx18 functions to query card hardware
+ *
+ *  Derived from ivtv-cards.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include "cx18-i2c.h"
+#include <media/cs5345.h>
+
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
+
+/********************** card configuration *******************************/
+
+/* usual i2c tuner addresses to probe */
+static struct cx18_card_tuner_i2c cx18_i2c_std = {
+	.radio = { I2C_CLIENT_END },
+	.demod = { 0x43, I2C_CLIENT_END },
+	.tv    = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/*
+ * usual i2c tuner addresses to probe with additional demod address for
+ * an NXP TDA8295 at 0x42 (N.B. it can possibly be at 0x4b or 0x4c too).
+ */
+static struct cx18_card_tuner_i2c cx18_i2c_nxp = {
+	.radio = { I2C_CLIENT_END },
+	.demod = { 0x42, 0x43, I2C_CLIENT_END },
+	.tv    = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/
+   This keeps the PCI ID database up to date. Note that the entries
+   must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+   New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge HVR-1600 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead
+   of PCI IDs */
+static const struct cx18_card cx18_card_hvr1600_esmt = {
+	.type = CX18_CARD_HVR_1600_ESMT,
+	.name = "Hauppauge HVR-1600",
+	.comment = "Simultaneous Digital and Analog TV capture supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_CS5345,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+		  CX18_HW_Z8F0811_IR_HAUP,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2, CX18_AV_SVIDEO2    },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+		{ CX18_CARD_INPUT_LINE_IN2,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+	.ddr = {
+		/* ESMT M13S128324A-5B memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x44220e82,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.gpio_init.initial_value = 0x3001,
+	.gpio_init.direction = 0x3001,
+	.gpio_i2c_slave_reset = {
+		.active_lo_mask = 0x3001,
+		.msecs_asserted = 10,
+		.msecs_recovery = 40,
+		.ir_reset_mask  = 0x0001,
+	},
+	.i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card cx18_card_hvr1600_s5h1411 = {
+	.type = CX18_CARD_HVR_1600_S5H1411,
+	.name = "Hauppauge HVR-1600",
+	.comment = "Simultaneous Digital and Analog TV capture supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_CS5345,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+		  CX18_HW_Z8F0811_IR_HAUP,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2, CX18_AV_SVIDEO2    },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+		{ CX18_CARD_INPUT_LINE_IN2,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+	.ddr = {
+		/* ESMT M13S128324A-5B memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x44220e82,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.gpio_init.initial_value = 0x3801,
+	.gpio_init.direction = 0x3801,
+	.gpio_i2c_slave_reset = {
+		.active_lo_mask = 0x3801,
+		.msecs_asserted = 10,
+		.msecs_recovery = 40,
+		.ir_reset_mask  = 0x0001,
+	},
+	.i2c = &cx18_i2c_nxp,
+};
+
+static const struct cx18_card cx18_card_hvr1600_samsung = {
+	.type = CX18_CARD_HVR_1600_SAMSUNG,
+	.name = "Hauppauge HVR-1600 (Preproduction)",
+	.comment = "Simultaneous Digital and Analog TV capture supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_CS5345,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+		  CX18_HW_Z8F0811_IR_HAUP,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2, CX18_AV_SVIDEO2    },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+		{ CX18_CARD_INPUT_LINE_IN2,
+		  CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+	.ddr = {
+		/* Samsung K4D263238G-VC33 memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x23230b73,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 2,
+	},
+	.gpio_init.initial_value = 0x3001,
+	.gpio_init.direction = 0x3001,
+	.gpio_i2c_slave_reset = {
+		.active_lo_mask = 0x3001,
+		.msecs_asserted = 10,
+		.msecs_recovery = 40,
+		.ir_reset_mask  = 0x0001,
+	},
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Compro VideoMate H900: note that this card is analog only! */
+
+static const struct cx18_card_pci_info cx18_pci_h900[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_h900 = {
+	.type = CX18_CARD_COMPRO_H900,
+	.name = "Compro VideoMate H900",
+	.comment = "Analog TV capture supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+			CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX18_AV_AUDIO5, 0 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX18_AV_AUDIO_SERIAL1, 0 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX18_AV_AUDIO_SERIAL1, 0 },
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.ddr = {
+		/* EtronTech EM6A9160TS-5G memory */
+		.chip_config = 0x50003,
+		.refresh = 0x753,
+		.timing1 = 0x24330e84,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.xceive_pin = 15,
+	.pci_list = cx18_pci_h900,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC718: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_mpc718 = {
+	.type = CX18_CARD_YUAN_MPC718,
+	.name = "Yuan MPC718 MiniPCI DVB-T/Analog",
+	.comment = "Experimenters needed for device to work well.\n"
+		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+		  CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+				CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2,
+				CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5,        0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+		{ CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL2, 1 },
+	},
+	.tuners = {
+		/* XC3028 tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	/* FIXME - the FM radio is just a guess and driver doesn't use SIF */
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+	.ddr = {
+		/* Hynix HY5DU283222B DDR RAM */
+		.chip_config = 0x303,
+		.refresh = 0x3bd,
+		.timing1 = 0x36320966,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 2,
+	},
+	.gpio_init.initial_value = 0x1,
+	.gpio_init.direction = 0x3,
+	/* FIXME - these GPIO's are just guesses */
+	.gpio_audio_input = { .mask   = 0x3,
+			      .tuner  = 0x1,
+			      .linein = 0x3,
+			      .radio  = 0x1 },
+	.xceive_pin = 0,
+	.pci_list = cx18_pci_mpc718,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GoTView PCI */
+
+static const struct cx18_card_pci_info cx18_pci_gotview_dvd3[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_GOTVIEW, 0x3343 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_gotview_dvd3 = {
+	.type = CX18_CARD_GOTVIEW_PCI_DVD3,
+	.name = "GoTView PCI DVD3 Hybrid",
+	.comment = "Experimenters needed for device to work well.\n"
+		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+		  CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+				CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2,
+				CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5,        0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+		{ CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL2, 1 },
+	},
+	.tuners = {
+		/* XC3028 tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	/* FIXME - the FM radio is just a guess and driver doesn't use SIF */
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+	.ddr = {
+		/* Hynix HY5DU283222B DDR RAM */
+		.chip_config = 0x303,
+		.refresh = 0x3bd,
+		.timing1 = 0x36320966,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 2,
+	},
+	.gpio_init.initial_value = 0x1,
+	.gpio_init.direction = 0x3,
+
+	.gpio_audio_input = { .mask   = 0x3,
+			      .tuner  = 0x1,
+			      .linein = 0x2,
+			      .radio  = 0x1 },
+	.xceive_pin = 0,
+	.pci_list = cx18_pci_gotview_dvd3,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Conexant Raptor PAL/SECAM: note that this card is analog only! */
+
+static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_cnxt_raptor_pal = {
+	.type = CX18_CARD_CNXT_RAPTOR_PAL,
+	.name = "Conexant Raptor PAL/SECAM",
+	.comment = "Analog TV capture supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+			CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2,
+			CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 	    0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+		{ CX18_CARD_INPUT_LINE_IN2,  CX18_AV_AUDIO_SERIAL2, 1 },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 },
+	.ddr = {
+		/* MT 46V16M16 memory */
+		.chip_config = 0x50306,
+		.refresh = 0x753,
+		.timing1 = 0x33220953,
+		.timing2 = 0x09,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.gpio_init.initial_value = 0x1002,
+	.gpio_init.direction = 0xf002,
+	.gpio_audio_input = { .mask   = 0xf002,
+			      .tuner  = 0x1002,   /* LED D1  Tuner AF  */
+			      .linein = 0x2000,   /* LED D2  Line In 1 */
+			      .radio  = 0x4002 }, /* LED D3  Tuner AF  */
+	.pci_list = cx18_pci_cnxt_raptor_pal,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Toshiba Qosmio laptop internal DVB-T/Analog Hybrid Tuner */
+
+static const struct cx18_card_pci_info cx18_pci_toshiba_qosmio_dvbt[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_TOSHIBA, 0x0110 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
+	.type = CX18_CARD_TOSHIBA_QOSMIO_DVBT,
+	.name = "Toshiba Qosmio DVB-T/Analog",
+	.comment = "Experimenters and photos needed for device to work well.\n"
+		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE6 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+			CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 	    0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.ddr = {
+		.chip_config = 0x202,
+		.refresh = 0x3bb,
+		.timing1 = 0x33320a63,
+		.timing2 = 0x0a,
+		.tune_lane = 0,
+		.initial_emrs = 0x42,
+	},
+	.xceive_pin = 15,
+	.pci_list = cx18_pci_toshiba_qosmio_dvbt,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Leadtek WinFast PVR2100 */
+
+static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100   */
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_leadtek_pvr2100 = {
+	.type = CX18_CARD_LEADTEK_PVR2100,
+	.name = "Leadtek WinFast PVR2100",
+	.comment = "Experimenters and photos needed for device to work well.\n"
+		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+		  CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+			CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 },
+		{ CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 	    0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+	},
+	.tuners = {
+		/* XC2028 tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+	.ddr = {
+		/* Pointer to proper DDR config values provided by Terry Wu */
+		.chip_config = 0x303,
+		.refresh = 0x3bb,
+		.timing1 = 0x24220e83,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 0x2,
+	},
+	.gpio_init.initial_value = 0x6,
+	.gpio_init.direction = 0x7,
+	.gpio_audio_input = { .mask   = 0x7,
+			      .tuner  = 0x6, .linein = 0x2, .radio  = 0x2 },
+	.xceive_pin = 1,
+	.pci_list = cx18_pci_leadtek_pvr2100,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Leadtek WinFast DVR3100 H */
+
+static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_leadtek_dvr3100h = {
+	.type = CX18_CARD_LEADTEK_DVR3100H,
+	.name = "Leadtek WinFast DVR3100 H",
+	.comment = "Simultaneous DVB-T and Analog capture supported,\n"
+		  "\texcept when capturing Analog from the antenna input.\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+		  CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1,
+			CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 },
+		{ CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 	    0 },
+		{ CX18_CARD_INPUT_LINE_IN1,  CX18_AV_AUDIO_SERIAL1, 1 },
+	},
+	.tuners = {
+		/* XC3028 tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+	.ddr = {
+		/* Pointer to proper DDR config values provided by Terry Wu */
+		.chip_config = 0x303,
+		.refresh = 0x3bb,
+		.timing1 = 0x24220e83,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 0x2,
+	},
+	.gpio_init.initial_value = 0x6,
+	.gpio_init.direction = 0x7,
+	.gpio_audio_input = { .mask   = 0x7,
+			      .tuner  = 0x6, .linein = 0x2, .radio  = 0x2 },
+	.xceive_pin = 1,
+	.pci_list = cx18_pci_leadtek_dvr3100h,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static const struct cx18_card *cx18_card_list[] = {
+	&cx18_card_hvr1600_esmt,
+	&cx18_card_hvr1600_samsung,
+	&cx18_card_h900,
+	&cx18_card_mpc718,
+	&cx18_card_cnxt_raptor_pal,
+	&cx18_card_toshiba_qosmio_dvbt,
+	&cx18_card_leadtek_pvr2100,
+	&cx18_card_leadtek_dvr3100h,
+	&cx18_card_gotview_dvd3,
+	&cx18_card_hvr1600_s5h1411
+};
+
+const struct cx18_card *cx18_get_card(u16 index)
+{
+	if (index >= ARRAY_SIZE(cx18_card_list))
+		return NULL;
+	return cx18_card_list[index];
+}
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
+{
+	const struct cx18_card_video_input *card_input =
+		cx->card->video_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"S-Video 1",
+		"S-Video 2",
+		"Composite 1",
+		"Composite 2",
+		"Component 1"
+	};
+
+	if (index >= cx->nof_inputs)
+		return -EINVAL;
+	input->index = index;
+	strlcpy(input->name, input_strs[card_input->video_type - 1],
+			sizeof(input->name));
+	input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
+			V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+	input->audioset = (1 << cx->nof_audio_inputs) - 1;
+	input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+				cx->tuner_std : V4L2_STD_ALL;
+	return 0;
+}
+
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
+{
+	const struct cx18_card_audio_input *aud_input =
+		cx->card->audio_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"Line In 1",
+		"Line In 2"
+	};
+
+	memset(audio, 0, sizeof(*audio));
+	if (index >= cx->nof_audio_inputs)
+		return -EINVAL;
+	strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+			sizeof(audio->name));
+	audio->index = index;
+	audio->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
diff --git a/drivers/media/pci/cx18/cx18-cards.h b/drivers/media/pci/cx18/cx18-cards.h
new file mode 100644
index 000000000000..add7391ecaba
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-cards.h
@@ -0,0 +1,157 @@
+/*
+ *  cx18 functions to query card hardware
+ *
+ *  Derived from ivtv-cards.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+/* hardware flags */
+#define CX18_HW_TUNER			(1 << 0)
+#define CX18_HW_TVEEPROM		(1 << 1)
+#define CX18_HW_CS5345			(1 << 2)
+#define CX18_HW_DVB			(1 << 3)
+#define CX18_HW_418_AV			(1 << 4)
+#define CX18_HW_GPIO_MUX		(1 << 5)
+#define CX18_HW_GPIO_RESET_CTRL		(1 << 6)
+#define CX18_HW_Z8F0811_IR_TX_HAUP	(1 << 7)
+#define CX18_HW_Z8F0811_IR_RX_HAUP	(1 << 8)
+#define CX18_HW_Z8F0811_IR_HAUP	(CX18_HW_Z8F0811_IR_RX_HAUP | \
+				 CX18_HW_Z8F0811_IR_TX_HAUP)
+
+#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \
+			CX18_HW_Z8F0811_IR_TX_HAUP)
+
+/* video inputs */
+#define	CX18_CARD_INPUT_VID_TUNER	1
+#define	CX18_CARD_INPUT_SVIDEO1 	2
+#define	CX18_CARD_INPUT_SVIDEO2 	3
+#define	CX18_CARD_INPUT_COMPOSITE1 	4
+#define	CX18_CARD_INPUT_COMPOSITE2 	5
+#define	CX18_CARD_INPUT_COMPONENT1 	6
+
+/* audio inputs */
+#define	CX18_CARD_INPUT_AUD_TUNER	1
+#define	CX18_CARD_INPUT_LINE_IN1 	2
+#define	CX18_CARD_INPUT_LINE_IN2 	3
+
+#define CX18_CARD_MAX_VIDEO_INPUTS 6
+#define CX18_CARD_MAX_AUDIO_INPUTS 3
+#define CX18_CARD_MAX_TUNERS  	   2
+
+/* V4L2 capability aliases */
+#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+			  V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+			  V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)
+
+struct cx18_card_video_input {
+	u8  video_type; 	/* video input type */
+	u8  audio_index;	/* index in cx18_card_audio_input array */
+	u32 video_input;	/* hardware video input */
+};
+
+struct cx18_card_audio_input {
+	u8  audio_type;		/* audio input type */
+	u32 audio_input;	/* hardware audio input */
+	u16 muxer_input;	/* hardware muxer input for boards with a
+				   multiplexer chip */
+};
+
+struct cx18_card_pci_info {
+	u16 device;
+	u16 subsystem_vendor;
+	u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */
+	u32 direction; 	/* DIR setting. Leave to 0 if no init is needed */
+	u32 initial_value;
+};
+
+struct cx18_gpio_i2c_slave_reset {
+	u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */
+	u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */
+	int msecs_asserted; /* time period reset must remain asserted */
+	int msecs_recovery; /* time after deassert for chips to be ready */
+	u32 ir_reset_mask;  /* GPIO to reset the Zilog Z8F0811 IR contoller */
+};
+
+struct cx18_gpio_audio_input { 	/* select tuner/line in input */
+	u32 mask; 		/* leave to 0 if not supported */
+	u32 tuner;
+	u32 linein;
+	u32 radio;
+};
+
+struct cx18_card_tuner {
+	v4l2_std_id std; 	/* standard for which the tuner is suitable */
+	int 	    tuner; 	/* tuner ID (from tuner.h) */
+};
+
+struct cx18_card_tuner_i2c {
+	unsigned short radio[2];/* radio tuner i2c address to probe */
+	unsigned short demod[3];/* demodulator i2c address to probe */
+	unsigned short tv[4];	/* tv tuner i2c addresses to probe */
+};
+
+struct cx18_ddr {		/* DDR config data */
+	u32 chip_config;
+	u32 refresh;
+	u32 timing1;
+	u32 timing2;
+	u32 tune_lane;
+	u32 initial_emrs;
+};
+
+/* for card information/parameters */
+struct cx18_card {
+	int type;
+	char *name;
+	char *comment;
+	u32 v4l2_capabilities;
+	u32 hw_audio_ctrl;	/* hardware used for the V4L2 controls (only
+				   1 dev allowed currently) */
+	u32 hw_muxer;		/* hardware used to multiplex audio input */
+	u32 hw_all;		/* all hardware used by the board */
+	struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
+	struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
+	struct cx18_card_audio_input radio_input;
+
+	/* GPIO card-specific settings */
+	u8 xceive_pin; 		/* XCeive tuner GPIO reset pin */
+	struct cx18_gpio_init 		 gpio_init;
+	struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset;
+	struct cx18_gpio_audio_input    gpio_audio_input;
+
+	struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
+	struct cx18_card_tuner_i2c *i2c;
+
+	struct cx18_ddr ddr;
+
+	/* list of device and subsystem vendor/devices that
+	   correspond to this card type. */
+	const struct cx18_card_pci_info *pci_list;
+};
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
+const struct cx18_card *cx18_get_card(u16 index);
diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
new file mode 100644
index 000000000000..282a3d29fdaa
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-controls.c
@@ -0,0 +1,131 @@
+/*
+ *  cx18 ioctl control functions
+ *
+ *  Derived from ivtv-controls.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-ioctl.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-controls.h"
+
+static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
+{
+	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+	int type = cxhdl->stream_type->val;
+
+	if (atomic_read(&cx->ana_capturing) > 0)
+		return -EBUSY;
+
+	if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
+	    !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS ||
+	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD ||
+	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) {
+		/* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */
+		cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
+		CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
+				"the MPEG stream\n");
+		return 0;
+	}
+
+	/* Allocate sliced VBI buffers if needed. */
+	if (cx->vbi.sliced_mpeg_data[0] == NULL) {
+		int i;
+
+		for (i = 0; i < CX18_VBI_FRAMES; i++) {
+			cx->vbi.sliced_mpeg_data[i] =
+			       kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL);
+			if (cx->vbi.sliced_mpeg_data[i] == NULL) {
+				while (--i >= 0) {
+					kfree(cx->vbi.sliced_mpeg_data[i]);
+					cx->vbi.sliced_mpeg_data[i] = NULL;
+				}
+				cx->vbi.insert_mpeg =
+						  V4L2_MPEG_STREAM_VBI_FMT_NONE;
+				CX18_WARN("Unable to allocate buffers for "
+					  "sliced VBI data insertion\n");
+				return -ENOMEM;
+			}
+		}
+	}
+
+	cx->vbi.insert_mpeg = fmt;
+	CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
+			"when sliced VBI is enabled\n");
+
+	/*
+	 * If our current settings have no lines set for capture, store a valid,
+	 * default set of service lines to capture, in our current settings.
+	 */
+	if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
+		if (cx->is_60hz)
+			cx->vbi.sliced_in->service_set =
+							V4L2_SLICED_CAPTION_525;
+		else
+			cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+		cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
+	}
+	return 0;
+}
+
+static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
+{
+	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+	int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+	struct v4l2_mbus_framefmt fmt;
+
+	/* fix videodecoder resolution */
+	fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+	fmt.height = cxhdl->height;
+	fmt.code = V4L2_MBUS_FMT_FIXED;
+	v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
+	return 0;
+}
+
+static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
+{
+	static const u32 freqs[3] = { 44100, 48000, 32000 };
+	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+
+	/* The audio clock of the digitizer must match the codec sample
+	   rate otherwise you get some very strange effects. */
+	if (idx < ARRAY_SIZE(freqs))
+		cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
+	return 0;
+}
+
+static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
+{
+	struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+
+	cx->dualwatch_stereo_mode = val;
+	return 0;
+}
+
+struct cx2341x_handler_ops cx18_cxhdl_ops = {
+	.s_audio_mode = cx18_s_audio_mode,
+	.s_audio_sampling_freq = cx18_s_audio_sampling_freq,
+	.s_video_encoding = cx18_s_video_encoding,
+	.s_stream_vbi_fmt = cx18_s_stream_vbi_fmt,
+};
diff --git a/drivers/media/pci/cx18/cx18-controls.h b/drivers/media/pci/cx18/cx18-controls.h
new file mode 100644
index 000000000000..cb5dfc7b2054
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-controls.h
@@ -0,0 +1,24 @@
+/*
+ *  cx18 ioctl control functions
+ *
+ *  Derived from ivtv-controls.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+extern struct cx2341x_handler_ops cx18_cxhdl_ops;
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
new file mode 100644
index 000000000000..039133d692e3
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -0,0 +1,1360 @@
+/*
+ *  cx18 driver initialization and card probing
+ *
+ *  Derived from ivtv-driver.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+#include "cx18-gpio.h"
+#include "cx18-firmware.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-av-core.h"
+#include "cx18-scb.h"
+#include "cx18-mailbox.h"
+#include "cx18-ioctl.h"
+#include "cx18-controls.h"
+#include "tuner-xc2028.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+
+/* If you have already X v4l cards, then set this to X. This way
+   the device numbers stay matched. Example: you have a WinTV card
+   without radio and a Compro H900 with. Normally this would give a
+   video1 device together with a radio0 device for the Compro. By
+   setting this to 1 you ensure that radio0 is now also radio1. */
+int cx18_first_minor;
+
+/* Callback for registering extensions */
+int (*cx18_ext_init)(struct cx18 *);
+EXPORT_SYMBOL(cx18_ext_init);
+
+/* add your revision and whatnot here */
+static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
+static atomic_t cx18_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[CX18_MAX_CARDS];
+static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1 };
+static unsigned cardtype_c = 1;
+static unsigned tuner_c = 1;
+static unsigned radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
+static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
+static int cx18_pci_latency = 1;
+
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
+int cx18_debug;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug, cx18_debug, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
+module_param(cx18_pci_latency, int, 0644);
+module_param(cx18_first_minor, int, 0644);
+
+module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+			"\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+		 "Enable or disable the radio. Use only if autodetection\n"
+		 "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+		 "Only use this option if your card is not detected properly.\n"
+		 "\t\tSpecify card type:\n"
+		 "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+		 "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+		 "\t\t\t 3 = Compro VideoMate H900\n"
+		 "\t\t\t 4 = Yuan MPC718\n"
+		 "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+		 "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+		 "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+		 "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
+		 "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
+		 "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
+		 "\t\t\t 0 = Autodetect (default)\n"
+		 "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: 0\n"
+		 "\t\t\t  1/0x0001: warning\n"
+		 "\t\t\t  2/0x0002: info\n"
+		 "\t\t\t  4/0x0004: mailbox\n"
+		 "\t\t\t  8/0x0008: dma\n"
+		 "\t\t\t 16/0x0010: ioctl\n"
+		 "\t\t\t 32/0x0020: file\n"
+		 "\t\t\t 64/0x0040: i2c\n"
+		 "\t\t\t128/0x0080: irq\n"
+		 "\t\t\t256/0x0100: high volume\n");
+MODULE_PARM_DESC(cx18_pci_latency,
+		 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+		 "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(retry_mmio,
+		 "(Deprecated) MMIO writes are now always checked and retried\n"
+		 "\t\t\tEffectively: 1 [Yes]");
+MODULE_PARM_DESC(mmio_ndelay,
+		 "(Deprecated) MMIO accesses are now never purposely delayed\n"
+		 "\t\t\tEffectively: 0 ns");
+MODULE_PARM_DESC(enc_ts_buffers,
+		 "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+		 "Size of an encoder TS buffer (kB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+		 "Number of encoder TS buffers\n"
+		 "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+		 "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+		 "Size of an encoder MPG buffer (kB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+		 "Number of encoder MPG buffers\n"
+		 "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+		 "(Deprecated) Encoder IDX buffer memory (MB)\n"
+		 "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+		 "\t\t\tDefault: 1 [Enabled]");
+MODULE_PARM_DESC(enc_idx_bufsize,
+		 "Size of an encoder IDX buffer (kB)\n"
+		 "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+		 "\t\t\t(multiples of size required for 64 index entries)\n"
+		 "\t\t\tDefault: 2");
+MODULE_PARM_DESC(enc_idx_bufs,
+		 "Number of encoder IDX buffers\n"
+		 "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
+MODULE_PARM_DESC(enc_yuv_buffers,
+		 "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+		 "Size of an encoder YUV buffer (kB)\n"
+		 "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+		 "\t\t\t(multiples of size required for 32 screen lines)\n"
+		 "\t\t\tDefault: 102");
+MODULE_PARM_DESC(enc_yuv_bufs,
+		 "Number of encoder YUV buffers\n"
+		 "\t\t\tDefault is computed from other enc_yuv_* parameters");
+MODULE_PARM_DESC(enc_vbi_buffers,
+		 "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+		 "Number of encoder VBI buffers\n"
+		 "\t\t\tDefault is computed from enc_vbi_buffers");
+MODULE_PARM_DESC(enc_pcm_buffers,
+		 "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+		 "Size of an encoder PCM buffer (kB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+		 "Number of encoder PCM buffers\n"
+		 "\t\t\tDefault is computed from other enc_pcm_* parameters");
+
+MODULE_PARM_DESC(cx18_first_minor,
+		 "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("CX23418 driver");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+
+	/* Make sure cx18-alsa module is loaded */
+	request_module("cx18-alsa");
+
+	/* Initialize cx18-alsa for this instance of the cx18 device */
+	if (cx18_ext_init != NULL)
+		cx18_ext_init(dev);
+}
+
+static void request_modules(struct cx18 *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx18 *dev)
+{
+	flush_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/* Generic utility functions */
+int cx18_msleep_timeout(unsigned int msecs, int intr)
+{
+	long int timeout = msecs_to_jiffies(msecs);
+	int sig;
+
+	do {
+		set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		timeout = schedule_timeout(timeout);
+		sig = intr ? signal_pending(current) : 0;
+	} while (!sig && timeout);
+	return sig;
+}
+
+/* Release ioremapped memory */
+static void cx18_iounmap(struct cx18 *cx)
+{
+	if (cx == NULL)
+		return;
+
+	/* Release io memory */
+	if (cx->enc_mem != NULL) {
+		CX18_DEBUG_INFO("releasing enc_mem\n");
+		iounmap(cx->enc_mem);
+		cx->enc_mem = NULL;
+	}
+}
+
+static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+{
+	int i;
+
+	CX18_INFO("eeprom dump:\n");
+	for (i = 0; i < len; i++) {
+		if (0 == (i % 16))
+			CX18_INFO("eeprom %02x:", i);
+		printk(KERN_CONT " %02x", eedata[i]);
+		if (15 == (i % 16))
+			printk(KERN_CONT "\n");
+	}
+}
+
+/* Hauppauge card? get values from tveeprom */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+{
+	struct i2c_client c;
+	u8 eedata[256];
+
+	memset(&c, 0, sizeof(c));
+	strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+	c.adapter = &cx->i2c_adap[0];
+	c.addr = 0xA0 >> 1;
+
+	memset(tv, 0, sizeof(*tv));
+	if (tveeprom_read(&c, eedata, sizeof(eedata)))
+		return;
+
+	switch (cx->card->type) {
+	case CX18_CARD_HVR_1600_ESMT:
+	case CX18_CARD_HVR_1600_SAMSUNG:
+	case CX18_CARD_HVR_1600_S5H1411:
+		tveeprom_hauppauge_analog(&c, tv, eedata);
+		break;
+	case CX18_CARD_YUAN_MPC718:
+	case CX18_CARD_GOTVIEW_PCI_DVD3:
+		tv->model = 0x718;
+		cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+		CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+			  eedata[2], eedata[1], eedata[4], eedata[3]);
+		break;
+	default:
+		tv->model = 0xffffffff;
+		cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+		break;
+	}
+}
+
+static void cx18_process_eeprom(struct cx18 *cx)
+{
+	struct tveeprom tv;
+
+	cx18_read_eeprom(cx, &tv);
+
+	/* Many thanks to Steven Toth from Hauppauge for providing the
+	   model numbers */
+	/* Note: the Samsung memory models cannot be reliably determined
+	   from the model number. Use the cardtype module option if you
+	   have one of these preproduction models. */
+	switch (tv.model) {
+	case 74301: /* Retail models */
+	case 74321:
+	case 74351: /* OEM models */
+	case 74361:
+		/* Digital side is s5h1411/tda18271 */
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
+		break;
+	case 74021: /* Retail models */
+	case 74031:
+	case 74041:
+	case 74141:
+	case 74541: /* OEM models */
+	case 74551:
+	case 74591:
+	case 74651:
+	case 74691:
+	case 74751:
+	case 74891:
+		/* Digital side is s5h1409/mxl5005s */
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		break;
+	case 0x718:
+		return;
+	case 0xffffffff:
+		CX18_INFO("Unknown EEPROM encoding\n");
+		return;
+	case 0:
+		CX18_ERR("Invalid EEPROM\n");
+		return;
+	default:
+		CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
+			 "(cardtype=1)\n", tv.model);
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		break;
+	}
+
+	cx->v4l2_cap = cx->card->v4l2_capabilities;
+	cx->card_name = cx->card->name;
+	cx->card_i2c = cx->card->i2c;
+
+	CX18_INFO("Autodetected %s\n", cx->card_name);
+
+	if (tv.tuner_type == TUNER_ABSENT)
+		CX18_ERR("tveeprom cannot autodetect tuner!\n");
+
+	if (cx->options.tuner == -1)
+		cx->options.tuner = tv.tuner_type;
+	if (cx->options.radio == -1)
+		cx->options.radio = (tv.has_radio != 0);
+
+	if (cx->std != 0)
+		/* user specified tuner standard */
+		return;
+
+	/* autodetect tuner standard */
+#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B  | V4L2_STD_GH | \
+				   V4L2_STD_MN | \
+				   V4L2_STD_PAL_I | \
+				   V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
+				   V4L2_STD_DK)
+	if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
+					== TVEEPROM_TUNER_FORMAT_ALL) {
+		CX18_DEBUG_INFO("Worldwide tuner detected\n");
+		cx->std = V4L2_STD_ALL;
+	} else if (tv.tuner_formats & V4L2_STD_PAL) {
+		CX18_DEBUG_INFO("PAL tuner detected\n");
+		cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+	} else if (tv.tuner_formats & V4L2_STD_NTSC) {
+		CX18_DEBUG_INFO("NTSC tuner detected\n");
+		cx->std |= V4L2_STD_NTSC_M;
+	} else if (tv.tuner_formats & V4L2_STD_SECAM) {
+		CX18_DEBUG_INFO("SECAM tuner detected\n");
+		cx->std |= V4L2_STD_SECAM_L;
+	} else {
+		CX18_INFO("No tuner detected, default to NTSC-M\n");
+		cx->std |= V4L2_STD_NTSC_M;
+	}
+}
+
+static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+{
+	switch (pal[0]) {
+	case '6':
+		return V4L2_STD_PAL_60;
+	case 'b':
+	case 'B':
+	case 'g':
+	case 'G':
+		return V4L2_STD_PAL_BG;
+	case 'h':
+	case 'H':
+		return V4L2_STD_PAL_H;
+	case 'n':
+	case 'N':
+		if (pal[1] == 'c' || pal[1] == 'C')
+			return V4L2_STD_PAL_Nc;
+		return V4L2_STD_PAL_N;
+	case 'i':
+	case 'I':
+		return V4L2_STD_PAL_I;
+	case 'd':
+	case 'D':
+	case 'k':
+	case 'K':
+		return V4L2_STD_PAL_DK;
+	case 'M':
+	case 'm':
+		return V4L2_STD_PAL_M;
+	case '-':
+		break;
+	default:
+		CX18_WARN("pal= argument not recognised\n");
+		return 0;
+	}
+
+	switch (secam[0]) {
+	case 'b':
+	case 'B':
+	case 'g':
+	case 'G':
+	case 'h':
+	case 'H':
+		return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+	case 'd':
+	case 'D':
+	case 'k':
+	case 'K':
+		return V4L2_STD_SECAM_DK;
+	case 'l':
+	case 'L':
+		if (secam[1] == 'C' || secam[1] == 'c')
+			return V4L2_STD_SECAM_LC;
+		return V4L2_STD_SECAM_L;
+	case '-':
+		break;
+	default:
+		CX18_WARN("secam= argument not recognised\n");
+		return 0;
+	}
+
+	switch (ntsc[0]) {
+	case 'm':
+	case 'M':
+		return V4L2_STD_NTSC_M;
+	case 'j':
+	case 'J':
+		return V4L2_STD_NTSC_M_JP;
+	case 'k':
+	case 'K':
+		return V4L2_STD_NTSC_M_KR;
+	case '-':
+		break;
+	default:
+		CX18_WARN("ntsc= argument not recognised\n");
+		return 0;
+	}
+
+	/* no match found */
+	return 0;
+}
+
+static void cx18_process_options(struct cx18 *cx)
+{
+	int i, j;
+
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+	cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+	/* Ensure stream_buffers & stream_buf_size are valid */
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		if (cx->stream_buffers[i] == 0 ||     /* User said 0 buffers */
+		    cx->options.megabytes[i] <= 0 ||  /* User said 0 MB total */
+		    cx->stream_buf_size[i] <= 0) {    /* User said buf size 0 */
+			cx->options.megabytes[i] = 0;
+			cx->stream_buffers[i] = 0;
+			cx->stream_buf_size[i] = 0;
+			continue;
+		}
+		/*
+		 * YUV is a special case where the stream_buf_size needs to be
+		 * an integral multiple of 33.75 kB (storage for 32 screens
+		 * lines to maintain alignment in case of lost buffers).
+		 *
+		 * IDX is a special case where the stream_buf_size should be
+		 * an integral multiple of 1.5 kB (storage for 64 index entries
+		 * to maintain alignment in case of lost buffers).
+		 *
+		 */
+		if (i == CX18_ENC_STREAM_TYPE_YUV) {
+			cx->stream_buf_size[i] *= 1024;
+			cx->stream_buf_size[i] -=
+			   (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+
+			if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+				cx->stream_buf_size[i] =
+						CX18_UNIT_ENC_YUV_BUFSIZE;
+		} else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+			cx->stream_buf_size[i] *= 1024;
+			cx->stream_buf_size[i] -=
+			   (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+
+			if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+				cx->stream_buf_size[i] =
+						CX18_UNIT_ENC_IDX_BUFSIZE;
+		}
+		/*
+		 * YUV and IDX are special cases where the stream_buf_size is
+		 * now in bytes.
+		 * VBI is a special case where the stream_buf_size is fixed
+		 * and already in bytes
+		 */
+		if (i == CX18_ENC_STREAM_TYPE_VBI ||
+		    i == CX18_ENC_STREAM_TYPE_YUV ||
+		    i == CX18_ENC_STREAM_TYPE_IDX) {
+			if (cx->stream_buffers[i] < 0) {
+				cx->stream_buffers[i] =
+					cx->options.megabytes[i] * 1024 * 1024
+					/ cx->stream_buf_size[i];
+			} else {
+				/* N.B. This might round down to 0 */
+				cx->options.megabytes[i] =
+					cx->stream_buffers[i]
+					* cx->stream_buf_size[i]/(1024 * 1024);
+			}
+		} else {
+			/* All other streams have stream_buf_size in kB here */
+			if (cx->stream_buffers[i] < 0) {
+				cx->stream_buffers[i] =
+						cx->options.megabytes[i] * 1024
+						/ cx->stream_buf_size[i];
+			} else {
+				/* N.B. This might round down to 0 */
+				cx->options.megabytes[i] =
+						cx->stream_buffers[i]
+						* cx->stream_buf_size[i] / 1024;
+			}
+			/* convert from kB to bytes */
+			cx->stream_buf_size[i] *= 1024;
+		}
+		CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+				"%d bytes\n", i, cx->options.megabytes[i],
+				cx->stream_buffers[i], cx->stream_buf_size[i]);
+	}
+
+	cx->options.cardtype = cardtype[cx->instance];
+	cx->options.tuner = tuner[cx->instance];
+	cx->options.radio = radio[cx->instance];
+
+	cx->std = cx18_parse_std(cx);
+	if (cx->options.cardtype == -1) {
+		CX18_INFO("Ignore card\n");
+		return;
+	}
+	cx->card = cx18_get_card(cx->options.cardtype - 1);
+	if (cx->card)
+		CX18_INFO("User specified %s card\n", cx->card->name);
+	else if (cx->options.cardtype != 0)
+		CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+	if (cx->card == NULL) {
+		if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+			cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+			CX18_INFO("Autodetected Hauppauge card\n");
+		}
+	}
+	if (cx->card == NULL) {
+		for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+			if (cx->card->pci_list == NULL)
+				continue;
+			for (j = 0; cx->card->pci_list[j].device; j++) {
+				if (cx->pci_dev->device !=
+				    cx->card->pci_list[j].device)
+					continue;
+				if (cx->pci_dev->subsystem_vendor !=
+				    cx->card->pci_list[j].subsystem_vendor)
+					continue;
+				if (cx->pci_dev->subsystem_device !=
+				    cx->card->pci_list[j].subsystem_device)
+					continue;
+				CX18_INFO("Autodetected %s card\n", cx->card->name);
+				goto done;
+			}
+		}
+	}
+done:
+
+	if (cx->card == NULL) {
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+			 cx->pci_dev->vendor, cx->pci_dev->device);
+		CX18_ERR("              subsystem vendor/device: [%04x:%04x]\n",
+			 cx->pci_dev->subsystem_vendor,
+			 cx->pci_dev->subsystem_device);
+		CX18_ERR("Defaulting to %s card\n", cx->card->name);
+		CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+		CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+		CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+	}
+	cx->v4l2_cap = cx->card->v4l2_capabilities;
+	cx->card_name = cx->card->name;
+	cx->card_i2c = cx->card->i2c;
+}
+
+static int __devinit cx18_create_in_workq(struct cx18 *cx)
+{
+	snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
+		 cx->v4l2_dev.name);
+	cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
+	if (cx->in_work_queue == NULL) {
+		CX18_ERR("Unable to create incoming mailbox handler thread\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+		cx->in_work_order[i].cx = cx;
+		cx->in_work_order[i].str = cx->epu_debug_str;
+		INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+	}
+}
+
+/* Precondition: the cx18 structure has been memset to 0. Only
+   the dev and instance fields have been filled in.
+   No assumptions on the card type may be made here (see cx18_init_struct2
+   for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+	int ret;
+
+	cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+
+	mutex_init(&cx->serialize_lock);
+	mutex_init(&cx->gpio_lock);
+	mutex_init(&cx->epu2apu_mb_lock);
+	mutex_init(&cx->epu2cpu_mb_lock);
+
+	ret = cx18_create_in_workq(cx);
+	if (ret)
+		return ret;
+
+	cx18_init_in_work_orders(cx);
+
+	/* start counting open_id at 1 */
+	cx->open_id = 1;
+
+	/* Initial settings */
+	cx->cxhdl.port = CX2341X_PORT_MEMORY;
+	cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+	cx->cxhdl.ops = &cx18_cxhdl_ops;
+	cx->cxhdl.func = cx18_api_func;
+	cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+	ret = cx2341x_handler_init(&cx->cxhdl, 50);
+	if (ret)
+		return ret;
+	cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+
+	cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+	cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+	cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+		(cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+		(cx->cxhdl.video_median_filter_type->cur.val << 2);
+
+	init_waitqueue_head(&cx->cap_w);
+	init_waitqueue_head(&cx->mb_apu_waitq);
+	init_waitqueue_head(&cx->mb_cpu_waitq);
+	init_waitqueue_head(&cx->dma_waitq);
+
+	/* VBI */
+	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+
+	/* IVTV style VBI insertion into MPEG streams */
+	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+	list_add(&cx->vbi.sliced_mpeg_buf.list,
+		 &cx->vbi.sliced_mpeg_mdl.buf_list);
+	return 0;
+}
+
+/* Second initialization part. Here the card type has been
+   autodetected. */
+static void __devinit cx18_init_struct2(struct cx18 *cx)
+{
+	int i;
+
+	for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+		if (cx->card->video_inputs[i].video_type == 0)
+			break;
+	cx->nof_inputs = i;
+	for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+		if (cx->card->audio_inputs[i].audio_type == 0)
+			break;
+	cx->nof_audio_inputs = i;
+
+	/* Find tuner input */
+	for (i = 0; i < cx->nof_inputs; i++) {
+		if (cx->card->video_inputs[i].video_type ==
+				CX18_CARD_INPUT_VID_TUNER)
+			break;
+	}
+	if (i == cx->nof_inputs)
+		i = 0;
+	cx->active_input = i;
+	cx->audio_input = cx->card->video_inputs[i].audio_index;
+}
+
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+			  const struct pci_device_id *pci_id)
+{
+	u16 cmd;
+	unsigned char pci_latency;
+
+	CX18_DEBUG_INFO("Enabling pci device\n");
+
+	if (pci_enable_device(pci_dev)) {
+		CX18_ERR("Can't enable device %d!\n", cx->instance);
+		return -EIO;
+	}
+	if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+		CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+		return -EIO;
+	}
+	if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+		CX18_ERR("Cannot request encoder memory region, card %d\n",
+			 cx->instance);
+		return -EIO;
+	}
+
+	/* Enable bus mastering and memory mapped IO for the CX23418 */
+	pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+	pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+
+	cx->card_rev = pci_dev->revision;
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+
+	if (pci_latency < 64 && cx18_pci_latency) {
+		CX18_INFO("Unreasonably low latency timer, "
+			       "setting to 64 (was %d)\n", pci_latency);
+		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+		pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+	}
+
+	CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+		   "irq: %d, latency: %d, memory: 0x%llx\n",
+		   cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+		   PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+		   cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
+
+	return 0;
+}
+
+static void cx18_init_subdevs(struct cx18 *cx)
+{
+	u32 hw = cx->card->hw_all;
+	u32 device;
+	int i;
+
+	for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+
+		if (!(device & hw))
+			continue;
+
+		switch (device) {
+		case CX18_HW_DVB:
+		case CX18_HW_TVEEPROM:
+			/* These subordinate devices do not use probing */
+			cx->hw_flags |= device;
+			break;
+		case CX18_HW_418_AV:
+			/* The A/V decoder gets probed earlier to set PLLs */
+			/* Just note that the card uses it (i.e. has analog) */
+			cx->hw_flags |= device;
+			break;
+		case CX18_HW_GPIO_RESET_CTRL:
+			/*
+			 * The Reset Controller gets probed and added to
+			 * hw_flags earlier for i2c adapter/bus initialization
+			 */
+			break;
+		case CX18_HW_GPIO_MUX:
+			if (cx18_gpio_register(cx, device) == 0)
+				cx->hw_flags |= device;
+			break;
+		default:
+			if (cx18_i2c_register(cx, i) == 0)
+				cx->hw_flags |= device;
+			break;
+		}
+	}
+
+	if (cx->hw_flags & CX18_HW_418_AV)
+		cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+
+	if (cx->card->hw_muxer != 0)
+		cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+}
+
+static int __devinit cx18_probe(struct pci_dev *pci_dev,
+				const struct pci_device_id *pci_id)
+{
+	int retval = 0;
+	int i;
+	u32 devtype;
+	struct cx18 *cx;
+
+	/* FIXME - module parameter arrays constrain max instances */
+	i = atomic_inc_return(&cx18_instance) - 1;
+	if (i >= CX18_MAX_CARDS) {
+		printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+		       "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+		return -ENOMEM;
+	}
+
+	cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+	if (cx == NULL) {
+		printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+		       i);
+		return -ENOMEM;
+	}
+	cx->pci_dev = pci_dev;
+	cx->instance = i;
+
+	retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+	if (retval) {
+		printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+		       "\n", cx->instance);
+		kfree(cx);
+		return retval;
+	}
+	snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+		 cx->instance);
+	CX18_INFO("Initializing card %d\n", cx->instance);
+
+	cx18_process_options(cx);
+	if (cx->options.cardtype == -1) {
+		retval = -ENODEV;
+		goto err;
+	}
+
+	retval = cx18_init_struct1(cx);
+	if (retval)
+		goto err;
+
+	CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
+
+	/* PCI Device Setup */
+	retval = cx18_setup_pci(cx, pci_dev, pci_id);
+	if (retval != 0)
+		goto free_workqueues;
+
+	/* map io memory */
+	CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+		   (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+	cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+				       CX18_MEM_SIZE);
+	if (!cx->enc_mem) {
+		CX18_ERR("ioremap failed. Can't get a window into CX23418 "
+			 "memory and register space\n");
+		CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
+			 "vmalloc address space for the window\n");
+		CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+		CX18_ERR("Use the vmalloc= kernel command line option to set "
+			 "VmallocTotal to a larger value\n");
+		retval = -ENOMEM;
+		goto free_mem;
+	}
+	cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+	devtype = cx18_read_reg(cx, 0xC72028);
+	switch (devtype & 0xff000000) {
+	case 0xff000000:
+		CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+		break;
+	case 0x01000000:
+		CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+		break;
+	default:
+		CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+		break;
+	}
+
+	cx18_init_power(cx, 1);
+	cx18_init_memory(cx);
+
+	cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
+	cx18_init_scb(cx);
+
+	cx18_gpio_init(cx);
+
+	/* Initialize integrated A/V decoder early to set PLLs, just in case */
+	retval = cx18_av_probe(cx);
+	if (retval) {
+		CX18_ERR("Could not register A/V decoder subdevice\n");
+		goto free_map;
+	}
+
+	/* Initialize GPIO Reset Controller to do chip resets during i2c init */
+	if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+		if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+			CX18_WARN("Could not register GPIO reset controller"
+				  "subdevice; proceeding anyway.\n");
+		else
+			cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+	}
+
+	/* active i2c  */
+	CX18_DEBUG_INFO("activating i2c...\n");
+	retval = init_cx18_i2c(cx);
+	if (retval) {
+		CX18_ERR("Could not initialize i2c\n");
+		goto free_map;
+	}
+
+	if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+		/* Based on the model number the cardtype may be changed.
+		   The PCI IDs are not always reliable. */
+		const struct cx18_card *orig_card = cx->card;
+		cx18_process_eeprom(cx);
+
+		if (cx->card != orig_card) {
+			/* Changed the cardtype; re-reset the I2C chips */
+			cx18_gpio_init(cx);
+			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+					core, reset, (u32) CX18_GPIO_RESET_I2C);
+		}
+	}
+	if (cx->card->comment)
+		CX18_INFO("%s", cx->card->comment);
+	if (cx->card->v4l2_capabilities == 0) {
+		retval = -ENODEV;
+		goto free_i2c;
+	}
+	cx18_init_memory(cx);
+	cx18_init_scb(cx);
+
+	/* Register IRQ */
+	retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+			     IRQF_SHARED | IRQF_DISABLED,
+			     cx->v4l2_dev.name, (void *)cx);
+	if (retval) {
+		CX18_ERR("Failed to register irq %d\n", retval);
+		goto free_i2c;
+	}
+
+	if (cx->std == 0)
+		cx->std = V4L2_STD_NTSC_M;
+
+	if (cx->options.tuner == -1) {
+		for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+			if ((cx->std & cx->card->tuners[i].std) == 0)
+				continue;
+			cx->options.tuner = cx->card->tuners[i].tuner;
+			break;
+		}
+	}
+	/* if no tuner was found, then pick the first tuner in the card list */
+	if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+		cx->std = cx->card->tuners[0].std;
+		if (cx->std & V4L2_STD_PAL)
+			cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+		else if (cx->std & V4L2_STD_NTSC)
+			cx->std = V4L2_STD_NTSC_M;
+		else if (cx->std & V4L2_STD_SECAM)
+			cx->std = V4L2_STD_SECAM_L;
+		cx->options.tuner = cx->card->tuners[0].tuner;
+	}
+	if (cx->options.radio == -1)
+		cx->options.radio = (cx->card->radio_input.audio_type != 0);
+
+	/* The card is now fully identified, continue with card-specific
+	   initialization. */
+	cx18_init_struct2(cx);
+
+	cx18_init_subdevs(cx);
+
+	if (cx->std & V4L2_STD_525_60)
+		cx->is_60hz = 1;
+	else
+		cx->is_50hz = 1;
+
+	cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
+
+	if (cx->options.radio > 0)
+		cx->v4l2_cap |= V4L2_CAP_RADIO;
+
+	if (cx->options.tuner > -1) {
+		struct tuner_setup setup;
+
+		setup.addr = ADDR_UNSET;
+		setup.type = cx->options.tuner;
+		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+		if (cx->options.radio > 0)
+			setup.mode_mask |= T_RADIO;
+		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+			cx18_reset_tuner_gpio : NULL;
+		cx18_call_all(cx, tuner, s_type_addr, &setup);
+		if (setup.type == TUNER_XC2028) {
+			static struct xc2028_ctrl ctrl = {
+				.fname = XC2028_DEFAULT_FIRMWARE,
+				.max_len = 64,
+			};
+			struct v4l2_priv_tun_config cfg = {
+				.tuner = cx->options.tuner,
+				.priv = &ctrl,
+			};
+			cx18_call_all(cx, tuner, s_config, &cfg);
+		}
+	}
+
+	/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+	   are not. */
+	cx->tuner_std = cx->std;
+	if (cx->std == V4L2_STD_ALL)
+		cx->std = V4L2_STD_NTSC_M;
+
+	retval = cx18_streams_setup(cx);
+	if (retval) {
+		CX18_ERR("Error %d setting up streams\n", retval);
+		goto free_irq;
+	}
+	retval = cx18_streams_register(cx);
+	if (retval) {
+		CX18_ERR("Error %d registering devices\n", retval);
+		goto free_streams;
+	}
+
+	CX18_INFO("Initialized card: %s\n", cx->card_name);
+
+	/* Load cx18 submodules (cx18-alsa) */
+	request_modules(cx);
+	return 0;
+
+free_streams:
+	cx18_streams_cleanup(cx, 1);
+free_irq:
+	free_irq(cx->pci_dev->irq, (void *)cx);
+free_i2c:
+	exit_cx18_i2c(cx);
+free_map:
+	cx18_iounmap(cx);
+free_mem:
+	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+free_workqueues:
+	destroy_workqueue(cx->in_work_queue);
+err:
+	if (retval == 0)
+		retval = -ENODEV;
+	CX18_ERR("Error %d on initialization\n", retval);
+
+	v4l2_device_unregister(&cx->v4l2_dev);
+	kfree(cx);
+	return retval;
+}
+
+int cx18_init_on_first_open(struct cx18 *cx)
+{
+	int video_input;
+	int fw_retry_count = 3;
+	struct v4l2_frequency vf;
+	struct cx18_open_id fh;
+	v4l2_std_id std;
+
+	fh.cx = cx;
+
+	if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+		return -ENXIO;
+
+	if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+		return 0;
+
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (cx18_firmware_init(cx) == 0)
+			break;
+		if (fw_retry_count > 1)
+			CX18_WARN("Retry loading firmware\n");
+	}
+
+	if (fw_retry_count == 0) {
+		set_bit(CX18_F_I_FAILED, &cx->i_flags);
+		return -ENXIO;
+	}
+	set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+	/*
+	 * Init the firmware twice to work around a silicon bug
+	 * with the digital TS.
+	 *
+	 * The second firmware load requires us to normalize the APU state,
+	 * or the audio for the first analog capture will be badly incorrect.
+	 *
+	 * I can't seem to call APU_RESETAI and have it succeed without the
+	 * APU capturing audio, so we start and stop it here to do the reset
+	 */
+
+	/* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+	cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+	cx18_vapi(cx, CX18_APU_RESETAI, 0);
+	cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+	fw_retry_count = 3;
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (cx18_firmware_init(cx) == 0)
+			break;
+		if (fw_retry_count > 1)
+			CX18_WARN("Retry loading firmware\n");
+	}
+
+	if (fw_retry_count == 0) {
+		set_bit(CX18_F_I_FAILED, &cx->i_flags);
+		return -ENXIO;
+	}
+
+	/*
+	 * The second firmware load requires us to normalize the APU state,
+	 * or the audio for the first analog capture will be badly incorrect.
+	 *
+	 * I can't seem to call APU_RESETAI and have it succeed without the
+	 * APU capturing audio, so we start and stop it here to do the reset
+	 */
+
+	/* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+	cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+	cx18_vapi(cx, CX18_APU_RESETAI, 0);
+	cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+	/* Init the A/V decoder, if it hasn't been already */
+	v4l2_subdev_call(cx->sd_av, core, load_fw);
+
+	vf.tuner = 0;
+	vf.type = V4L2_TUNER_ANALOG_TV;
+	vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+	/* Set initial frequency. For PAL/SECAM broadcasts no
+	   'default' channel exists AFAIK. */
+	if (cx->std == V4L2_STD_NTSC_M_JP)
+		vf.frequency = 1460;	/* ch. 1 91250*16/1000 */
+	else if (cx->std & V4L2_STD_NTSC_M)
+		vf.frequency = 1076;	/* ch. 4 67250*16/1000 */
+
+	video_input = cx->active_input;
+	cx->active_input++;	/* Force update of input */
+	cx18_s_input(NULL, &fh, video_input);
+
+	/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+	   in one place. */
+	cx->std++;		/* Force full standard initialization */
+	std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
+	cx18_s_std(NULL, &fh, &std);
+	cx18_s_frequency(NULL, &fh, &vf);
+	return 0;
+}
+
+static void cx18_cancel_in_work_orders(struct cx18 *cx)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+		cancel_work_sync(&cx->in_work_order[i].work);
+}
+
+static void cx18_cancel_out_work_orders(struct cx18 *cx)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_STREAMS; i++)
+		if (&cx->streams[i].video_dev != NULL)
+			cancel_work_sync(&cx->streams[i].out_work_order);
+}
+
+static void cx18_remove(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct cx18 *cx = to_cx18(v4l2_dev);
+	int i;
+
+	CX18_DEBUG_INFO("Removing Card\n");
+
+	flush_request_modules(cx);
+
+	/* Stop all captures */
+	CX18_DEBUG_INFO("Stopping all streams\n");
+	if (atomic_read(&cx->tot_capturing) > 0)
+		cx18_stop_all_captures(cx);
+
+	/* Stop interrupts that cause incoming work to be queued */
+	cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+
+	/* Incoming work can cause outgoing work, so clean up incoming first */
+	cx18_cancel_in_work_orders(cx);
+	cx18_cancel_out_work_orders(cx);
+
+	/* Stop ack interrupts that may have been needed for work to finish */
+	cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+	cx18_halt_firmware(cx);
+
+	destroy_workqueue(cx->in_work_queue);
+
+	cx18_streams_cleanup(cx, 1);
+
+	exit_cx18_i2c(cx);
+
+	free_irq(cx->pci_dev->irq, (void *)cx);
+
+	cx18_iounmap(cx);
+
+	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+	pci_disable_device(cx->pci_dev);
+
+	if (cx->vbi.sliced_mpeg_data[0] != NULL)
+		for (i = 0; i < CX18_VBI_FRAMES; i++)
+			kfree(cx->vbi.sliced_mpeg_data[i]);
+
+	v4l2_ctrl_handler_free(&cx->av_state.hdl);
+
+	CX18_INFO("Removed %s\n", cx->card_name);
+
+	v4l2_device_unregister(v4l2_dev);
+	kfree(cx);
+}
+
+
+/* define a pci_driver for card detection */
+static struct pci_driver cx18_pci_driver = {
+      .name =     "cx18",
+      .id_table = cx18_pci_tbl,
+      .probe =    cx18_probe,
+      .remove =   cx18_remove,
+};
+
+static int __init module_start(void)
+{
+	printk(KERN_INFO "cx18:  Start initialization, version %s\n",
+	       CX18_VERSION);
+
+	/* Validate parameters */
+	if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+		printk(KERN_ERR "cx18:  Exiting, cx18_first_minor must be between 0 and %d\n",
+		     CX18_MAX_CARDS - 1);
+		return -1;
+	}
+
+	if (cx18_debug < 0 || cx18_debug > 511) {
+		cx18_debug = 0;
+		printk(KERN_INFO "cx18:   Debug value must be >= 0 and <= 511!\n");
+	}
+
+	if (pci_register_driver(&cx18_pci_driver)) {
+		printk(KERN_ERR "cx18:   Error detecting PCI card\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "cx18:  End initialization\n");
+	return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+	pci_unregister_driver(&cx18_pci_driver);
+}
+
+module_init(module_start);
+module_exit(module_cleanup);
+MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
new file mode 100644
index 000000000000..2767c64df0c8
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -0,0 +1,730 @@
+/*
+ *  cx18 driver internal defines and structures
+ *
+ *  Derived from ivtv-driver.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#ifndef CX18_DRIVER_H
+#define CX18_DRIVER_H
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/ir-kbd-i2c.h>
+#include "cx18-mailbox.h"
+#include "cx18-av-core.h"
+#include "cx23418.h"
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+/* Videobuf / YUV support */
+#include <media/videobuf-core.h>
+#include <media/videobuf-vmalloc.h>
+
+#ifndef CONFIG_PCI
+#  error "This driver requires kernel PCI support."
+#endif
+
+#define CX18_MEM_OFFSET	0x00000000
+#define CX18_MEM_SIZE	0x04000000
+#define CX18_REG_OFFSET	0x02000000
+
+/* Maximum cx18 driver instances. */
+#define CX18_MAX_CARDS 32
+
+/* Supported cards */
+#define CX18_CARD_HVR_1600_ESMT	      0	/* Hauppauge HVR 1600 (ESMT memory) */
+#define CX18_CARD_HVR_1600_SAMSUNG    1	/* Hauppauge HVR 1600 (Samsung memory) */
+#define CX18_CARD_COMPRO_H900 	      2	/* Compro VideoMate H900 */
+#define CX18_CARD_YUAN_MPC718 	      3	/* Yuan MPC718 */
+#define CX18_CARD_CNXT_RAPTOR_PAL     4	/* Conexant Raptor PAL */
+#define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/
+#define CX18_CARD_LEADTEK_PVR2100     6 /* Leadtek WinFast PVR2100 */
+#define CX18_CARD_LEADTEK_DVR3100H    7 /* Leadtek WinFast DVR3100 H */
+#define CX18_CARD_GOTVIEW_PCI_DVD3    8 /* GoTView PCI DVD3 Hybrid */
+#define CX18_CARD_HVR_1600_S5H1411    9 /* Hauppauge HVR 1600 s5h1411/tda18271*/
+#define CX18_CARD_LAST		      9
+
+#define CX18_ENC_STREAM_TYPE_MPG  0
+#define CX18_ENC_STREAM_TYPE_TS   1
+#define CX18_ENC_STREAM_TYPE_YUV  2
+#define CX18_ENC_STREAM_TYPE_VBI  3
+#define CX18_ENC_STREAM_TYPE_PCM  4
+#define CX18_ENC_STREAM_TYPE_IDX  5
+#define CX18_ENC_STREAM_TYPE_RAD  6
+#define CX18_MAX_STREAMS	  7
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_CX      0x14f1
+#define PCI_DEVICE_ID_CX23418 0x5b7a
+
+/* subsystem vendor ID */
+#define CX18_PCI_ID_HAUPPAUGE 		0x0070
+#define CX18_PCI_ID_COMPRO 		0x185b
+#define CX18_PCI_ID_YUAN 		0x12ab
+#define CX18_PCI_ID_CONEXANT		0x14f1
+#define CX18_PCI_ID_TOSHIBA		0x1179
+#define CX18_PCI_ID_LEADTEK		0x107D
+#define CX18_PCI_ID_GOTVIEW		0x5854
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+/* DMA Buffers, Default size in MB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFFERS  1
+#define CX18_DEFAULT_ENC_MPG_BUFFERS 2
+#define CX18_DEFAULT_ENC_IDX_BUFFERS 1
+#define CX18_DEFAULT_ENC_YUV_BUFFERS 2
+#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
+#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */
+#define CX18_UNIT_ENC_YUV_BUFSIZE	(720 *  32 * 3 / 2) /* bytes */
+#define CX18_625_LINE_ENC_YUV_BUFSIZE	(CX18_UNIT_ENC_YUV_BUFSIZE * 576/32)
+#define CX18_525_LINE_ENC_YUV_BUFSIZE	(CX18_UNIT_ENC_YUV_BUFSIZE * 480/32)
+
+/* IDX buffer size should be a multiple of the index entry size from the chip */
+struct cx18_enc_idx_entry {
+	__le32 length;
+	__le32 offset_low;
+	__le32 offset_high;
+	__le32 flags;
+	__le32 pts_low;
+	__le32 pts_high;
+} __attribute__ ((packed));
+#define CX18_UNIT_ENC_IDX_BUFSIZE \
+	(sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES)
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE   32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE  32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE  (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1)
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE  (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE   4
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_WARN  (1 << 0)
+#define CX18_DBGFLG_INFO  (1 << 1)
+#define CX18_DBGFLG_API   (1 << 2)
+#define CX18_DBGFLG_DMA   (1 << 3)
+#define CX18_DBGFLG_IOCTL (1 << 4)
+#define CX18_DBGFLG_FILE  (1 << 5)
+#define CX18_DBGFLG_I2C   (1 << 6)
+#define CX18_DBGFLG_IRQ   (1 << 7)
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_HIGHVOL (1 << 8)
+
+/* NOTE: extra space before comma in 'fmt , ## args' is required for
+   gcc-2.95, otherwise it won't compile. */
+#define CX18_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & cx18_debug) \
+			v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+	} while (0)
+#define CX18_DEBUG_WARN(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_API(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_DMA(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_I2C(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+	do { \
+		if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+			v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+	} while (0)
+#define CX18_DEBUG_HI_WARN(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+/* Standard kernel messages */
+#define CX18_ERR(fmt, args...)      v4l2_err(&cx->v4l2_dev, fmt , ## args)
+#define CX18_WARN(fmt, args...)     v4l2_warn(&cx->v4l2_dev, fmt , ## args)
+#define CX18_INFO(fmt, args...)     v4l2_info(&cx->v4l2_dev, fmt , ## args)
+
+/* Messages for internal subdevs to use */
+#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \
+	do { \
+		if ((x) & cx18_debug) \
+			v4l2_info(dev, " " type ": " fmt , ## args); \
+	} while (0)
+#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_API_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \
+		CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \
+	do { \
+		if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+			v4l2_info(dev, " " type ": " fmt , ## args); \
+	} while (0)
+#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \
+	CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_ERR_DEV(dev, fmt, args...)      v4l2_err(dev, fmt , ## args)
+#define CX18_WARN_DEV(dev, fmt, args...)     v4l2_warn(dev, fmt , ## args)
+#define CX18_INFO_DEV(dev, fmt, args...)     v4l2_info(dev, fmt , ## args)
+
+extern int cx18_debug;
+
+struct cx18_options {
+	int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
+	int cardtype;		/* force card type on load */
+	int tuner;		/* set tuner on load */
+	int radio;		/* enable/disable radio */
+};
+
+/* per-mdl bit flags */
+#define CX18_F_M_NEED_SWAP  0	/* mdl buffer data must be endianess swapped */
+
+/* per-stream, s_flags */
+#define CX18_F_S_CLAIMED 	3	/* this stream is claimed */
+#define CX18_F_S_STREAMING      4	/* the fw is decoding/encoding this stream */
+#define CX18_F_S_INTERNAL_USE	5	/* this stream is used internally (sliced VBI processing) */
+#define CX18_F_S_STREAMOFF	7	/* signal end of stream EOS */
+#define CX18_F_S_APPL_IO        8	/* this stream is used read/written by an application */
+#define CX18_F_S_STOPPING	9	/* telling the fw to stop capturing */
+
+/* per-cx18, i_flags */
+#define CX18_F_I_LOADED_FW		0 	/* Loaded firmware 1st time */
+#define CX18_F_I_EOS			4 	/* End of encoder stream */
+#define CX18_F_I_RADIO_USER		5 	/* radio tuner is selected */
+#define CX18_F_I_ENC_PAUSED		13 	/* the encoder is paused */
+#define CX18_F_I_INITED			21 	/* set after first open */
+#define CX18_F_I_FAILED			22 	/* set if first open failed */
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define CX18_SLICED_TYPE_TELETEXT_B     (1)
+#define CX18_SLICED_TYPE_CAPTION_525    (4)
+#define CX18_SLICED_TYPE_WSS_625        (5)
+#define CX18_SLICED_TYPE_VPS            (7)
+
+/**
+ * list_entry_is_past_end - check if a previous loop cursor is off list end
+ * @pos:	the type * previously used as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Check if the entry's list_head is the head of the list, thus it's not a
+ * real entry but was the loop cursor that walked past the end
+ */
+#define list_entry_is_past_end(pos, head, member) \
+	(&pos->member == (head))
+
+struct cx18_buffer {
+	struct list_head list;
+	dma_addr_t dma_handle;
+	char *buf;
+
+	u32 bytesused;
+	u32 readpos;
+};
+
+struct cx18_mdl {
+	struct list_head list;
+	u32 id;		/* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */
+
+	unsigned int skipped;
+	unsigned long m_flags;
+
+	struct list_head buf_list;
+	struct cx18_buffer *curr_buf; /* current buffer in list for reading */
+
+	u32 bytesused;
+	u32 readpos;
+};
+
+struct cx18_queue {
+	struct list_head list;
+	atomic_t depth;
+	u32 bytesused;
+	spinlock_t lock;
+};
+
+struct cx18_stream; /* forward reference */
+
+struct cx18_dvb {
+	struct cx18_stream *stream;
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct dmxdev dmxdev;
+	struct dvb_adapter dvb_adapter;
+	struct dvb_demux demux;
+	struct dvb_frontend *fe;
+	struct dvb_net dvbnet;
+	int enabled;
+	int feeding;
+	struct mutex feedlock;
+};
+
+struct cx18;	 /* forward reference */
+struct cx18_scb; /* forward reference */
+
+
+#define CX18_MAX_MDL_ACKS 2
+#define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
+
+#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
+#define CX18_F_EWO_MB_STALE_WHILE_PROC   0x2
+#define CX18_F_EWO_MB_STALE \
+	     (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC)
+
+struct cx18_in_work_order {
+	struct work_struct work;
+	atomic_t pending;
+	struct cx18 *cx;
+	unsigned long flags;
+	int rpu;
+	struct cx18_mailbox mb;
+	struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
+	char *str;
+};
+
+#define CX18_INVALID_TASK_HANDLE 0xffffffff
+
+struct cx18_stream {
+	/* These first five fields are always set, even if the stream
+	   is not actually created. */
+	struct video_device *video_dev;	/* NULL when stream not created */
+	struct cx18_dvb *dvb;		/* DVB / Digital Transport */
+	struct cx18 *cx; 		/* for ease of use */
+	const char *name;		/* name of the stream */
+	int type;			/* stream type */
+	u32 handle;			/* task handle */
+	unsigned int mdl_base_idx;
+
+	u32 id;
+	unsigned long s_flags;	/* status flags, see above */
+	int dma;		/* can be PCI_DMA_TODEVICE,
+				   PCI_DMA_FROMDEVICE or
+				   PCI_DMA_NONE */
+	wait_queue_head_t waitq;
+
+	/* Buffers */
+	struct list_head buf_pool;	/* buffers not attached to an MDL */
+	u32 buffers;			/* total buffers owned by this stream */
+	u32 buf_size;			/* size in bytes of a single buffer */
+
+	/* MDL sizes - all stream MDLs are the same size */
+	u32 bufs_per_mdl;
+	u32 mdl_size;		/* total bytes in all buffers in a mdl */
+
+	/* MDL Queues */
+	struct cx18_queue q_free;	/* free - in rotation, not committed */
+	struct cx18_queue q_busy;	/* busy - in use by firmware */
+	struct cx18_queue q_full;	/* full - data for user apps */
+	struct cx18_queue q_idle;	/* idle - not in rotation */
+
+	struct work_struct out_work_order;
+
+	/* Videobuf for YUV video */
+	u32 pixelformat;
+	u32 vb_bytes_per_frame;
+	struct list_head vb_capture;    /* video capture queue */
+	spinlock_t vb_lock;
+	struct timer_list vb_timeout;
+
+	struct videobuf_queue vbuf_q;
+	spinlock_t vbuf_q_lock; /* Protect vbuf_q */
+	enum v4l2_buf_type vb_type;
+};
+
+struct cx18_videobuf_buffer {
+	/* Common video buffer sub-system struct */
+	struct videobuf_buffer vb;
+	v4l2_std_id tvnorm; /* selected tv norm */
+	u32 bytes_used;
+};
+
+struct cx18_open_id {
+	struct v4l2_fh fh;
+	u32 open_id;
+	int type;
+	struct cx18 *cx;
+};
+
+static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh)
+{
+	return container_of(fh, struct cx18_open_id, fh);
+}
+
+static inline struct cx18_open_id *file2id(struct file *file)
+{
+	return fh2id(file->private_data);
+}
+
+/* forward declaration of struct defined in cx18-cards.h */
+struct cx18_card;
+
+/*
+ * A note about "sliced" VBI data as implemented in this driver:
+ *
+ * Currently we collect the sliced VBI in the form of Ancillary Data
+ * packets, inserted by the AV core decoder/digitizer/slicer in the
+ * horizontal blanking region of the VBI lines, in "raw" mode as far as
+ * the Encoder is concerned.  We don't ever tell the Encoder itself
+ * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode)
+ *
+ * We then process the ancillary data ourselves to send the sliced data
+ * to the user application directly or build up MPEG-2 private stream 1
+ * packets to splice into (only!) MPEG-2 PS streams for the user app.
+ *
+ * (That's how ivtv essentially does it.)
+ *
+ * The Encoder should be able to extract certain sliced VBI data for
+ * us and provide it in a separate stream or splice it into any type of
+ * MPEG PS or TS stream, but this isn't implemented yet.
+ */
+
+/*
+ * Number of "raw" VBI samples per horizontal line we tell the Encoder to
+ * grab from the decoder/digitizer/slicer output for raw or sliced VBI.
+ * It depends on the pixel clock and the horiz rate:
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ *     = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ *  Sliced VBI data is sent as ancillary data during horizontal blanking
+ *  Raw VBI is sent as active video samples during vertcal blanking
+ *
+ *  We use a  BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ *  length of 720 pixels @ 4:2:2 sampling.  Thus...
+ *
+ *  For systems that use a 15.734 kHz horizontal rate, such as
+ *  NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ *  (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ *  4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ *  For systems that use a 15.625 kHz horizontal rate, such as
+ *  PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ *  (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ *  4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ */
+static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
+static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
+static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
+
+#define CX18_VBI_FRAMES 32
+
+struct vbi_info {
+	/* Current state of v4l2 VBI settings for this device */
+	struct v4l2_format in;
+	struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */
+	u32 count;    /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */
+	u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */
+
+	u32 frame; /* Count of VBI buffers/frames received from Encoder */
+
+	/*
+	 * Vars for creation and insertion of MPEG Private Stream 1 packets
+	 * of sliced VBI data into an MPEG PS
+	 */
+
+	/* Boolean: create and insert Private Stream 1 packets into the PS */
+	int insert_mpeg;
+
+	/*
+	 * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+	 * Used in cx18-vbi.c only for collecting sliced data, and as a source
+	 * during conversion of sliced VBI data into MPEG Priv Stream 1 packets.
+	 * We don't need to save state here, but the array may have been a bit
+	 * too big (2304 bytes) to alloc from the stack.
+	 */
+	struct v4l2_sliced_vbi_data sliced_data[36];
+
+	/*
+	 * A ring buffer of driver-generated MPEG-2 PS
+	 * Program Pack/Private Stream 1 packets for sliced VBI data insertion
+	 * into the MPEG PS stream.
+	 *
+	 * In each sliced_mpeg_data[] buffer is:
+	 * 	16 byte MPEG-2 PS Program Pack Header
+	 * 	16 byte MPEG-2 Private Stream 1 PES Header
+	 * 	 4 byte magic number: "itv0" or "ITV0"
+	 * 	 4 byte first  field line mask, if "itv0"
+	 * 	 4 byte second field line mask, if "itv0"
+	 * 	36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data
+	 *
+	 * 	Each line in the payload is
+	 *	 1 byte line header derived from the SDID (WSS, CC, VPS, etc.)
+	 *	42 bytes of line data
+	 *
+	 * That's a maximum 1552 bytes of payload in the Private Stream 1 packet
+	 * which is the payload size a PVR-350 (CX23415) MPEG decoder will
+	 * accept for VBI data. So, including the headers, it's a maximum 1584
+	 * bytes total.
+	 */
+#define CX18_SLICED_MPEG_DATA_MAXSZ	1584
+	/* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */
+#define CX18_SLICED_MPEG_DATA_BUFSZ	(CX18_SLICED_MPEG_DATA_MAXSZ+8)
+	u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
+	u32 sliced_mpeg_size[CX18_VBI_FRAMES];
+
+	/* Count of Program Pack/Program Stream 1 packets inserted into PS */
+	u32 inserted_frame;
+
+	/*
+	 * A dummy driver stream transfer mdl & buffer with a copy of the next
+	 * sliced_mpeg_data[] buffer for output to userland apps.
+	 * Only used in cx18-fileops.c, but its state needs to persist at times.
+	 */
+	struct cx18_mdl sliced_mpeg_mdl;
+	struct cx18_buffer sliced_mpeg_buf;
+};
+
+/* Per cx23418, per I2C bus private algo callback data */
+struct cx18_i2c_algo_callback_data {
+	struct cx18 *cx;
+	int bus_index;   /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
+};
+
+#define CX18_MAX_MMIO_WR_RETRIES 10
+
+/* Struct to hold info about cx18 cards */
+struct cx18 {
+	int instance;
+	struct pci_dev *pci_dev;
+	struct v4l2_device v4l2_dev;
+	struct v4l2_subdev *sd_av;     /* A/V decoder/digitizer sub-device */
+	struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */
+
+	const struct cx18_card *card;	/* card information */
+	const char *card_name;  /* full name of the card */
+	const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+	u8 is_50hz;
+	u8 is_60hz;
+	u8 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 */
+	unsigned int free_mdl_idx;
+	struct cx18_scb __iomem *scb; /* pointer to SCB */
+	struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
+	struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
+
+	struct cx18_av_state av_state;
+
+	/* codec settings */
+	struct cx2341x_handler cxhdl;
+	u32 filter_mode;
+	u32 temporal_strength;
+	u32 spatial_strength;
+
+	/* dualwatch */
+	unsigned long dualwatch_jiffies;
+	u32 dualwatch_stereo_mode;
+
+	struct mutex serialize_lock;    /* mutex used to serialize open/close/start/stop/ioctl operations */
+	struct cx18_options options; 	/* User options */
+	int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
+	int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
+	struct cx18_stream streams[CX18_MAX_STREAMS]; 	/* Stream data */
+	struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */
+	void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data,
+				      size_t num_bytes);
+
+	unsigned long i_flags;  /* global cx18 flags */
+	atomic_t ana_capturing;	/* count number of active analog capture streams */
+	atomic_t tot_capturing;	/* total count number of active capture streams */
+	int search_pack_header;
+
+	int open_id;		/* incremented each time an open occurs, used as
+				   unique ID. Starts at 1, so 0 can be used as
+				   uninitialized value in the stream->id. */
+
+	resource_size_t base_addr;
+
+	u8 card_rev;
+	void __iomem *enc_mem, *reg_mem;
+
+	struct vbi_info vbi;
+
+	u64 mpg_data_received;
+	u64 vbi_data_inserted;
+
+	wait_queue_head_t mb_apu_waitq;
+	wait_queue_head_t mb_cpu_waitq;
+	wait_queue_head_t cap_w;
+	/* when the current DMA is finished this queue is woken up */
+	wait_queue_head_t dma_waitq;
+
+	u32 sw1_irq_mask;
+	u32 sw2_irq_mask;
+	u32 hw2_irq_mask;
+
+	struct workqueue_struct *in_work_queue;
+	char in_workq_name[11]; /* "cx18-NN-in" */
+	struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS];
+	char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
+
+	/* i2c */
+	struct i2c_adapter i2c_adap[2];
+	struct i2c_algo_bit_data i2c_algo[2];
+	struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+
+	struct IR_i2c_init_data ir_i2c_init_data;
+
+	/* gpio */
+	u32 gpio_dir;
+	u32 gpio_val;
+	struct mutex gpio_lock;
+	struct v4l2_subdev sd_gpiomux;
+	struct v4l2_subdev sd_resetctrl;
+
+	/* v4l2 and User settings */
+
+	/* codec settings */
+	u32 audio_input;
+	u32 active_input;
+	v4l2_std_id std;
+	v4l2_std_id tuner_std;	/* The norm of the tuner (fixed) */
+
+	/* Used for cx18-alsa module loading */
+	struct work_struct request_module_wk;
+};
+
+static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct cx18, v4l2_dev);
+}
+
+/* cx18 extensions to be loaded */
+extern int (*cx18_ext_init)(struct cx18 *);
+
+/* Globals */
+extern int cx18_first_minor;
+
+/*==============Prototypes==================*/
+
+/* Return non-zero if a signal is pending */
+int cx18_msleep_timeout(unsigned int msecs, int intr);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, etc. */
+int cx18_init_on_first_open(struct cx18 *cx);
+
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+	return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). Ignore any errors. */
+#define cx18_call_hw(cx, hw, o, f, args...)				\
+	do {								\
+		struct v4l2_subdev *__sd;				\
+		__v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd,	\
+			!(hw) || (__sd->grp_id & (hw)), o, f , ##args);	\
+	} while (0)
+
+#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). If the callback returns an error
+ * other than 0 or -ENOIOCTLCMD, then return with that error code. */
+#define cx18_call_hw_err(cx, hw, o, f, args...)				\
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev,		\
+			__sd, !(hw) || (__sd->grp_id & (hw)), o, f,	\
+			##args);					\
+})
+
+#define cx18_call_all_err(cx, o, f, args...) \
+	cx18_call_hw_err(cx, 0, o, f , ##args)
+
+#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
new file mode 100644
index 000000000000..3eac59c51231
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -0,0 +1,609 @@
+/*
+ *  cx18 functions for DVB support
+ *
+ *  Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-version.h"
+#include "cx18-dvb.h"
+#include "cx18-io.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "s5h1409.h"
+#include "mxl5005s.h"
+#include "s5h1411.h"
+#include "tda18271.h"
+#include "zl10353.h"
+
+#include <linux/firmware.h>
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "tuner-xc2028.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define FWFILE "dvb-cx18-mpc718-mt352.fw"
+
+#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
+#define CX18_CLOCK_ENABLE2		 0xc71024
+#define CX18_DMUX_CLK_MASK		 0x0080
+
+/*
+ * CX18_CARD_HVR_1600_ESMT
+ * CX18_CARD_HVR_1600_SAMSUNG
+ */
+
+static struct mxl5005s_config hauppauge_hvr1600_tuner = {
+	.i2c_address     = 0xC6 >> 1,
+	.if_freq         = IF_FREQ_5380000HZ,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_C_H,
+	.rssi_enable     = MXL_RSSI_ENABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.qam_gain        = 0x02,
+	.AgcMasterByte   = 0x00,
+};
+
+static struct s5h1409_config hauppauge_hvr1600_config = {
+	.demod_address = 0x32 >> 1,
+	.output_mode   = S5H1409_SERIAL_OUTPUT,
+	.gpio          = S5H1409_GPIO_ON,
+	.qam_if        = 44000,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+	.hvr1600_opt   = S5H1409_HVR1600_OPTIMIZE
+};
+
+/*
+ * CX18_CARD_HVR_1600_S5H1411
+ */
+static struct s5h1411_config hcw_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.vsb_if        = S5H1411_IF_44000,
+	.qam_if        = S5H1411_IF_4000,
+	.inversion     = S5H1411_INVERSION_ON,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+	.atsc_6   = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+	.std_map = &hauppauge_tda18271_std_map,
+	.gate    = TDA18271_GATE_DIGITAL,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+/*
+ * CX18_CARD_LEADTEK_DVR3100H
+ */
+/* Information/confirmation of proper config values provided by Terry Wu */
+static struct zl10353_config leadtek_dvr3100h_demod = {
+	.demod_address         = 0x1e >> 1, /* Datasheet suggested straps */
+	.if2                   = 45600,     /* 4.560 MHz IF from the XC3028 */
+	.parallel_ts           = 1,         /* Not a serial TS */
+	.no_tuner              = 1,         /* XC3028 is not behind the gate */
+	.disable_i2c_gate_ctrl = 1,         /* Disable the I2C gate */
+};
+
+/*
+ * CX18_CARD_YUAN_MPC718
+ */
+/*
+ * Due to
+ *
+ * 1. an absence of information on how to prgram the MT352
+ * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
+ *
+ * We have to use an init sequence that *you* must extract from the Windows
+ * driver (yuanrap.sys) and which we load as a firmware.
+ *
+ * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual
+ * with chip programming details, then I can remove this annoyance.
+ */
+static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
+				   const struct firmware **fw)
+{
+	struct cx18 *cx = stream->cx;
+	const char *fn = FWFILE;
+	int ret;
+
+	ret = request_firmware(fw, fn, &cx->pci_dev->dev);
+	if (ret)
+		CX18_ERR("Unable to open firmware file %s\n", fn);
+	else {
+		size_t sz = (*fw)->size;
+		if (sz < 2 || sz > 64 || (sz % 2) != 0) {
+			CX18_ERR("Firmware %s has a bad size: %lu bytes\n",
+				 fn, (unsigned long) sz);
+			ret = -EILSEQ;
+			release_firmware(*fw);
+			*fw = NULL;
+		}
+	}
+
+	if (ret) {
+		CX18_ERR("The MPC718 board variant with the MT352 DVB-T"
+			  "demodualtor will not work without it\n");
+		CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware "
+			  "mpc718' if you need the firmware\n");
+	}
+	return ret;
+}
+
+static int yuan_mpc718_mt352_init(struct dvb_frontend *fe)
+{
+	struct cx18_dvb *dvb = container_of(fe->dvb,
+					    struct cx18_dvb, dvb_adapter);
+	struct cx18_stream *stream = dvb->stream;
+	const struct firmware *fw = NULL;
+	int ret;
+	int i;
+	u8 buf[3];
+
+	ret = yuan_mpc718_mt352_reqfw(stream, &fw);
+	if (ret)
+		return ret;
+
+	/* Loop through all the register-value pairs in the firmware file */
+	for (i = 0; i < fw->size; i += 2) {
+		buf[0] = fw->data[i];
+		/* Intercept a few registers we want to set ourselves */
+		switch (buf[0]) {
+		case TRL_NOMINAL_RATE_0:
+			/* Set our custom OFDM bandwidth in the case below */
+			break;
+		case TRL_NOMINAL_RATE_1:
+			/* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */
+			/* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */
+			/* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */
+			buf[1] = 0x72;
+			buf[2] = 0x49;
+			mt352_write(fe, buf, 3);
+			break;
+		case INPUT_FREQ_0:
+			/* Set our custom IF in the case below */
+			break;
+		case INPUT_FREQ_1:
+			/* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */
+			buf[1] = 0x31;
+			buf[2] = 0xc0;
+			mt352_write(fe, buf, 3);
+			break;
+		default:
+			/* Pass through the register-value pair from the fw */
+			buf[1] = fw->data[i+1];
+			mt352_write(fe, buf, 2);
+			break;
+		}
+	}
+
+	buf[0] = (u8) TUNER_GO;
+	buf[1] = 0x01; /* Go */
+	mt352_write(fe, buf, 2);
+	release_firmware(fw);
+	return 0;
+}
+
+static struct mt352_config yuan_mpc718_mt352_demod = {
+	.demod_address = 0x1e >> 1,
+	.adc_clock     = 20480,     /* 20.480 MHz */
+	.if2           =  4560,     /*  4.560 MHz */
+	.no_tuner      = 1,         /* XC3028 is not behind the gate */
+	.demod_init    = yuan_mpc718_mt352_init,
+};
+
+static struct zl10353_config yuan_mpc718_zl10353_demod = {
+	.demod_address         = 0x1e >> 1, /* Datasheet suggested straps */
+	.if2                   = 45600,     /* 4.560 MHz IF from the XC3028 */
+	.parallel_ts           = 1,         /* Not a serial TS */
+	.no_tuner              = 1,         /* XC3028 is not behind the gate */
+	.disable_i2c_gate_ctrl = 1,         /* Disable the I2C gate */
+};
+
+static struct zl10353_config gotview_dvd3_zl10353_demod = {
+	.demod_address         = 0x1e >> 1, /* Datasheet suggested straps */
+	.if2                   = 45600,     /* 4.560 MHz IF from the XC3028 */
+	.parallel_ts           = 1,         /* Not a serial TS */
+	.no_tuner              = 1,         /* XC3028 is not behind the gate */
+	.disable_i2c_gate_ctrl = 1,         /* Disable the I2C gate */
+};
+
+static int dvb_register(struct cx18_stream *stream);
+
+/* Kernel DVB framework calls this when the feed needs to start.
+ * The CX18 framework should enable the transport DMA handling
+ * and queue processing.
+ */
+static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
+	struct cx18 *cx;
+	int ret;
+	u32 v;
+
+	if (!stream)
+		return -EINVAL;
+
+	cx = stream->cx;
+	CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
+			feed->pid, feed->index);
+
+	mutex_lock(&cx->serialize_lock);
+	ret = cx18_init_on_first_open(cx);
+	mutex_unlock(&cx->serialize_lock);
+	if (ret) {
+		CX18_ERR("Failed to initialize firmware starting DVB feed\n");
+		return ret;
+	}
+	ret = -EINVAL;
+
+	switch (cx->card->type) {
+	case CX18_CARD_HVR_1600_ESMT:
+	case CX18_CARD_HVR_1600_SAMSUNG:
+	case CX18_CARD_HVR_1600_S5H1411:
+		v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+		v |= 0x00400000; /* Serial Mode */
+		v |= 0x00002000; /* Data Length - Byte */
+		v |= 0x00010000; /* Error - Polarity */
+		v |= 0x00020000; /* Error - Passthru */
+		v |= 0x000c0000; /* Error - Ignore */
+		cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+		break;
+
+	case CX18_CARD_LEADTEK_DVR3100H:
+	case CX18_CARD_YUAN_MPC718:
+	case CX18_CARD_GOTVIEW_PCI_DVD3:
+	default:
+		/* Assumption - Parallel transport - Signalling
+		 * undefined or default.
+		 */
+		break;
+	}
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	mutex_lock(&stream->dvb->feedlock);
+	if (stream->dvb->feeding++ == 0) {
+		CX18_DEBUG_INFO("Starting Transport DMA\n");
+		mutex_lock(&cx->serialize_lock);
+		set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+		ret = cx18_start_v4l2_encode_stream(stream);
+		if (ret < 0) {
+			CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+			stream->dvb->feeding--;
+			if (stream->dvb->feeding == 0)
+				clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+		}
+		mutex_unlock(&cx->serialize_lock);
+	} else
+		ret = 0;
+	mutex_unlock(&stream->dvb->feedlock);
+
+	return ret;
+}
+
+/* Kernel DVB framework calls this when the feed needs to stop. */
+static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
+	struct cx18 *cx;
+	int ret = -EINVAL;
+
+	if (stream) {
+		cx = stream->cx;
+		CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+				feed->pid, feed->index);
+
+		mutex_lock(&stream->dvb->feedlock);
+		if (--stream->dvb->feeding == 0) {
+			CX18_DEBUG_INFO("Stopping Transport DMA\n");
+			mutex_lock(&cx->serialize_lock);
+			ret = cx18_stop_v4l2_encode_stream(stream, 0);
+			mutex_unlock(&cx->serialize_lock);
+		} else
+			ret = 0;
+		mutex_unlock(&stream->dvb->feedlock);
+	}
+
+	return ret;
+}
+
+int cx18_dvb_register(struct cx18_stream *stream)
+{
+	struct cx18 *cx = stream->cx;
+	struct cx18_dvb *dvb = stream->dvb;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret;
+
+	if (!dvb)
+		return -EINVAL;
+
+	dvb->enabled = 0;
+	dvb->stream = stream;
+
+	ret = dvb_register_adapter(&dvb->dvb_adapter,
+			CX18_DRIVER_NAME,
+			THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_out;
+
+	dvb_adapter = &dvb->dvb_adapter;
+
+	dvbdemux = &dvb->demux;
+
+	dvbdemux->priv = (void *)stream;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = cx18_dvb_start_feed;
+	dvbdemux->stop_feed = cx18_dvb_stop_feed;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+		DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+	ret = dvb_dmx_init(dvbdemux);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter;
+
+	dmx = &dvbdemux->dmx;
+
+	dvb->hw_frontend.source = DMX_FRONTEND_0;
+	dvb->mem_frontend.source = DMX_MEMORY_FE;
+	dvb->dmxdev.filternum = 256;
+	dvb->dmxdev.demux = dmx;
+
+	ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
+	if (ret < 0)
+		goto err_dvb_dmx_release;
+
+	ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
+	if (ret < 0)
+		goto err_dvb_dmxdev_release;
+
+	ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	ret = dvb_register(stream);
+	if (ret < 0)
+		goto err_disconnect_frontend;
+
+	dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
+
+	CX18_INFO("DVB Frontend registered\n");
+	CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
+		  stream->dvb->dvb_adapter.num, stream->name,
+		  stream->buffers, stream->buf_size/1024,
+		  (stream->buf_size * 100 / 1024) % 100);
+
+	mutex_init(&dvb->feedlock);
+	dvb->enabled = 1;
+	return ret;
+
+err_disconnect_frontend:
+	dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+	dmx->remove_frontend(dmx, &dvb->mem_frontend);
+err_remove_hw_frontend:
+	dmx->remove_frontend(dmx, &dvb->hw_frontend);
+err_dvb_dmxdev_release:
+	dvb_dmxdev_release(&dvb->dmxdev);
+err_dvb_dmx_release:
+	dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+	dvb_unregister_adapter(dvb_adapter);
+err_out:
+	return ret;
+}
+
+void cx18_dvb_unregister(struct cx18_stream *stream)
+{
+	struct cx18 *cx = stream->cx;
+	struct cx18_dvb *dvb = stream->dvb;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+
+	CX18_INFO("unregister DVB\n");
+
+	if (dvb == NULL || !dvb->enabled)
+		return;
+
+	dvb_adapter = &dvb->dvb_adapter;
+	dvbdemux = &dvb->demux;
+	dmx = &dvbdemux->dmx;
+
+	dmx->close(dmx);
+	dvb_net_release(&dvb->dvbnet);
+	dmx->remove_frontend(dmx, &dvb->mem_frontend);
+	dmx->remove_frontend(dmx, &dvb->hw_frontend);
+	dvb_dmxdev_release(&dvb->dmxdev);
+	dvb_dmx_release(dvbdemux);
+	dvb_unregister_frontend(dvb->fe);
+	dvb_frontend_detach(dvb->fe);
+	dvb_unregister_adapter(dvb_adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. cx18_dvb_start_feed() will also need changes.
+ */
+static int dvb_register(struct cx18_stream *stream)
+{
+	struct cx18_dvb *dvb = stream->dvb;
+	struct cx18 *cx = stream->cx;
+	int ret = 0;
+
+	switch (cx->card->type) {
+	case CX18_CARD_HVR_1600_ESMT:
+	case CX18_CARD_HVR_1600_SAMSUNG:
+		dvb->fe = dvb_attach(s5h1409_attach,
+			&hauppauge_hvr1600_config,
+			&cx->i2c_adap[0]);
+		if (dvb->fe != NULL) {
+			dvb_attach(mxl5005s_attach, dvb->fe,
+				&cx->i2c_adap[0],
+				&hauppauge_hvr1600_tuner);
+			ret = 0;
+		}
+		break;
+	case CX18_CARD_HVR_1600_S5H1411:
+		dvb->fe = dvb_attach(s5h1411_attach,
+				     &hcw_s5h1411_config,
+				     &cx->i2c_adap[0]);
+		if (dvb->fe != NULL)
+			dvb_attach(tda18271_attach, dvb->fe,
+				   0x60, &cx->i2c_adap[0],
+				   &hauppauge_tda18271_config);
+		break;
+	case CX18_CARD_LEADTEK_DVR3100H:
+		dvb->fe = dvb_attach(zl10353_attach,
+				     &leadtek_dvr3100h_demod,
+				     &cx->i2c_adap[1]);
+		if (dvb->fe != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap = &cx->i2c_adap[1],
+				.i2c_addr = 0xc2 >> 1,
+				.ctrl = NULL,
+			};
+			static struct xc2028_ctrl ctrl = {
+				.fname   = XC2028_DEFAULT_FIRMWARE,
+				.max_len = 64,
+				.demod   = XC3028_FE_ZARLINK456,
+				.type    = XC2028_AUTO,
+			};
+
+			fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctrl);
+		}
+		break;
+	case CX18_CARD_YUAN_MPC718:
+		/*
+		 * TODO
+		 * Apparently, these cards also could instead have a
+		 * DiBcom demod supported by one of the db7000 drivers
+		 */
+		dvb->fe = dvb_attach(mt352_attach,
+				     &yuan_mpc718_mt352_demod,
+				     &cx->i2c_adap[1]);
+		if (dvb->fe == NULL)
+			dvb->fe = dvb_attach(zl10353_attach,
+					     &yuan_mpc718_zl10353_demod,
+					     &cx->i2c_adap[1]);
+		if (dvb->fe != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap = &cx->i2c_adap[1],
+				.i2c_addr = 0xc2 >> 1,
+				.ctrl = NULL,
+			};
+			static struct xc2028_ctrl ctrl = {
+				.fname   = XC2028_DEFAULT_FIRMWARE,
+				.max_len = 64,
+				.demod   = XC3028_FE_ZARLINK456,
+				.type    = XC2028_AUTO,
+			};
+
+			fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctrl);
+		}
+		break;
+	case CX18_CARD_GOTVIEW_PCI_DVD3:
+			dvb->fe = dvb_attach(zl10353_attach,
+					     &gotview_dvd3_zl10353_demod,
+					     &cx->i2c_adap[1]);
+		if (dvb->fe != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap = &cx->i2c_adap[1],
+				.i2c_addr = 0xc2 >> 1,
+				.ctrl = NULL,
+			};
+			static struct xc2028_ctrl ctrl = {
+				.fname   = XC2028_DEFAULT_FIRMWARE,
+				.max_len = 64,
+				.demod   = XC3028_FE_ZARLINK456,
+				.type    = XC2028_AUTO,
+			};
+
+			fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctrl);
+		}
+		break;
+	default:
+		/* No Digital Tv Support */
+		break;
+	}
+
+	if (dvb->fe == NULL) {
+		CX18_ERR("frontend initialization failed\n");
+		return -1;
+	}
+
+	dvb->fe->callback = cx18_reset_tuner_gpio;
+
+	ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
+	if (ret < 0) {
+		if (dvb->fe->ops.release)
+			dvb->fe->ops.release(dvb->fe);
+		return ret;
+	}
+
+	/*
+	 * The firmware seems to enable the TS DMUX clock
+	 * under various circumstances.  However, since we know we
+	 * might use it, let's just turn it on ourselves here.
+	 */
+	cx18_write_reg_expect(cx,
+			      (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK,
+			      CX18_CLOCK_ENABLE2,
+			      CX18_DMUX_CLK_MASK,
+			      (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK);
+
+	return ret;
+}
+
+MODULE_FIRMWARE(FWFILE);
diff --git a/drivers/media/pci/cx18/cx18-dvb.h b/drivers/media/pci/cx18/cx18-dvb.h
new file mode 100644
index 000000000000..bf8d8f6f5455
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-dvb.h
@@ -0,0 +1,25 @@
+/*
+ *  cx18 functions for DVB support
+ *
+ *  Copyright (c) 2008 Steven Toth <stoth@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 "cx18-driver.h"
+
+int cx18_dvb_register(struct cx18_stream *stream);
+void cx18_dvb_unregister(struct cx18_stream *stream);
diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
new file mode 100644
index 000000000000..4bfd865a4106
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-fileops.c
@@ -0,0 +1,881 @@
+/*
+ *  cx18 file operation functions
+ *
+ *  Derived from ivtv-fileops.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+#include "cx18-streams.h"
+#include "cx18-controls.h"
+#include "cx18-ioctl.h"
+#include "cx18-cards.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+   If no one else is using this stream then the stream is claimed and
+   associated VBI and IDX streams are also automatically claimed.
+   Possible error returns: -EBUSY if someone else has claimed
+   the stream or 0 on success. */
+int cx18_claim_stream(struct cx18_open_id *id, int type)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[type];
+	struct cx18_stream *s_assoc;
+
+	/* Nothing should ever try to directly claim the IDX stream */
+	if (type == CX18_ENC_STREAM_TYPE_IDX) {
+		CX18_WARN("MPEG Index stream cannot be claimed "
+			  "directly, but something tried.\n");
+		return -EINVAL;
+	}
+
+	if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+		/* someone already claimed this stream */
+		if (s->id == id->open_id) {
+			/* yes, this file descriptor did. So that's OK. */
+			return 0;
+		}
+		if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {
+			/* VBI is handled already internally, now also assign
+			   the file descriptor to this stream for external
+			   reading of the stream. */
+			s->id = id->open_id;
+			CX18_DEBUG_INFO("Start Read VBI\n");
+			return 0;
+		}
+		/* someone else is using this stream already */
+		CX18_DEBUG_INFO("Stream %d is busy\n", type);
+		return -EBUSY;
+	}
+	s->id = id->open_id;
+
+	/*
+	 * CX18_ENC_STREAM_TYPE_MPG needs to claim:
+	 * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or
+	 * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI
+	 * (We don't yet fix up MPEG Index entries for our inserted packets).
+	 *
+	 * For all other streams we're done.
+	 */
+	if (type != CX18_ENC_STREAM_TYPE_MPG)
+		return 0;
+
+	s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+	if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx))
+		s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	else if (!cx18_stream_enabled(s_assoc))
+		return 0;
+
+	set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+
+	/* mark that it is used internally */
+	set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags);
+	return 0;
+}
+EXPORT_SYMBOL(cx18_claim_stream);
+
+/* This function releases a previously claimed stream. It will take into
+   account associated VBI streams. */
+void cx18_release_stream(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_stream *s_assoc;
+
+	s->id = -1;
+	if (s->type == CX18_ENC_STREAM_TYPE_IDX) {
+		/*
+		 * The IDX stream is only used internally, and can
+		 * only be indirectly unclaimed by unclaiming the MPG stream.
+		 */
+		return;
+	}
+
+	if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
+		test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
+		/* this stream is still in use internally */
+		return;
+	}
+	if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+		CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+		return;
+	}
+
+	cx18_flush_queues(s);
+
+	/*
+	 * CX18_ENC_STREAM_TYPE_MPG needs to release the
+	 * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams.
+	 *
+	 * For all other streams we're done.
+	 */
+	if (s->type != CX18_ENC_STREAM_TYPE_MPG)
+		return;
+
+	/* Unclaim the associated MPEG Index stream */
+	s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+	if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+		clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+		cx18_flush_queues(s_assoc);
+	}
+
+	/* Unclaim the associated VBI stream */
+	s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+		if (s_assoc->id == -1) {
+			/*
+			 * The VBI stream is not still claimed by a file
+			 * descriptor, so completely unclaim it.
+			 */
+			clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+			cx18_flush_queues(s_assoc);
+		}
+	}
+}
+EXPORT_SYMBOL(cx18_release_stream);
+
+static void cx18_dualwatch(struct cx18 *cx)
+{
+	struct v4l2_tuner vt;
+	u32 new_stereo_mode;
+	const u32 dual = 0x0200;
+
+	new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
+	memset(&vt, 0, sizeof(vt));
+	cx18_call_all(cx, tuner, g_tuner, &vt);
+	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
+			(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+		new_stereo_mode = dual;
+
+	if (new_stereo_mode == cx->dualwatch_stereo_mode)
+		return;
+
+	CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+			   cx->dualwatch_stereo_mode, new_stereo_mode);
+	if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode))
+		CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+
+static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
+				     int *err)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	struct cx18_mdl *mdl;
+	DEFINE_WAIT(wait);
+
+	*err = 0;
+	while (1) {
+		if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+			/* Process pending program updates and VBI data */
+			if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
+				cx->dualwatch_jiffies = jiffies;
+				cx18_dualwatch(cx);
+			}
+			if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+			    !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+				while ((mdl = cx18_dequeue(s_vbi,
+							   &s_vbi->q_full))) {
+					/* byteswap and process VBI data */
+					cx18_process_vbi_data(cx, mdl,
+							      s_vbi->type);
+					cx18_stream_put_mdl_fw(s_vbi, mdl);
+				}
+			}
+			mdl = &cx->vbi.sliced_mpeg_mdl;
+			if (mdl->readpos != mdl->bytesused)
+				return mdl;
+		}
+
+		/* do we have new data? */
+		mdl = cx18_dequeue(s, &s->q_full);
+		if (mdl) {
+			if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
+						&mdl->m_flags))
+				return mdl;
+			if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+				/* byteswap MPG data */
+				cx18_mdl_swap(mdl);
+			else {
+				/* byteswap and process VBI data */
+				cx18_process_vbi_data(cx, mdl, s->type);
+			}
+			return mdl;
+		}
+
+		/* return if end of stream */
+		if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+			CX18_DEBUG_INFO("EOS %s\n", s->name);
+			return NULL;
+		}
+
+		/* return if file was opened with O_NONBLOCK */
+		if (non_block) {
+			*err = -EAGAIN;
+			return NULL;
+		}
+
+		/* wait for more data to arrive */
+		prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+		/* New buffers might have become available before we were added
+		   to the waitqueue */
+		if (!atomic_read(&s->q_full.depth))
+			schedule();
+		finish_wait(&s->waitq, &wait);
+		if (signal_pending(current)) {
+			/* return if a signal was received */
+			CX18_DEBUG_INFO("User stopped %s\n", s->name);
+			*err = -EINTR;
+			return NULL;
+		}
+	}
+}
+
+static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx)
+{
+	struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl;
+	struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf;
+	int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+	buf->buf = cx->vbi.sliced_mpeg_data[idx];
+	buf->bytesused = cx->vbi.sliced_mpeg_size[idx];
+	buf->readpos = 0;
+
+	mdl->curr_buf = NULL;
+	mdl->bytesused = cx->vbi.sliced_mpeg_size[idx];
+	mdl->readpos = 0;
+}
+
+static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+	struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop)
+{
+	struct cx18 *cx = s->cx;
+	size_t len = buf->bytesused - buf->readpos;
+
+	*stop = false;
+	if (len > ucount)
+		len = ucount;
+	if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
+		/*
+		 * Try to find a good splice point in the PS, just before
+		 * an MPEG-2 Program Pack start code, and provide only
+		 * up to that point to the user, so it's easy to insert VBI data
+		 * the next time around.
+		 *
+		 * This will not work for an MPEG-2 TS and has only been
+		 * verified by analysis to work for an MPEG-2 PS.  Helen Buus
+		 * pointed out this works for the CX23416 MPEG-2 DVD compatible
+		 * stream, and research indicates both the MPEG 2 SVCD and DVD
+		 * stream types use an MPEG-2 PS container.
+		 */
+		/*
+		 * An MPEG-2 Program Stream (PS) is a series of
+		 * MPEG-2 Program Packs terminated by an
+		 * MPEG Program End Code after the last Program Pack.
+		 * A Program Pack may hold a PS System Header packet and any
+		 * number of Program Elementary Stream (PES) Packets
+		 */
+		const char *start = buf->buf + buf->readpos;
+		const char *p = start + 1;
+		const u8 *q;
+		u8 ch = cx->search_pack_header ? 0xba : 0xe0;
+		int stuffing, i;
+
+		while (start + len > p) {
+			/* Scan for a 0 to find a potential MPEG-2 start code */
+			q = memchr(p, 0, start + len - p);
+			if (q == NULL)
+				break;
+			p = q + 1;
+			/*
+			 * Keep looking if not a
+			 * MPEG-2 Pack header start code:  0x00 0x00 0x01 0xba
+			 * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0
+			 */
+			if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+			    q[1] != 0 || q[2] != 1 || q[3] != ch)
+				continue;
+
+			/* If expecting the primary video PES */
+			if (!cx->search_pack_header) {
+				/* Continue if it couldn't be a PES packet */
+				if ((q[6] & 0xc0) != 0x80)
+					continue;
+				/* Check if a PTS or PTS & DTS follow */
+				if (((q[7] & 0xc0) == 0x80 &&  /* PTS only */
+				     (q[9] & 0xf0) == 0x20) || /* PTS only */
+				    ((q[7] & 0xc0) == 0xc0 &&  /* PTS & DTS */
+				     (q[9] & 0xf0) == 0x30)) { /* DTS follows */
+					/* Assume we found the video PES hdr */
+					ch = 0xba; /* next want a Program Pack*/
+					cx->search_pack_header = 1;
+					p = q + 9; /* Skip this video PES hdr */
+				}
+				continue;
+			}
+
+			/* We may have found a Program Pack start code */
+
+			/* Get the count of stuffing bytes & verify them */
+			stuffing = q[13] & 7;
+			/* all stuffing bytes must be 0xff */
+			for (i = 0; i < stuffing; i++)
+				if (q[14 + i] != 0xff)
+					break;
+			if (i == stuffing && /* right number of stuffing bytes*/
+			    (q[4] & 0xc4) == 0x44 && /* marker check */
+			    (q[12] & 3) == 3 &&  /* marker check */
+			    q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */
+			    q[15 + stuffing] == 0 &&
+			    q[16 + stuffing] == 1) {
+				/* We declare we actually found a Program Pack*/
+				cx->search_pack_header = 0; /* expect vid PES */
+				len = (char *)q - start;
+				cx18_setup_sliced_vbi_mdl(cx);
+				*stop = true;
+				break;
+			}
+		}
+	}
+	if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+		CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
+				len, s->name);
+		return -EFAULT;
+	}
+	buf->readpos += len;
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    buf != &cx->vbi.sliced_mpeg_buf)
+		cx->mpg_data_received += len;
+	return len;
+}
+
+static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
+		struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
+{
+	size_t tot_written = 0;
+	int rc;
+	bool stop = false;
+
+	if (mdl->curr_buf == NULL)
+		mdl->curr_buf = list_first_entry(&mdl->buf_list,
+						 struct cx18_buffer, list);
+
+	if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+		/*
+		 * For some reason we've exhausted the buffers, but the MDL
+		 * object still said some data was unread.
+		 * Fix that and bail out.
+		 */
+		mdl->readpos = mdl->bytesused;
+		return 0;
+	}
+
+	list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+		if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+			continue;
+
+		rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written,
+					   ucount - tot_written, &stop);
+		if (rc < 0)
+			return rc;
+		mdl->readpos += rc;
+		tot_written += rc;
+
+		if (stop ||	/* Forced stopping point for VBI insertion */
+		    tot_written >= ucount ||	/* Reader request statisfied */
+		    mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+		    mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
+			break;
+	}
+	return tot_written;
+}
+
+static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
+		size_t tot_count, int non_block)
+{
+	struct cx18 *cx = s->cx;
+	size_t tot_written = 0;
+	int single_frame = 0;
+
+	if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) {
+		/* shouldn't happen */
+		CX18_DEBUG_WARN("Stream %s not initialized before read\n",
+				s->name);
+		return -EIO;
+	}
+
+	/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
+	   frames should arrive one-by-one, so make sure we never output more
+	   than one VBI frame at a time */
+	if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
+		single_frame = 1;
+
+	for (;;) {
+		struct cx18_mdl *mdl;
+		int rc;
+
+		mdl = cx18_get_mdl(s, non_block, &rc);
+		/* if there is no data available... */
+		if (mdl == NULL) {
+			/* if we got data, then return that regardless */
+			if (tot_written)
+				break;
+			/* EOS condition */
+			if (rc == 0) {
+				clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+				clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+				cx18_release_stream(s);
+			}
+			/* set errno */
+			return rc;
+		}
+
+		rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written,
+				tot_count - tot_written);
+
+		if (mdl != &cx->vbi.sliced_mpeg_mdl) {
+			if (mdl->readpos == mdl->bytesused)
+				cx18_stream_put_mdl_fw(s, mdl);
+			else
+				cx18_push(s, mdl, &s->q_full);
+		} else if (mdl->readpos == mdl->bytesused) {
+			int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+			cx->vbi.sliced_mpeg_size[idx] = 0;
+			cx->vbi.inserted_frame++;
+			cx->vbi_data_inserted += mdl->bytesused;
+		}
+		if (rc < 0)
+			return rc;
+		tot_written += rc;
+
+		if (tot_written == tot_count || single_frame)
+			break;
+	}
+	return tot_written;
+}
+
+static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
+		size_t count, loff_t *pos, int non_block)
+{
+	ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
+	struct cx18 *cx = s->cx;
+
+	CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+	if (rc > 0)
+		pos += rc;
+	return rc;
+}
+
+int cx18_start_capture(struct cx18_open_id *id)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	struct cx18_stream *s_vbi;
+	struct cx18_stream *s_idx;
+
+	if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
+		/* you cannot read from these stream types. */
+		return -EPERM;
+	}
+
+	/* Try to claim this stream. */
+	if (cx18_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* If capture is already in progress, then we also have to
+	   do nothing extra. */
+	if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+	    test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* Start associated VBI or IDX stream capture if required */
+	s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+		/*
+		 * The VBI and IDX streams should have been claimed
+		 * automatically, if for internal use, when the MPG stream was
+		 * claimed.  We only need to start these streams capturing.
+		 */
+		if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) &&
+		    !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+			if (cx18_start_v4l2_encode_stream(s_idx)) {
+				CX18_DEBUG_WARN("IDX capture start failed\n");
+				clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+				goto start_failed;
+			}
+			CX18_DEBUG_INFO("IDX capture started\n");
+		}
+		if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+		    !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+			if (cx18_start_v4l2_encode_stream(s_vbi)) {
+				CX18_DEBUG_WARN("VBI capture start failed\n");
+				clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+				goto start_failed;
+			}
+			CX18_DEBUG_INFO("VBI insertion started\n");
+		}
+	}
+
+	/* Tell the card to start capturing */
+	if (!cx18_start_v4l2_encode_stream(s)) {
+		/* We're done */
+		set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		/* Resume a possibly paused encoder */
+		if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+			cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
+		return 0;
+	}
+
+start_failed:
+	CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+	/*
+	 * The associated VBI and IDX streams for internal use are released
+	 * automatically when the MPG stream is released.  We only need to stop
+	 * the associated stream.
+	 */
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+		/* Stop the IDX stream which is always for internal use */
+		if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+			cx18_stop_v4l2_encode_stream(s_idx, 0);
+			clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+		}
+		/* Stop the VBI stream, if only running for internal use */
+		if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+		    !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+			cx18_stop_v4l2_encode_stream(s_vbi, 0);
+			clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+		}
+	}
+	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+	cx18_release_stream(s); /* Also releases associated streams */
+	return -EIO;
+}
+
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		loff_t *pos)
+{
+	struct cx18_open_id *id = file2id(filp);
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int rc;
+
+	CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+	mutex_lock(&cx->serialize_lock);
+	rc = cx18_start_capture(id);
+	mutex_unlock(&cx->serialize_lock);
+	if (rc)
+		return rc;
+
+	if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+		return videobuf_read_stream(&s->vbuf_q, buf, count, pos, 0,
+			filp->f_flags & O_NONBLOCK);
+	}
+
+	return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+	struct cx18_open_id *id = file2id(filp);
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	/* Start a capture if there is none */
+	if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		int rc;
+
+		mutex_lock(&cx->serialize_lock);
+		rc = cx18_start_capture(id);
+		mutex_unlock(&cx->serialize_lock);
+		if (rc) {
+			CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
+					s->name, rc);
+			return POLLERR;
+		}
+		CX18_DEBUG_FILE("Encoder poll started capture\n");
+	}
+
+	if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+		int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait);
+                if (eof && videobuf_poll == POLLERR)
+                        return POLLHUP;
+                else
+                        return videobuf_poll;
+	}
+
+	/* add stream's waitq to the poll list */
+	CX18_DEBUG_HI_FILE("Encoder poll\n");
+	poll_wait(filp, &s->waitq, wait);
+
+	if (atomic_read(&s->q_full.depth))
+		return POLLIN | POLLRDNORM;
+	if (eof)
+		return POLLHUP;
+	return 0;
+}
+
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+
+		/* Start a capture if there is none */
+		if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+			int rc;
+
+			mutex_lock(&cx->serialize_lock);
+			rc = cx18_start_capture(id);
+			mutex_unlock(&cx->serialize_lock);
+			if (rc) {
+				CX18_DEBUG_INFO(
+					"Could not start capture for %s (%d)\n",
+					s->name, rc);
+				return -EINVAL;
+			}
+			CX18_DEBUG_FILE("Encoder mmap started capture\n");
+		}
+
+		return videobuf_mmap_mapper(&s->vbuf_q, vma);
+	}
+
+	return -EINVAL;
+}
+
+void cx18_vb_timeout(unsigned long data)
+{
+	struct cx18_stream *s = (struct cx18_stream *)data;
+	struct cx18_videobuf_buffer *buf;
+	unsigned long flags;
+
+	/* Return all of the buffers in error state, so the vbi/vid inode
+	 * can return from blocking.
+	 */
+	spin_lock_irqsave(&s->vb_lock, flags);
+	while (!list_empty(&s->vb_capture)) {
+		buf = list_entry(s->vb_capture.next,
+			struct cx18_videobuf_buffer, vb.queue);
+		list_del(&buf->vb.queue);
+		buf->vb.state = VIDEOBUF_ERROR;
+		wake_up(&buf->vb.done);
+	}
+	spin_unlock_irqrestore(&s->vb_lock, flags);
+}
+
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+
+	CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	/* 'Unclaim' this stream */
+
+	/* Stop capturing */
+	if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		CX18_DEBUG_INFO("close stopping capture\n");
+		if (id->type == CX18_ENC_STREAM_TYPE_MPG) {
+			/* Stop internal use associated VBI and IDX streams */
+			if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+			    !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+				CX18_DEBUG_INFO("close stopping embedded VBI "
+						"capture\n");
+				cx18_stop_v4l2_encode_stream(s_vbi, 0);
+			}
+			if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+				CX18_DEBUG_INFO("close stopping IDX capture\n");
+				cx18_stop_v4l2_encode_stream(s_idx, 0);
+			}
+		}
+		if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
+		    test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
+			/* Also used internally, don't stop capturing */
+			s->id = -1;
+		else
+			cx18_stop_v4l2_encode_stream(s, gop_end);
+	}
+	if (!gop_end) {
+		clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+		cx18_release_stream(s);
+	}
+}
+
+int cx18_v4l2_close(struct file *filp)
+{
+	struct v4l2_fh *fh = filp->private_data;
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	mutex_lock(&cx->serialize_lock);
+	/* Stop radio */
+	if (id->type == CX18_ENC_STREAM_TYPE_RAD &&
+			v4l2_fh_is_singular_file(filp)) {
+		/* Closing radio device, return to TV mode */
+		cx18_mute(cx);
+		/* Mark that the radio is no longer in use */
+		clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+		/* Switch tuner to TV */
+		cx18_call_all(cx, core, s_std, cx->std);
+		/* Select correct audio input (i.e. TV tuner or Line in) */
+		cx18_audio_set_io(cx);
+		if (atomic_read(&cx->ana_capturing) > 0) {
+			/* Undo video mute */
+			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+			    (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) |
+			    (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8)));
+		}
+		/* Done! Unmute and continue. */
+		cx18_unmute(cx);
+	}
+
+	v4l2_fh_del(fh);
+	v4l2_fh_exit(fh);
+
+	/* 'Unclaim' this stream */
+	if (s->id == id->open_id)
+		cx18_stop_capture(id, 0);
+	kfree(id);
+	mutex_unlock(&cx->serialize_lock);
+	return 0;
+}
+
+static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_open_id *item;
+
+	CX18_DEBUG_FILE("open %s\n", s->name);
+
+	/* Allocate memory */
+	item = kzalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
+	if (NULL == item) {
+		CX18_DEBUG_WARN("nomem on v4l2 open\n");
+		return -ENOMEM;
+	}
+	v4l2_fh_init(&item->fh, s->video_dev);
+
+	item->cx = cx;
+	item->type = s->type;
+
+	item->open_id = cx->open_id++;
+	filp->private_data = &item->fh;
+	v4l2_fh_add(&item->fh);
+
+	if (item->type == CX18_ENC_STREAM_TYPE_RAD &&
+			v4l2_fh_is_singular_file(filp)) {
+		if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+			if (atomic_read(&cx->ana_capturing) > 0) {
+				/* switching to radio while capture is
+				   in progress is not polite */
+				v4l2_fh_del(&item->fh);
+				v4l2_fh_exit(&item->fh);
+				kfree(item);
+				return -EBUSY;
+			}
+		}
+
+		/* Mark that the radio is being used. */
+		set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+		/* We have the radio */
+		cx18_mute(cx);
+		/* Switch tuner to radio */
+		cx18_call_all(cx, tuner, s_radio);
+		/* Select the correct audio input (i.e. radio tuner) */
+		cx18_audio_set_io(cx);
+		/* Done! Unmute and continue. */
+		cx18_unmute(cx);
+	}
+	return 0;
+}
+
+int cx18_v4l2_open(struct file *filp)
+{
+	int res;
+	struct video_device *video_dev = video_devdata(filp);
+	struct cx18_stream *s = video_get_drvdata(video_dev);
+	struct cx18 *cx = s->cx;
+
+	mutex_lock(&cx->serialize_lock);
+	if (cx18_init_on_first_open(cx)) {
+		CX18_ERR("Failed to initialize on %s\n",
+			 video_device_node_name(video_dev));
+		mutex_unlock(&cx->serialize_lock);
+		return -ENXIO;
+	}
+	res = cx18_serialized_open(s, filp);
+	mutex_unlock(&cx->serialize_lock);
+	return res;
+}
+
+void cx18_mute(struct cx18 *cx)
+{
+	u32 h;
+	if (atomic_read(&cx->ana_capturing)) {
+		h = cx18_find_handle(cx);
+		if (h != CX18_INVALID_TASK_HANDLE)
+			cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1);
+		else
+			CX18_ERR("Can't find valid task handle for mute\n");
+	}
+	CX18_DEBUG_INFO("Mute\n");
+}
+
+void cx18_unmute(struct cx18 *cx)
+{
+	u32 h;
+	if (atomic_read(&cx->ana_capturing)) {
+		h = cx18_find_handle(cx);
+		if (h != CX18_INVALID_TASK_HANDLE) {
+			cx18_msleep_timeout(100, 0);
+			cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12);
+			cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0);
+		} else
+			CX18_ERR("Can't find valid task handle for unmute\n");
+	}
+	CX18_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/pci/cx18/cx18-fileops.h b/drivers/media/pci/cx18/cx18-fileops.h
new file mode 100644
index 000000000000..b9e5110ad043
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-fileops.h
@@ -0,0 +1,41 @@
+/*
+ *  cx18 file operation functions
+ *
+ *  Derived from ivtv-fileops.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+/* Testing/Debugging */
+int cx18_v4l2_open(struct file *filp);
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		      loff_t *pos);
+ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+		       loff_t *pos);
+int cx18_v4l2_close(struct file *filp);
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
+int cx18_start_capture(struct cx18_open_id *id);
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
+void cx18_mute(struct cx18 *cx);
+void cx18_unmute(struct cx18 *cx);
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma);
+void cx18_vb_timeout(unsigned long data);
+
+/* Shared with cx18-alsa module */
+int cx18_claim_stream(struct cx18_open_id *id, int type);
+void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c
new file mode 100644
index 000000000000..a1c1cec05f98
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-firmware.c
@@ -0,0 +1,459 @@
+/*
+ *  cx18 firmware functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-firmware.h"
+#include "cx18-cards.h"
+#include <linux/firmware.h>
+
+#define CX18_PROC_SOFT_RESET 		0xc70010
+#define CX18_DDR_SOFT_RESET          	0xc70014
+#define CX18_CLOCK_SELECT1           	0xc71000
+#define CX18_CLOCK_SELECT2           	0xc71004
+#define CX18_HALF_CLOCK_SELECT1      	0xc71008
+#define CX18_HALF_CLOCK_SELECT2      	0xc7100C
+#define CX18_CLOCK_POLARITY1         	0xc71010
+#define CX18_CLOCK_POLARITY2         	0xc71014
+#define CX18_ADD_DELAY_ENABLE1       	0xc71018
+#define CX18_ADD_DELAY_ENABLE2       	0xc7101C
+#define CX18_CLOCK_ENABLE1           	0xc71020
+#define CX18_CLOCK_ENABLE2           	0xc71024
+
+#define CX18_REG_BUS_TIMEOUT_EN      	0xc72024
+
+#define CX18_FAST_CLOCK_PLL_INT      	0xc78000
+#define CX18_FAST_CLOCK_PLL_FRAC     	0xc78004
+#define CX18_FAST_CLOCK_PLL_POST     	0xc78008
+#define CX18_FAST_CLOCK_PLL_PRESCALE 	0xc7800C
+#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
+
+#define CX18_SLOW_CLOCK_PLL_INT      	0xc78014
+#define CX18_SLOW_CLOCK_PLL_FRAC     	0xc78018
+#define CX18_SLOW_CLOCK_PLL_POST     	0xc7801C
+#define CX18_MPEG_CLOCK_PLL_INT		0xc78040
+#define CX18_MPEG_CLOCK_PLL_FRAC	0xc78044
+#define CX18_MPEG_CLOCK_PLL_POST	0xc78048
+#define CX18_PLL_POWER_DOWN          	0xc78088
+#define CX18_SW1_INT_STATUS             0xc73104
+#define CX18_SW1_INT_ENABLE_PCI         0xc7311C
+#define CX18_SW2_INT_SET                0xc73140
+#define CX18_SW2_INT_STATUS             0xc73144
+#define CX18_ADEC_CONTROL            	0xc78120
+
+#define CX18_DDR_REQUEST_ENABLE      	0xc80000
+#define CX18_DDR_CHIP_CONFIG         	0xc80004
+#define CX18_DDR_REFRESH            	0xc80008
+#define CX18_DDR_TIMING1             	0xc8000C
+#define CX18_DDR_TIMING2             	0xc80010
+#define CX18_DDR_POWER_REG		0xc8001C
+
+#define CX18_DDR_TUNE_LANE           	0xc80048
+#define CX18_DDR_INITIAL_EMRS        	0xc80054
+#define CX18_DDR_MB_PER_ROW_7        	0xc8009C
+#define CX18_DDR_BASE_63_ADDR        	0xc804FC
+
+#define CX18_WMB_CLIENT02            	0xc90108
+#define CX18_WMB_CLIENT05            	0xc90114
+#define CX18_WMB_CLIENT06            	0xc90118
+#define CX18_WMB_CLIENT07            	0xc9011C
+#define CX18_WMB_CLIENT08            	0xc90120
+#define CX18_WMB_CLIENT09            	0xc90124
+#define CX18_WMB_CLIENT10            	0xc90128
+#define CX18_WMB_CLIENT11            	0xc9012C
+#define CX18_WMB_CLIENT12            	0xc90130
+#define CX18_WMB_CLIENT13            	0xc90134
+#define CX18_WMB_CLIENT14            	0xc90138
+
+#define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
+
+#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
+#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
+
+struct cx18_apu_rom_seghdr {
+	u32 sync1;
+	u32 sync2;
+	u32 addr;
+	u32 size;
+};
+
+static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
+{
+	const struct firmware *fw = NULL;
+	int i, j;
+	unsigned size;
+	u32 __iomem *dst = (u32 __iomem *)mem;
+	const u32 *src;
+
+	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+		CX18_ERR("Unable to open firmware %s\n", fn);
+		CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+		return -ENOMEM;
+	}
+
+	src = (const u32 *)fw->data;
+
+	for (i = 0; i < fw->size; i += 4096) {
+		cx18_setup_page(cx, i);
+		for (j = i; j < fw->size && j < i + 4096; j += 4) {
+			/* no need for endianness conversion on the ppc */
+			cx18_raw_writel(cx, *src, dst);
+			if (cx18_raw_readl(cx, dst) != *src) {
+				CX18_ERR("Mismatch at offset %x\n", i);
+				release_firmware(fw);
+				cx18_setup_page(cx, 0);
+				return -EIO;
+			}
+			dst++;
+			src++;
+		}
+	}
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+		CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+	size = fw->size;
+	release_firmware(fw);
+	cx18_setup_page(cx, SCB_OFFSET);
+	return size;
+}
+
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+				u32 *entry_addr)
+{
+	const struct firmware *fw = NULL;
+	int i, j;
+	unsigned size;
+	const u32 *src;
+	struct cx18_apu_rom_seghdr seghdr;
+	const u8 *vers;
+	u32 offset = 0;
+	u32 apu_version = 0;
+	int sz;
+
+	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+		CX18_ERR("unable to open firmware %s\n", fn);
+		CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+		cx18_setup_page(cx, 0);
+		return -ENOMEM;
+	}
+
+	*entry_addr = 0;
+	src = (const u32 *)fw->data;
+	vers = fw->data + sizeof(seghdr);
+	sz = fw->size;
+
+	apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
+	while (offset + sizeof(seghdr) < fw->size) {
+		const u32 *shptr = src + offset / 4;
+
+		seghdr.sync1 = le32_to_cpu(shptr[0]);
+		seghdr.sync2 = le32_to_cpu(shptr[1]);
+		seghdr.addr = le32_to_cpu(shptr[2]);
+		seghdr.size = le32_to_cpu(shptr[3]);
+
+		offset += sizeof(seghdr);
+		if (seghdr.sync1 != APU_ROM_SYNC1 ||
+		    seghdr.sync2 != APU_ROM_SYNC2) {
+			offset += seghdr.size;
+			continue;
+		}
+		CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
+				seghdr.addr + seghdr.size - 1);
+		if (*entry_addr == 0)
+			*entry_addr = seghdr.addr;
+		if (offset + seghdr.size > sz)
+			break;
+		for (i = 0; i < seghdr.size; i += 4096) {
+			cx18_setup_page(cx, seghdr.addr + i);
+			for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
+				/* no need for endianness conversion on the ppc */
+				cx18_raw_writel(cx, src[(offset + j) / 4],
+						dst + seghdr.addr + j);
+				if (cx18_raw_readl(cx, dst + seghdr.addr + j)
+				    != src[(offset + j) / 4]) {
+					CX18_ERR("Mismatch at offset %x\n",
+						 offset + j);
+					release_firmware(fw);
+					cx18_setup_page(cx, 0);
+					return -EIO;
+				}
+			}
+		}
+		offset += seghdr.size;
+	}
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+		CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+				fn, apu_version, fw->size);
+	size = fw->size;
+	release_firmware(fw);
+	cx18_setup_page(cx, 0);
+	return size;
+}
+
+void cx18_halt_firmware(struct cx18 *cx)
+{
+	CX18_DEBUG_INFO("Preparing for firmware halt.\n");
+	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+				  0x0000000F, 0x000F000F);
+	cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+				  0x00000002, 0x00020002);
+}
+
+void cx18_init_power(struct cx18 *cx, int lowpwr)
+{
+	/* power-down Spare and AOM PLLs */
+	/* power-up fast, slow and mpeg PLLs */
+	cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
+
+	/* ADEC out of sleep */
+	cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+				  0x00000000, 0x00020002);
+
+	/*
+	 * The PLL parameters are based on the external crystal frequency that
+	 * would ideally be:
+	 *
+	 * NTSC Color subcarrier freq * 8 =
+	 * 	4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+	 *
+	 * The accidents of history and rationale that explain from where this
+	 * combination of magic numbers originate can be found in:
+	 *
+	 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+	 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+	 *
+	 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+	 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+	 *
+	 * As Mike Bradley has rightly pointed out, it's not the exact crystal
+	 * frequency that matters, only that all parts of the driver and
+	 * firmware are using the same value (close to the ideal value).
+	 *
+	 * Since I have a strong suspicion that, if the firmware ever assumes a
+	 * crystal value at all, it will assume 28.636360 MHz, the crystal
+	 * freq used in calculations in this driver will be:
+	 *
+	 *	xtal_freq = 28.636360 MHz
+	 *
+	 * an error of less than 0.13 ppm which is way, way better than any off
+	 * the shelf crystal will have for accuracy anyway.
+	 *
+	 * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+	 *
+	 * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+	 * investigation, experimentation, testing, and suggested solutions of
+	 * of audio/video sync problems with SVideo and CVBS captures.
+	 */
+
+	/* the fast clock is at 200/245 MHz */
+	/* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+	/* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
+	cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+	cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
+						CX18_FAST_CLOCK_PLL_FRAC);
+
+	cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
+	cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
+	cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+
+	/* set slow clock to 125/120 MHz */
+	/* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+	/* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+	cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+	cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
+						CX18_SLOW_CLOCK_PLL_FRAC);
+	cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
+
+	/* mpeg clock pll 54MHz */
+	/* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
+	cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
+	cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
+	cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+
+	/* Defaults */
+	/* APU = SC or SC/2 = 125/62.5 */
+	/* EPU = SC = 125 */
+	/* DDR = FC = 180 */
+	/* ENC = SC = 125 */
+	/* AI1 = SC = 125 */
+	/* VIM2 = disabled */
+	/* PCI = FC/2 = 90 */
+	/* AI2 = disabled */
+	/* DEMUX = disabled */
+	/* AO = SC/2 = 62.5 */
+	/* SER = 54MHz */
+	/* VFC = disabled */
+	/* USB = disabled */
+
+	if (lowpwr) {
+		cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+					  0x00000020, 0xFFFFFFFF);
+		cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+					  0x00000004, 0xFFFFFFFF);
+	} else {
+		/* This doesn't explicitly set every clock select */
+		cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+					  0x00000004, 0x00060006);
+		cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+					  0x00000006, 0x00060006);
+	}
+
+	cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+				  0x00000002, 0xFFFFFFFF);
+	cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+				  0x00000104, 0xFFFFFFFF);
+	cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+				  0x00009026, 0xFFFFFFFF);
+	cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+				  0x00003105, 0xFFFFFFFF);
+}
+
+void cx18_init_memory(struct cx18 *cx)
+{
+	cx18_msleep_timeout(10, 0);
+	cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+				  0x00000000, 0x00010001);
+	cx18_msleep_timeout(10, 0);
+
+	cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+
+	cx18_msleep_timeout(10, 0);
+
+	cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
+	cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
+	cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
+
+	cx18_msleep_timeout(10, 0);
+
+	/* Initialize DQS pad time */
+	cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+	cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+
+	cx18_msleep_timeout(10, 0);
+
+	cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+				  0x00000000, 0x00020002);
+	cx18_msleep_timeout(10, 0);
+
+	/* use power-down mode when idle */
+	cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
+
+	cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+				  0x00000001, 0x00010001);
+
+	cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
+	cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
+
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02);  /* AO */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09);  /* AI2 */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05);  /* VIM1 */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06);  /* AI1 */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07);  /* 3D comb */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10);  /* ME */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12);  /* ENC */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13);  /* PK */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11);  /* RC */
+	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14);  /* AVO */
+}
+
+#define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw"
+#define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw"
+
+int cx18_firmware_init(struct cx18 *cx)
+{
+	u32 fw_entry_addr;
+	int sz, retries;
+	u32 api_args[MAX_MB_ARGUMENTS];
+
+	/* Allow chip to control CLKRUN */
+	cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
+
+	/* Stop the firmware */
+	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+				  0x0000000F, 0x000F000F);
+
+	cx18_msleep_timeout(1, 0);
+
+	/* If the CPU is still running */
+	if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+		CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+		return -EIO;
+	}
+
+	cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+	cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+	sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx);
+	if (sz <= 0)
+		return sz;
+
+	/* The SCB & IPC area *must* be correct before starting the firmwares */
+	cx18_init_scb(cx);
+
+	fw_entry_addr = 0;
+	sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx,
+				&fw_entry_addr);
+	if (sz <= 0)
+		return sz;
+
+	/* Start the CPU. The CPU will take care of the APU for us. */
+	cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+				  0x00000000, 0x00080008);
+
+	/* Wait up to 500 ms for the APU to come out of reset */
+	for (retries = 0;
+	     retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+	     retries++)
+		cx18_msleep_timeout(10, 0);
+
+	cx18_msleep_timeout(200, 0);
+
+	if (retries == 50 &&
+	    (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+		CX18_ERR("Could not start the CPU\n");
+		return -EIO;
+	}
+
+	/*
+	 * The CPU had once before set up to receive an interrupt for it's
+	 * outgoing IRQ_CPU_TO_EPU_ACK to us.  If it ever does this, we get an
+	 * interrupt when it sends us an ack, but by the time we process it,
+	 * that flag in the SW2 status register has been cleared by the CPU
+	 * firmware.  We'll prevent that not so useful condition from happening
+	 * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+	 * process.
+	 */
+	cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+	/* Try a benign command to see if the CPU is alive and well */
+	sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+	if (sz < 0)
+		return sz;
+
+	/* initialize GPIO */
+	cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
+	return 0;
+}
+
+MODULE_FIRMWARE(CX18_CPU_FIRMWARE);
+MODULE_FIRMWARE(CX18_APU_FIRMWARE);
diff --git a/drivers/media/pci/cx18/cx18-firmware.h b/drivers/media/pci/cx18/cx18-firmware.h
new file mode 100644
index 000000000000..38d4c05e8499
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-firmware.h
@@ -0,0 +1,25 @@
+/*
+ *  cx18 firmware functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+int cx18_firmware_init(struct cx18 *cx);
+void cx18_halt_firmware(struct cx18 *cx);
+void cx18_init_memory(struct cx18 *cx);
+void cx18_init_power(struct cx18 *cx, int lowpwr);
diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c
new file mode 100644
index 000000000000..5374aeb0cd22
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-gpio.c
@@ -0,0 +1,347 @@
+/*
+ *  cx18 gpio functions
+ *
+ *  Derived from ivtv-gpio.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "tuner-xc2028.h"
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define CX18_REG_GPIO_IN     0xc72010
+#define CX18_REG_GPIO_OUT1   0xc78100
+#define CX18_REG_GPIO_DIR1   0xc78108
+#define CX18_REG_GPIO_OUT2   0xc78104
+#define CX18_REG_GPIO_DIR2   0xc7810c
+
+/*
+ * HVR-1600 GPIO pins, courtesy of Hauppauge:
+ *
+ * gpio0: zilog ir process reset pin
+ * gpio1: zilog programming pin (you should never use this)
+ * gpio12: cx24227 reset pin
+ * gpio13: cs5345 reset pin
+*/
+
+/*
+ * File scope utility functions
+ */
+static void gpio_write(struct cx18 *cx)
+{
+	u32 dir_lo = cx->gpio_dir & 0xffff;
+	u32 val_lo = cx->gpio_val & 0xffff;
+	u32 dir_hi = cx->gpio_dir >> 16;
+	u32 val_hi = cx->gpio_val >> 16;
+
+	cx18_write_reg_expect(cx, dir_lo << 16,
+					CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+	cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+					CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+	cx18_write_reg_expect(cx, dir_hi << 16,
+					CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+	cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+					CX18_REG_GPIO_OUT2, val_hi, dir_hi);
+}
+
+static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
+{
+	if (mask == 0)
+		return;
+
+	mutex_lock(&cx->gpio_lock);
+	cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+	gpio_write(cx);
+	mutex_unlock(&cx->gpio_lock);
+}
+
+static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
+			   unsigned int assert_msecs,
+			   unsigned int recovery_msecs)
+{
+	u32 mask;
+
+	mask = active_lo | active_hi;
+	if (mask == 0)
+		return;
+
+	/*
+	 * Assuming that active_hi and active_lo are a subsets of the bits in
+	 * gpio_dir.  Also assumes that active_lo and active_hi don't overlap
+	 * in any bit position
+	 */
+
+	/* Assert */
+	gpio_update(cx, mask, ~active_lo);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
+
+	/* Deassert */
+	gpio_update(cx, mask, ~active_hi);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
+}
+
+/*
+ * GPIO Multiplexer - logical device
+ */
+static int gpiomux_log_status(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	mutex_lock(&cx->gpio_lock);
+	CX18_INFO_DEV(sd, "GPIO:  direction 0x%08x, value 0x%08x\n",
+		      cx->gpio_dir, cx->gpio_val);
+	mutex_unlock(&cx->gpio_lock);
+	return 0;
+}
+
+static int gpiomux_s_radio(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	/*
+	 * FIXME - work out the cx->active/audio_input mess - this is
+	 * intended to handle the switch to radio mode and set the
+	 * audio routing, but we need to update the state in cx
+	 */
+	gpio_update(cx, cx->card->gpio_audio_input.mask,
+			cx->card->gpio_audio_input.radio);
+	return 0;
+}
+
+static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u32 data;
+
+	switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
+	case 1:
+		data = cx->card->gpio_audio_input.linein;
+		break;
+	case 0:
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	default:
+		/*
+		 * FIXME - work out the cx->active/audio_input mess - this is
+		 * intended to handle the switch from radio mode and set the
+		 * audio routing, but we need to update the state in cx
+		 */
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	}
+	gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+	return 0;
+}
+
+static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
+				   u32 input, u32 output, u32 config)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u32 data;
+
+	switch (input) {
+	case 0:
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	case 1:
+		data = cx->card->gpio_audio_input.linein;
+		break;
+	case 2:
+		data = cx->card->gpio_audio_input.radio;
+		break;
+	default:
+		return -EINVAL;
+	}
+	gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
+	.log_status = gpiomux_log_status,
+	.s_std = gpiomux_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
+	.s_radio = gpiomux_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
+	.s_routing = gpiomux_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops gpiomux_ops = {
+	.core = &gpiomux_core_ops,
+	.tuner = &gpiomux_tuner_ops,
+	.audio = &gpiomux_audio_ops,
+};
+
+/*
+ * GPIO Reset Controller - logical device
+ */
+static int resetctrl_log_status(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	mutex_lock(&cx->gpio_lock);
+	CX18_INFO_DEV(sd, "GPIO:  direction 0x%08x, value 0x%08x\n",
+		      cx->gpio_dir, cx->gpio_val);
+	mutex_unlock(&cx->gpio_lock);
+	return 0;
+}
+
+static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	const struct cx18_gpio_i2c_slave_reset *p;
+
+	p = &cx->card->gpio_i2c_slave_reset;
+	switch (val) {
+	case CX18_GPIO_RESET_I2C:
+		gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
+			       p->msecs_asserted, p->msecs_recovery);
+		break;
+	case CX18_GPIO_RESET_Z8F0811:
+		/*
+		 * Assert timing for the Z8F0811 on HVR-1600 boards:
+		 * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to
+		 *    initiate
+		 * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock
+		 *    cycles (6,601,085 nanoseconds ~= 7 milliseconds)
+		 * 3. DBG pin must be high before chip exits reset for normal
+		 *    operation.  DBG is open drain and hopefully pulled high
+		 *    since we don't normally drive it (GPIO 1?) for the
+		 *    HVR-1600
+		 * 4. Z8F0811 won't exit reset until RESET is deasserted
+		 * 5. Zilog comes out of reset, loads reset vector address and
+		 *    executes from there. Required recovery delay unknown.
+		 */
+		gpio_reset_seq(cx, p->ir_reset_mask, 0,
+			       p->msecs_asserted, p->msecs_recovery);
+		break;
+	case CX18_GPIO_RESET_XC2028:
+		if (cx->card->tuners[0].tuner == TUNER_XC2028)
+			gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
+				       1, 1);
+		break;
+	}
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
+	.log_status = resetctrl_log_status,
+	.reset = resetctrl_reset,
+};
+
+static const struct v4l2_subdev_ops resetctrl_ops = {
+	.core = &resetctrl_core_ops,
+};
+
+/*
+ * External entry points
+ */
+void cx18_gpio_init(struct cx18 *cx)
+{
+	mutex_lock(&cx->gpio_lock);
+	cx->gpio_dir = cx->card->gpio_init.direction;
+	cx->gpio_val = cx->card->gpio_init.initial_value;
+
+	if (cx->card->tuners[0].tuner == TUNER_XC2028) {
+		cx->gpio_dir |= 1 << cx->card->xceive_pin;
+		cx->gpio_val |= 1 << cx->card->xceive_pin;
+	}
+
+	if (cx->gpio_dir == 0) {
+		mutex_unlock(&cx->gpio_lock);
+		return;
+	}
+
+	CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n",
+			cx18_read_reg(cx, CX18_REG_GPIO_DIR1),
+			cx18_read_reg(cx, CX18_REG_GPIO_DIR2),
+			cx18_read_reg(cx, CX18_REG_GPIO_OUT1),
+			cx18_read_reg(cx, CX18_REG_GPIO_OUT2));
+
+	gpio_write(cx);
+	mutex_unlock(&cx->gpio_lock);
+}
+
+int cx18_gpio_register(struct cx18 *cx, u32 hw)
+{
+	struct v4l2_subdev *sd;
+	const struct v4l2_subdev_ops *ops;
+	char *str;
+
+	switch (hw) {
+	case CX18_HW_GPIO_MUX:
+		sd = &cx->sd_gpiomux;
+		ops = &gpiomux_ops;
+		str = "gpio-mux";
+		break;
+	case CX18_HW_GPIO_RESET_CTRL:
+		sd = &cx->sd_resetctrl;
+		ops = &resetctrl_ops;
+		str = "gpio-reset-ctrl";
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_subdev_init(sd, ops);
+	v4l2_set_subdevdata(sd, cx);
+	snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
+	sd->grp_id = hw;
+	return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
+
+void cx18_reset_ir_gpio(void *data)
+{
+	struct cx18 *cx = to_cx18((struct v4l2_device *)data);
+
+	if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
+		return;
+
+	CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+	v4l2_subdev_call(&cx->sd_resetctrl,
+			 core, reset, CX18_GPIO_RESET_Z8F0811);
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
+
+/* Xceive tuner reset function */
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+{
+	struct i2c_algo_bit_data *algo = dev;
+	struct cx18_i2c_algo_callback_data *cb_data = algo->data;
+	struct cx18 *cx = cb_data->cx;
+
+	if (cmd != XC2028_TUNER_RESET ||
+	    cx->card->tuners[0].tuner != TUNER_XC2028)
+		return 0;
+
+	CX18_DEBUG_INFO("Resetting XCeive tuner\n");
+	return v4l2_subdev_call(&cx->sd_resetctrl,
+				core, reset, CX18_GPIO_RESET_XC2028);
+}
diff --git a/drivers/media/pci/cx18/cx18-gpio.h b/drivers/media/pci/cx18/cx18-gpio.h
new file mode 100644
index 000000000000..4aea2ef88e8d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-gpio.h
@@ -0,0 +1,34 @@
+/*
+ *  cx18 gpio functions
+ *
+ *  Derived from ivtv-gpio.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+void cx18_gpio_init(struct cx18 *cx);
+int cx18_gpio_register(struct cx18 *cx, u32 hw);
+
+enum cx18_gpio_reset_type {
+	CX18_GPIO_RESET_I2C     = 0,
+	CX18_GPIO_RESET_Z8F0811 = 1,
+	CX18_GPIO_RESET_XC2028  = 2,
+};
+
+void cx18_reset_ir_gpio(void *data);
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c
new file mode 100644
index 000000000000..51609d5c88ce
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-i2c.c
@@ -0,0 +1,330 @@
+/*
+ *  cx18 I2C functions
+ *
+ *  Derived from ivtv-i2c.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+
+#define CX18_REG_I2C_1_WR   0xf15000
+#define CX18_REG_I2C_1_RD   0xf15008
+#define CX18_REG_I2C_2_WR   0xf25100
+#define CX18_REG_I2C_2_RD   0xf25108
+
+#define SETSCL_BIT      0x0001
+#define SETSDL_BIT      0x0002
+#define GETSCL_BIT      0x0004
+#define GETSDL_BIT      0x0008
+
+#define CX18_CS5345_I2C_ADDR		0x4c
+#define CX18_Z8F0811_IR_TX_I2C_ADDR	0x70
+#define CX18_Z8F0811_IR_RX_I2C_ADDR	0x71
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_addrs[] = {
+	0,				/* CX18_HW_TUNER */
+	0,				/* CX18_HW_TVEEPROM */
+	CX18_CS5345_I2C_ADDR,		/* CX18_HW_CS5345 */
+	0,				/* CX18_HW_DVB */
+	0,				/* CX18_HW_418_AV */
+	0,				/* CX18_HW_GPIO_MUX */
+	0,				/* CX18_HW_GPIO_RESET_CTRL */
+	CX18_Z8F0811_IR_TX_I2C_ADDR,	/* CX18_HW_Z8F0811_IR_TX_HAUP */
+	CX18_Z8F0811_IR_RX_I2C_ADDR,	/* CX18_HW_Z8F0811_IR_RX_HAUP */
+};
+
+/* This array should match the CX18_HW_ defines */
+/* This might well become a card-specific array */
+static const u8 hw_bus[] = {
+	1,	/* CX18_HW_TUNER */
+	0,	/* CX18_HW_TVEEPROM */
+	0,	/* CX18_HW_CS5345 */
+	0,	/* CX18_HW_DVB */
+	0,	/* CX18_HW_418_AV */
+	0,	/* CX18_HW_GPIO_MUX */
+	0,	/* CX18_HW_GPIO_RESET_CTRL */
+	0,	/* CX18_HW_Z8F0811_IR_TX_HAUP */
+	0,	/* CX18_HW_Z8F0811_IR_RX_HAUP */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_devicenames[] = {
+	"tuner",
+	"tveeprom",
+	"cs5345",
+	"cx23418_DTV",
+	"cx23418_AV",
+	"gpio_mux",
+	"gpio_reset_ctrl",
+	"ir_tx_z8f0811_haup",
+	"ir_rx_z8f0811_haup",
+};
+
+static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
+			   const char *type, u8 addr)
+{
+	struct i2c_board_info info;
+	struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data;
+	unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, type, I2C_NAME_SIZE);
+
+	/* Our default information for ir-kbd-i2c.c to use */
+	switch (hw) {
+	case CX18_HW_Z8F0811_IR_RX_HAUP:
+		init_data->ir_codes = RC_MAP_HAUPPAUGE;
+		init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+		init_data->type = RC_TYPE_RC5;
+		init_data->name = cx->card_name;
+		info.platform_data = init_data;
+		break;
+	}
+
+	return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+	       -1 : 0;
+}
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx)
+{
+	struct v4l2_subdev *sd;
+	int bus = hw_bus[idx];
+	struct i2c_adapter *adap = &cx->i2c_adap[bus];
+	const char *type = hw_devicenames[idx];
+	u32 hw = 1 << idx;
+
+	if (idx >= ARRAY_SIZE(hw_addrs))
+		return -1;
+
+	if (hw == CX18_HW_TUNER) {
+		/* special tuner group handling */
+		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+				adap, type, 0, cx->card_i2c->radio);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+				adap, type, 0, cx->card_i2c->demod);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+				adap, type, 0, cx->card_i2c->tv);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		return sd != NULL ? 0 : -1;
+	}
+
+	if (hw & CX18_HW_IR_ANY)
+		return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]);
+
+	/* Is it not an I2C device or one we do not wish to register? */
+	if (!hw_addrs[idx])
+		return -1;
+
+	/* It's an I2C device other than an analog tuner or IR chip */
+	sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, type, hw_addrs[idx],
+				 NULL);
+	if (sd != NULL)
+		sd->grp_id = hw;
+	return sd != NULL ? 0 : -1;
+}
+
+/* Find the first member of the subdev group id in hw */
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
+{
+	struct v4l2_subdev *result = NULL;
+	struct v4l2_subdev *sd;
+
+	spin_lock(&cx->v4l2_dev.lock);
+	v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
+		if (sd->grp_id == hw) {
+			result = sd;
+			break;
+		}
+	}
+	spin_unlock(&cx->v4l2_dev.lock);
+	return result;
+}
+
+static void cx18_setscl(void *data, int state)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+	u32 r = cx18_read_reg(cx, addr);
+
+	if (state)
+		cx18_write_reg(cx, r | SETSCL_BIT, addr);
+	else
+		cx18_write_reg(cx, r & ~SETSCL_BIT, addr);
+}
+
+static void cx18_setsda(void *data, int state)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+	u32 r = cx18_read_reg(cx, addr);
+
+	if (state)
+		cx18_write_reg(cx, r | SETSDL_BIT, addr);
+	else
+		cx18_write_reg(cx, r & ~SETSDL_BIT, addr);
+}
+
+static int cx18_getscl(void *data)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+	return cx18_read_reg(cx, addr) & GETSCL_BIT;
+}
+
+static int cx18_getsda(void *data)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+	return cx18_read_reg(cx, addr) & GETSDL_BIT;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter cx18_i2c_adap_template = {
+	.name = "cx18 i2c driver",
+	.algo = NULL,                   /* set by i2c-algo-bit */
+	.algo_data = NULL,              /* filled from template */
+	.owner = THIS_MODULE,
+};
+
+#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
+#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
+
+static struct i2c_algo_bit_data cx18_i2c_algo_template = {
+	.setsda		= cx18_setsda,
+	.setscl		= cx18_setscl,
+	.getsda		= cx18_getsda,
+	.getscl		= cx18_getscl,
+	.udelay		= CX18_SCL_PERIOD/2,       /* 1/2 clock period in usec*/
+	.timeout	= CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
+};
+
+/* init + register i2c adapter */
+int init_cx18_i2c(struct cx18 *cx)
+{
+	int i, err;
+	CX18_DEBUG_I2C("i2c init\n");
+
+	for (i = 0; i < 2; i++) {
+		/* Setup algorithm for adapter */
+		memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
+			sizeof(struct i2c_algo_bit_data));
+		cx->i2c_algo_cb_data[i].cx = cx;
+		cx->i2c_algo_cb_data[i].bus_index = i;
+		cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
+
+		/* Setup adapter */
+		memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+			sizeof(struct i2c_adapter));
+		cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+		sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
+				" #%d-%d", cx->instance, i);
+		i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
+		cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
+	}
+
+	if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
+		/* Reset/Unreset I2C hardware block */
+		/* Clock select 220MHz */
+		cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
+					  0x00000000, 0x10001000);
+		/* Clock Enable */
+		cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
+					  0x00001000, 0x10001000);
+	}
+	/* courtesy of Steven Toth <stoth@hauppauge.com> */
+	cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
+	mdelay(10);
+	cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
+	mdelay(10);
+	cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
+	mdelay(10);
+
+	/* Set to edge-triggered intrs. */
+	cx18_write_reg(cx, 0x00c00000, 0xc730c8);
+	/* Clear any stale intrs */
+	cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
+		       ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
+
+	/* Hw I2C1 Clock Freq ~100kHz */
+	cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+	cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
+	cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
+
+	/* Hw I2C2 Clock Freq ~100kHz */
+	cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+	cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
+	cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
+
+	cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+		     core, reset, (u32) CX18_GPIO_RESET_I2C);
+
+	err = i2c_bit_add_bus(&cx->i2c_adap[0]);
+	if (err)
+		goto err;
+	err = i2c_bit_add_bus(&cx->i2c_adap[1]);
+	if (err)
+		goto err_del_bus_0;
+	return 0;
+
+ err_del_bus_0:
+	i2c_del_adapter(&cx->i2c_adap[0]);
+ err:
+	return err;
+}
+
+void exit_cx18_i2c(struct cx18 *cx)
+{
+	int i;
+	CX18_DEBUG_I2C("i2c exit\n");
+	cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4,
+							CX18_REG_I2C_1_WR);
+	cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4,
+							CX18_REG_I2C_2_WR);
+
+	for (i = 0; i < 2; i++) {
+		i2c_del_adapter(&cx->i2c_adap[i]);
+	}
+}
+
+/*
+   Hauppauge HVR1600 should have:
+   32 cx24227
+   98 unknown
+   a0 eeprom
+   c2 tuner
+   e? zilog ir
+   */
diff --git a/drivers/media/pci/cx18/cx18-i2c.h b/drivers/media/pci/cx18/cx18-i2c.h
new file mode 100644
index 000000000000..1180fdc8d983
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-i2c.h
@@ -0,0 +1,29 @@
+/*
+ *  cx18 I2C functions
+ *
+ *  Derived from ivtv-i2c.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
+
+/* init + register i2c adapter */
+int init_cx18_i2c(struct cx18 *cx);
+void exit_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/pci/cx18/cx18-io.c b/drivers/media/pci/cx18/cx18-io.c
new file mode 100644
index 000000000000..49b9dbd06248
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-io.c
@@ -0,0 +1,97 @@
+/*
+ *  cx18 driver PCI memory mapped IO access routines
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-irq.h"
+
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
+{
+	u8 __iomem *dst = addr;
+	u16 val2 = val | (val << 8);
+	u32 val4 = val2 | (val2 << 16);
+
+	/* Align writes on the CX23418's addresses */
+	if ((count > 0) && ((unsigned long)dst & 1)) {
+		cx18_writeb(cx, (u8) val, dst);
+		count--;
+		dst++;
+	}
+	if ((count > 1) && ((unsigned long)dst & 2)) {
+		cx18_writew(cx, val2, dst);
+		count -= 2;
+		dst += 2;
+	}
+	while (count > 3) {
+		cx18_writel(cx, val4, dst);
+		count -= 4;
+		dst += 4;
+	}
+	if (count > 1) {
+		cx18_writew(cx, val2, dst);
+		count -= 2;
+		dst += 2;
+	}
+	if (count > 0)
+		cx18_writeb(cx, (u8) val, dst);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
+{
+	cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
+	cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val;
+	cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
+{
+	cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val;
+	cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
+{
+	cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
+	cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val;
+	cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
+{
+	cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val;
+	cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val)
+{
+	u32 r;
+	r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU);
+	cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU);
+}
+
+void cx18_setup_page(struct cx18 *cx, u32 addr)
+{
+	u32 val;
+	val = cx18_read_reg(cx, 0xD000F8);
+	val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00);
+	cx18_write_reg(cx, val, 0xD000F8);
+}
diff --git a/drivers/media/pci/cx18/cx18-io.h b/drivers/media/pci/cx18/cx18-io.h
new file mode 100644
index 000000000000..18974d886cf7
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-io.h
@@ -0,0 +1,191 @@
+/*
+ *  cx18 driver PCI memory mapped IO access routines
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#ifndef CX18_IO_H
+#define CX18_IO_H
+
+#include "cx18-driver.h"
+
+/*
+ * Readback and retry of MMIO access for reliability:
+ * The concept was suggested by Steve Toth <stoth@linuxtv.org>.
+ * The implmentation is the fault of Andy Walls <awalls@md.metrocast.net>.
+ *
+ * *write* functions are implied to retry the mmio unless suffixed with _noretry
+ * *read* functions never retry the mmio (it never helps to do so)
+ */
+
+/* Non byteswapping memory mapped IO */
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+{
+	return __raw_readl(addr);
+}
+
+static inline
+void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+	__raw_writel(val, addr);
+}
+
+static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+		cx18_raw_writel_noretry(cx, val, addr);
+		if (val == cx18_raw_readl(cx, addr))
+			break;
+	}
+}
+
+/* Normal memory mapped IO */
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
+{
+	return readl(addr);
+}
+
+static inline
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+	writel(val, addr);
+}
+
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+		cx18_writel_noretry(cx, val, addr);
+		if (val == cx18_readl(cx, addr))
+			break;
+	}
+}
+
+static inline
+void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+			u32 eval, u32 mask)
+{
+	int i;
+	u32 r;
+	eval &= mask;
+	for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+		cx18_writel_noretry(cx, val, addr);
+		r = cx18_readl(cx, addr);
+		if (r == 0xffffffff && eval != 0xffffffff)
+			continue;
+		if (eval == (r & mask))
+			break;
+	}
+}
+
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
+{
+	return readw(addr);
+}
+
+static inline
+void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+	writew(val, addr);
+}
+
+static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+		cx18_writew_noretry(cx, val, addr);
+		if (val == cx18_readw(cx, addr))
+			break;
+	}
+}
+
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+	return readb(addr);
+}
+
+static inline
+void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+	writeb(val, addr);
+}
+
+static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+	int i;
+	for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+		cx18_writeb_noretry(cx, val, addr);
+		if (val == cx18_readb(cx, addr))
+			break;
+	}
+}
+
+static inline
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+			const void __iomem *from, unsigned int len)
+{
+	memcpy_fromio(to, from, len);
+}
+
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
+
+
+/* Access "register" region of CX23418 memory mapped I/O */
+static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
+{
+	cx18_writel_noretry(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
+{
+	cx18_writel(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
+					 u32 eval, u32 mask)
+{
+	cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+}
+
+static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
+{
+	return cx18_readl(cx, cx->reg_mem + reg);
+}
+
+
+/* Access "encoder memory" region of CX23418 memory mapped I/O */
+static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
+{
+	cx18_writel(cx, val, cx->enc_mem + addr);
+}
+
+static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
+{
+	return cx18_readl(cx, cx->enc_mem + addr);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val);
+void cx18_setup_page(struct cx18 *cx, u32 addr);
+
+#endif /* CX18_IO_H */
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
new file mode 100644
index 000000000000..cd8d2c2b1624
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -0,0 +1,1190 @@
+/*
+ *  cx18 ioctl system call
+ *
+ *  Derived from ivtv-ioctl.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-fileops.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-video.h"
+#include "cx18-streams.h"
+#include "cx18-ioctl.h"
+#include "cx18-gpio.h"
+#include "cx18-controls.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+u16 cx18_service2vbi(int type)
+{
+	switch (type) {
+	case V4L2_SLICED_TELETEXT_B:
+		return CX18_SLICED_TYPE_TELETEXT_B;
+	case V4L2_SLICED_CAPTION_525:
+		return CX18_SLICED_TYPE_CAPTION_525;
+	case V4L2_SLICED_WSS_625:
+		return CX18_SLICED_TYPE_WSS_625;
+	case V4L2_SLICED_VPS:
+		return CX18_SLICED_TYPE_VPS;
+	default:
+		return 0;
+	}
+}
+
+/* Check if VBI services are allowed on the (field, line) for the video std */
+static int valid_service_line(int field, int line, int is_pal)
+{
+	return (is_pal && line >= 6 &&
+		((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
+	       (!is_pal && line >= 10 && line < 22);
+}
+
+/*
+ * For a (field, line, std) and inbound potential set of services for that line,
+ * return the first valid service of those passed in the incoming set for that
+ * line in priority order:
+ * CC, VPS, or WSS over TELETEXT for well known lines
+ * TELETEXT, before VPS, before CC, before WSS, for other lines
+ */
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+	int i;
+
+	set = set & valid_set;
+	if (set == 0 || !valid_service_line(field, line, is_pal))
+		return 0;
+	if (!is_pal) {
+		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+			return V4L2_SLICED_CAPTION_525;
+	} else {
+		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+			return V4L2_SLICED_VPS;
+		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+			return V4L2_SLICED_WSS_625;
+		if (line == 23)
+			return 0;
+	}
+	for (i = 0; i < 32; i++) {
+		if ((1 << i) & set)
+			return 1 << i;
+	}
+	return 0;
+}
+
+/*
+ * Expand the service_set of *fmt into valid service_lines for the std,
+ * and clear the passed in fmt->service_set
+ */
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	u16 set = fmt->service_set;
+	int f, l;
+
+	fmt->service_set = 0;
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++)
+			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+	}
+}
+
+/*
+ * Sanitize the service_lines in *fmt per the video std, and return 1
+ * if any service_line is left as valid after santization
+ */
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	int f, l;
+	u16 set = 0;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+			set |= fmt->service_lines[f][l];
+		}
+	}
+	return set != 0;
+}
+
+/* Compute the service_set from the assumed valid service_lines of *fmt */
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+	int f, l;
+	u16 set = 0;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++)
+			set |= fmt->service_lines[f][l];
+	}
+	return set;
+}
+
+static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+	pixfmt->width = cx->cxhdl.width;
+	pixfmt->height = cx->cxhdl.height;
+	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	pixfmt->field = V4L2_FIELD_INTERLACED;
+	pixfmt->priv = 0;
+	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+		pixfmt->pixelformat = s->pixelformat;
+		pixfmt->sizeimage = s->vb_bytes_per_frame;
+		pixfmt->bytesperline = 720;
+	} else {
+		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+		pixfmt->sizeimage = 128 * 1024;
+		pixfmt->bytesperline = 0;
+	}
+	return 0;
+}
+
+static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+	vbifmt->sampling_rate = 27000000;
+	vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
+	vbifmt->samples_per_line = vbi_active_samples - 4;
+	vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+	vbifmt->start[0] = cx->vbi.start[0];
+	vbifmt->start[1] = cx->vbi.start[1];
+	vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
+	vbifmt->flags = 0;
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+	return 0;
+}
+
+static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_format *fmt)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+	/* sane, V4L2 spec compliant, defaults */
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+	memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+	vbifmt->service_set = 0;
+
+	/*
+	 * Fetch the configured service_lines and total service_set from the
+	 * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
+	 * fmt->fmt.sliced under valid calling conditions
+	 */
+	if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
+		return -EINVAL;
+
+	vbifmt->service_set = cx18_get_service_set(vbifmt);
+	return 0;
+}
+
+static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	int w = fmt->fmt.pix.width;
+	int h = fmt->fmt.pix.height;
+	int min_h = 2;
+
+	w = min(w, 720);
+	w = max(w, 2);
+	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+		/* YUV height must be a multiple of 32 */
+		h &= ~0x1f;
+		min_h = 32;
+	}
+	h = min(h, cx->is_50hz ? 576 : 480);
+	h = max(h, min_h);
+
+	fmt->fmt.pix.width = w;
+	fmt->fmt.pix.height = h;
+	return 0;
+}
+
+static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_format *fmt)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+
+	/* If given a service set, expand it validly & clear passed in set */
+	if (vbifmt->service_set)
+		cx18_expand_service_set(vbifmt, cx->is_50hz);
+	/* Sanitize the service_lines, and compute the new set if any valid */
+	if (check_service_set(vbifmt, cx->is_50hz))
+		vbifmt->service_set = cx18_get_service_set(vbifmt);
+	return 0;
+}
+
+static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int ret;
+	int w, h;
+
+	ret = cx18_try_fmt_vid_cap(file, fh, fmt);
+	if (ret)
+		return ret;
+	w = fmt->fmt.pix.width;
+	h = fmt->fmt.pix.height;
+
+	if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
+	    s->pixelformat == fmt->fmt.pix.pixelformat)
+		return 0;
+
+	if (atomic_read(&cx->ana_capturing) > 0)
+		return -EBUSY;
+
+	s->pixelformat = fmt->fmt.pix.pixelformat;
+	/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+	   UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+	if (s->pixelformat == V4L2_PIX_FMT_HM12)
+		s->vb_bytes_per_frame = h * 720 * 3 / 2;
+	else
+		s->vb_bytes_per_frame = h * 720 * 2;
+
+	mbus_fmt.width = cx->cxhdl.width = w;
+	mbus_fmt.height = cx->cxhdl.height = h;
+	mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+	v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
+	return cx18_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
+				struct v4l2_format *fmt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	int ret;
+
+	/*
+	 * Changing the Encoder's Raw VBI parameters won't have any effect
+	 * if any analog capture is ongoing
+	 */
+	if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+		return -EBUSY;
+
+	/*
+	 * Set the digitizer registers for raw active VBI.
+	 * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
+	 * calling conditions
+	 */
+	ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
+	if (ret)
+		return ret;
+
+	/* Store our new v4l2 (non-)sliced VBI state */
+	cx->vbi.sliced_in->service_set = 0;
+	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+
+	return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_format *fmt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	int ret;
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+	cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+	/*
+	 * Changing the Encoder's Raw VBI parameters won't have any effect
+	 * if any analog capture is ongoing
+	 */
+	if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+		return -EBUSY;
+
+	/*
+	 * Set the service_lines requested in the digitizer/slicer registers.
+	 * Note, cx18_av_vbi() wipes some "impossible" service lines in the
+	 * passed in fmt->fmt.sliced under valid calling conditions
+	 */
+	ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
+	if (ret)
+		return ret;
+	/* Store our current v4l2 sliced VBI settings */
+	cx->vbi.in.type =  V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+	return 0;
+}
+
+static int cx18_g_chip_ident(struct file *file, void *fh,
+				struct v4l2_dbg_chip_ident *chip)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	int err = 0;
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	switch (chip->match.type) {
+	case V4L2_CHIP_MATCH_HOST:
+		switch (chip->match.addr) {
+		case 0:
+			chip->ident = V4L2_IDENT_CX23418;
+			chip->revision = cx18_read_reg(cx, 0xC72028);
+			break;
+		case 1:
+			/*
+			 * The A/V decoder is always present, but in the rare
+			 * case that the card doesn't have analog, we don't
+			 * use it.  We find it w/o using the cx->sd_av pointer
+			 */
+			cx18_call_hw(cx, CX18_HW_418_AV,
+				     core, g_chip_ident, chip);
+			break;
+		default:
+			/*
+			 * Could return ident = V4L2_IDENT_UNKNOWN if we had
+			 * other host chips at higher addresses, but we don't
+			 */
+			err = -EINVAL; /* per V4L2 spec */
+			break;
+		}
+		break;
+	case V4L2_CHIP_MATCH_I2C_DRIVER:
+		/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+		cx18_call_all(cx, core, g_chip_ident, chip);
+		break;
+	case V4L2_CHIP_MATCH_I2C_ADDR:
+		/*
+		 * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+		 * to look if a chip is at the address with no driver.  That's a
+		 * dangerous thing to do with EEPROMs anyway.
+		 */
+		cx18_call_all(cx, core, g_chip_ident, chip);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct v4l2_dbg_register *regs = arg;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+		return -EINVAL;
+
+	regs->size = 4;
+	if (cmd == VIDIOC_DBG_S_REGISTER)
+		cx18_write_enc(cx, regs->val, regs->reg);
+	else
+		regs->val = cx18_read_enc(cx, regs->reg);
+	return 0;
+}
+
+static int cx18_g_register(struct file *file, void *fh,
+				struct v4l2_dbg_register *reg)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (v4l2_chip_match_host(&reg->match))
+		return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
+	/* FIXME - errors shouldn't be ignored */
+	cx18_call_all(cx, core, g_register, reg);
+	return 0;
+}
+
+static int cx18_s_register(struct file *file, void *fh,
+				struct v4l2_dbg_register *reg)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (v4l2_chip_match_host(&reg->match))
+		return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
+	/* FIXME - errors shouldn't be ignored */
+	cx18_call_all(cx, core, s_register, reg);
+	return 0;
+}
+#endif
+
+static int cx18_querycap(struct file *file, void *fh,
+				struct v4l2_capability *vcap)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+	strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+	snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+		 "PCI:%s", pci_name(cx->pci_dev));
+	vcap->capabilities = cx->v4l2_cap; 	    /* capabilities */
+	if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+		vcap->capabilities |= V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	vin->index = cx->audio_input;
+	return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (vout->index >= cx->nof_audio_inputs)
+		return -EINVAL;
+	cx->audio_input = vout->index;
+	cx18_audio_set_io(cx);
+	return 0;
+}
+
+static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	/* set it to defaults from our table */
+	return cx18_get_input(cx, vin->index, vin);
+}
+
+static int cx18_cropcap(struct file *file, void *fh,
+			struct v4l2_cropcap *cropcap)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	cropcap->bounds.top = cropcap->bounds.left = 0;
+	cropcap->bounds.width = 720;
+	cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+	cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+	cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+	cropcap->defrect = cropcap->bounds;
+	return 0;
+}
+
+static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
+	return -EINVAL;
+}
+
+static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
+	return -EINVAL;
+}
+
+static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
+					struct v4l2_fmtdesc *fmt)
+{
+	static const struct v4l2_fmtdesc formats[] = {
+		{ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+		  "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
+		},
+		{ 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+		  "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
+		},
+		{ 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+		  "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 }
+		},
+	};
+
+	if (fmt->index > ARRAY_SIZE(formats) - 1)
+		return -EINVAL;
+	*fmt = formats[fmt->index];
+	return 0;
+}
+
+static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	*i = cx->active_input;
+	return 0;
+}
+
+int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	if (inp >= cx->nof_inputs)
+		return -EINVAL;
+
+	if (inp == cx->active_input) {
+		CX18_DEBUG_INFO("Input unchanged\n");
+		return 0;
+	}
+
+	CX18_DEBUG_INFO("Changing input from %d to %d\n",
+			cx->active_input, inp);
+
+	cx->active_input = inp;
+	/* Set the audio input to whatever is appropriate for the input type. */
+	cx->audio_input = cx->card->video_inputs[inp].audio_index;
+
+	/* prevent others from messing with the streams until
+	   we're finished changing inputs. */
+	cx18_mute(cx);
+	cx18_video_set_io(cx);
+	cx18_audio_set_io(cx);
+	cx18_unmute(cx);
+	return 0;
+}
+
+static int cx18_g_frequency(struct file *file, void *fh,
+				struct v4l2_frequency *vf)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	cx18_call_all(cx, tuner, g_frequency, vf);
+	return 0;
+}
+
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	cx18_mute(cx);
+	CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+	cx18_call_all(cx, tuner, s_frequency, vf);
+	cx18_unmute(cx);
+	return 0;
+}
+
+static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	*std = cx->std;
+	return 0;
+}
+
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	if ((*std & V4L2_STD_ALL) == 0)
+		return -EINVAL;
+
+	if (*std == cx->std)
+		return 0;
+
+	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+	    atomic_read(&cx->ana_capturing) > 0) {
+		/* Switching standard would turn off the radio or mess
+		   with already running streams, prevent that by
+		   returning EBUSY. */
+		return -EBUSY;
+	}
+
+	cx->std = *std;
+	cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+	cx->is_50hz = !cx->is_60hz;
+	cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
+	cx->cxhdl.width = 720;
+	cx->cxhdl.height = cx->is_50hz ? 576 : 480;
+	cx->vbi.count = cx->is_50hz ? 18 : 12;
+	cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+	cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+	CX18_DEBUG_INFO("Switching standard to %llx.\n",
+			(unsigned long long) cx->std);
+
+	/* Tuner */
+	cx18_call_all(cx, core, s_std, cx->std);
+	return 0;
+}
+
+static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+
+	if (vt->index != 0)
+		return -EINVAL;
+
+	cx18_call_all(cx, tuner, s_tuner, vt);
+	return 0;
+}
+
+static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	if (vt->index != 0)
+		return -EINVAL;
+
+	cx18_call_all(cx, tuner, g_tuner, vt);
+
+	if (vt->type == V4L2_TUNER_RADIO)
+		strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+	else
+		strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+	return 0;
+}
+
+static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_sliced_vbi_cap *cap)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+	int f, l;
+
+	if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+		return -EINVAL;
+
+	cap->service_set = 0;
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			if (valid_service_line(f, l, cx->is_50hz)) {
+				/*
+				 * We can find all v4l2 supported vbi services
+				 * for the standard, on a valid line for the std
+				 */
+				cap->service_lines[f][l] = set;
+				cap->service_set |= set;
+			} else
+				cap->service_lines[f][l] = 0;
+		}
+	}
+	for (f = 0; f < 3; f++)
+		cap->reserved[f] = 0;
+	return 0;
+}
+
+static int _cx18_process_idx_data(struct cx18_buffer *buf,
+				  struct v4l2_enc_idx *idx)
+{
+	int consumed, remaining;
+	struct v4l2_enc_idx_entry *e_idx;
+	struct cx18_enc_idx_entry *e_buf;
+
+	/* Frame type lookup: 1=I, 2=P, 4=B */
+	const int mapping[8] = {
+		-1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
+		-1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
+	};
+
+	/*
+	 * Assumption here is that a buf holds an integral number of
+	 * struct cx18_enc_idx_entry objects and is properly aligned.
+	 * This is enforced by the module options on IDX buffer sizes.
+	 */
+	remaining = buf->bytesused - buf->readpos;
+	consumed = 0;
+	e_idx = &idx->entry[idx->entries];
+	e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
+
+	while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
+	       idx->entries < V4L2_ENC_IDX_ENTRIES) {
+
+		e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
+				| le32_to_cpu(e_buf->offset_low);
+
+		e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
+			     | le32_to_cpu(e_buf->pts_low);
+
+		e_idx->length = le32_to_cpu(e_buf->length);
+
+		e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
+
+		e_idx->reserved[0] = 0;
+		e_idx->reserved[1] = 0;
+
+		idx->entries++;
+		e_idx = &idx->entry[idx->entries];
+		e_buf++;
+
+		remaining -= sizeof(struct cx18_enc_idx_entry);
+		consumed += sizeof(struct cx18_enc_idx_entry);
+	}
+
+	/* Swallow any partial entries at the end, if there are any */
+	if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
+		consumed += remaining;
+
+	buf->readpos += consumed;
+	return consumed;
+}
+
+static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
+				 struct v4l2_enc_idx *idx)
+{
+	if (s->type != CX18_ENC_STREAM_TYPE_IDX)
+		return -EINVAL;
+
+	if (mdl->curr_buf == NULL)
+		mdl->curr_buf = list_first_entry(&mdl->buf_list,
+						 struct cx18_buffer, list);
+
+	if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+		/*
+		 * For some reason we've exhausted the buffers, but the MDL
+		 * object still said some data was unread.
+		 * Fix that and bail out.
+		 */
+		mdl->readpos = mdl->bytesused;
+		return 0;
+	}
+
+	list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+		/* Skip any empty buffers in the MDL */
+		if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+			continue;
+
+		mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
+
+		/* exit when MDL drained or request satisfied */
+		if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
+		    mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+		    mdl->readpos >= mdl->bytesused)
+			break;
+	}
+	return 0;
+}
+
+static int cx18_g_enc_index(struct file *file, void *fh,
+				struct v4l2_enc_idx *idx)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+	s32 tmp;
+	struct cx18_mdl *mdl;
+
+	if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
+		return -EINVAL;
+
+	/* Compute the best case number of entries we can buffer */
+	tmp = s->buffers -
+			  s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
+	if (tmp <= 0)
+		tmp = 1;
+	tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
+
+	/* Fill out the header of the return structure */
+	idx->entries = 0;
+	idx->entries_cap = tmp;
+	memset(idx->reserved, 0, sizeof(idx->reserved));
+
+	/* Pull IDX MDLs and buffers from q_full and populate the entries */
+	do {
+		mdl = cx18_dequeue(s, &s->q_full);
+		if (mdl == NULL) /* No more IDX data right now */
+			break;
+
+		/* Extract the Index entry data from the MDL and buffers */
+		cx18_process_idx_data(s, mdl, idx);
+		if (mdl->readpos < mdl->bytesused) {
+			/* We finished with data remaining, push the MDL back */
+			cx18_push(s, mdl, &s->q_full);
+			break;
+		}
+
+		/* We drained this MDL, schedule it to go to the firmware */
+		cx18_enqueue(s, mdl, &s->q_free);
+
+	} while (idx->entries < V4L2_ENC_IDX_ENTRIES);
+
+	/* Tell the work handler to send free IDX MDLs to the firmware */
+	cx18_stream_load_fw_queue(s);
+	return 0;
+}
+
+static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
+{
+	struct videobuf_queue *q = NULL;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	switch (s->vb_type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		q = &s->vbuf_q;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		break;
+	default:
+		break;
+	}
+	return q;
+}
+
+static int cx18_streamon(struct file *file, void *priv,
+	enum v4l2_buf_type type)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	/* Start the hardware only if we're the video device */
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+		return -EINVAL;
+
+	/* Establish a buffer timeout */
+	mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
+
+	return videobuf_streamon(cx18_vb_queue(id));
+}
+
+static int cx18_streamoff(struct file *file, void *priv,
+	enum v4l2_buf_type type)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	/* Start the hardware only if we're the video device */
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+		return -EINVAL;
+
+	return videobuf_streamoff(cx18_vb_queue(id));
+}
+
+static int cx18_reqbufs(struct file *file, void *priv,
+	struct v4l2_requestbuffers *rb)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_reqbufs(cx18_vb_queue(id), rb);
+}
+
+static int cx18_querybuf(struct file *file, void *priv,
+	struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_querybuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_qbuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
+}
+
+static int cx18_encoder_cmd(struct file *file, void *fh,
+				struct v4l2_encoder_cmd *enc)
+{
+	struct cx18_open_id *id = fh2id(fh);
+	struct cx18 *cx = id->cx;
+	u32 h;
+
+	switch (enc->cmd) {
+	case V4L2_ENC_CMD_START:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+		enc->flags = 0;
+		return cx18_start_capture(id);
+
+	case V4L2_ENC_CMD_STOP:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+		cx18_stop_capture(id,
+				  enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+		break;
+
+	case V4L2_ENC_CMD_PAUSE:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+		enc->flags = 0;
+		if (!atomic_read(&cx->ana_capturing))
+			return -EPERM;
+		if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+			return 0;
+		h = cx18_find_handle(cx);
+		if (h == CX18_INVALID_TASK_HANDLE) {
+			CX18_ERR("Can't find valid task handle for "
+				 "V4L2_ENC_CMD_PAUSE\n");
+			return -EBADFD;
+		}
+		cx18_mute(cx);
+		cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
+		break;
+
+	case V4L2_ENC_CMD_RESUME:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+		enc->flags = 0;
+		if (!atomic_read(&cx->ana_capturing))
+			return -EPERM;
+		if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+			return 0;
+		h = cx18_find_handle(cx);
+		if (h == CX18_INVALID_TASK_HANDLE) {
+			CX18_ERR("Can't find valid task handle for "
+				 "V4L2_ENC_CMD_RESUME\n");
+			return -EBADFD;
+		}
+		cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
+		cx18_unmute(cx);
+		break;
+
+	default:
+		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_try_encoder_cmd(struct file *file, void *fh,
+				struct v4l2_encoder_cmd *enc)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	switch (enc->cmd) {
+	case V4L2_ENC_CMD_START:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+		enc->flags = 0;
+		break;
+
+	case V4L2_ENC_CMD_STOP:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+		break;
+
+	case V4L2_ENC_CMD_PAUSE:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+		enc->flags = 0;
+		break;
+
+	case V4L2_ENC_CMD_RESUME:
+		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+		enc->flags = 0;
+		break;
+
+	default:
+		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_log_status(struct file *file, void *fh)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+	struct v4l2_input vidin;
+	struct v4l2_audio audin;
+	int i;
+
+	CX18_INFO("Version: %s  Card: %s\n", CX18_VERSION, cx->card_name);
+	if (cx->hw_flags & CX18_HW_TVEEPROM) {
+		struct tveeprom tv;
+
+		cx18_read_eeprom(cx, &tv);
+	}
+	cx18_call_all(cx, core, log_status);
+	cx18_get_input(cx, cx->active_input, &vidin);
+	cx18_get_audio_input(cx, cx->audio_input, &audin);
+	CX18_INFO("Video Input: %s\n", vidin.name);
+	CX18_INFO("Audio Input: %s\n", audin.name);
+	mutex_lock(&cx->gpio_lock);
+	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
+		cx->gpio_dir, cx->gpio_val);
+	mutex_unlock(&cx->gpio_lock);
+	CX18_INFO("Tuner: %s\n",
+		test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?  "Radio" : "TV");
+	v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
+	CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		struct cx18_stream *s = &cx->streams[i];
+
+		if (s->video_dev == NULL || s->buffers == 0)
+			continue;
+		CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+			  s->name, s->s_flags,
+			  atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
+			   / s->buffers,
+			  (s->buffers * s->buf_size) / 1024, s->buffers);
+	}
+	CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+			(long long)cx->mpg_data_received,
+			(long long)cx->vbi_data_inserted);
+	return 0;
+}
+
+static long cx18_default(struct file *file, void *fh, bool valid_prio,
+							int cmd, void *arg)
+{
+	struct cx18 *cx = fh2id(fh)->cx;
+
+	switch (cmd) {
+	case VIDIOC_INT_RESET: {
+		u32 val = *(u32 *)arg;
+
+		if ((val == 0) || (val & 0x01))
+			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
+				     (u32) CX18_GPIO_RESET_Z8F0811);
+		break;
+	}
+
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
+	.vidioc_querycap                = cx18_querycap,
+	.vidioc_s_audio                 = cx18_s_audio,
+	.vidioc_g_audio                 = cx18_g_audio,
+	.vidioc_enumaudio               = cx18_enumaudio,
+	.vidioc_enum_input              = cx18_enum_input,
+	.vidioc_cropcap                 = cx18_cropcap,
+	.vidioc_s_crop                  = cx18_s_crop,
+	.vidioc_g_crop                  = cx18_g_crop,
+	.vidioc_g_input                 = cx18_g_input,
+	.vidioc_s_input                 = cx18_s_input,
+	.vidioc_g_frequency             = cx18_g_frequency,
+	.vidioc_s_frequency             = cx18_s_frequency,
+	.vidioc_s_tuner                 = cx18_s_tuner,
+	.vidioc_g_tuner                 = cx18_g_tuner,
+	.vidioc_g_enc_index             = cx18_g_enc_index,
+	.vidioc_g_std                   = cx18_g_std,
+	.vidioc_s_std                   = cx18_s_std,
+	.vidioc_log_status              = cx18_log_status,
+	.vidioc_enum_fmt_vid_cap        = cx18_enum_fmt_vid_cap,
+	.vidioc_encoder_cmd             = cx18_encoder_cmd,
+	.vidioc_try_encoder_cmd         = cx18_try_encoder_cmd,
+	.vidioc_g_fmt_vid_cap           = cx18_g_fmt_vid_cap,
+	.vidioc_g_fmt_vbi_cap           = cx18_g_fmt_vbi_cap,
+	.vidioc_g_fmt_sliced_vbi_cap    = cx18_g_fmt_sliced_vbi_cap,
+	.vidioc_s_fmt_vid_cap           = cx18_s_fmt_vid_cap,
+	.vidioc_s_fmt_vbi_cap           = cx18_s_fmt_vbi_cap,
+	.vidioc_s_fmt_sliced_vbi_cap    = cx18_s_fmt_sliced_vbi_cap,
+	.vidioc_try_fmt_vid_cap         = cx18_try_fmt_vid_cap,
+	.vidioc_try_fmt_vbi_cap         = cx18_try_fmt_vbi_cap,
+	.vidioc_try_fmt_sliced_vbi_cap  = cx18_try_fmt_sliced_vbi_cap,
+	.vidioc_g_sliced_vbi_cap        = cx18_g_sliced_vbi_cap,
+	.vidioc_g_chip_ident            = cx18_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register              = cx18_g_register,
+	.vidioc_s_register              = cx18_s_register,
+#endif
+	.vidioc_default                 = cx18_default,
+	.vidioc_streamon                = cx18_streamon,
+	.vidioc_streamoff               = cx18_streamoff,
+	.vidioc_reqbufs                 = cx18_reqbufs,
+	.vidioc_querybuf                = cx18_querybuf,
+	.vidioc_qbuf                    = cx18_qbuf,
+	.vidioc_dqbuf                   = cx18_dqbuf,
+};
+
+void cx18_set_funcs(struct video_device *vdev)
+{
+	vdev->ioctl_ops = &cx18_ioctl_ops;
+}
diff --git a/drivers/media/pci/cx18/cx18-ioctl.h b/drivers/media/pci/cx18/cx18-ioctl.h
new file mode 100644
index 000000000000..2f9dd591ee0f
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-ioctl.h
@@ -0,0 +1,31 @@
+/*
+ *  cx18 ioctl system call
+ *
+ *  Derived from ivtv-ioctl.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+u16 cx18_service2vbi(int type);
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+void cx18_set_funcs(struct video_device *vdev);
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int cx18_s_input(struct file *file, void *fh, unsigned int inp);
diff --git a/drivers/media/pci/cx18/cx18-irq.c b/drivers/media/pci/cx18/cx18-irq.c
new file mode 100644
index 000000000000..80edfe93a3d8
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-irq.c
@@ -0,0 +1,81 @@
+/*
+ *  cx18 interrupt handling
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+
+static void xpu_ack(struct cx18 *cx, u32 sw2)
+{
+	if (sw2 & IRQ_CPU_TO_EPU_ACK)
+		wake_up(&cx->mb_cpu_waitq);
+	if (sw2 & IRQ_APU_TO_EPU_ACK)
+		wake_up(&cx->mb_apu_waitq);
+}
+
+static void epu_cmd(struct cx18 *cx, u32 sw1)
+{
+	if (sw1 & IRQ_CPU_TO_EPU)
+		cx18_api_epu_cmd_irq(cx, CPU);
+	if (sw1 & IRQ_APU_TO_EPU)
+		cx18_api_epu_cmd_irq(cx, APU);
+}
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id)
+{
+	struct cx18 *cx = (struct cx18 *)dev_id;
+	u32 sw1, sw2, hw2;
+
+	sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask;
+	sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask;
+	hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask;
+
+	if (sw1)
+		cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
+	if (sw2)
+		cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2);
+	if (hw2)
+		cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
+
+	if (sw1 || sw2 || hw2)
+		CX18_DEBUG_HI_IRQ("received interrupts "
+				  "SW1: %x  SW2: %x  HW2: %x\n", sw1, sw2, hw2);
+
+	/*
+	 * SW1 responses have to happen first.  The sending XPU times out the
+	 * incoming mailboxes on us rather rapidly.
+	 */
+	if (sw1)
+		epu_cmd(cx, sw1);
+
+	/* To do: interrupt-based I2C handling
+	if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
+	}
+	*/
+
+	if (sw2)
+		xpu_ack(cx, sw2);
+
+	return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
+}
diff --git a/drivers/media/pci/cx18/cx18-irq.h b/drivers/media/pci/cx18/cx18-irq.h
new file mode 100644
index 000000000000..30e7eaf8cb55
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-irq.h
@@ -0,0 +1,35 @@
+/*
+ *  cx18 interrupt handling
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#define HW2_I2C1_INT			(1 << 22)
+#define HW2_I2C2_INT			(1 << 23)
+#define HW2_INT_CLR_STATUS		0xc730c4
+#define HW2_INT_MASK5_PCI		0xc730e4
+#define SW1_INT_SET                     0xc73100
+#define SW1_INT_STATUS                  0xc73104
+#define SW1_INT_ENABLE_PCI              0xc7311c
+#define SW2_INT_SET                     0xc73140
+#define SW2_INT_STATUS                  0xc73144
+#define SW2_INT_ENABLE_CPU              0xc73158
+#define SW2_INT_ENABLE_PCI              0xc7315c
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id);
diff --git a/drivers/media/pci/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c
new file mode 100644
index 000000000000..eabf00c6351b
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-mailbox.c
@@ -0,0 +1,870 @@
+/*
+ *  cx18 mailbox functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include <stdarg.h>
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-alsa-pcm.h" /* FIXME make configurable */
+
+static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
+
+#define API_FAST (1 << 2) /* Short timeout */
+#define API_SLOW (1 << 3) /* Additional 300ms timeout */
+
+struct cx18_api_info {
+	u32 cmd;
+	u8 flags;		/* Flags, see above */
+	u8 rpu;			/* Processing unit */
+	const char *name; 	/* The name of the command */
+};
+
+#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
+
+static const struct cx18_api_info api_info[] = {
+	/* MPEG encoder API */
+	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,		0),
+	API_ENTRY(CPU, CX18_EPU_DEBUG, 				0),
+	API_ENTRY(CPU, CX18_CREATE_TASK, 			0),
+	API_ENTRY(CPU, CX18_DESTROY_TASK, 			0),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_START,                  API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP,                   API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE,                  0),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE,         0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN,                   0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE,        0),
+	API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS,            0),
+	API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM,              API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO,            0),
+	API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION,     0),
+	API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER,      0),
+	API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
+	API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM,                  0),
+	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,			0),
+	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,			API_FAST),
+	API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL,			API_SLOW),
+	API_ENTRY(APU, CX18_APU_START,				0),
+	API_ENTRY(APU, CX18_APU_STOP,				0),
+	API_ENTRY(APU, CX18_APU_RESETAI,			0),
+	API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32,			0),
+	API_ENTRY(0, 0,						0),
+};
+
+static const struct cx18_api_info *find_api_info(u32 cmd)
+{
+	int i;
+
+	for (i = 0; api_info[i].cmd; i++)
+		if (api_info[i].cmd == cmd)
+			return &api_info[i];
+	return NULL;
+}
+
+/* Call with buf of n*11+1 bytes */
+static char *u32arr2hex(u32 data[], int n, char *buf)
+{
+	char *p;
+	int i;
+
+	for (i = 0, p = buf; i < n; i++, p += 11) {
+		/* kernel snprintf() appends '\0' always */
+		snprintf(p, 12, " %#010x", data[i]);
+	}
+	*p = '\0';
+	return buf;
+}
+
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
+{
+	char argstr[MAX_MB_ARGUMENTS*11+1];
+
+	if (!(cx18_debug & CX18_DBGFLG_API))
+		return;
+
+	CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+		       "\n", name, mb->request, mb->ack, mb->cmd, mb->error,
+		       u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr));
+}
+
+
+/*
+ * Functions that run in a work_queue work handling context
+ */
+
+static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+	struct cx18_buffer *buf;
+
+	if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0)
+		return;
+
+	/* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+	/* The likely case */
+	if (list_is_singular(&mdl->buf_list)) {
+		buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+				       list);
+		if (buf->bytesused)
+			dvb_dmx_swfilter(&s->dvb->demux,
+					 buf->buf, buf->bytesused);
+		return;
+	}
+
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		if (buf->bytesused == 0)
+			break;
+		dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused);
+	}
+}
+
+static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
+	struct cx18_mdl *mdl)
+{
+	struct cx18_videobuf_buffer *vb_buf;
+	struct cx18_buffer *buf;
+	u8 *p;
+	u32 offset = 0;
+	int dispatch = 0;
+
+	if (mdl->bytesused == 0)
+		return;
+
+	/* Acquire a videobuf buffer, clone to and and release it */
+	spin_lock(&s->vb_lock);
+	if (list_empty(&s->vb_capture))
+		goto out;
+
+	vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer,
+		vb.queue);
+
+	p = videobuf_to_vmalloc(&vb_buf->vb);
+	if (!p)
+		goto out;
+
+	offset = vb_buf->bytes_used;
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		if (buf->bytesused == 0)
+			break;
+
+		if ((offset + buf->bytesused) <= vb_buf->vb.bsize) {
+			memcpy(p + offset, buf->buf, buf->bytesused);
+			offset += buf->bytesused;
+			vb_buf->bytes_used += buf->bytesused;
+		}
+	}
+
+	/* If we've filled the buffer as per the callers res then dispatch it */
+	if (vb_buf->bytes_used >= s->vb_bytes_per_frame) {
+		dispatch = 1;
+		vb_buf->bytes_used = 0;
+	}
+
+	if (dispatch) {
+		vb_buf->vb.ts = ktime_to_timeval(ktime_get());
+		list_del(&vb_buf->vb.queue);
+		vb_buf->vb.state = VIDEOBUF_DONE;
+		wake_up(&vb_buf->vb.done);
+	}
+
+	mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
+
+out:
+	spin_unlock(&s->vb_lock);
+}
+
+static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,
+				  struct cx18_mdl *mdl)
+{
+	struct cx18_buffer *buf;
+
+	if (mdl->bytesused == 0)
+		return;
+
+	/* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+	/* The likely case */
+	if (list_is_singular(&mdl->buf_list)) {
+		buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+				       list);
+		if (buf->bytesused)
+			cx->pcm_announce_callback(cx->alsa, buf->buf,
+						  buf->bytesused);
+		return;
+	}
+
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		if (buf->bytesused == 0)
+			break;
+		cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused);
+	}
+}
+
+static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	u32 handle, mdl_ack_count, id;
+	struct cx18_mailbox *mb;
+	struct cx18_mdl_ack *mdl_ack;
+	struct cx18_stream *s;
+	struct cx18_mdl *mdl;
+	int i;
+
+	mb = &order->mb;
+	handle = mb->args[0];
+	s = cx18_handle_to_stream(cx, handle);
+
+	if (s == NULL) {
+		CX18_WARN("Got DMA done notification for unknown/inactive"
+			  " handle %d, %s mailbox seq no %d\n", handle,
+			  (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?
+			  "stale" : "good", mb->request);
+		return;
+	}
+
+	mdl_ack_count = mb->args[2];
+	mdl_ack = order->mdl_ack;
+	for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
+		id = mdl_ack->id;
+		/*
+		 * Simple integrity check for processing a stale (and possibly
+		 * inconsistent mailbox): make sure the MDL id is in the
+		 * valid range for the stream.
+		 *
+		 * We go through the trouble of dealing with stale mailboxes
+		 * because most of the time, the mailbox data is still valid and
+		 * unchanged (and in practice the firmware ping-pongs the
+		 * two mdl_ack buffers so mdl_acks are not stale).
+		 *
+		 * There are occasions when we get a half changed mailbox,
+		 * which this check catches for a handle & id mismatch.  If the
+		 * handle and id do correspond, the worst case is that we
+		 * completely lost the old MDL, but pick up the new MDL
+		 * early (but the new mdl_ack is guaranteed to be good in this
+		 * case as the firmware wouldn't point us to a new mdl_ack until
+		 * it's filled in).
+		 *
+		 * cx18_queue_get_mdl() will detect the lost MDLs
+		 * and send them back to q_free for fw rotation eventually.
+		 */
+		if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
+		    !(id >= s->mdl_base_idx &&
+		      id < (s->mdl_base_idx + s->buffers))) {
+			CX18_WARN("Fell behind! Ignoring stale mailbox with "
+				  " inconsistent data. Lost MDL for mailbox "
+				  "seq no %d\n", mb->request);
+			break;
+		}
+		mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);
+
+		CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);
+		if (mdl == NULL) {
+			CX18_WARN("Could not find MDL %d for stream %s\n",
+				  id, s->name);
+			continue;
+		}
+
+		CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
+				  s->name, mdl->bytesused);
+
+		if (s->type == CX18_ENC_STREAM_TYPE_TS) {
+			cx18_mdl_send_to_dvb(s, mdl);
+			cx18_enqueue(s, mdl, &s->q_free);
+		} else if (s->type == CX18_ENC_STREAM_TYPE_PCM) {
+			/* Pass the data to cx18-alsa */
+			if (cx->pcm_announce_callback != NULL) {
+				cx18_mdl_send_to_alsa(cx, s, mdl);
+				cx18_enqueue(s, mdl, &s->q_free);
+			} else {
+				cx18_enqueue(s, mdl, &s->q_full);
+			}
+		} else if (s->type == CX18_ENC_STREAM_TYPE_YUV) {
+			cx18_mdl_send_to_videobuf(s, mdl);
+			cx18_enqueue(s, mdl, &s->q_free);
+		} else {
+			cx18_enqueue(s, mdl, &s->q_full);
+			if (s->type == CX18_ENC_STREAM_TYPE_IDX)
+				cx18_stream_rotate_idx_mdls(cx);
+		}
+	}
+	/* Put as many MDLs as possible back into fw use */
+	cx18_stream_load_fw_queue(s);
+
+	wake_up(&cx->dma_waitq);
+	if (s->id != -1)
+		wake_up(&s->waitq);
+}
+
+static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	char *p;
+	char *str = order->str;
+
+	CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
+	p = strchr(str, '.');
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+		CX18_INFO("FW version: %s\n", p - 1);
+}
+
+static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	switch (order->rpu) {
+	case CPU:
+	{
+		switch (order->mb.cmd) {
+		case CX18_EPU_DMA_DONE:
+			epu_dma_done(cx, order);
+			break;
+		case CX18_EPU_DEBUG:
+			epu_debug(cx, order);
+			break;
+		default:
+			CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+				  order->mb.cmd);
+			break;
+		}
+		break;
+	}
+	case APU:
+		CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+			  order->mb.cmd);
+		break;
+	default:
+		break;
+	}
+}
+
+static
+void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	atomic_set(&order->pending, 0);
+}
+
+void cx18_in_work_handler(struct work_struct *work)
+{
+	struct cx18_in_work_order *order =
+			container_of(work, struct cx18_in_work_order, work);
+	struct cx18 *cx = order->cx;
+	epu_cmd(cx, order);
+	free_in_work_order(cx, order);
+}
+
+
+/*
+ * Functions that run in an interrupt handling context
+ */
+
+static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	struct cx18_mailbox __iomem *ack_mb;
+	u32 ack_irq, req;
+
+	switch (order->rpu) {
+	case APU:
+		ack_irq = IRQ_EPU_TO_APU_ACK;
+		ack_mb = &cx->scb->apu2epu_mb;
+		break;
+	case CPU:
+		ack_irq = IRQ_EPU_TO_CPU_ACK;
+		ack_mb = &cx->scb->cpu2epu_mb;
+		break;
+	default:
+		CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
+			  order->rpu, order->mb.cmd);
+		return;
+	}
+
+	req = order->mb.request;
+	/* Don't ack if the RPU has gotten impatient and timed us out */
+	if (req != cx18_readl(cx, &ack_mb->request) ||
+	    req == cx18_readl(cx, &ack_mb->ack)) {
+		CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+				"incoming %s to EPU mailbox (sequence no. %u) "
+				"while processing\n",
+				rpu_str[order->rpu], rpu_str[order->rpu], req);
+		order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;
+		return;
+	}
+	cx18_writel(cx, req, &ack_mb->ack);
+	cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
+	return;
+}
+
+static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	u32 handle, mdl_ack_offset, mdl_ack_count;
+	struct cx18_mailbox *mb;
+	int i;
+
+	mb = &order->mb;
+	handle = mb->args[0];
+	mdl_ack_offset = mb->args[1];
+	mdl_ack_count = mb->args[2];
+
+	if (handle == CX18_INVALID_TASK_HANDLE ||
+	    mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
+		if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+			mb_ack_irq(cx, order);
+		return -1;
+	}
+
+	for (i = 0; i < sizeof(struct cx18_mdl_ack) * mdl_ack_count; i += sizeof(u32))
+		((u32 *)order->mdl_ack)[i / sizeof(u32)] =
+			cx18_readl(cx, cx->enc_mem + mdl_ack_offset + i);
+
+	if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+		mb_ack_irq(cx, order);
+	return 1;
+}
+
+static
+int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	u32 str_offset;
+	char *str = order->str;
+
+	str[0] = '\0';
+	str_offset = order->mb.args[1];
+	if (str_offset) {
+		cx18_setup_page(cx, str_offset);
+		cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
+		str[252] = '\0';
+		cx18_setup_page(cx, SCB_OFFSET);
+	}
+
+	if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+		mb_ack_irq(cx, order);
+
+	return str_offset ? 1 : 0;
+}
+
+static inline
+int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+	int ret = -1;
+
+	switch (order->rpu) {
+	case CPU:
+	{
+		switch (order->mb.cmd) {
+		case CX18_EPU_DMA_DONE:
+			ret = epu_dma_done_irq(cx, order);
+			break;
+		case CX18_EPU_DEBUG:
+			ret = epu_debug_irq(cx, order);
+			break;
+		default:
+			CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+				  order->mb.cmd);
+			break;
+		}
+		break;
+	}
+	case APU:
+		CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+			  order->mb.cmd);
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static inline
+struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx)
+{
+	int i;
+	struct cx18_in_work_order *order = NULL;
+
+	for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+		/*
+		 * We only need "pending" atomic to inspect its contents,
+		 * and need not do a check and set because:
+		 * 1. Any work handler thread only clears "pending" and only
+		 * on one, particular work order at a time, per handler thread.
+		 * 2. "pending" is only set here, and we're serialized because
+		 * we're called in an IRQ handler context.
+		 */
+		if (atomic_read(&cx->in_work_order[i].pending) == 0) {
+			order = &cx->in_work_order[i];
+			atomic_set(&order->pending, 1);
+			break;
+		}
+	}
+	return order;
+}
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+{
+	struct cx18_mailbox __iomem *mb;
+	struct cx18_mailbox *order_mb;
+	struct cx18_in_work_order *order;
+	int submit;
+	int i;
+
+	switch (rpu) {
+	case CPU:
+		mb = &cx->scb->cpu2epu_mb;
+		break;
+	case APU:
+		mb = &cx->scb->apu2epu_mb;
+		break;
+	default:
+		return;
+	}
+
+	order = alloc_in_work_order_irq(cx);
+	if (order == NULL) {
+		CX18_WARN("Unable to find blank work order form to schedule "
+			  "incoming mailbox command processing\n");
+		return;
+	}
+
+	order->flags = 0;
+	order->rpu = rpu;
+	order_mb = &order->mb;
+
+	/* mb->cmd and mb->args[0] through mb->args[2] */
+	for (i = 0; i < 4; i++)
+		(&order_mb->cmd)[i] = cx18_readl(cx, &mb->cmd + i);
+
+	/* mb->request and mb->ack.  N.B. we want to read mb->ack last */
+	for (i = 0; i < 2; i++)
+		(&order_mb->request)[i] = cx18_readl(cx, &mb->request + i);
+
+	if (order_mb->request == order_mb->ack) {
+		CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+				"incoming %s to EPU mailbox (sequence no. %u)"
+				"\n",
+				rpu_str[rpu], rpu_str[rpu], order_mb->request);
+		if (cx18_debug & CX18_DBGFLG_WARN)
+			dump_mb(cx, order_mb, "incoming");
+		order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+	}
+
+	/*
+	 * Individual EPU command processing is responsible for ack-ing
+	 * a non-stale mailbox as soon as possible
+	 */
+	submit = epu_cmd_irq(cx, order);
+	if (submit > 0) {
+		queue_work(cx->in_work_queue, &order->work);
+	}
+}
+
+
+/*
+ * Functions called from a non-interrupt, non work_queue context
+ */
+
+static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+	const struct cx18_api_info *info = find_api_info(cmd);
+	u32 irq, req, ack, err;
+	struct cx18_mailbox __iomem *mb;
+	wait_queue_head_t *waitq;
+	struct mutex *mb_lock;
+	unsigned long int t0, timeout, ret;
+	int i;
+	char argstr[MAX_MB_ARGUMENTS*11+1];
+	DEFINE_WAIT(w);
+
+	if (info == NULL) {
+		CX18_WARN("unknown cmd %x\n", cmd);
+		return -EINVAL;
+	}
+
+	if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */
+		if (cmd == CX18_CPU_DE_SET_MDL) {
+			if (cx18_debug & CX18_DBGFLG_HIGHVOL)
+				CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",
+						info->name, cmd,
+						u32arr2hex(data, args, argstr));
+		} else
+			CX18_DEBUG_API("%s\tcmd %#010x args%s\n",
+				       info->name, cmd,
+				       u32arr2hex(data, args, argstr));
+	}
+
+	switch (info->rpu) {
+	case APU:
+		waitq = &cx->mb_apu_waitq;
+		mb_lock = &cx->epu2apu_mb_lock;
+		irq = IRQ_EPU_TO_APU;
+		mb = &cx->scb->epu2apu_mb;
+		break;
+	case CPU:
+		waitq = &cx->mb_cpu_waitq;
+		mb_lock = &cx->epu2cpu_mb_lock;
+		irq = IRQ_EPU_TO_CPU;
+		mb = &cx->scb->epu2cpu_mb;
+		break;
+	default:
+		CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
+		return -EINVAL;
+	}
+
+	mutex_lock(mb_lock);
+	/*
+	 * Wait for an in-use mailbox to complete
+	 *
+	 * If the XPU is responding with Ack's, the mailbox shouldn't be in
+	 * a busy state, since we serialize access to it on our end.
+	 *
+	 * If the wait for ack after sending a previous command was interrupted
+	 * by a signal, we may get here and find a busy mailbox.  After waiting,
+	 * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
+	 */
+	req = cx18_readl(cx, &mb->request);
+	timeout = msecs_to_jiffies(10);
+	ret = wait_event_timeout(*waitq,
+				 (ack = cx18_readl(cx, &mb->ack)) == req,
+				 timeout);
+	if (req != ack) {
+		/* waited long enough, make the mbox "not busy" from our end */
+		cx18_writel(cx, req, &mb->ack);
+		CX18_ERR("mbox was found stuck busy when setting up for %s; "
+			 "clearing busy and trying to proceed\n", info->name);
+	} else if (ret != timeout)
+		CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
+			       jiffies_to_msecs(timeout-ret));
+
+	/* Build the outgoing mailbox */
+	req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
+
+	cx18_writel(cx, cmd, &mb->cmd);
+	for (i = 0; i < args; i++)
+		cx18_writel(cx, data[i], &mb->args[i]);
+	cx18_writel(cx, 0, &mb->error);
+	cx18_writel(cx, req, &mb->request);
+	cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
+
+	/*
+	 * Notify the XPU and wait for it to send an Ack back
+	 */
+	timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);
+
+	CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
+			  irq, info->name);
+
+	/* So we don't miss the wakeup, prepare to wait before notifying fw */
+	prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE);
+	cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
+
+	t0 = jiffies;
+	ack = cx18_readl(cx, &mb->ack);
+	if (ack != req) {
+		schedule_timeout(timeout);
+		ret = jiffies - t0;
+		ack = cx18_readl(cx, &mb->ack);
+	} else {
+		ret = jiffies - t0;
+	}
+
+	finish_wait(waitq, &w);
+
+	if (req != ack) {
+		mutex_unlock(mb_lock);
+		if (ret >= timeout) {
+			/* Timed out */
+			CX18_DEBUG_WARN("sending %s timed out waiting %d msecs "
+					"for RPU acknowledgement\n",
+					info->name, jiffies_to_msecs(ret));
+		} else {
+			CX18_DEBUG_WARN("woken up before mailbox ack was ready "
+					"after submitting %s to RPU.  only "
+					"waited %d msecs on req %u but awakened"
+					" with unmatched ack %u\n",
+					info->name,
+					jiffies_to_msecs(ret),
+					req, ack);
+		}
+		return -EINVAL;
+	}
+
+	if (ret >= timeout)
+		CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment "
+				"sending %s; timed out waiting %d msecs\n",
+				info->name, jiffies_to_msecs(ret));
+	else
+		CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
+				  jiffies_to_msecs(ret), info->name);
+
+	/* Collect data returned by the XPU */
+	for (i = 0; i < MAX_MB_ARGUMENTS; i++)
+		data[i] = cx18_readl(cx, &mb->args[i]);
+	err = cx18_readl(cx, &mb->error);
+	mutex_unlock(mb_lock);
+
+	/*
+	 * Wait for XPU to perform extra actions for the caller in some cases.
+	 * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs
+	 * back in a burst shortly thereafter
+	 */
+	if (info->flags & API_SLOW)
+		cx18_msleep_timeout(300, 0);
+
+	if (err)
+		CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
+				info->name);
+	return err ? -EIO : 0;
+}
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+	return cx18_api_call(cx, cmd, args, data);
+}
+
+static int cx18_set_filter_param(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	u32 mode;
+	int ret;
+
+	mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
+	ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 1, mode, cx->spatial_strength);
+	mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
+	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 0, mode, cx->temporal_strength);
+	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 2, cx->filter_mode >> 2, 0);
+	return ret;
+}
+
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+		u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct cx18_stream *s = priv;
+	struct cx18 *cx = s->cx;
+
+	switch (cmd) {
+	case CX2341X_ENC_SET_OUTPUT_PORT:
+		return 0;
+	case CX2341X_ENC_SET_FRAME_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
+				s->handle, 0, 0, 0, 0, data[0]);
+	case CX2341X_ENC_SET_FRAME_SIZE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
+				s->handle, data[1], data[0]);
+	case CX2341X_ENC_SET_STREAM_TYPE:
+		return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_ASPECT_RATIO:
+		return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
+				s->handle, data[0]);
+
+	case CX2341X_ENC_SET_GOP_PROPERTIES:
+		return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
+				s->handle, data[0], data[1]);
+	case CX2341X_ENC_SET_GOP_CLOSURE:
+		return 0;
+	case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_MUTE_AUDIO:
+		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_BIT_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
+				s->handle, data[0], data[1], data[2], data[3]);
+	case CX2341X_ENC_MUTE_VIDEO:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_FRAME_DROP_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_MISC:
+		return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
+				s->handle, data[0], data[1], data[2]);
+	case CX2341X_ENC_SET_DNR_FILTER_MODE:
+		cx->filter_mode = (data[0] & 3) | (data[1] << 2);
+		return cx18_set_filter_param(s);
+	case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+		cx->spatial_strength = data[0];
+		cx->temporal_strength = data[1];
+		return cx18_set_filter_param(s);
+	case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+		return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
+				s->handle, data[0], data[1]);
+	case CX2341X_ENC_SET_CORING_LEVELS:
+		return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
+				s->handle, data[0], data[1], data[2], data[3]);
+	}
+	CX18_WARN("Unknown cmd %x\n", cmd);
+	return 0;
+}
+
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
+		u32 cmd, int args, ...)
+{
+	va_list ap;
+	int i;
+
+	va_start(ap, args);
+	for (i = 0; i < args; i++)
+		data[i] = va_arg(ap, u32);
+	va_end(ap);
+	return cx18_api(cx, cmd, args, data);
+}
+
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
+{
+	u32 data[MAX_MB_ARGUMENTS];
+	va_list ap;
+	int i;
+
+	if (cx == NULL) {
+		CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
+		return 0;
+	}
+	if (args > MAX_MB_ARGUMENTS) {
+		CX18_ERR("args too big (cmd=%x)\n", cmd);
+		args = MAX_MB_ARGUMENTS;
+	}
+	va_start(ap, args);
+	for (i = 0; i < args; i++)
+		data[i] = va_arg(ap, u32);
+	va_end(ap);
+	return cx18_api(cx, cmd, args, data);
+}
diff --git a/drivers/media/pci/cx18/cx18-mailbox.h b/drivers/media/pci/cx18/cx18-mailbox.h
new file mode 100644
index 000000000000..b63fdfaac49e
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-mailbox.h
@@ -0,0 +1,95 @@
+/*
+ *  cx18 mailbox functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#ifndef _CX18_MAILBOX_H_
+#define _CX18_MAILBOX_H_
+
+/* mailbox max args */
+#define MAX_MB_ARGUMENTS 6
+/* compatibility, should be same as the define in cx2341x.h */
+#define CX2341X_MBOX_MAX_DATA 16
+
+#define MB_RESERVED_HANDLE_0 0
+#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
+struct cx18;
+
+/*
+ * This structure is used by CPU to provide completed MDL & buffers information.
+ * Its structure is dictated by the layout of the SCB, required by the
+ * firmware, but its definition needs to be here, instead of in cx18-scb.h,
+ * for mailbox work order scheduling
+ */
+struct cx18_mdl_ack {
+    u32 id;        /* ID of a completed MDL */
+    u32 data_used; /* Total data filled in the MDL with 'id' */
+};
+
+/* The cx18_mailbox struct is the mailbox structure which is used for passing
+   messages between processors */
+struct cx18_mailbox {
+    /* The sender sets a handle in 'request' after he fills the command. The
+       'request' should be different than 'ack'. The sender, also, generates
+       an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the
+       receiver. */
+    u32       request;
+    /* The receiver detects a new command when 'req' is different than 'ack'.
+       He sets 'ack' to the same value as 'req' to clear the command. He, also,
+       generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU
+       is the receiver. */
+    u32       ack;
+    u32       reserved[6];
+    /* 'cmd' identifies the command. The list of these commands are in
+       cx23418.h */
+    u32       cmd;
+    /* Each command can have up to 6 arguments */
+    u32       args[MAX_MB_ARGUMENTS];
+    /* The return code can be one of the codes in the file cx23418.h. If the
+       command is completed successfully, the error will be ERR_SYS_SUCCESS.
+       If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
+       code would indicate the task from which the error originated and will
+       be one of the errors in cx23418.h. In that case, the following
+       applies ((error & 0xff) != 0).
+       If the command is pending, the return will be passed in a MB from the
+       receiver to the sender. 'req' will be returned in args[0] */
+    u32       error;
+};
+
+struct cx18_stream;
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
+		int args, ...);
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+		u32 data[CX2341X_MBOX_MAX_DATA]);
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
+
+void cx18_in_work_handler(struct work_struct *work);
+
+#endif
diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c
new file mode 100644
index 000000000000..8884537bd62f
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-queue.c
@@ -0,0 +1,443 @@
+/*
+ *  cx18 buffer queues
+ *
+ *  Derived from ivtv-queue.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-scb.h"
+#include "cx18-io.h"
+
+void cx18_buf_swap(struct cx18_buffer *buf)
+{
+	int i;
+
+	for (i = 0; i < buf->bytesused; i += 4)
+		swab32s((u32 *)(buf->buf + i));
+}
+
+void _cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+	struct cx18_buffer *buf;
+
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		if (buf->bytesused == 0)
+			break;
+		cx18_buf_swap(buf);
+	}
+}
+
+void cx18_queue_init(struct cx18_queue *q)
+{
+	INIT_LIST_HEAD(&q->list);
+	atomic_set(&q->depth, 0);
+	q->bytesused = 0;
+}
+
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+				 struct cx18_queue *q, int to_front)
+{
+	/* clear the mdl if it is not to be enqueued to the full queue */
+	if (q != &s->q_full) {
+		mdl->bytesused = 0;
+		mdl->readpos = 0;
+		mdl->m_flags = 0;
+		mdl->skipped = 0;
+		mdl->curr_buf = NULL;
+	}
+
+	/* q_busy is restricted to a max buffer count imposed by firmware */
+	if (q == &s->q_busy &&
+	    atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
+		q = &s->q_free;
+
+	spin_lock(&q->lock);
+
+	if (to_front)
+		list_add(&mdl->list, &q->list); /* LIFO */
+	else
+		list_add_tail(&mdl->list, &q->list); /* FIFO */
+	q->bytesused += mdl->bytesused - mdl->readpos;
+	atomic_inc(&q->depth);
+
+	spin_unlock(&q->lock);
+	return q;
+}
+
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+{
+	struct cx18_mdl *mdl = NULL;
+
+	spin_lock(&q->lock);
+	if (!list_empty(&q->list)) {
+		mdl = list_first_entry(&q->list, struct cx18_mdl, list);
+		list_del_init(&mdl->list);
+		q->bytesused -= mdl->bytesused - mdl->readpos;
+		mdl->skipped = 0;
+		atomic_dec(&q->depth);
+	}
+	spin_unlock(&q->lock);
+	return mdl;
+}
+
+static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+					  struct cx18_mdl *mdl)
+{
+	struct cx18_buffer *buf;
+	u32 buf_size = s->buf_size;
+	u32 bytesused = mdl->bytesused;
+
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		buf->readpos = 0;
+		if (bytesused >= buf_size) {
+			buf->bytesused = buf_size;
+			bytesused -= buf_size;
+		} else {
+			buf->bytesused = bytesused;
+			bytesused = 0;
+		}
+		cx18_buf_sync_for_cpu(s, buf);
+	}
+}
+
+static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+						struct cx18_mdl *mdl)
+{
+	struct cx18_buffer *buf;
+
+	if (list_is_singular(&mdl->buf_list)) {
+		buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+				       list);
+		buf->bytesused = mdl->bytesused;
+		buf->readpos = 0;
+		cx18_buf_sync_for_cpu(s, buf);
+	} else {
+		_cx18_mdl_update_bufs_for_cpu(s, mdl);
+	}
+}
+
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
+	u32 bytesused)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_mdl *mdl;
+	struct cx18_mdl *tmp;
+	struct cx18_mdl *ret = NULL;
+	LIST_HEAD(sweep_up);
+
+	/*
+	 * We don't have to acquire multiple q locks here, because we are
+	 * serialized by the single threaded work handler.
+	 * MDLs from the firmware will thus remain in order as
+	 * they are moved from q_busy to q_full or to the dvb ring buffer.
+	 */
+	spin_lock(&s->q_busy.lock);
+	list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) {
+		/*
+		 * We should find what the firmware told us is done,
+		 * right at the front of the queue.  If we don't, we likely have
+		 * missed an mdl done message from the firmware.
+		 * Once we skip an mdl repeatedly, relative to the size of
+		 * q_busy, we have high confidence we've missed it.
+		 */
+		if (mdl->id != id) {
+			mdl->skipped++;
+			if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) {
+				/* mdl must have fallen out of rotation */
+				CX18_WARN("Skipped %s, MDL %d, %d "
+					  "times - it must have dropped out of "
+					  "rotation\n", s->name, mdl->id,
+					  mdl->skipped);
+				/* Sweep it up to put it back into rotation */
+				list_move_tail(&mdl->list, &sweep_up);
+				atomic_dec(&s->q_busy.depth);
+			}
+			continue;
+		}
+		/*
+		 * We pull the desired mdl off of the queue here.  Something
+		 * will have to put it back on a queue later.
+		 */
+		list_del_init(&mdl->list);
+		atomic_dec(&s->q_busy.depth);
+		ret = mdl;
+		break;
+	}
+	spin_unlock(&s->q_busy.lock);
+
+	/*
+	 * We found the mdl for which we were looking.  Get it ready for
+	 * the caller to put on q_full or in the dvb ring buffer.
+	 */
+	if (ret != NULL) {
+		ret->bytesused = bytesused;
+		ret->skipped = 0;
+		/* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */
+		cx18_mdl_update_bufs_for_cpu(s, ret);
+		if (s->type != CX18_ENC_STREAM_TYPE_TS)
+			set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags);
+	}
+
+	/* Put any mdls the firmware is ignoring back into normal rotation */
+	list_for_each_entry_safe(mdl, tmp, &sweep_up, list) {
+		list_del_init(&mdl->list);
+		cx18_enqueue(s, mdl, &s->q_free);
+	}
+	return ret;
+}
+
+/* Move all mdls of a queue, while flushing the mdl */
+static void cx18_queue_flush(struct cx18_stream *s,
+			     struct cx18_queue *q_src, struct cx18_queue *q_dst)
+{
+	struct cx18_mdl *mdl;
+
+	/* It only makes sense to flush to q_free or q_idle */
+	if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy)
+		return;
+
+	spin_lock(&q_src->lock);
+	spin_lock(&q_dst->lock);
+	while (!list_empty(&q_src->list)) {
+		mdl = list_first_entry(&q_src->list, struct cx18_mdl, list);
+		list_move_tail(&mdl->list, &q_dst->list);
+		mdl->bytesused = 0;
+		mdl->readpos = 0;
+		mdl->m_flags = 0;
+		mdl->skipped = 0;
+		mdl->curr_buf = NULL;
+		atomic_inc(&q_dst->depth);
+	}
+	cx18_queue_init(q_src);
+	spin_unlock(&q_src->lock);
+	spin_unlock(&q_dst->lock);
+}
+
+void cx18_flush_queues(struct cx18_stream *s)
+{
+	cx18_queue_flush(s, &s->q_busy, &s->q_free);
+	cx18_queue_flush(s, &s->q_full, &s->q_free);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_unload_queues(struct cx18_stream *s)
+{
+	struct cx18_queue *q_idle = &s->q_idle;
+	struct cx18_mdl *mdl;
+	struct cx18_buffer *buf;
+
+	/* Move all MDLS to q_idle */
+	cx18_queue_flush(s, &s->q_busy, q_idle);
+	cx18_queue_flush(s, &s->q_full, q_idle);
+	cx18_queue_flush(s, &s->q_free, q_idle);
+
+	/* Reset MDL id's and move all buffers back to the stream's buf_pool */
+	spin_lock(&q_idle->lock);
+	list_for_each_entry(mdl, &q_idle->list, list) {
+		while (!list_empty(&mdl->buf_list)) {
+			buf = list_first_entry(&mdl->buf_list,
+					       struct cx18_buffer, list);
+			list_move_tail(&buf->list, &s->buf_pool);
+			buf->bytesused = 0;
+			buf->readpos = 0;
+		}
+		mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */
+		/* all other mdl fields were cleared by cx18_queue_flush() */
+	}
+	spin_unlock(&q_idle->lock);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_load_queues(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_mdl *mdl;
+	struct cx18_buffer *buf;
+	int mdl_id;
+	int i;
+	u32 partial_buf_size;
+
+	/*
+	 * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free
+	 * Excess MDLs are left on q_idle
+	 * Excess buffers are left in buf_pool and/or on an MDL in q_idle
+	 */
+	mdl_id = s->mdl_base_idx;
+	for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl;
+	     mdl != NULL && i == s->bufs_per_mdl;
+	     mdl = cx18_dequeue(s, &s->q_idle)) {
+
+		mdl->id = mdl_id;
+
+		for (i = 0; i < s->bufs_per_mdl; i++) {
+			if (list_empty(&s->buf_pool))
+				break;
+
+			buf = list_first_entry(&s->buf_pool, struct cx18_buffer,
+					       list);
+			list_move_tail(&buf->list, &mdl->buf_list);
+
+			/* update the firmware's MDL array with this buffer */
+			cx18_writel(cx, buf->dma_handle,
+				    &cx->scb->cpu_mdl[mdl_id + i].paddr);
+			cx18_writel(cx, s->buf_size,
+				    &cx->scb->cpu_mdl[mdl_id + i].length);
+		}
+
+		if (i == s->bufs_per_mdl) {
+			/*
+			 * The encoder doesn't honor s->mdl_size.  So in the
+			 * case of a non-integral number of buffers to meet
+			 * mdl_size, we lie about the size of the last buffer
+			 * in the MDL to get the encoder to really only send
+			 * us mdl_size bytes per MDL transfer.
+			 */
+			partial_buf_size = s->mdl_size % s->buf_size;
+			if (partial_buf_size) {
+				cx18_writel(cx, partial_buf_size,
+				      &cx->scb->cpu_mdl[mdl_id + i - 1].length);
+			}
+			cx18_enqueue(s, mdl, &s->q_free);
+		} else {
+			/* Not enough buffers for this MDL; we won't use it */
+			cx18_push(s, mdl, &s->q_idle);
+		}
+		mdl_id += i;
+	}
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+	int dma = s->dma;
+	u32 buf_size = s->buf_size;
+	struct pci_dev *pci_dev = s->cx->pci_dev;
+	struct cx18_buffer *buf;
+
+	list_for_each_entry(buf, &mdl->buf_list, list)
+		pci_dma_sync_single_for_device(pci_dev, buf->dma_handle,
+					       buf_size, dma);
+}
+
+int cx18_stream_alloc(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	int i;
+
+	if (s->buffers == 0)
+		return 0;
+
+	CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers "
+			"(%d.%02d kB total)\n",
+		s->name, s->buffers, s->buf_size,
+		s->buffers * s->buf_size / 1024,
+		(s->buffers * s->buf_size * 100 / 1024) % 100);
+
+	if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] -
+				(char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
+		unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
+					((char __iomem *)cx->scb->cpu_mdl));
+
+		CX18_ERR("Too many buffers, cannot fit in SCB area\n");
+		CX18_ERR("Max buffers = %zd\n",
+			bufsz / sizeof(struct cx18_mdl_ent));
+		return -ENOMEM;
+	}
+
+	s->mdl_base_idx = cx->free_mdl_idx;
+
+	/* allocate stream buffers and MDLs */
+	for (i = 0; i < s->buffers; i++) {
+		struct cx18_mdl *mdl;
+		struct cx18_buffer *buf;
+
+		/* 1 MDL per buffer to handle the worst & also default case */
+		mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN);
+		if (mdl == NULL)
+			break;
+
+		buf = kzalloc(sizeof(struct cx18_buffer),
+				GFP_KERNEL|__GFP_NOWARN);
+		if (buf == NULL) {
+			kfree(mdl);
+			break;
+		}
+
+		buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
+		if (buf->buf == NULL) {
+			kfree(mdl);
+			kfree(buf);
+			break;
+		}
+
+		INIT_LIST_HEAD(&mdl->list);
+		INIT_LIST_HEAD(&mdl->buf_list);
+		mdl->id = s->mdl_base_idx; /* a somewhat safe value */
+		cx18_enqueue(s, mdl, &s->q_idle);
+
+		INIT_LIST_HEAD(&buf->list);
+		buf->dma_handle = pci_map_single(s->cx->pci_dev,
+				buf->buf, s->buf_size, s->dma);
+		cx18_buf_sync_for_cpu(s, buf);
+		list_add_tail(&buf->list, &s->buf_pool);
+	}
+	if (i == s->buffers) {
+		cx->free_mdl_idx += s->buffers;
+		return 0;
+	}
+	CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+	cx18_stream_free(s);
+	return -ENOMEM;
+}
+
+void cx18_stream_free(struct cx18_stream *s)
+{
+	struct cx18_mdl *mdl;
+	struct cx18_buffer *buf;
+	struct cx18 *cx = s->cx;
+
+	CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name);
+
+	/* move all buffers to buf_pool and all MDLs to q_idle */
+	cx18_unload_queues(s);
+
+	/* empty q_idle */
+	while ((mdl = cx18_dequeue(s, &s->q_idle)))
+		kfree(mdl);
+
+	/* empty buf_pool */
+	while (!list_empty(&s->buf_pool)) {
+		buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list);
+		list_del_init(&buf->list);
+
+		pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
+				s->buf_size, s->dma);
+		kfree(buf->buf);
+		kfree(buf);
+	}
+}
diff --git a/drivers/media/pci/cx18/cx18-queue.h b/drivers/media/pci/cx18/cx18-queue.h
new file mode 100644
index 000000000000..4201ddc16091
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-queue.h
@@ -0,0 +1,98 @@
+/*
+ *  cx18 buffer queues
+ *
+ *  Derived from ivtv-queue.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#define CX18_DMA_UNMAPPED	((u32) -1)
+
+/* cx18_buffer utility functions */
+
+static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
+	struct cx18_buffer *buf)
+{
+	pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle,
+				s->buf_size, s->dma);
+}
+
+static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
+	struct cx18_buffer *buf)
+{
+	pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle,
+				s->buf_size, s->dma);
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_sync_for_device(struct cx18_stream *s,
+					    struct cx18_mdl *mdl)
+{
+	if (list_is_singular(&mdl->buf_list))
+		cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list,
+							     struct cx18_buffer,
+							     list));
+	else
+		_cx18_mdl_sync_for_device(s, mdl);
+}
+
+void cx18_buf_swap(struct cx18_buffer *buf);
+void _cx18_mdl_swap(struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+	if (list_is_singular(&mdl->buf_list))
+		cx18_buf_swap(list_first_entry(&mdl->buf_list,
+					       struct cx18_buffer, list));
+	else
+		_cx18_mdl_swap(mdl);
+}
+
+/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+				 struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+				struct cx18_queue *q)
+{
+	return _cx18_enqueue(s, mdl, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl,
+			     struct cx18_queue *q)
+{
+	return _cx18_enqueue(s, mdl, q, 1); /* LIFO */
+}
+
+void cx18_queue_init(struct cx18_queue *q);
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
+	u32 bytesused);
+void cx18_flush_queues(struct cx18_stream *s);
+
+/* queue MDL reconfiguration helpers */
+void cx18_unload_queues(struct cx18_stream *s);
+void cx18_load_queues(struct cx18_stream *s);
+
+/* cx18_stream utility functions */
+int cx18_stream_alloc(struct cx18_stream *s);
+void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/pci/cx18/cx18-scb.c b/drivers/media/pci/cx18/cx18-scb.c
new file mode 100644
index 000000000000..85cc59637e54
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-scb.c
@@ -0,0 +1,122 @@
+/*
+ *  cx18 System Control Block initialization
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+
+void cx18_init_scb(struct cx18 *cx)
+{
+	cx18_setup_page(cx, SCB_OFFSET);
+	cx18_memset_io(cx, cx->scb, 0, 0x10000);
+
+	cx18_writel(cx, IRQ_APU_TO_CPU,     &cx->scb->apu2cpu_irq);
+	cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
+	cx18_writel(cx, IRQ_HPU_TO_CPU,     &cx->scb->hpu2cpu_irq);
+	cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
+	cx18_writel(cx, IRQ_PPU_TO_CPU,     &cx->scb->ppu2cpu_irq);
+	cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
+	cx18_writel(cx, IRQ_EPU_TO_CPU,     &cx->scb->epu2cpu_irq);
+	cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
+
+	cx18_writel(cx, IRQ_CPU_TO_APU,     &cx->scb->cpu2apu_irq);
+	cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
+	cx18_writel(cx, IRQ_HPU_TO_APU,     &cx->scb->hpu2apu_irq);
+	cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
+	cx18_writel(cx, IRQ_PPU_TO_APU,     &cx->scb->ppu2apu_irq);
+	cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
+	cx18_writel(cx, IRQ_EPU_TO_APU,     &cx->scb->epu2apu_irq);
+	cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
+
+	cx18_writel(cx, IRQ_CPU_TO_HPU,     &cx->scb->cpu2hpu_irq);
+	cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
+	cx18_writel(cx, IRQ_APU_TO_HPU,     &cx->scb->apu2hpu_irq);
+	cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
+	cx18_writel(cx, IRQ_PPU_TO_HPU,     &cx->scb->ppu2hpu_irq);
+	cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
+	cx18_writel(cx, IRQ_EPU_TO_HPU,     &cx->scb->epu2hpu_irq);
+	cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
+
+	cx18_writel(cx, IRQ_CPU_TO_PPU,     &cx->scb->cpu2ppu_irq);
+	cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
+	cx18_writel(cx, IRQ_APU_TO_PPU,     &cx->scb->apu2ppu_irq);
+	cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
+	cx18_writel(cx, IRQ_HPU_TO_PPU,     &cx->scb->hpu2ppu_irq);
+	cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
+	cx18_writel(cx, IRQ_EPU_TO_PPU,     &cx->scb->epu2ppu_irq);
+	cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
+
+	cx18_writel(cx, IRQ_CPU_TO_EPU,     &cx->scb->cpu2epu_irq);
+	cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
+	cx18_writel(cx, IRQ_APU_TO_EPU,     &cx->scb->apu2epu_irq);
+	cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
+	cx18_writel(cx, IRQ_HPU_TO_EPU,     &cx->scb->hpu2epu_irq);
+	cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
+	cx18_writel(cx, IRQ_PPU_TO_EPU,     &cx->scb->ppu2epu_irq);
+	cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
+
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
+			&cx->scb->apu2cpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
+			&cx->scb->hpu2cpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
+			&cx->scb->ppu2cpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
+			&cx->scb->epu2cpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
+			&cx->scb->cpu2apu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
+			&cx->scb->hpu2apu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
+			&cx->scb->ppu2apu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
+			&cx->scb->epu2apu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
+			&cx->scb->cpu2hpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
+			&cx->scb->apu2hpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
+			&cx->scb->ppu2hpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
+			&cx->scb->epu2hpu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
+			&cx->scb->cpu2ppu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
+			&cx->scb->apu2ppu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
+			&cx->scb->hpu2ppu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
+			&cx->scb->epu2ppu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
+			&cx->scb->cpu2epu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
+			&cx->scb->apu2epu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
+			&cx->scb->hpu2epu_mb_offset);
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
+			&cx->scb->ppu2epu_mb_offset);
+
+	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
+			&cx->scb->ipc_offset);
+
+	cx18_writel(cx, 1, &cx->scb->epu_state);
+}
diff --git a/drivers/media/pci/cx18/cx18-scb.h b/drivers/media/pci/cx18/cx18-scb.h
new file mode 100644
index 000000000000..08877652e321
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-scb.h
@@ -0,0 +1,280 @@
+/*
+ *  cx18 System Control Block initialization
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#ifndef CX18_SCB_H
+#define CX18_SCB_H
+
+#include "cx18-mailbox.h"
+
+/* NOTE: All ACK interrupts are in the SW2 register.  All non-ACK interrupts
+   are in the SW1 register. */
+
+#define IRQ_APU_TO_CPU         0x00000001
+#define IRQ_CPU_TO_APU_ACK     0x00000001
+#define IRQ_HPU_TO_CPU         0x00000002
+#define IRQ_CPU_TO_HPU_ACK     0x00000002
+#define IRQ_PPU_TO_CPU         0x00000004
+#define IRQ_CPU_TO_PPU_ACK     0x00000004
+#define IRQ_EPU_TO_CPU         0x00000008
+#define IRQ_CPU_TO_EPU_ACK     0x00000008
+
+#define IRQ_CPU_TO_APU         0x00000010
+#define IRQ_APU_TO_CPU_ACK     0x00000010
+#define IRQ_HPU_TO_APU         0x00000020
+#define IRQ_APU_TO_HPU_ACK     0x00000020
+#define IRQ_PPU_TO_APU         0x00000040
+#define IRQ_APU_TO_PPU_ACK     0x00000040
+#define IRQ_EPU_TO_APU         0x00000080
+#define IRQ_APU_TO_EPU_ACK     0x00000080
+
+#define IRQ_CPU_TO_HPU         0x00000100
+#define IRQ_HPU_TO_CPU_ACK     0x00000100
+#define IRQ_APU_TO_HPU         0x00000200
+#define IRQ_HPU_TO_APU_ACK     0x00000200
+#define IRQ_PPU_TO_HPU         0x00000400
+#define IRQ_HPU_TO_PPU_ACK     0x00000400
+#define IRQ_EPU_TO_HPU         0x00000800
+#define IRQ_HPU_TO_EPU_ACK     0x00000800
+
+#define IRQ_CPU_TO_PPU         0x00001000
+#define IRQ_PPU_TO_CPU_ACK     0x00001000
+#define IRQ_APU_TO_PPU         0x00002000
+#define IRQ_PPU_TO_APU_ACK     0x00002000
+#define IRQ_HPU_TO_PPU         0x00004000
+#define IRQ_PPU_TO_HPU_ACK     0x00004000
+#define IRQ_EPU_TO_PPU         0x00008000
+#define IRQ_PPU_TO_EPU_ACK     0x00008000
+
+#define IRQ_CPU_TO_EPU         0x00010000
+#define IRQ_EPU_TO_CPU_ACK     0x00010000
+#define IRQ_APU_TO_EPU         0x00020000
+#define IRQ_EPU_TO_APU_ACK     0x00020000
+#define IRQ_HPU_TO_EPU         0x00040000
+#define IRQ_EPU_TO_HPU_ACK     0x00040000
+#define IRQ_PPU_TO_EPU         0x00080000
+#define IRQ_EPU_TO_PPU_ACK     0x00080000
+
+#define SCB_OFFSET  0xDC0000
+
+/* If Firmware uses fixed memory map, it shall not allocate the area
+   between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */
+#define SCB_RESERVED_SIZE 0x10000
+
+
+/* This structure is used by EPU to provide memory descriptors in its memory */
+struct cx18_mdl_ent {
+    u32 paddr;  /* Physical address of a buffer segment */
+    u32 length; /* Length of the buffer segment */
+};
+
+struct cx18_scb {
+	/* These fields form the System Control Block which is used at boot time
+	   for localizing the IPC data as well as the code positions for all
+	   processors. The offsets are from the start of this struct. */
+
+	/* Offset where to find the Inter-Processor Communication data */
+	u32 ipc_offset;
+	u32 reserved01[7];
+	/* Offset where to find the start of the CPU code */
+	u32 cpu_code_offset;
+	u32 reserved02[3];
+	/* Offset where to find the start of the APU code */
+	u32 apu_code_offset;
+	u32 reserved03[3];
+	/* Offset where to find the start of the HPU code */
+	u32 hpu_code_offset;
+	u32 reserved04[3];
+	/* Offset where to find the start of the PPU code */
+	u32 ppu_code_offset;
+	u32 reserved05[3];
+
+	/* These fields form Inter-Processor Communication data which is used
+	   by all processors to locate the information needed for communicating
+	   with other processors */
+
+	/* Fields for CPU: */
+
+	/* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */
+	u32 cpu_state;
+	u32 reserved1[7];
+	/* Offset to the mailbox used for sending commands from APU to CPU */
+	u32 apu2cpu_mb_offset;
+	/* Value to write to register SW1 register set (0xC7003100) after the
+	   command is ready */
+	u32 apu2cpu_irq;
+	/* Value to write to register SW2 register set (0xC7003140) after the
+	   command is cleared */
+	u32 cpu2apu_irq_ack;
+	u32 reserved2[13];
+
+	u32 hpu2cpu_mb_offset;
+	u32 hpu2cpu_irq;
+	u32 cpu2hpu_irq_ack;
+	u32 reserved3[13];
+
+	u32 ppu2cpu_mb_offset;
+	u32 ppu2cpu_irq;
+	u32 cpu2ppu_irq_ack;
+	u32 reserved4[13];
+
+	u32 epu2cpu_mb_offset;
+	u32 epu2cpu_irq;
+	u32 cpu2epu_irq_ack;
+	u32 reserved5[13];
+	u32 reserved6[8];
+
+	/* Fields for APU: */
+
+	u32 apu_state;
+	u32 reserved11[7];
+	u32 cpu2apu_mb_offset;
+	u32 cpu2apu_irq;
+	u32 apu2cpu_irq_ack;
+	u32 reserved12[13];
+
+	u32 hpu2apu_mb_offset;
+	u32 hpu2apu_irq;
+	u32 apu2hpu_irq_ack;
+	u32 reserved13[13];
+
+	u32 ppu2apu_mb_offset;
+	u32 ppu2apu_irq;
+	u32 apu2ppu_irq_ack;
+	u32 reserved14[13];
+
+	u32 epu2apu_mb_offset;
+	u32 epu2apu_irq;
+	u32 apu2epu_irq_ack;
+	u32 reserved15[13];
+	u32 reserved16[8];
+
+	/* Fields for HPU: */
+
+	u32 hpu_state;
+	u32 reserved21[7];
+	u32 cpu2hpu_mb_offset;
+	u32 cpu2hpu_irq;
+	u32 hpu2cpu_irq_ack;
+	u32 reserved22[13];
+
+	u32 apu2hpu_mb_offset;
+	u32 apu2hpu_irq;
+	u32 hpu2apu_irq_ack;
+	u32 reserved23[13];
+
+	u32 ppu2hpu_mb_offset;
+	u32 ppu2hpu_irq;
+	u32 hpu2ppu_irq_ack;
+	u32 reserved24[13];
+
+	u32 epu2hpu_mb_offset;
+	u32 epu2hpu_irq;
+	u32 hpu2epu_irq_ack;
+	u32 reserved25[13];
+	u32 reserved26[8];
+
+	/* Fields for PPU: */
+
+	u32 ppu_state;
+	u32 reserved31[7];
+	u32 cpu2ppu_mb_offset;
+	u32 cpu2ppu_irq;
+	u32 ppu2cpu_irq_ack;
+	u32 reserved32[13];
+
+	u32 apu2ppu_mb_offset;
+	u32 apu2ppu_irq;
+	u32 ppu2apu_irq_ack;
+	u32 reserved33[13];
+
+	u32 hpu2ppu_mb_offset;
+	u32 hpu2ppu_irq;
+	u32 ppu2hpu_irq_ack;
+	u32 reserved34[13];
+
+	u32 epu2ppu_mb_offset;
+	u32 epu2ppu_irq;
+	u32 ppu2epu_irq_ack;
+	u32 reserved35[13];
+	u32 reserved36[8];
+
+	/* Fields for EPU: */
+
+	u32 epu_state;
+	u32 reserved41[7];
+	u32 cpu2epu_mb_offset;
+	u32 cpu2epu_irq;
+	u32 epu2cpu_irq_ack;
+	u32 reserved42[13];
+
+	u32 apu2epu_mb_offset;
+	u32 apu2epu_irq;
+	u32 epu2apu_irq_ack;
+	u32 reserved43[13];
+
+	u32 hpu2epu_mb_offset;
+	u32 hpu2epu_irq;
+	u32 epu2hpu_irq_ack;
+	u32 reserved44[13];
+
+	u32 ppu2epu_mb_offset;
+	u32 ppu2epu_irq;
+	u32 epu2ppu_irq_ack;
+	u32 reserved45[13];
+	u32 reserved46[8];
+
+	u32 semaphores[8];  /* Semaphores */
+
+	u32 reserved50[32]; /* Reserved for future use */
+
+	struct cx18_mailbox  apu2cpu_mb;
+	struct cx18_mailbox  hpu2cpu_mb;
+	struct cx18_mailbox  ppu2cpu_mb;
+	struct cx18_mailbox  epu2cpu_mb;
+
+	struct cx18_mailbox  cpu2apu_mb;
+	struct cx18_mailbox  hpu2apu_mb;
+	struct cx18_mailbox  ppu2apu_mb;
+	struct cx18_mailbox  epu2apu_mb;
+
+	struct cx18_mailbox  cpu2hpu_mb;
+	struct cx18_mailbox  apu2hpu_mb;
+	struct cx18_mailbox  ppu2hpu_mb;
+	struct cx18_mailbox  epu2hpu_mb;
+
+	struct cx18_mailbox  cpu2ppu_mb;
+	struct cx18_mailbox  apu2ppu_mb;
+	struct cx18_mailbox  hpu2ppu_mb;
+	struct cx18_mailbox  epu2ppu_mb;
+
+	struct cx18_mailbox  cpu2epu_mb;
+	struct cx18_mailbox  apu2epu_mb;
+	struct cx18_mailbox  hpu2epu_mb;
+	struct cx18_mailbox  ppu2epu_mb;
+
+	struct cx18_mdl_ack  cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
+	struct cx18_mdl_ent  cpu_mdl[1];
+};
+
+void cx18_init_scb(struct cx18 *cx);
+
+#endif
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
new file mode 100644
index 000000000000..72af9b5c2d7d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -0,0 +1,1059 @@
+/*
+ *  cx18 init/start/stop/exit stream functions
+ *
+ *  Derived from ivtv-streams.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-fileops.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-ioctl.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-scb.h"
+#include "cx18-dvb.h"
+
+#define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
+
+static struct v4l2_file_operations cx18_v4l2_enc_fops = {
+	.owner = THIS_MODULE,
+	.read = cx18_v4l2_read,
+	.open = cx18_v4l2_open,
+	.unlocked_ioctl = video_ioctl2,
+	.release = cx18_v4l2_close,
+	.poll = cx18_v4l2_enc_poll,
+	.mmap = cx18_v4l2_mmap,
+};
+
+/* offset from 0 to register ts v4l2 minors on */
+#define CX18_V4L2_ENC_TS_OFFSET   16
+/* offset from 0 to register pcm v4l2 minors on */
+#define CX18_V4L2_ENC_PCM_OFFSET  24
+/* offset from 0 to register yuv v4l2 minors on */
+#define CX18_V4L2_ENC_YUV_OFFSET  32
+
+static struct {
+	const char *name;
+	int vfl_type;
+	int num_offset;
+	int dma;
+} cx18_stream_info[] = {
+	{	/* CX18_ENC_STREAM_TYPE_MPG */
+		"encoder MPEG",
+		VFL_TYPE_GRABBER, 0,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_TS */
+		"TS",
+		VFL_TYPE_GRABBER, -1,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_YUV */
+		"encoder YUV",
+		VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_VBI */
+		"encoder VBI",
+		VFL_TYPE_VBI, 0,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_PCM */
+		"encoder PCM audio",
+		VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_IDX */
+		"encoder IDX",
+		VFL_TYPE_GRABBER, -1,
+		PCI_DMA_FROMDEVICE,
+	},
+	{	/* CX18_ENC_STREAM_TYPE_RAD */
+		"encoder radio",
+		VFL_TYPE_RADIO, 0,
+		PCI_DMA_NONE,
+	},
+};
+
+
+void cx18_dma_free(struct videobuf_queue *q,
+	struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
+{
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_vmalloc_free(&buf->vb);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int cx18_prepare_buffer(struct videobuf_queue *q,
+	struct cx18_stream *s,
+	struct cx18_videobuf_buffer *buf,
+	u32 pixelformat,
+	unsigned int width, unsigned int height,
+	enum v4l2_field field)
+{
+        struct cx18 *cx = s->cx;
+	int rc = 0;
+
+	/* check settings */
+	buf->bytes_used = 0;
+
+	if ((width  < 48) || (height < 32))
+		return -EINVAL;
+
+	buf->vb.size = (width * height * 2);
+	if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+		return -EINVAL;
+
+	/* alloc + fill struct (if changed) */
+	if (buf->vb.width != width || buf->vb.height != height ||
+	    buf->vb.field != field || s->pixelformat != pixelformat ||
+	    buf->tvnorm != cx->std) {
+
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = cx->std;
+		s->pixelformat = pixelformat;
+
+		/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+		   UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+		if (s->pixelformat == V4L2_PIX_FMT_HM12)
+			s->vb_bytes_per_frame = height * 720 * 3 / 2;
+		else
+			s->vb_bytes_per_frame = height * 720 * 2;
+		cx18_dma_free(q, s, buf);
+	}
+
+	if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+		return -EINVAL;
+
+	if (buf->vb.field == 0)
+		buf->vb.field = V4L2_FIELD_INTERLACED;
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = cx->std;
+		s->pixelformat = pixelformat;
+
+		/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+		   UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+		if (s->pixelformat == V4L2_PIX_FMT_HM12)
+			s->vb_bytes_per_frame = height * 720 * 3 / 2;
+		else
+			s->vb_bytes_per_frame = height * 720 * 2;
+		rc = videobuf_iolock(q, &buf->vb, NULL);
+		if (rc != 0)
+			goto fail;
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+fail:
+	cx18_dma_free(q, s, buf);
+	return rc;
+
+}
+
+/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576)
+   1440 is a single line of 4:2:2 YUV at 720 luma samples wide
+*/
+#define VB_MIN_BUFFERS 32
+#define VB_MIN_BUFSIZE 4147200
+
+static int buffer_setup(struct videobuf_queue *q,
+	unsigned int *count, unsigned int *size)
+{
+	struct cx18_stream *s = q->priv_data;
+	struct cx18 *cx = s->cx;
+
+	*size = 2 * cx->cxhdl.width * cx->cxhdl.height;
+	if (*count == 0)
+		*count = VB_MIN_BUFFERS;
+
+	while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
+		(*count)--;
+
+	q->field = V4L2_FIELD_INTERLACED;
+	q->last = V4L2_FIELD_INTERLACED;
+
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+	struct videobuf_buffer *vb,
+	enum v4l2_field field)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_stream *s = q->priv_data;
+	struct cx18 *cx = s->cx;
+
+	return cx18_prepare_buffer(q, s, buf, s->pixelformat,
+		cx->cxhdl.width, cx->cxhdl.height, field);
+}
+
+static void buffer_release(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_stream *s = q->priv_data;
+
+	cx18_dma_free(q, s, buf);
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_stream *s = q->priv_data;
+
+	buf->vb.state = VIDEOBUF_QUEUED;
+
+	list_add_tail(&buf->vb.queue, &s->vb_capture);
+}
+
+static struct videobuf_queue_ops cx18_videobuf_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
+static void cx18_stream_init(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	struct video_device *video_dev = s->video_dev;
+
+	/* we need to keep video_dev, so restore it afterwards */
+	memset(s, 0, sizeof(*s));
+	s->video_dev = video_dev;
+
+	/* initialize cx18_stream fields */
+	s->dvb = NULL;
+	s->cx = cx;
+	s->type = type;
+	s->name = cx18_stream_info[type].name;
+	s->handle = CX18_INVALID_TASK_HANDLE;
+
+	s->dma = cx18_stream_info[type].dma;
+	s->buffers = cx->stream_buffers[type];
+	s->buf_size = cx->stream_buf_size[type];
+	INIT_LIST_HEAD(&s->buf_pool);
+	s->bufs_per_mdl = 1;
+	s->mdl_size = s->buf_size * s->bufs_per_mdl;
+
+	init_waitqueue_head(&s->waitq);
+	s->id = -1;
+	spin_lock_init(&s->q_free.lock);
+	cx18_queue_init(&s->q_free);
+	spin_lock_init(&s->q_busy.lock);
+	cx18_queue_init(&s->q_busy);
+	spin_lock_init(&s->q_full.lock);
+	cx18_queue_init(&s->q_full);
+	spin_lock_init(&s->q_idle.lock);
+	cx18_queue_init(&s->q_idle);
+
+	INIT_WORK(&s->out_work_order, cx18_out_work_handler);
+
+	INIT_LIST_HEAD(&s->vb_capture);
+	s->vb_timeout.function = cx18_vb_timeout;
+	s->vb_timeout.data = (unsigned long)s;
+	init_timer(&s->vb_timeout);
+	spin_lock_init(&s->vb_lock);
+	if (type == CX18_ENC_STREAM_TYPE_YUV) {
+		spin_lock_init(&s->vbuf_q_lock);
+
+		s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops,
+			&cx->pci_dev->dev, &s->vbuf_q_lock,
+			V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			V4L2_FIELD_INTERLACED,
+			sizeof(struct cx18_videobuf_buffer),
+			s, &cx->serialize_lock);
+
+		/* Assume the previous pixel default */
+		s->pixelformat = V4L2_PIX_FMT_HM12;
+		s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
+	}
+}
+
+static int cx18_prep_dev(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	u32 cap = cx->v4l2_cap;
+	int num_offset = cx18_stream_info[type].num_offset;
+	int num = cx->instance + cx18_first_minor + num_offset;
+
+	/*
+	 * These five fields are always initialized.
+	 * For analog capture related streams, if video_dev == NULL then the
+	 * stream is not in use.
+	 * For the TS stream, if dvb == NULL then the stream is not in use.
+	 * In those cases no other fields but these four can be used.
+	 */
+	s->video_dev = NULL;
+	s->dvb = NULL;
+	s->cx = cx;
+	s->type = type;
+	s->name = cx18_stream_info[type].name;
+
+	/* Check whether the radio is supported */
+	if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
+		return 0;
+
+	/* Check whether VBI is supported */
+	if (type == CX18_ENC_STREAM_TYPE_VBI &&
+	    !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
+		return 0;
+
+	/* User explicitly selected 0 buffers for these streams, so don't
+	   create them. */
+	if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
+	    cx->stream_buffers[type] == 0) {
+		CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
+		return 0;
+	}
+
+	cx18_stream_init(cx, type);
+
+	/* Allocate the cx18_dvb struct only for the TS on cards with DTV */
+	if (type == CX18_ENC_STREAM_TYPE_TS) {
+		if (cx->card->hw_all & CX18_HW_DVB) {
+			s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
+			if (s->dvb == NULL) {
+				CX18_ERR("Couldn't allocate cx18_dvb structure"
+					 " for %s\n", s->name);
+				return -ENOMEM;
+			}
+		} else {
+			/* Don't need buffers for the TS, if there is no DVB */
+			s->buffers = 0;
+		}
+	}
+
+	if (num_offset == -1)
+		return 0;
+
+	/* allocate and initialize the v4l2 video device structure */
+	s->video_dev = video_device_alloc();
+	if (s->video_dev == NULL) {
+		CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
+				s->name);
+		return -ENOMEM;
+	}
+
+	snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
+		 cx->v4l2_dev.name, s->name);
+
+	s->video_dev->num = num;
+	s->video_dev->v4l2_dev = &cx->v4l2_dev;
+	s->video_dev->fops = &cx18_v4l2_enc_fops;
+	s->video_dev->release = video_device_release;
+	s->video_dev->tvnorms = V4L2_STD_ALL;
+	s->video_dev->lock = &cx->serialize_lock;
+	set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
+	cx18_set_funcs(s->video_dev);
+	return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int cx18_streams_setup(struct cx18 *cx)
+{
+	int type, ret;
+
+	/* Setup V4L2 Devices */
+	for (type = 0; type < CX18_MAX_STREAMS; type++) {
+		/* Prepare device */
+		ret = cx18_prep_dev(cx, type);
+		if (ret < 0)
+			break;
+
+		/* Allocate Stream */
+		ret = cx18_stream_alloc(&cx->streams[type]);
+		if (ret < 0)
+			break;
+	}
+	if (type == CX18_MAX_STREAMS)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	cx18_streams_cleanup(cx, 0);
+	return ret;
+}
+
+static int cx18_reg_dev(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	int vfl_type = cx18_stream_info[type].vfl_type;
+	const char *name;
+	int num, ret;
+
+	if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
+		ret = cx18_dvb_register(s);
+		if (ret < 0) {
+			CX18_ERR("DVB failed to register\n");
+			return ret;
+		}
+	}
+
+	if (s->video_dev == NULL)
+		return 0;
+
+	num = s->video_dev->num;
+	/* card number + user defined offset + device offset */
+	if (type != CX18_ENC_STREAM_TYPE_MPG) {
+		struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+		if (s_mpg->video_dev)
+			num = s_mpg->video_dev->num
+			    + cx18_stream_info[type].num_offset;
+	}
+	video_set_drvdata(s->video_dev, s);
+
+	/* Register device. First try the desired minor, then any free one. */
+	ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
+	if (ret < 0) {
+		CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+			s->name, num);
+		video_device_release(s->video_dev);
+		s->video_dev = NULL;
+		return ret;
+	}
+
+	name = video_device_node_name(s->video_dev);
+
+	switch (vfl_type) {
+	case VFL_TYPE_GRABBER:
+		CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
+			  name, s->name, cx->stream_buffers[type],
+			  cx->stream_buf_size[type] / 1024,
+			  (cx->stream_buf_size[type] * 100 / 1024) % 100);
+		break;
+
+	case VFL_TYPE_RADIO:
+		CX18_INFO("Registered device %s for %s\n", name, s->name);
+		break;
+
+	case VFL_TYPE_VBI:
+		if (cx->stream_buffers[type])
+			CX18_INFO("Registered device %s for %s "
+				  "(%d x %d bytes)\n",
+				  name, s->name, cx->stream_buffers[type],
+				  cx->stream_buf_size[type]);
+		else
+			CX18_INFO("Registered device %s for %s\n",
+				name, s->name);
+		break;
+	}
+
+	return 0;
+}
+
+/* Register v4l2 devices */
+int cx18_streams_register(struct cx18 *cx)
+{
+	int type;
+	int err;
+	int ret = 0;
+
+	/* Register V4L2 devices */
+	for (type = 0; type < CX18_MAX_STREAMS; type++) {
+		err = cx18_reg_dev(cx, type);
+		if (err && ret == 0)
+			ret = err;
+	}
+
+	if (ret == 0)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	cx18_streams_cleanup(cx, 1);
+	return ret;
+}
+
+/* Unregister v4l2 devices */
+void cx18_streams_cleanup(struct cx18 *cx, int unregister)
+{
+	struct video_device *vdev;
+	int type;
+
+	/* Teardown all streams */
+	for (type = 0; type < CX18_MAX_STREAMS; type++) {
+
+		/* The TS has a cx18_dvb structure, not a video_device */
+		if (type == CX18_ENC_STREAM_TYPE_TS) {
+			if (cx->streams[type].dvb != NULL) {
+				if (unregister)
+					cx18_dvb_unregister(&cx->streams[type]);
+				kfree(cx->streams[type].dvb);
+				cx->streams[type].dvb = NULL;
+				cx18_stream_free(&cx->streams[type]);
+			}
+			continue;
+		}
+
+		/* No struct video_device, but can have buffers allocated */
+		if (type == CX18_ENC_STREAM_TYPE_IDX) {
+			/* If the module params didn't inhibit IDX ... */
+			if (cx->stream_buffers[type] != 0) {
+				cx->stream_buffers[type] = 0;
+				/*
+				 * Before calling cx18_stream_free(),
+				 * check if the IDX stream was actually set up.
+				 * Needed, since the cx18_probe() error path
+				 * exits through here as well as normal clean up
+				 */
+				if (cx->streams[type].buffers != 0)
+					cx18_stream_free(&cx->streams[type]);
+			}
+			continue;
+		}
+
+		/* If struct video_device exists, can have buffers allocated */
+		vdev = cx->streams[type].video_dev;
+
+		cx->streams[type].video_dev = NULL;
+		if (vdev == NULL)
+			continue;
+
+		if (type == CX18_ENC_STREAM_TYPE_YUV)
+			videobuf_mmap_free(&cx->streams[type].vbuf_q);
+
+		cx18_stream_free(&cx->streams[type]);
+
+		/* Unregister or release device */
+		if (unregister)
+			video_unregister_device(vdev);
+		else
+			video_device_release(vdev);
+	}
+}
+
+static void cx18_vbi_setup(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	int raw = cx18_raw_vbi(cx);
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int lines;
+
+	if (cx->is_60hz) {
+		cx->vbi.count = 12;
+		cx->vbi.start[0] = 10;
+		cx->vbi.start[1] = 273;
+	} else {        /* PAL/SECAM */
+		cx->vbi.count = 18;
+		cx->vbi.start[0] = 6;
+		cx->vbi.start[1] = 318;
+	}
+
+	/* setup VBI registers */
+	if (raw)
+		v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
+	else
+		v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
+
+	/*
+	 * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
+	 * VBI when the first analog capture channel starts, as once it starts
+	 * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
+	 * (i.e. for the VBI capture channels).  We also send it for each
+	 * analog capture channel anyway just to make sure we get the proper
+	 * behavior
+	 */
+	if (raw) {
+		lines = cx->vbi.count * 2;
+	} else {
+		/*
+		 * For 525/60 systems, according to the VIP 2 & BT.656 std:
+		 * The EAV RP code's Field bit toggles on line 4, a few lines
+		 * after the Vertcal Blank bit has already toggled.
+		 * Tell the encoder to capture 21-4+1=18 lines per field,
+		 * since we want lines 10 through 21.
+		 *
+		 * For 625/50 systems, according to the VIP 2 & BT.656 std:
+		 * The EAV RP code's Field bit toggles on line 1, a few lines
+		 * after the Vertcal Blank bit has already toggled.
+		 * (We've actually set the digitizer so that the Field bit
+		 * toggles on line 2.) Tell the encoder to capture 23-2+1=22
+		 * lines per field, since we want lines 6 through 23.
+		 */
+		lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
+	}
+
+	data[0] = s->handle;
+	/* Lines per field */
+	data[1] = (lines / 2) | ((lines / 2) << 16);
+	/* bytes per line */
+	data[2] = (raw ? vbi_active_samples
+		       : (cx->is_60hz ? vbi_hblank_samples_60Hz
+				      : vbi_hblank_samples_50Hz));
+	/* Every X number of frames a VBI interrupt arrives
+	   (frames as in 25 or 30 fps) */
+	data[3] = 1;
+	/*
+	 * Set the SAV/EAV RP codes to look for as start/stop points
+	 * when in VIP-1.1 mode
+	 */
+	if (raw) {
+		/*
+		 * Start codes for beginning of "active" line in vertical blank
+		 * 0x20 (               VerticalBlank                )
+		 * 0x60 (     EvenField VerticalBlank                )
+		 */
+		data[4] = 0x20602060;
+		/*
+		 * End codes for end of "active" raw lines and regular lines
+		 * 0x30 (               VerticalBlank HorizontalBlank)
+		 * 0x70 (     EvenField VerticalBlank HorizontalBlank)
+		 * 0x90 (Task                         HorizontalBlank)
+		 * 0xd0 (Task EvenField               HorizontalBlank)
+		 */
+		data[5] = 0x307090d0;
+	} else {
+		/*
+		 * End codes for active video, we want data in the hblank region
+		 * 0xb0 (Task         0 VerticalBlank HorizontalBlank)
+		 * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
+		 *
+		 * Since the V bit is only allowed to toggle in the EAV RP code,
+		 * just before the first active region line, these two
+		 * are problematic:
+		 * 0x90 (Task                         HorizontalBlank)
+		 * 0xd0 (Task EvenField               HorizontalBlank)
+		 *
+		 * We have set the digitzer such that we don't have to worry
+		 * about these problem codes.
+		 */
+		data[4] = 0xB0F0B0F0;
+		/*
+		 * Start codes for beginning of active line in vertical blank
+		 * 0xa0 (Task           VerticalBlank                )
+		 * 0xe0 (Task EvenField VerticalBlank                )
+		 */
+		data[5] = 0xA0E0A0E0;
+	}
+
+	CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+			data[0], data[1], data[2], data[3], data[4], data[5]);
+
+	cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+}
+
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
+{
+	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+	struct cx18_mdl *mdl;
+
+	if (!cx18_stream_enabled(s))
+		return;
+
+	/* Return if the firmware is not running low on MDLs */
+	if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
+					    CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
+		return;
+
+	/* Return if there are no MDLs to rotate back to the firmware */
+	if (atomic_read(&s->q_full.depth) < 2)
+		return;
+
+	/*
+	 * Take the oldest IDX MDL still holding data, and discard its index
+	 * entries by scheduling the MDL to go back to the firmware
+	 */
+	mdl = cx18_dequeue(s, &s->q_full);
+	if (mdl != NULL)
+		cx18_enqueue(s, mdl, &s->q_free);
+}
+
+static
+struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
+					   struct cx18_mdl *mdl)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_queue *q;
+
+	/* Don't give it to the firmware, if we're not running a capture */
+	if (s->handle == CX18_INVALID_TASK_HANDLE ||
+	    test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
+	    !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+		return cx18_enqueue(s, mdl, &s->q_free);
+
+	q = cx18_enqueue(s, mdl, &s->q_busy);
+	if (q != &s->q_busy)
+		return q; /* The firmware has the max MDLs it can handle */
+
+	cx18_mdl_sync_for_device(s, mdl);
+	cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+		  (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
+		  s->bufs_per_mdl, mdl->id, s->mdl_size);
+	return q;
+}
+
+static
+void _cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+	struct cx18_queue *q;
+	struct cx18_mdl *mdl;
+
+	if (atomic_read(&s->q_free.depth) == 0 ||
+	    atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
+		return;
+
+	/* Move from q_free to q_busy notifying the firmware, until the limit */
+	do {
+		mdl = cx18_dequeue(s, &s->q_free);
+		if (mdl == NULL)
+			break;
+		q = _cx18_stream_put_mdl_fw(s, mdl);
+	} while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
+		 && q == &s->q_busy);
+}
+
+void cx18_out_work_handler(struct work_struct *work)
+{
+	struct cx18_stream *s =
+			 container_of(work, struct cx18_stream, out_work_order);
+
+	_cx18_stream_load_fw_queue(s);
+}
+
+static void cx18_stream_configure_mdls(struct cx18_stream *s)
+{
+	cx18_unload_queues(s);
+
+	switch (s->type) {
+	case CX18_ENC_STREAM_TYPE_YUV:
+		/*
+		 * Height should be a multiple of 32 lines.
+		 * Set the MDL size to the exact size needed for one frame.
+		 * Use enough buffers per MDL to cover the MDL size
+		 */
+		if (s->pixelformat == V4L2_PIX_FMT_HM12)
+			s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
+		else
+			s->mdl_size = 720 * s->cx->cxhdl.height * 2;
+		s->bufs_per_mdl = s->mdl_size / s->buf_size;
+		if (s->mdl_size % s->buf_size)
+			s->bufs_per_mdl++;
+		break;
+	case CX18_ENC_STREAM_TYPE_VBI:
+		s->bufs_per_mdl = 1;
+		if  (cx18_raw_vbi(s->cx)) {
+			s->mdl_size = (s->cx->is_60hz ? 12 : 18)
+						       * 2 * vbi_active_samples;
+		} else {
+			/*
+			 * See comment in cx18_vbi_setup() below about the
+			 * extra lines we capture in sliced VBI mode due to
+			 * the lines on which EAV RP codes toggle.
+			*/
+			s->mdl_size = s->cx->is_60hz
+				   ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
+				   : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
+		}
+		break;
+	default:
+		s->bufs_per_mdl = 1;
+		s->mdl_size = s->buf_size * s->bufs_per_mdl;
+		break;
+	}
+
+	cx18_load_queues(s);
+}
+
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+{
+	u32 data[MAX_MB_ARGUMENTS];
+	struct cx18 *cx = s->cx;
+	int captype = 0;
+	struct cx18_stream *s_idx;
+
+	if (!cx18_stream_enabled(s))
+		return -EINVAL;
+
+	CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+	switch (s->type) {
+	case CX18_ENC_STREAM_TYPE_MPG:
+		captype = CAPTURE_CHANNEL_TYPE_MPEG;
+		cx->mpg_data_received = cx->vbi_data_inserted = 0;
+		cx->dualwatch_jiffies = jiffies;
+		cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
+		cx->search_pack_header = 0;
+		break;
+
+	case CX18_ENC_STREAM_TYPE_IDX:
+		captype = CAPTURE_CHANNEL_TYPE_INDEX;
+		break;
+	case CX18_ENC_STREAM_TYPE_TS:
+		captype = CAPTURE_CHANNEL_TYPE_TS;
+		break;
+	case CX18_ENC_STREAM_TYPE_YUV:
+		captype = CAPTURE_CHANNEL_TYPE_YUV;
+		break;
+	case CX18_ENC_STREAM_TYPE_PCM:
+		captype = CAPTURE_CHANNEL_TYPE_PCM;
+		break;
+	case CX18_ENC_STREAM_TYPE_VBI:
+#ifdef CX18_ENCODER_PARSES_SLICED
+		captype = cx18_raw_vbi(cx) ?
+		     CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
+#else
+		/*
+		 * Currently we set things up so that Sliced VBI from the
+		 * digitizer is handled as Raw VBI by the encoder
+		 */
+		captype = CAPTURE_CHANNEL_TYPE_VBI;
+#endif
+		cx->vbi.frame = 0;
+		cx->vbi.inserted_frame = 0;
+		memset(cx->vbi.sliced_mpeg_size,
+			0, sizeof(cx->vbi.sliced_mpeg_size));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Clear Streamoff flags in case left from last capture */
+	clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
+	s->handle = data[0];
+	cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+	/*
+	 * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
+	 * set up all the parameters, as it is not obvious which parameters the
+	 * firmware shares across capture channel types and which it does not.
+	 *
+	 * Some of the cx18_vapi() calls below apply to only certain capture
+	 * channel types.  We're hoping there's no harm in calling most of them
+	 * anyway, as long as the values are all consistent.  Setting some
+	 * shared parameters will have no effect once an analog capture channel
+	 * has started streaming.
+	 */
+	if (captype != CAPTURE_CHANNEL_TYPE_TS) {
+		cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+
+		/*
+		 * Audio related reset according to
+		 * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+		 */
+		if (atomic_read(&cx->ana_capturing) == 0)
+			cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+				  s->handle, 12);
+
+		/*
+		 * Number of lines for Field 1 & Field 2 according to
+		 * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+		 * Field 1 is 312 for 625 line systems in BT.656
+		 * Field 2 is 313 for 625 line systems in BT.656
+		 */
+		cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+			  s->handle, 312, 313);
+
+		if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+			cx18_vbi_setup(s);
+
+		/*
+		 * Select to receive I, P, and B frame index entries, if the
+		 * index stream is enabled.  Otherwise disable index entry
+		 * generation.
+		 */
+		s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+		cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
+				 s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
+
+		/* Call out to the common CX2341x API setup for user controls */
+		cx->cxhdl.priv = s;
+		cx2341x_handler_setup(&cx->cxhdl);
+
+		/*
+		 * When starting a capture and we're set for radio,
+		 * ensure the video is muted, despite the user control.
+		 */
+		if (!cx->cxhdl.video_mute &&
+		    test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+			  (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
+
+		/* Enable the Video Format Converter for UYVY 4:2:2 support,
+		 * rather than the default HM12 Macroblovk 4:2:0 support.
+		 */
+		if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
+			if (s->pixelformat == V4L2_PIX_FMT_UYVY)
+				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+					s->handle, 1);
+			else
+				/* If in doubt, default to HM12 */
+				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+					s->handle, 0);
+		}
+	}
+
+	if (atomic_read(&cx->tot_capturing) == 0) {
+		cx2341x_handler_set_busy(&cx->cxhdl, 1);
+		clear_bit(CX18_F_I_EOS, &cx->i_flags);
+		cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
+	}
+
+	cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
+		(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
+		(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
+
+	/* Init all the cpu_mdls for this stream */
+	cx18_stream_configure_mdls(s);
+	_cx18_stream_load_fw_queue(s);
+
+	/* begin_capture */
+	if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
+		CX18_DEBUG_WARN("Error starting capture!\n");
+		/* Ensure we're really not capturing before releasing MDLs */
+		set_bit(CX18_F_S_STOPPING, &s->s_flags);
+		if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+			cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
+		else
+			cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+		clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+		/* FIXME - CX18_F_S_STREAMOFF as well? */
+		cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+		cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+		s->handle = CX18_INVALID_TASK_HANDLE;
+		clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+		if (atomic_read(&cx->tot_capturing) == 0) {
+			set_bit(CX18_F_I_EOS, &cx->i_flags);
+			cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+		}
+		return -EINVAL;
+	}
+
+	/* you're live! sit back and await interrupts :) */
+	if (captype != CAPTURE_CHANNEL_TYPE_TS)
+		atomic_inc(&cx->ana_capturing);
+	atomic_inc(&cx->tot_capturing);
+	return 0;
+}
+EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
+
+void cx18_stop_all_captures(struct cx18 *cx)
+{
+	int i;
+
+	for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+		struct cx18_stream *s = &cx->streams[i];
+
+		if (!cx18_stream_enabled(s))
+			continue;
+		if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+			cx18_stop_v4l2_encode_stream(s, 0);
+	}
+}
+
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+{
+	struct cx18 *cx = s->cx;
+
+	if (!cx18_stream_enabled(s))
+		return -EINVAL;
+
+	/* This function assumes that you are allowed to stop the capture
+	   and that we are actually capturing */
+
+	CX18_DEBUG_INFO("Stop Capture\n");
+
+	if (atomic_read(&cx->tot_capturing) == 0)
+		return 0;
+
+	set_bit(CX18_F_S_STOPPING, &s->s_flags);
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
+	else
+		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
+		CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
+	}
+
+	if (s->type != CX18_ENC_STREAM_TYPE_TS)
+		atomic_dec(&cx->ana_capturing);
+	atomic_dec(&cx->tot_capturing);
+
+	/* Clear capture and no-read bits */
+	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+	/* Tell the CX23418 it can't use our buffers anymore */
+	cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
+	cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+	s->handle = CX18_INVALID_TASK_HANDLE;
+	clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+
+	if (atomic_read(&cx->tot_capturing) > 0)
+		return 0;
+
+	cx2341x_handler_set_busy(&cx->cxhdl, 0);
+	cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+	wake_up(&s->waitq);
+
+	return 0;
+}
+EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
+
+u32 cx18_find_handle(struct cx18 *cx)
+{
+	int i;
+
+	/* find first available handle to be used for global settings */
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		struct cx18_stream *s = &cx->streams[i];
+
+		if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+			return s->handle;
+	}
+	return CX18_INVALID_TASK_HANDLE;
+}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+	int i;
+	struct cx18_stream *s;
+
+	if (handle == CX18_INVALID_TASK_HANDLE)
+		return NULL;
+
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		s = &cx->streams[i];
+		if (s->handle != handle)
+			continue;
+		if (cx18_stream_enabled(s))
+			return s;
+	}
+	return NULL;
+}
diff --git a/drivers/media/pci/cx18/cx18-streams.h b/drivers/media/pci/cx18/cx18-streams.h
new file mode 100644
index 000000000000..713b0e61536d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-streams.h
@@ -0,0 +1,62 @@
+/*
+ *  cx18 init/start/stop/exit stream functions
+ *
+ *  Derived from ivtv-streams.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
+ */
+
+u32 cx18_find_handle(struct cx18 *cx);
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
+int cx18_streams_setup(struct cx18 *cx);
+int cx18_streams_register(struct cx18 *cx);
+void cx18_streams_cleanup(struct cx18 *cx, int unregister);
+
+#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3)
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx);
+
+static inline bool cx18_stream_enabled(struct cx18_stream *s)
+{
+	return s->video_dev ||
+	       (s->dvb && s->dvb->enabled) ||
+	       (s->type == CX18_ENC_STREAM_TYPE_IDX &&
+		s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0);
+}
+
+/* Related to submission of mdls to firmware */
+static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+	schedule_work(&s->out_work_order);
+}
+
+static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s,
+					  struct cx18_mdl *mdl)
+{
+	/* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */
+	cx18_enqueue(s, mdl, &s->q_free);
+	cx18_stream_load_fw_queue(s);
+}
+
+void cx18_out_work_handler(struct work_struct *work);
+
+/* Capture related */
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
+
+void cx18_stop_all_captures(struct cx18 *cx);
diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c
new file mode 100644
index 000000000000..6d3121ff45a2
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-vbi.c
@@ -0,0 +1,277 @@
+/*
+ *  cx18 Vertical Blank Interval support functions
+ *
+ *  Derived from ivtv-vbi.c
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-vbi.h"
+#include "cx18-ioctl.h"
+#include "cx18-queue.h"
+
+/*
+ * Raster Reference/Protection (RP) bytes, used in Start/End Active
+ * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
+ * of VBI sample or VBI ancillary data regions in the digitial ratser line.
+ *
+ * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
+ */
+static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 };    /* __V_, _FV_ */
+static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
+
+static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+{
+	int line = 0;
+	int i;
+	u32 linemask[2] = { 0, 0 };
+	unsigned short size;
+	static const u8 mpeg_hdr_data[] = {
+		/* MPEG-2 Program Pack */
+		0x00, 0x00, 0x01, 0xba,		    /* Prog Pack start code */
+		0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
+		0x01, 0xd1, 0xd3,		    /* Mux Rate, markers */
+		0xfa, 0xff, 0xff,		    /* Res, Suff cnt, Stuff */
+		/* MPEG-2 Private Stream 1 PES Packet */
+		0x00, 0x00, 0x01, 0xbd,		    /* Priv Stream 1 start */
+		0x00, 0x1a,			    /* length */
+		0x84, 0x80, 0x07,		    /* flags, hdr data len */
+		0x21, 0x00, 0x5d, 0x63, 0xa7, 	    /* PTS, markers */
+		0xff, 0xff			    /* stuffing */
+	};
+	const int sd = sizeof(mpeg_hdr_data);	/* start of vbi data */
+	int idx = cx->vbi.frame % CX18_VBI_FRAMES;
+	u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
+
+	for (i = 0; i < lines; i++) {
+		struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
+		int f, l;
+
+		if (sdata->id == 0)
+			continue;
+
+		l = sdata->line - 6;
+		f = sdata->field;
+		if (f)
+			l += 18;
+		if (l < 32)
+			linemask[0] |= (1 << l);
+		else
+			linemask[1] |= (1 << (l - 32));
+		dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
+		memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42);
+		line++;
+	}
+	memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+	if (line == 36) {
+		/* All lines are used, so there is no space for the linemask
+		   (the max size of the VBI data is 36 * 43 + 4 bytes).
+		   So in this case we use the magic number 'ITV0'. */
+		memcpy(dst + sd, "ITV0", 4);
+		memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+		size = 4 + ((43 * line + 3) & ~3);
+	} else {
+		memcpy(dst + sd, "itv0", 4);
+		cpu_to_le32s(&linemask[0]);
+		cpu_to_le32s(&linemask[1]);
+		memcpy(dst + sd + 4, &linemask[0], 8);
+		size = 12 + ((43 * line + 3) & ~3);
+	}
+	dst[4+16] = (size + 10) >> 8;
+	dst[5+16] = (size + 10) & 0xff;
+	dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+	dst[10+16] = (pts_stamp >> 22) & 0xff;
+	dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+	dst[12+16] = (pts_stamp >> 7) & 0xff;
+	dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+	cx->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space
+   after the frame.  Returns new compressed size. */
+/* FIXME - this function ignores the input size. */
+static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
+{
+	u32 line_size = vbi_active_samples;
+	u32 lines = cx->vbi.count * 2;
+	u8 *q = buf;
+	u8 *p;
+	int i;
+
+	/* Skip the header */
+	buf += hdr_size;
+
+	for (i = 0; i < lines; i++) {
+		p = buf + i * line_size;
+
+		/* Look for SAV code */
+		if (p[0] != 0xff || p[1] || p[2] ||
+		    (p[3] != raw_vbi_sav_rp[0] &&
+		     p[3] != raw_vbi_sav_rp[1]))
+			break;
+		if (i == lines - 1) {
+			/* last line is hdr_size bytes short - extrapolate it */
+			memcpy(q, p + 4, line_size - 4 - hdr_size);
+			q += line_size - 4 - hdr_size;
+			p += line_size - hdr_size - 1;
+			memset(q, (int) *p, hdr_size);
+		} else {
+			memcpy(q, p + 4, line_size - 4);
+			q += line_size - 4;
+		}
+	}
+	return lines * (line_size - 4);
+}
+
+static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
+			       const u32 hdr_size)
+{
+	struct v4l2_decode_vbi_line vbi;
+	int i;
+	u32 line = 0;
+	u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz
+				    : vbi_hblank_samples_50Hz;
+
+	/* find the first valid line */
+	for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
+		if (buf[0] == 0xff && !buf[1] && !buf[2] &&
+		    (buf[3] == sliced_vbi_eav_rp[0] ||
+		     buf[3] == sliced_vbi_eav_rp[1]))
+			break;
+	}
+
+	/*
+	 * The last line is short by hdr_size bytes, but for the remaining
+	 * checks against size, we pretend that it is not, by counting the
+	 * header bytes we knowingly skipped
+	 */
+	size -= (i - hdr_size);
+	if (size < line_size)
+		return line;
+
+	for (i = 0; i < size / line_size; i++) {
+		u8 *p = buf + i * line_size;
+
+		/* Look for EAV code  */
+		if (p[0] != 0xff || p[1] || p[2] ||
+		    (p[3] != sliced_vbi_eav_rp[0] &&
+		     p[3] != sliced_vbi_eav_rp[1]))
+			continue;
+		vbi.p = p + 4;
+		v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi);
+		if (vbi.type) {
+			cx->vbi.sliced_data[line].id = vbi.type;
+			cx->vbi.sliced_data[line].field = vbi.is_second_field;
+			cx->vbi.sliced_data[line].line = vbi.line;
+			memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
+			line++;
+		}
+	}
+	return line;
+}
+
+static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
+{
+	/*
+	 * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
+	 * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
+	 */
+	struct vbi_data_hdr {
+		__be32 magic;
+		__be32 unknown;
+		__be32 pts;
+	} *hdr = (struct vbi_data_hdr *) buf->buf;
+
+	u8 *p = (u8 *) buf->buf;
+	u32 size = buf->bytesused;
+	u32 pts;
+	int lines;
+
+	/*
+	 * The CX23418 sends us data that is 32 bit little-endian swapped,
+	 * but we want the raw VBI bytes in the order they were in the raster
+	 * line.  This has a side effect of making the header big endian
+	 */
+	cx18_buf_swap(buf);
+
+	/* Raw VBI data */
+	if (cx18_raw_vbi(cx)) {
+
+		size = buf->bytesused =
+		     compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
+		/*
+		 * Hack needed for compatibility with old VBI software.
+		 * Write the frame # at the last 4 bytes of the frame
+		 */
+		p += size - 4;
+		memcpy(p, &cx->vbi.frame, 4);
+		cx->vbi.frame++;
+		return;
+	}
+
+	/* Sliced VBI data with data insertion */
+
+	pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
+						      : 0;
+
+	lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
+	/* always return at least one empty line */
+	if (lines == 0) {
+		cx->vbi.sliced_data[0].id = 0;
+		cx->vbi.sliced_data[0].line = 0;
+		cx->vbi.sliced_data[0].field = 0;
+		lines = 1;
+	}
+	buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
+	memcpy(p, &cx->vbi.sliced_data[0], size);
+
+	if (cx->vbi.insert_mpeg)
+		copy_vbi_data(cx, lines, pts);
+	cx->vbi.frame++;
+}
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+			   int streamtype)
+{
+	struct cx18_buffer *buf;
+	u32 orig_used;
+
+	if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+		return;
+
+	/*
+	 * Big assumption here:
+	 * Every buffer hooked to the MDL's buf_list is a complete VBI frame
+	 * that ends at the end of the buffer.
+	 *
+	 * To assume anything else would make the code in this file
+	 * more complex, or require extra memcpy()'s to make the
+	 * buffers satisfy the above assumption.  It's just simpler to set
+	 * up the encoder buffer transfers to make the assumption true.
+	 */
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		orig_used = buf->bytesused;
+		if (orig_used == 0)
+			break;
+		_cx18_process_vbi_data(cx, buf);
+		mdl->bytesused -= (orig_used - buf->bytesused);
+	}
+}
diff --git a/drivers/media/pci/cx18/cx18-vbi.h b/drivers/media/pci/cx18/cx18-vbi.h
new file mode 100644
index 000000000000..b365cf4b4668
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-vbi.h
@@ -0,0 +1,26 @@
+/*
+ *  cx18 Vertical Blank Interval support functions
+ *
+ *  Derived from ivtv-vbi.h
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+			   int streamtype);
+int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/pci/cx18/cx18-version.h b/drivers/media/pci/cx18/cx18-version.h
new file mode 100644
index 000000000000..fed48b6bb67b
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-version.h
@@ -0,0 +1,28 @@
+/*
+ *  cx18 driver version information
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#ifndef CX18_VERSION_H
+#define CX18_VERSION_H
+
+#define CX18_DRIVER_NAME "cx18"
+#define CX18_VERSION "1.5.1"
+
+#endif
diff --git a/drivers/media/pci/cx18/cx18-video.c b/drivers/media/pci/cx18/cx18-video.c
new file mode 100644
index 000000000000..6dc84aac8f44
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-video.c
@@ -0,0 +1,32 @@
+/*
+ *  cx18 video interface functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-video.h"
+#include "cx18-cards.h"
+
+void cx18_video_set_io(struct cx18 *cx)
+{
+	int inp = cx->active_input;
+
+	v4l2_subdev_call(cx->sd_av, video, s_routing,
+			cx->card->video_inputs[inp].video_input, 0, 0);
+}
diff --git a/drivers/media/pci/cx18/cx18-video.h b/drivers/media/pci/cx18/cx18-video.h
new file mode 100644
index 000000000000..529006a06e5c
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-video.h
@@ -0,0 +1,22 @@
+/*
+ *  cx18 video interface functions
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+void cx18_video_set_io(struct cx18 *cx);
diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h
new file mode 100644
index 000000000000..767a8d23e3f2
--- /dev/null
+++ b/drivers/media/pci/cx18/cx23418.h
@@ -0,0 +1,492 @@
+/*
+ *  cx18 header containing common defines.
+ *
+ *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#ifndef CX23418_H
+#define CX23418_H
+
+#include <media/cx2341x.h>
+
+#define MGR_CMD_MASK            		0x40000000
+/* The MSB of the command code indicates that this is the completion of a
+   command */
+#define MGR_CMD_MASK_ACK        		(MGR_CMD_MASK | 0x80000000)
+
+/* Description: This command creates a new instance of a certain task
+   IN[0]  - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is
+	    the processor on which the task YYY will be created
+   OUT[0] - Task handle. This handle is passed along with commands to
+	    dispatch to the right instance of the task
+   ReturnCode - One of the ERR_SYS_... */
+#define CX18_CREATE_TASK      			(MGR_CMD_MASK | 0x0001)
+
+/* Description: This command destroys an instance of a task
+   IN[0] - Task handle. Hanlde of the task to destroy
+   ReturnCode - One of the ERR_SYS_... */
+#define CX18_DESTROY_TASK     			(MGR_CMD_MASK | 0x0002)
+
+/* All commands for CPU have the following mask set */
+#define CPU_CMD_MASK                        	0x20000000
+#define CPU_CMD_MASK_DEBUG       		(CPU_CMD_MASK | 0x00000000)
+#define CPU_CMD_MASK_ACK                    	(CPU_CMD_MASK | 0x80000000)
+#define CPU_CMD_MASK_CAPTURE                	(CPU_CMD_MASK | 0x00020000)
+#define CPU_CMD_MASK_TS                     	(CPU_CMD_MASK | 0x00040000)
+
+#define EPU_CMD_MASK                        	0x02000000
+#define EPU_CMD_MASK_DEBUG       		(EPU_CMD_MASK | 0x000000)
+#define EPU_CMD_MASK_DE                     	(EPU_CMD_MASK | 0x040000)
+
+#define APU_CMD_MASK 				0x10000000
+#define APU_CMD_MASK_ACK 			(APU_CMD_MASK | 0x80000000)
+
+#define CX18_APU_ENCODING_METHOD_MPEG		(0 << 28)
+#define CX18_APU_ENCODING_METHOD_AC3		(1 << 28)
+
+/* Description: Command APU to start audio
+   IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?)
+   IN[1] - caller buffer address, or 0
+   ReturnCode - ??? */
+#define CX18_APU_START				(APU_CMD_MASK | 0x01)
+
+/* Description: Command APU to stop audio
+   IN[0] - encoding method to stop
+   ReturnCode - ??? */
+#define CX18_APU_STOP				(APU_CMD_MASK | 0x02)
+
+/* Description: Command APU to reset the AI
+   ReturnCode - ??? */
+#define CX18_APU_RESETAI 			(APU_CMD_MASK | 0x05)
+
+/* Description: This command indicates that a Memory Descriptor List has been
+   filled with the requested channel type
+   IN[0] - Task handle. Handle of the task
+   IN[1] - Offset of the MDL_ACK from the beginning of the local DDR.
+   IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1]
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_EPU_DMA_DONE              		(EPU_CMD_MASK_DE | 0x0001)
+
+/* Something interesting happened
+   IN[0] - A value to log
+   IN[1] - An offset of a string in the MiniMe memory;
+	   0/zero/NULL means "I have nothing to say" */
+#define CX18_EPU_DEBUG 				(EPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Reads memory/registers (32-bit)
+   IN[0] - Address
+   OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32			(CPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Description: This command starts streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to start
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_START               	(CPU_CMD_MASK_CAPTURE | 0x0002)
+
+/* Description: This command stops streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to stop
+   IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only)
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_STOP                	(CPU_CMD_MASK_CAPTURE | 0x0003)
+
+/* Description: This command pauses streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to pause
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_PAUSE               	(CPU_CMD_MASK_CAPTURE | 0x0007)
+
+/* Description: This command resumes streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to resume
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_RESUME              	(CPU_CMD_MASK_CAPTURE | 0x0008)
+
+#define CAPTURE_CHANNEL_TYPE_NONE  		0
+#define CAPTURE_CHANNEL_TYPE_MPEG  		1
+#define CAPTURE_CHANNEL_TYPE_INDEX 		2
+#define CAPTURE_CHANNEL_TYPE_YUV   		3
+#define CAPTURE_CHANNEL_TYPE_PCM   		4
+#define CAPTURE_CHANNEL_TYPE_VBI   		5
+#define CAPTURE_CHANNEL_TYPE_SLICED_VBI		6
+#define CAPTURE_CHANNEL_TYPE_TS			7
+#define CAPTURE_CHANNEL_TYPE_MAX   		15
+
+/* Description: This command sets the channel type. This can only be done
+   when stopped.
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Channel Type. See Below.
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CHANNEL_TYPE      		(CPU_CMD_MASK_CAPTURE + 1)
+
+/* Description: Set stream output type
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - type
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_STREAM_OUTPUT_TYPE		(CPU_CMD_MASK_CAPTURE | 0x0012)
+
+/* Description: Set video input resolution and frame rate
+   IN[0] - task handle
+   IN[1] - reserved
+   IN[2] - reserved
+   IN[3] - reserved
+   IN[4] - reserved
+   IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_IN                	(CPU_CMD_MASK_CAPTURE | 0x0004)
+
+/* Description: Set video frame rate
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - video bit rate mode
+   IN[2] - video average rate
+   IN[3] - video peak rate
+   IN[4] - system mux rate
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RATE              	(CPU_CMD_MASK_CAPTURE | 0x0005)
+
+/* Description: Set video output resolution
+   IN[0] - task handle
+   IN[1] - horizontal size
+   IN[2] - vertical size
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RESOLUTION		(CPU_CMD_MASK_CAPTURE | 0x0006)
+
+/* Description: This command set filter parameters
+   IN[0] - Task handle. Handle of the task
+   IN[1] - type, 0 - temporal, 1 - spatial, 2 - median
+   IN[2] - mode,  temporal/spatial: 0 - disable, 1 - static, 2 - dynamic
+			median:	0 = disable, 1 = horizontal, 2 = vertical,
+				3 = horizontal/vertical, 4 = diagonal
+   IN[3] - strength, temporal 0 - 31, spatial 0 - 15
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_FILTER_PARAM            	(CPU_CMD_MASK_CAPTURE | 0x0009)
+
+/* Description: This command set spatial filter type
+   IN[0] - Task handle.
+   IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only,
+		      3 = 2D H/V separable, 4 = 2D symmetric non-separable
+   IN[2] - chroma type: 0 - disable, 1 = 1D horizontal
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SPATIAL_FILTER_TYPE     	(CPU_CMD_MASK_CAPTURE | 0x000C)
+
+/* Description: This command set coring levels for median filter
+   IN[0] - Task handle.
+   IN[1] - luma_high
+   IN[2] - luma_low
+   IN[3] - chroma_high
+   IN[4] - chroma_low
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MEDIAN_CORING           	(CPU_CMD_MASK_CAPTURE | 0x000E)
+
+/* Description: This command set the picture type mask for index file
+   IN[0] - Task handle (ignored by firmware)
+   IN[1] - 	0 = disable index file output
+			1 = output I picture
+			2 = P picture
+			4 = B picture
+			other = illegal */
+#define CX18_CPU_SET_INDEXTABLE         	(CPU_CMD_MASK_CAPTURE | 0x0010)
+
+/* Description: Set audio parameters
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - audio parameter
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PARAMETERS		(CPU_CMD_MASK_CAPTURE | 0x0011)
+
+/* Description: Set video mute
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - bit31-24: muteYvalue
+	   bit23-16: muteUvalue
+	   bit15-8:  muteVvalue
+	   bit0:     1:mute, 0: unmute
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_MUTE			(CPU_CMD_MASK_CAPTURE | 0x0013)
+
+/* Description: Set audio mute
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - mute/unmute
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_MUTE			(CPU_CMD_MASK_CAPTURE | 0x0014)
+
+/* Description: Set stream output type
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - subType
+	    SET_INITIAL_SCR      		1
+	    SET_QUALITY_MODE            2
+	    SET_VIM_PROTECT_MODE        3
+	    SET_PTS_CORRECTION          4
+	    SET_USB_FLUSH_MODE          5
+	    SET_MERAQPAR_ENABLE         6
+	    SET_NAV_PACK_INSERTION      7
+	    SET_SCENE_CHANGE_ENABLE     8
+   IN[2] - parameter 1
+   IN[3] - parameter 2
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MISC_PARAMETERS		(CPU_CMD_MASK_CAPTURE | 0x0015)
+
+/* Description: Set raw VBI parameters
+   IN[0] - Task handle
+   IN[1] - No. of input lines per field:
+				bit[15:0]: field 1,
+				bit[31:16]: field 2
+   IN[2] - No. of input bytes per line
+   IN[3] - No. of output frames per transfer
+   IN[4] - start code
+   IN[5] - stop code
+   ReturnCode */
+#define CX18_CPU_SET_RAW_VBI_PARAM		(CPU_CMD_MASK_CAPTURE | 0x0016)
+
+/* Description: Set capture line No.
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - height1
+   IN[2] - height2
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CAPTURE_LINE_NO		(CPU_CMD_MASK_CAPTURE | 0x0017)
+
+/* Description: Set copyright
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - copyright
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_COPYRIGHT			(CPU_CMD_MASK_CAPTURE | 0x0018)
+
+/* Description: Set audio PID
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - PID
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PID			(CPU_CMD_MASK_CAPTURE | 0x0019)
+
+/* Description: Set video PID
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - PID
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_PID			(CPU_CMD_MASK_CAPTURE | 0x001A)
+
+/* Description: Set Vertical Crop Line
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - Line
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VER_CROP_LINE		(CPU_CMD_MASK_CAPTURE | 0x001B)
+
+/* Description: Set COP structure
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - M
+   IN[2] - N
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_GOP_STRUCTURE		(CPU_CMD_MASK_CAPTURE | 0x001C)
+
+/* Description: Set Scene Change Detection
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - scene change
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SCENE_CHANGE_DETECTION	(CPU_CMD_MASK_CAPTURE | 0x001D)
+
+/* Description: Set Aspect Ratio
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - AspectRatio
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_ASPECT_RATIO		(CPU_CMD_MASK_CAPTURE | 0x001E)
+
+/* Description: Set Skip Input Frame
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - skip input frames
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SKIP_INPUT_FRAME		(CPU_CMD_MASK_CAPTURE | 0x001F)
+
+/* Description: Set sliced VBI parameters -
+   Note This API will only apply to MPEG and Sliced VBI Channels
+   IN[0] - Task handle
+   IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext
+   IN[2] - start / stop line
+			bit[15:0] start line number
+			bit[31:16] stop line number
+   IN[3] - number of output frames per interrupt
+   IN[4] - VBI insertion mode
+			bit 0:	output user data, 1 - enable
+			bit 1:	output private stream, 1 - enable
+			bit 2:	mux option, 0 - in GOP, 1 - in picture
+			bit[7:0] 	private stream ID
+   IN[5] - insertion period while mux option is in picture
+   ReturnCode - VBI data offset */
+#define CX18_CPU_SET_SLICED_VBI_PARAM		(CPU_CMD_MASK_CAPTURE | 0x0020)
+
+/* Description: Set the user data place holder
+   IN[0] - type of data (0 for user)
+   IN[1] - Stuffing period
+   IN[2] - ID data size in word (less than 10)
+   IN[3] - Pointer to ID buffer */
+#define CX18_CPU_SET_USERDATA_PLACE_HOLDER	(CPU_CMD_MASK_CAPTURE | 0x0021)
+
+
+/* Description:
+   In[0] Task Handle
+   return parameter:
+   Out[0]  Reserved
+   Out[1]  Video PTS bit[32:2] of last output video frame.
+   Out[2]  Video PTS bit[ 1:0] of last output video frame.
+   Out[3]  Hardware Video PTS counter bit[31:0],
+	     these bits get incremented on every 90kHz clock tick.
+   Out[4]  Hardware Video PTS counter bit32,
+	     these bits get incremented on every 90kHz clock tick.
+   ReturnCode */
+#define CX18_CPU_GET_ENC_PTS			(CPU_CMD_MASK_CAPTURE | 0x0022)
+
+/* Description: Set VFC parameters
+   IN[0] - task handle
+   IN[1] - VFC enable flag, 1 - enable, 0 - disable
+*/
+#define CX18_CPU_SET_VFC_PARAM                  (CPU_CMD_MASK_CAPTURE | 0x0023)
+
+/* Below is the list of commands related to the data exchange */
+#define CPU_CMD_MASK_DE 			(CPU_CMD_MASK | 0x040000)
+
+/* Description: This command provides the physical base address of the local
+   DDR as viewed by EPU
+   IN[0] - Physical offset where EPU has the local DDR mapped
+   ReturnCode - One of the ERR_DE_... */
+#define CPU_CMD_DE_SetBase 			(CPU_CMD_MASK_DE | 0x0001)
+
+/* Description: This command provides the offsets in the device memory where
+   the 2 cx18_mdl_ack blocks reside
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Offset of the first cx18_mdl_ack from the beginning of the
+	   local DDR.
+   IN[2] - Offset of the second cx18_mdl_ack from the beginning of the
+	   local DDR.
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL_ACK                	(CPU_CMD_MASK_DE | 0x0002)
+
+/* Description: This command provides the offset to a Memory Descriptor List
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Offset of the MDL from the beginning of the local DDR.
+   IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1]
+   IN[3] - Buffer ID
+   IN[4] - Total buffer length
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL                   	(CPU_CMD_MASK_DE | 0x0005)
+
+/* Description: This command requests return of all current Memory
+   Descriptor Lists to the driver
+   IN[0] - Task handle. Handle of the task to start
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_RELEASE_MDL               	(CPU_CMD_MASK_DE | 0x0006)
+
+/* Description: This command signals the cpu that the dat buffer has been
+   consumed and ready for re-use.
+   IN[0] - Task handle. Handle of the task
+   IN[1] - Offset of the data block from the beginning of the local DDR.
+   IN[2] - Number of bytes in the data block
+   ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_RELEASE_BUFFER           (CPU_CMD_MASK_DE | 0x0007) */
+
+/* No Error / Success */
+#define CNXT_OK                 0x000000
+
+/* Received unknown command */
+#define CXERR_UNK_CMD           0x000001
+
+/* First parameter in the command is invalid */
+#define CXERR_INVALID_PARAM1    0x000002
+
+/* Second parameter in the command is invalid */
+#define CXERR_INVALID_PARAM2    0x000003
+
+/* Device interface is not open/found */
+#define CXERR_DEV_NOT_FOUND     0x000004
+
+/* Requested function is not implemented/available */
+#define CXERR_NOTSUPPORTED      0x000005
+
+/* Invalid pointer is provided */
+#define CXERR_BADPTR            0x000006
+
+/* Unable to allocate memory */
+#define CXERR_NOMEM             0x000007
+
+/* Object/Link not found */
+#define CXERR_LINK              0x000008
+
+/* Device busy, command cannot be executed */
+#define CXERR_BUSY              0x000009
+
+/* File/device/handle is not open. */
+#define CXERR_NOT_OPEN          0x00000A
+
+/* Value is out of range */
+#define CXERR_OUTOFRANGE        0x00000B
+
+/* Buffer overflow */
+#define CXERR_OVERFLOW          0x00000C
+
+/* Version mismatch */
+#define CXERR_BADVER            0x00000D
+
+/* Operation timed out */
+#define CXERR_TIMEOUT           0x00000E
+
+/* Operation aborted */
+#define CXERR_ABORT             0x00000F
+
+/* Specified I2C device not found for read/write */
+#define CXERR_I2CDEV_NOTFOUND   0x000010
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_XFERERR    0x000011
+
+/* Chanel changing component not ready */
+#define CXERR_CHANNELNOTREADY   0x000012
+
+/* PPU (Presensation/Decoder) mail box is corrupted */
+#define CXERR_PPU_MB_CORRUPT    0x000013
+
+/* CPU (Capture/Encoder) mail box is corrupted */
+#define CXERR_CPU_MB_CORRUPT    0x000014
+
+/* APU (Audio) mail box is corrupted */
+#define CXERR_APU_MB_CORRUPT    0x000015
+
+/* Unable to open file for reading */
+#define CXERR_FILE_OPEN_READ    0x000016
+
+/* Unable to open file for writing */
+#define CXERR_FILE_OPEN_WRITE   0x000017
+
+/* Unable to find the I2C section specified */
+#define CXERR_I2C_BADSECTION    0x000018
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_DATALOW    0x000019
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_CLOCKLOW   0x00001A
+
+/* No Interrupt received from HW (for I2C access) */
+#define CXERR_NO_HW_I2C_INTR    0x00001B
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NOT_READY     0x00001C
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NO_ACK        0x00001D
+
+/* The are no buffers ready. Try again soon! */
+#define CXERR_NODATA_AGAIN      0x00001E
+
+/* The stream is stopping. Function not allowed now! */
+#define CXERR_STOPPING_STATUS   0x00001F
+
+/* Trying to access hardware when the power is turned OFF */
+#define CXERR_DEVPOWER_OFF      0x000020
+
+#endif /* CX23418_H */
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
new file mode 100644
index 000000000000..eafa1144b17d
--- /dev/null
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -0,0 +1,50 @@
+config VIDEO_CX23885
+	tristate "Conexant cx23885 (2388x successor) support"
+	depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
+	select SND_PCM
+	select I2C_ALGOBIT
+	select VIDEO_BTCX
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	depends on RC_CORE
+	select VIDEOBUF_DVB
+	select VIDEOBUF_DMA_SG
+	select VIDEO_CX25840
+	select VIDEO_CX2341X
+	select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for Conexant 23885 based
+	  TV cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx23885
+
+config MEDIA_ALTERA_CI
+	tristate "Altera FPGA based CI module"
+	depends on VIDEO_CX23885 && DVB_CORE
+	select ALTERA_STAPL
+	---help---
+	  An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called altera-ci
diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile
new file mode 100644
index 000000000000..a2cbdcf15a8c
--- /dev/null
+++ b/drivers/media/pci/cx23885/Makefile
@@ -0,0 +1,15 @@
+cx23885-objs	:= cx23885-cards.o cx23885-video.o cx23885-vbi.o \
+		    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
+		    cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+		    cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+		    cx23885-f300.o cx23885-alsa.o
+
+obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
+obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c
new file mode 100644
index 000000000000..aee7f0dacff1
--- /dev/null
+++ b/drivers/media/pci/cx23885/altera-ci.c
@@ -0,0 +1,839 @@
+/*
+ * altera-ci.c
+ *
+ *  CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * 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.
+ */
+
+/*
+ * currently cx23885 GPIO's used.
+ * GPIO-0 ~INT in
+ * GPIO-1 TMS out
+ * GPIO-2 ~reset chips out
+ * GPIO-3 to GPIO-10 data/addr for CA in/out
+ * GPIO-11 ~CS out
+ * GPIO-12 AD_RG out
+ * GPIO-13 ~WR out
+ * GPIO-14 ~RD out
+ * GPIO-15 ~RDY in
+ * GPIO-16 TCK out
+ * GPIO-17 TDO in
+ * GPIO-18 TDI out
+ */
+/*
+ *  Bit definitions for MC417_RWD and MC417_OEN registers
+ * bits 31-16
+ * +-----------+
+ * | Reserved  |
+ * +-----------+
+ *   bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |  TDI  |  TDO  |  TCK  |  RDY# |  #RD  |  #WR  | AD_RG |  #CS  |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include "altera-ci.h"
+#include "dvb_ca_en50221.h"
+
+/* FPGA regs */
+#define NETUP_CI_INT_CTRL	0x00
+#define NETUP_CI_BUSCTRL2	0x01
+#define NETUP_CI_ADDR0		0x04
+#define NETUP_CI_ADDR1		0x05
+#define NETUP_CI_DATA		0x06
+#define NETUP_CI_BUSCTRL	0x07
+#define NETUP_CI_PID_ADDR0	0x08
+#define NETUP_CI_PID_ADDR1	0x09
+#define NETUP_CI_PID_DATA	0x0a
+#define NETUP_CI_TSA_DIV	0x0c
+#define NETUP_CI_TSB_DIV	0x0d
+#define NETUP_CI_REVISION	0x0f
+
+/* const for ci op */
+#define NETUP_CI_FLG_CTL	1
+#define NETUP_CI_FLG_RD		1
+#define NETUP_CI_FLG_AD		1
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int pid_dbg;
+module_param(pid_dbg, int, 0644);
+MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
+
+MODULE_DESCRIPTION("altera FPGA CI module");
+MODULE_AUTHOR("Igor M. Liplianin  <liplianin@netup.ru>");
+MODULE_LICENSE("GPL");
+
+#define ci_dbg_print(args...) \
+	do { \
+		if (ci_dbg) \
+			printk(KERN_DEBUG args); \
+	} while (0)
+
+#define pid_dbg_print(args...) \
+	do { \
+		if (pid_dbg) \
+			printk(KERN_DEBUG args); \
+	} while (0)
+
+struct altera_ci_state;
+struct netup_hw_pid_filter;
+
+struct fpga_internal {
+	void *dev;
+	struct mutex fpga_mutex;/* two CI's on the same fpga */
+	struct netup_hw_pid_filter *pid_filt[2];
+	struct altera_ci_state *state[2];
+	struct work_struct work;
+	int (*fpga_rw) (void *dev, int flag, int data, int rw);
+	int cis_used;
+	int filts_used;
+	int strt_wrk;
+};
+
+/* stores all private variables for communication with CI */
+struct altera_ci_state {
+	struct fpga_internal *internal;
+	struct dvb_ca_en50221 ca;
+	int status;
+	int nr;
+};
+
+/* stores all private variables for hardware pid filtering */
+struct netup_hw_pid_filter {
+	struct fpga_internal *internal;
+	struct dvb_demux *demux;
+	/* save old functions */
+	int (*start_feed)(struct dvb_demux_feed *feed);
+	int (*stop_feed)(struct dvb_demux_feed *feed);
+
+	int status;
+	int nr;
+};
+
+/* internal params node */
+struct fpga_inode {
+	/* pointer for internal params, one for each pair of CI's */
+	struct fpga_internal		*internal;
+	struct fpga_inode		*next_inode;
+};
+
+/* first internal params */
+static struct fpga_inode *fpga_first_inode;
+
+/* find chip by dev */
+static struct fpga_inode *find_inode(void *dev)
+{
+	struct fpga_inode *temp_chip = fpga_first_inode;
+
+	if (temp_chip == NULL)
+		return temp_chip;
+
+	/*
+	 Search for the last fpga CI chip or
+	 find it by dev */
+	while ((temp_chip != NULL) &&
+				(temp_chip->internal->dev != dev))
+		temp_chip = temp_chip->next_inode;
+
+	return temp_chip;
+}
+/* check demux */
+static struct fpga_internal *check_filter(struct fpga_internal *temp_int,
+						void *demux_dev, int filt_nr)
+{
+	if (temp_int == NULL)
+		return NULL;
+
+	if ((temp_int->pid_filt[filt_nr]) == NULL)
+		return NULL;
+
+	if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
+		return temp_int;
+
+	return NULL;
+}
+
+/* find chip by demux */
+static struct fpga_inode *find_dinode(void *demux_dev)
+{
+	struct fpga_inode *temp_chip = fpga_first_inode;
+	struct fpga_internal *temp_int;
+
+	/*
+	 * Search of the last fpga CI chip or
+	 * find it by demux
+	 */
+	while (temp_chip != NULL) {
+		if (temp_chip->internal != NULL) {
+			temp_int = temp_chip->internal;
+			if (check_filter(temp_int, demux_dev, 0))
+				break;
+			if (check_filter(temp_int, demux_dev, 1))
+				break;
+		}
+
+		temp_chip = temp_chip->next_inode;
+	}
+
+	return temp_chip;
+}
+
+/* deallocating chip */
+static void remove_inode(struct fpga_internal *internal)
+{
+	struct fpga_inode *prev_node = fpga_first_inode;
+	struct fpga_inode *del_node = find_inode(internal->dev);
+
+	if (del_node != NULL) {
+		if (del_node == fpga_first_inode) {
+			fpga_first_inode = del_node->next_inode;
+		} else {
+			while (prev_node->next_inode != del_node)
+				prev_node = prev_node->next_inode;
+
+			if (del_node->next_inode == NULL)
+				prev_node->next_inode = NULL;
+			else
+				prev_node->next_inode =
+					prev_node->next_inode->next_inode;
+		}
+
+		kfree(del_node);
+	}
+}
+
+/* allocating new chip */
+static struct fpga_inode *append_internal(struct fpga_internal *internal)
+{
+	struct fpga_inode *new_node = fpga_first_inode;
+
+	if (new_node == NULL) {
+		new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+		fpga_first_inode = new_node;
+	} else {
+		while (new_node->next_inode != NULL)
+			new_node = new_node->next_inode;
+
+		new_node->next_inode =
+				kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+		if (new_node->next_inode != NULL)
+			new_node = new_node->next_inode;
+		else
+			new_node = NULL;
+	}
+
+	if (new_node != NULL) {
+		new_node->internal = internal;
+		new_node->next_inode = NULL;
+	}
+
+	return new_node;
+}
+
+static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
+							u8 val, u8 read)
+{
+	inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
+	return inter->fpga_rw(inter->dev, 0, val, read);
+}
+
+/* flag - mem/io, read - read/write */
+int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+				u8 flag, u8 read, int addr, u8 val)
+{
+
+	struct altera_ci_state *state = en50221->data;
+	struct fpga_internal *inter = state->internal;
+
+	u8 store;
+	int mem = 0;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	mutex_lock(&inter->fpga_mutex);
+
+	netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
+	netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
+	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+	store &= 0x0f;
+	store |= ((state->nr << 7) | (flag << 6));
+
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
+	mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
+			(read) ? "read" : "write", addr,
+			(flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
+			(read) ? mem : val);
+
+	return mem;
+}
+
+int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr)
+{
+	return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr, u8 data)
+{
+	return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
+						NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+						u8 addr, u8 data)
+{
+	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
+}
+
+int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct altera_ci_state *state = en50221->data;
+	struct fpga_internal *inter = state->internal;
+	/* reasonable timeout for CI reset is 10 seconds */
+	unsigned long t_out = jiffies + msecs_to_jiffies(9999);
+	int ret;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (0 != slot)
+		return -EINVAL;
+
+	mutex_lock(&inter->fpga_mutex);
+
+	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+				(ret & 0xcf) | (1 << (5 - state->nr)), 0);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	for (;;) {
+		mdelay(50);
+
+		mutex_lock(&inter->fpga_mutex);
+
+		ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+						0, NETUP_CI_FLG_RD);
+		mutex_unlock(&inter->fpga_mutex);
+
+		if ((ret & (1 << (5 - state->nr))) == 0)
+			break;
+		if (time_after(jiffies, t_out))
+			break;
+	}
+
+
+	ci_dbg_print("%s: %d msecs\n", __func__,
+		jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
+
+	return 0;
+}
+
+int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+	/* not implemented */
+	return 0;
+}
+
+int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct altera_ci_state *state = en50221->data;
+	struct fpga_internal *inter = state->internal;
+	int ret;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (0 != slot)
+		return -EINVAL;
+
+	mutex_lock(&inter->fpga_mutex);
+
+	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+				(ret & 0x0f) | (1 << (3 - state->nr)), 0);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	return 0;
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+	struct fpga_internal *inter =
+			container_of(work, struct fpga_internal, work);
+	int ret;
+
+	ci_dbg_print("%s\n", __func__);
+
+	mutex_lock(&inter->fpga_mutex);
+	/* ack' irq */
+	ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
+	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	if (inter->state[1] != NULL) {
+		inter->state[1]->status =
+				((ret & 1) == 0 ?
+				DVB_CA_EN50221_POLL_CAM_PRESENT |
+				DVB_CA_EN50221_POLL_CAM_READY : 0);
+		ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
+				__func__, inter->state[1]->status);
+	};
+
+	if (inter->state[0] != NULL) {
+		inter->state[0]->status =
+				((ret & 2) == 0 ?
+				DVB_CA_EN50221_POLL_CAM_PRESENT |
+				DVB_CA_EN50221_POLL_CAM_READY : 0);
+		ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
+				__func__, inter->state[0]->status);
+	};
+}
+
+/* CI irq handler */
+int altera_ci_irq(void *dev)
+{
+	struct fpga_inode *temp_int = NULL;
+	struct fpga_internal *inter = NULL;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (dev != NULL) {
+		temp_int = find_inode(dev);
+		if (temp_int != NULL) {
+			inter = temp_int->internal;
+			schedule_work(&inter->work);
+		}
+	}
+
+	return 1;
+}
+EXPORT_SYMBOL(altera_ci_irq);
+
+int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
+								int open)
+{
+	struct altera_ci_state *state = en50221->data;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	return state->status;
+}
+
+void altera_hw_filt_release(void *main_dev, int filt_nr)
+{
+	struct fpga_inode *temp_int = find_inode(main_dev);
+	struct netup_hw_pid_filter *pid_filt = NULL;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (temp_int != NULL) {
+		pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
+		/* stored old feed controls */
+		pid_filt->demux->start_feed = pid_filt->start_feed;
+		pid_filt->demux->stop_feed = pid_filt->stop_feed;
+
+		if (((--(temp_int->internal->filts_used)) <= 0) &&
+			 ((temp_int->internal->cis_used) <= 0)) {
+
+			ci_dbg_print("%s: Actually removing\n", __func__);
+
+			remove_inode(temp_int->internal);
+			kfree(pid_filt->internal);
+		}
+
+		kfree(pid_filt);
+
+	}
+
+}
+EXPORT_SYMBOL(altera_hw_filt_release);
+
+void altera_ci_release(void *dev, int ci_nr)
+{
+	struct fpga_inode *temp_int = find_inode(dev);
+	struct altera_ci_state *state = NULL;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (temp_int != NULL) {
+		state = temp_int->internal->state[ci_nr - 1];
+		altera_hw_filt_release(dev, ci_nr);
+
+
+		if (((temp_int->internal->filts_used) <= 0) &&
+				((--(temp_int->internal->cis_used)) <= 0)) {
+
+			ci_dbg_print("%s: Actually removing\n", __func__);
+
+			remove_inode(temp_int->internal);
+			kfree(state->internal);
+		}
+
+		if (state != NULL) {
+			if (state->ca.data != NULL)
+				dvb_ca_en50221_release(&state->ca);
+
+			kfree(state);
+		}
+	}
+
+}
+EXPORT_SYMBOL(altera_ci_release);
+
+static void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
+		u16 pid, int onoff)
+{
+	struct fpga_internal *inter = pid_filt->internal;
+	u8 store = 0;
+
+	/* pid 0-0x1f always enabled, don't touch them */
+	if ((pid == 0x2000) || (pid < 0x20))
+		return;
+
+	mutex_lock(&inter->fpga_mutex);
+
+	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
+	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+			((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
+
+	store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
+
+	if (onoff)/* 0 - on, 1 - off */
+		store |= (1 << (pid & 7));
+	else
+		store &= ~(1 << (pid & 7));
+
+	netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
+		pid_filt->nr, pid, pid, onoff ? "off" : "on");
+}
+
+static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
+					int filt_nr, int onoff)
+{
+	struct fpga_internal *inter = pid_filt->internal;
+	u8 store = 0;
+	int i;
+
+	pid_dbg_print("%s: pid_filt->nr[%d]  now %s\n", __func__, pid_filt->nr,
+			onoff ? "off" : "on");
+
+	if (onoff)/* 0 - on, 1 - off */
+		store = 0xff;/* ignore pid */
+	else
+		store = 0;/* enable pid */
+
+	mutex_lock(&inter->fpga_mutex);
+
+	for (i = 0; i < 1024; i++) {
+		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
+
+		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+				((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
+		/* pid 0-0x1f always enabled */
+		netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
+				(i > 3 ? store : 0), 0);
+	}
+
+	mutex_unlock(&inter->fpga_mutex);
+}
+
+int altera_pid_feed_control(void *demux_dev, int filt_nr,
+		struct dvb_demux_feed *feed, int onoff)
+{
+	struct fpga_inode *temp_int = find_dinode(demux_dev);
+	struct fpga_internal *inter = temp_int->internal;
+	struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
+
+	altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
+	/* call old feed proc's */
+	if (onoff)
+		pid_filt->start_feed(feed);
+	else
+		pid_filt->stop_feed(feed);
+
+	if (feed->pid == 0x2000)
+		altera_toggle_fullts_streaming(pid_filt, filt_nr,
+						onoff ? 0 : 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(altera_pid_feed_control);
+
+int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
+{
+	altera_pid_feed_control(feed->demux, num, feed, 1);
+
+	return 0;
+}
+
+int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
+{
+	altera_pid_feed_control(feed->demux, num, feed, 0);
+
+	return 0;
+}
+
+int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
+{
+	return altera_ci_start_feed(feed, 1);
+}
+
+int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
+{
+	return altera_ci_stop_feed(feed, 1);
+}
+
+int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
+{
+	return altera_ci_start_feed(feed, 2);
+}
+
+int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
+{
+	return altera_ci_stop_feed(feed, 2);
+}
+
+int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
+{
+	struct netup_hw_pid_filter *pid_filt = NULL;
+	struct fpga_inode *temp_int = find_inode(config->dev);
+	struct fpga_internal *inter = NULL;
+	int ret = 0;
+
+	pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (!pid_filt) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	if (temp_int != NULL) {
+		inter = temp_int->internal;
+		(inter->filts_used)++;
+		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+	} else {
+		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+		if (!inter) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		temp_int = append_internal(inter);
+		inter->filts_used = 1;
+		inter->dev = config->dev;
+		inter->fpga_rw = config->fpga_rw;
+		mutex_init(&inter->fpga_mutex);
+		inter->strt_wrk = 1;
+		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+	}
+
+	ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
+						pid_filt, hw_filt_nr - 1);
+	inter->pid_filt[hw_filt_nr - 1] = pid_filt;
+	pid_filt->demux = config->demux;
+	pid_filt->internal = inter;
+	pid_filt->nr = hw_filt_nr - 1;
+	/* store old feed controls */
+	pid_filt->start_feed = config->demux->start_feed;
+	pid_filt->stop_feed = config->demux->stop_feed;
+	/* replace with new feed controls */
+	if (hw_filt_nr == 1) {
+		pid_filt->demux->start_feed = altera_ci_start_feed_1;
+		pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
+	} else if (hw_filt_nr == 2) {
+		pid_filt->demux->start_feed = altera_ci_start_feed_2;
+		pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
+	}
+
+	altera_toggle_fullts_streaming(pid_filt, 0, 1);
+
+	return 0;
+err:
+	ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
+		     __func__, ret);
+
+	kfree(pid_filt);
+
+	return ret;
+}
+EXPORT_SYMBOL(altera_hw_filt_init);
+
+int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+	struct altera_ci_state *state;
+	struct fpga_inode *temp_int = find_inode(config->dev);
+	struct fpga_internal *inter = NULL;
+	int ret = 0;
+	u8 store = 0;
+
+	state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (!state) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	if (temp_int != NULL) {
+		inter = temp_int->internal;
+		(inter->cis_used)++;
+                inter->fpga_rw = config->fpga_rw;
+		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+	} else {
+		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+		if (!inter) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		temp_int = append_internal(inter);
+		inter->cis_used = 1;
+		inter->dev = config->dev;
+		inter->fpga_rw = config->fpga_rw;
+		mutex_init(&inter->fpga_mutex);
+		inter->strt_wrk = 1;
+		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+	}
+
+	ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
+						state, ci_nr - 1);
+	state->internal = inter;
+	state->nr = ci_nr - 1;
+
+	state->ca.owner = THIS_MODULE;
+	state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
+	state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
+	state->ca.read_cam_control = altera_ci_read_cam_ctl;
+	state->ca.write_cam_control = altera_ci_write_cam_ctl;
+	state->ca.slot_reset = altera_ci_slot_reset;
+	state->ca.slot_shutdown = altera_ci_slot_shutdown;
+	state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
+	state->ca.poll_slot_status = altera_poll_ci_slot_status;
+	state->ca.data = state;
+
+	ret = dvb_ca_en50221_init(config->adapter,
+				   &state->ca,
+				   /* flags */ 0,
+				   /* n_slots */ 1);
+	if (0 != ret)
+		goto err;
+
+       inter->state[ci_nr - 1] = state;
+
+	altera_hw_filt_init(config, ci_nr);
+
+	if (inter->strt_wrk) {
+		INIT_WORK(&inter->work, netup_read_ci_status);
+		inter->strt_wrk = 0;
+	}
+
+	ci_dbg_print("%s: CI initialized!\n", __func__);
+
+	mutex_lock(&inter->fpga_mutex);
+
+	/* Enable div */
+	netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
+	netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
+
+	/* enable TS out */
+	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+	store |= (3 << 4);
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+	ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
+	/* enable irq */
+	netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
+
+	schedule_work(&inter->work);
+
+	return 0;
+err:
+	ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+
+	kfree(state);
+
+	return ret;
+}
+EXPORT_SYMBOL(altera_ci_init);
+
+int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+	struct fpga_inode *temp_int = find_inode(dev);
+	struct fpga_internal *inter = NULL;
+	u8 store;
+
+	ci_dbg_print("%s\n", __func__);
+
+	if (temp_int == NULL)
+		return -1;
+
+	if (temp_int->internal == NULL)
+		return -1;
+
+	inter = temp_int->internal;
+
+	mutex_lock(&inter->fpga_mutex);
+
+	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+	store &= ~(4 << (2 - ci_nr));
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+	msleep(100);
+	store |= (4 << (2 - ci_nr));
+	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+	mutex_unlock(&inter->fpga_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(altera_ci_tuner_reset);
diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h
new file mode 100644
index 000000000000..70e4fd69ad9e
--- /dev/null
+++ b/drivers/media/pci/cx23885/altera-ci.h
@@ -0,0 +1,100 @@
+/*
+ * altera-ci.c
+ *
+ *  CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * 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 __ALTERA_CI_H
+#define __ALTERA_CI_H
+
+#define ALT_DATA	0x000000ff
+#define ALT_TDI		0x00008000
+#define ALT_TDO		0x00004000
+#define ALT_TCK		0x00002000
+#define ALT_RDY		0x00001000
+#define ALT_RD		0x00000800
+#define ALT_WR		0x00000400
+#define ALT_AD_RG	0x00000200
+#define ALT_CS		0x00000100
+
+struct altera_ci_config {
+	void *dev;/* main dev, for example cx23885_dev */
+	void *adapter;/* for CI to connect to */
+	struct dvb_demux *demux;/* for hardware PID filter to connect to */
+	int (*fpga_rw) (void *dev, int ad_rg, int val, int rw);
+};
+
+#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \
+							&& defined(MODULE))
+
+extern int altera_ci_init(struct altera_ci_config *config, int ci_nr);
+extern void altera_ci_release(void *dev, int ci_nr);
+extern int altera_ci_irq(void *dev);
+extern int altera_ci_tuner_reset(void *dev, int ci_nr);
+
+#else
+
+static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return 0;
+}
+
+static inline void altera_ci_release(void *dev, int ci_nr)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_ci_irq(void *dev)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return 0;
+}
+
+static inline int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return 0;
+}
+
+#endif
+#if 0
+static inline int altera_hw_filt_init(struct altera_ci_config *config,
+							int hw_filt_nr)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return 0;
+}
+
+static inline void altera_hw_filt_release(void *dev, int filt_nr)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_pid_feed_control(void *dev, int filt_nr,
+		struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return 0;
+}
+
+#endif /* CONFIG_MEDIA_ALTERA_CI */
+
+#endif /* __ALTERA_CI_H */
diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c
new file mode 100644
index 000000000000..c9f15d6dec40
--- /dev/null
+++ b/drivers/media/pci/cx23885/cimax2.c
@@ -0,0 +1,536 @@
+/*
+ * cimax2.c
+ *
+ * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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 "cx23885.h"
+#include "dvb_ca_en50221.h"
+/**** Bit definitions for MC417_RWD and MC417_OEN registers  ***
+  bits 31-16
++-----------+
+| Reserved  |
++-----------+
+  bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
++-------+-------+-------+-------+-------+-------+-------+-------+
+|  WR#  |  RD#  |       |  ACK# |  ADHI |  ADLO |  CS1# |  CS0# |
++-------+-------+-------+-------+-------+-------+-------+-------+
+ bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
++-------+-------+-------+-------+-------+-------+-------+-------+
+|  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+***/
+/* MC417 */
+#define NETUP_DATA		0x000000ff
+#define NETUP_WR		0x00008000
+#define NETUP_RD		0x00004000
+#define NETUP_ACK		0x00001000
+#define NETUP_ADHI		0x00000800
+#define NETUP_ADLO		0x00000400
+#define NETUP_CS1		0x00000200
+#define NETUP_CS0		0x00000100
+#define NETUP_EN_ALL		0x00001000
+#define NETUP_CTRL_OFF		(NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD)
+#define NETUP_CI_CTL		0x04
+#define NETUP_CI_RD		1
+
+#define NETUP_IRQ_DETAM 	0x1
+#define NETUP_IRQ_IRQAM		0x4
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int ci_irq_enable;
+module_param(ci_irq_enable, int, 0644);
+MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM");
+
+#define ci_dbg_print(args...) \
+	do { \
+		if (ci_dbg) \
+			printk(KERN_DEBUG args); \
+	} while (0)
+
+#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0)
+
+/* stores all private variables for communication with CI */
+struct netup_ci_state {
+	struct dvb_ca_en50221 ca;
+	struct mutex ca_mutex;
+	struct i2c_adapter *i2c_adap;
+	u8 ci_i2c_addr;
+	int status;
+	struct work_struct work;
+	void *priv;
+	u8 current_irq_mode;
+	int current_ci_flag;
+	unsigned long next_status_checked_time;
+};
+
+
+int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+						u8 *buf, int len)
+{
+	int ret;
+	struct i2c_msg msg[] = {
+		{
+			.addr	= addr,
+			.flags	= 0,
+			.buf	= &reg,
+			.len	= 1
+		}, {
+			.addr	= addr,
+			.flags	= I2C_M_RD,
+			.buf	= buf,
+			.len	= len
+		}
+	};
+
+	ret = i2c_transfer(i2c_adap, msg, 2);
+
+	if (ret != 2) {
+		ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n",
+						__func__, reg, ret);
+
+		return -1;
+	}
+
+	ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n",
+						__func__, addr, reg, buf[0]);
+
+	return 0;
+}
+
+int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+						u8 *buf, int len)
+{
+	int ret;
+	u8 buffer[len + 1];
+
+	struct i2c_msg msg = {
+		.addr	= addr,
+		.flags	= 0,
+		.buf	= &buffer[0],
+		.len	= len + 1
+	};
+
+	buffer[0] = reg;
+	memcpy(&buffer[1], buf, len);
+
+	ret = i2c_transfer(i2c_adap, &msg, 1);
+
+	if (ret != 1) {
+		ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n",
+						__func__, reg, ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+int netup_ci_get_mem(struct cx23885_dev *dev)
+{
+	int mem;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+	for (;;) {
+		mem = cx_read(MC417_RWD);
+		if ((mem & NETUP_ACK) == 0)
+			break;
+		if (time_after(jiffies, timeout))
+			break;
+		udelay(1);
+	}
+
+	cx_set(MC417_RWD, NETUP_CTRL_OFF);
+
+	return mem & 0xff;
+}
+
+int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+				u8 flag, u8 read, int addr, u8 data)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct cx23885_tsport *port = state->priv;
+	struct cx23885_dev *dev = port->dev;
+
+	u8 store;
+	int mem;
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	if (state->current_ci_flag != flag) {
+		ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+				0, &store, 1);
+		if (ret != 0)
+			return ret;
+
+		store &= ~0x0c;
+		store |= flag;
+
+		ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+				0, &store, 1);
+		if (ret != 0)
+			return ret;
+	};
+	state->current_ci_flag = flag;
+
+	mutex_lock(&dev->gpio_lock);
+
+	/* write addr */
+	cx_write(MC417_OEN, NETUP_EN_ALL);
+	cx_write(MC417_RWD, NETUP_CTRL_OFF |
+				NETUP_ADLO | (0xff & addr));
+	cx_clear(MC417_RWD, NETUP_ADLO);
+	cx_write(MC417_RWD, NETUP_CTRL_OFF |
+				NETUP_ADHI | (0xff & (addr >> 8)));
+	cx_clear(MC417_RWD, NETUP_ADHI);
+
+	if (read) { /* data in */
+		cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA);
+	} else /* data out */
+		cx_write(MC417_RWD, NETUP_CTRL_OFF | data);
+
+	/* choose chip */
+	cx_clear(MC417_RWD,
+			(state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1);
+	/* read/write */
+	cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR);
+	mem = netup_ci_get_mem(dev);
+
+	mutex_unlock(&dev->gpio_lock);
+
+	if (!read)
+		if (mem < 0)
+			return -EREMOTEIO;
+
+	ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__,
+			(read) ? "read" : "write", state->ci_i2c_addr, addr,
+			(flag == NETUP_CI_CTL) ? "ctl" : "mem",
+			(read) ? mem : data);
+
+	if (read)
+		return mem;
+
+	return 0;
+}
+
+int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr)
+{
+	return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0);
+}
+
+int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr, u8 data)
+{
+	return netup_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+	return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL,
+							NETUP_CI_RD, addr, 0);
+}
+
+int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+							u8 addr, u8 data)
+{
+	return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data);
+}
+
+int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct netup_ci_state *state = en50221->data;
+	u8 buf =  0x80;
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	udelay(500);
+	ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+
+	if (ret != 0)
+		return ret;
+
+	udelay(500);
+
+	buf = 0x00;
+	ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+
+	msleep(1000);
+	dvb_ca_en50221_camready_irq(&state->ca, 0);
+
+	return 0;
+
+}
+
+int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+	/* not implemented */
+	return 0;
+}
+
+int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode)
+{
+	struct netup_ci_state *state = en50221->data;
+	int ret;
+
+	if (irq_mode == state->current_irq_mode)
+		return 0;
+
+	ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n",
+			__func__, state->ci_i2c_addr, irq_mode);
+	ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+							0x1b, &irq_mode, 1);
+
+	if (ret != 0)
+		return ret;
+
+	state->current_irq_mode = irq_mode;
+
+	return 0;
+}
+
+int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct netup_ci_state *state = en50221->data;
+	u8 buf;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+			0, &buf, 1);
+	buf |= 0x60;
+
+	return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+	struct netup_ci_state *state =
+			container_of(work, struct netup_ci_state, work);
+	u8 buf[33];
+	int ret;
+
+	/* CAM module IRQ processing. fast operation */
+	dvb_ca_en50221_frda_irq(&state->ca, 0);
+
+	/* CAM module INSERT/REMOVE processing. slow operation because of i2c
+	 * transfers */
+	if (time_after(jiffies, state->next_status_checked_time)
+			|| !state->status) {
+		ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+				0, &buf[0], 33);
+
+		state->next_status_checked_time = jiffies
+			+ msecs_to_jiffies(1000);
+
+		if (ret != 0)
+			return;
+
+		ci_dbg_print("%s: Slot Status Addr=[0x%04x], "
+				"Reg=[0x%02x], data=%02x, "
+				"TS config = %02x\n", __func__,
+				state->ci_i2c_addr, 0, buf[0],
+				buf[0]);
+
+
+		if (buf[0] & 1)
+			state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+				DVB_CA_EN50221_POLL_CAM_READY;
+		else
+			state->status = 0;
+	}
+}
+
+/* CI irq handler */
+int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status)
+{
+	struct cx23885_tsport *port = NULL;
+	struct netup_ci_state *state = NULL;
+
+	ci_dbg_print("%s:\n", __func__);
+
+	if (0 == (pci_status & (PCI_MSK_GPIO0 | PCI_MSK_GPIO1)))
+		return 0;
+
+	if (pci_status & PCI_MSK_GPIO0) {
+		port = &dev->ts1;
+		state = port->port_priv;
+		schedule_work(&state->work);
+		ci_dbg_print("%s: Wakeup CI0\n", __func__);
+	}
+
+	if (pci_status & PCI_MSK_GPIO1) {
+		port = &dev->ts2;
+		state = port->port_priv;
+		schedule_work(&state->work);
+		ci_dbg_print("%s: Wakeup CI1\n", __func__);
+	}
+
+	return 1;
+}
+
+int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open)
+{
+	struct netup_ci_state *state = en50221->data;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags())
+			: NETUP_IRQ_DETAM);
+
+	return state->status;
+}
+
+int netup_ci_init(struct cx23885_tsport *port)
+{
+	struct netup_ci_state *state;
+	u8 cimax_init[34] = {
+		0x00, /* module A control*/
+		0x00, /* auto select mask high A */
+		0x00, /* auto select mask low A */
+		0x00, /* auto select pattern high A */
+		0x00, /* auto select pattern low A */
+		0x44, /* memory access time A */
+		0x00, /* invert input A */
+		0x00, /* RFU */
+		0x00, /* RFU */
+		0x00, /* module B control*/
+		0x00, /* auto select mask high B */
+		0x00, /* auto select mask low B */
+		0x00, /* auto select pattern high B */
+		0x00, /* auto select pattern low B */
+		0x44, /* memory access time B */
+		0x00, /* invert input B */
+		0x00, /* RFU */
+		0x00, /* RFU */
+		0x00, /* auto select mask high Ext */
+		0x00, /* auto select mask low Ext */
+		0x00, /* auto select pattern high Ext */
+		0x00, /* auto select pattern low Ext */
+		0x00, /* RFU */
+		0x02, /* destination - module A */
+		0x01, /* power on (use it like store place) */
+		0x00, /* RFU */
+		0x00, /* int status read only */
+		ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */
+		0x05, /* EXTINT=active-high, INT=push-pull */
+		0x00, /* USCG1 */
+		0x04, /* ack active low */
+		0x00, /* LOCK = 0 */
+		0x33, /* serial mode, rising in, rising out, MSB first*/
+		0x31, /* synchronization */
+	};
+	int ret;
+
+	ci_dbg_print("%s\n", __func__);
+	state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL);
+	if (!state) {
+		ci_dbg_print("%s: Unable create CI structure!\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	port->port_priv = state;
+
+	switch (port->nr) {
+	case 1:
+		state->ci_i2c_addr = 0x40;
+		break;
+	case 2:
+		state->ci_i2c_addr = 0x41;
+		break;
+	}
+
+	state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap;
+	state->ca.owner = THIS_MODULE;
+	state->ca.read_attribute_mem = netup_ci_read_attribute_mem;
+	state->ca.write_attribute_mem = netup_ci_write_attribute_mem;
+	state->ca.read_cam_control = netup_ci_read_cam_ctl;
+	state->ca.write_cam_control = netup_ci_write_cam_ctl;
+	state->ca.slot_reset = netup_ci_slot_reset;
+	state->ca.slot_shutdown = netup_ci_slot_shutdown;
+	state->ca.slot_ts_enable = netup_ci_slot_ts_ctl;
+	state->ca.poll_slot_status = netup_poll_ci_slot_status;
+	state->ca.data = state;
+	state->priv = port;
+	state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM;
+
+	ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+						0, &cimax_init[0], 34);
+	/* lock registers */
+	ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+						0x1f, &cimax_init[0x18], 1);
+	/* power on slots */
+	ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+						0x18, &cimax_init[0x18], 1);
+
+	if (0 != ret)
+		goto err;
+
+	ret = dvb_ca_en50221_init(&port->frontends.adapter,
+				   &state->ca,
+				   /* flags */ 0,
+				   /* n_slots */ 1);
+	if (0 != ret)
+		goto err;
+
+	INIT_WORK(&state->work, netup_read_ci_status);
+	schedule_work(&state->work);
+
+	ci_dbg_print("%s: CI initialized!\n", __func__);
+
+	return 0;
+err:
+	ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+	kfree(state);
+	return ret;
+}
+
+void netup_ci_exit(struct cx23885_tsport *port)
+{
+	struct netup_ci_state *state;
+
+	if (NULL == port)
+		return;
+
+	state = (struct netup_ci_state *)port->port_priv;
+	if (NULL == state)
+		return;
+
+	if (NULL == state->ca.data)
+		return;
+
+	dvb_ca_en50221_release(&state->ca);
+	kfree(state);
+}
diff --git a/drivers/media/pci/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h
new file mode 100644
index 000000000000..518744a4c8a5
--- /dev/null
+++ b/drivers/media/pci/cx23885/cimax2.h
@@ -0,0 +1,47 @@
+/*
+ * cimax2.h
+ *
+ * CIMax(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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 CIMAX2_H
+#define CIMAX2_H
+#include "dvb_ca_en50221.h"
+
+extern int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr);
+extern int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+						int slot, int addr, u8 data);
+extern int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
+						int slot, u8 addr);
+extern int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
+						int slot, u8 addr, u8 data);
+extern int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status);
+extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+						int slot, int open);
+extern int netup_ci_init(struct cx23885_tsport *port);
+extern void netup_ci_exit(struct cx23885_tsport *port);
+
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
new file mode 100644
index 000000000000..5d5052d0253f
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -0,0 +1,1790 @@
+/*
+ *
+ *  Support for a cx23417 mpeg encoder via cx23885 host port.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.us>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *    (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *      - CX23885/7/8 support
+ *
+ *  Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+
+#include "cx23885.h"
+#include "cx23885-ioctl.h"
+
+#define CX23885_FIRM_IMAGE_SIZE 376836
+#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 32;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+	"number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+
+#define dprintk(level, fmt, arg...)\
+	do { if (v4l_debug >= level) \
+		printk(KERN_DEBUG "%s: " fmt, \
+		(dev) ? dev->name : "cx23885[?]", ## arg); \
+	} while (0)
+
+static struct cx23885_tvnorm cx23885_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}, {
+		.name      = "PAL-BG",
+		.id        = V4L2_STD_PAL_BG,
+	}, {
+		.name      = "PAL-DK",
+		.id        = V4L2_STD_PAL_DK,
+	}, {
+		.name      = "PAL-I",
+		.id        = V4L2_STD_PAL_I,
+	}, {
+		.name      = "PAL-M",
+		.id        = V4L2_STD_PAL_M,
+	}, {
+		.name      = "PAL-N",
+		.id        = V4L2_STD_PAL_N,
+	}, {
+		.name      = "PAL-Nc",
+		.id        = V4L2_STD_PAL_Nc,
+	}, {
+		.name      = "PAL-60",
+		.id        = V4L2_STD_PAL_60,
+	}, {
+		.name      = "SECAM-L",
+		.id        = V4L2_STD_SECAM_L,
+	}, {
+		.name      = "SECAM-DK",
+		.id        = V4L2_STD_SECAM_DK,
+	}
+};
+
+/* ------------------------------------------------------------------ */
+enum cx23885_capture_type {
+	CX23885_MPEG_CAPTURE,
+	CX23885_RAW_CAPTURE,
+	CX23885_RAW_PASSTHRU_CAPTURE
+};
+enum cx23885_capture_bits {
+	CX23885_RAW_BITS_NONE             = 0x00,
+	CX23885_RAW_BITS_YUV_CAPTURE      = 0x01,
+	CX23885_RAW_BITS_PCM_CAPTURE      = 0x02,
+	CX23885_RAW_BITS_VBI_CAPTURE      = 0x04,
+	CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+	CX23885_RAW_BITS_TO_HOST_CAPTURE  = 0x10
+};
+enum cx23885_capture_end {
+	CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */
+	CX23885_END_NOW, /* stop immediately, no irq */
+};
+enum cx23885_framerate {
+	CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+	CX23885_FRAMERATE_PAL_25   /* PAL: 25fps */
+};
+enum cx23885_stream_port {
+	CX23885_OUTPUT_PORT_MEMORY,
+	CX23885_OUTPUT_PORT_STREAMING,
+	CX23885_OUTPUT_PORT_SERIAL
+};
+enum cx23885_data_xfer_status {
+	CX23885_MORE_BUFFERS_FOLLOW,
+	CX23885_LAST_BUFFER,
+};
+enum cx23885_picture_mask {
+	CX23885_PICTURE_MASK_NONE,
+	CX23885_PICTURE_MASK_I_FRAMES,
+	CX23885_PICTURE_MASK_I_P_FRAMES = 0x3,
+	CX23885_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx23885_vbi_mode_bits {
+	CX23885_VBI_BITS_SLICED,
+	CX23885_VBI_BITS_RAW,
+};
+enum cx23885_vbi_insertion_bits {
+	CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+	CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+	CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+	CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+	CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx23885_dma_unit {
+	CX23885_DMA_BYTES,
+	CX23885_DMA_FRAMES,
+};
+enum cx23885_dma_transfer_status_bits {
+	CX23885_DMA_TRANSFER_BITS_DONE = 0x01,
+	CX23885_DMA_TRANSFER_BITS_ERROR = 0x04,
+	CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx23885_pause {
+	CX23885_PAUSE_ENCODING,
+	CX23885_RESUME_ENCODING,
+};
+enum cx23885_copyright {
+	CX23885_COPYRIGHT_OFF,
+	CX23885_COPYRIGHT_ON,
+};
+enum cx23885_notification_type {
+	CX23885_NOTIFICATION_REFRESH,
+};
+enum cx23885_notification_status {
+	CX23885_NOTIFICATION_OFF,
+	CX23885_NOTIFICATION_ON,
+};
+enum cx23885_notification_mailbox {
+	CX23885_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx23885_field1_lines {
+	CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */
+	CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */
+	CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx23885_field2_lines {
+	CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */
+	CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */
+	CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx23885_custom_data_type {
+	CX23885_CUSTOM_EXTENSION_USR_DATA,
+	CX23885_CUSTOM_PRIVATE_PACKET,
+};
+enum cx23885_mute {
+	CX23885_UNMUTE,
+	CX23885_MUTE,
+};
+enum cx23885_mute_video_mask {
+	CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00,
+	CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000,
+	CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx23885_mute_video_shift {
+	CX23885_MUTE_VIDEO_V_SHIFT = 8,
+	CX23885_MUTE_VIDEO_U_SHIFT = 16,
+	CX23885_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/**** Bit definitions for MC417_RWD and MC417_OEN registers  ***
+  bits 31-16
++-----------+
+| Reserved  |
++-----------+
+  bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
++-------+-------+-------+-------+-------+-------+-------+-------+
+| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+ bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
++-------+-------+-------+-------+-------+-------+-------+-------+
+|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+***/
+#define MC417_MIWR	0x8000
+#define MC417_MIRD	0x4000
+#define MC417_MICS	0x2000
+#define MC417_MIRDY	0x1000
+#define MC417_MIADDR	0x0F00
+#define MC417_MIDATA	0x00FF
+
+/* MIADDR* nibble definitions */
+#define  MCI_MEMORY_DATA_BYTE0          0x000
+#define  MCI_MEMORY_DATA_BYTE1          0x100
+#define  MCI_MEMORY_DATA_BYTE2          0x200
+#define  MCI_MEMORY_DATA_BYTE3          0x300
+#define  MCI_MEMORY_ADDRESS_BYTE2       0x400
+#define  MCI_MEMORY_ADDRESS_BYTE1       0x500
+#define  MCI_MEMORY_ADDRESS_BYTE0       0x600
+#define  MCI_REGISTER_DATA_BYTE0        0x800
+#define  MCI_REGISTER_DATA_BYTE1        0x900
+#define  MCI_REGISTER_DATA_BYTE2        0xA00
+#define  MCI_REGISTER_DATA_BYTE3        0xB00
+#define  MCI_REGISTER_ADDRESS_BYTE0     0xC00
+#define  MCI_REGISTER_ADDRESS_BYTE1     0xD00
+#define  MCI_REGISTER_MODE              0xE00
+
+/* Read and write modes */
+#define  MCI_MODE_REGISTER_READ         0
+#define  MCI_MODE_REGISTER_WRITE        1
+#define  MCI_MODE_MEMORY_READ           0
+#define  MCI_MODE_MEMORY_WRITE          0x40
+
+/*** Bit definitions for MC417_CTL register ****
+ bits 31-6   bits 5-4   bit 3    bits 2-1       Bit 0
++--------+-------------+--------+--------------+------------+
+|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
++--------+-------------+--------+--------------+------------+
+***/
+#define MC417_SPD_CTL(x)	(((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x)	(((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN	0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW	0x1
+#define MC417_SPD_CTL_MEDIUM	0x0
+#define MC417_SPD_CTL_FAST	0x3     /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3	0x3
+#define MC417_GPIO_SEL_GPIO2	0x2
+#define MC417_GPIO_SEL_GPIO1	0x1
+#define MC417_GPIO_SEL_GPIO0	0x0
+
+void cx23885_mc417_init(struct cx23885_dev *dev)
+{
+	u32 regval;
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* Configure MC417_CTL register to defaults. */
+	regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST)	|
+		 MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3)	|
+		 MC417_UART_GPIO_EN;
+	cx_write(MC417_CTL, regval);
+
+	/* Configure MC417_OEN to defaults. */
+	regval = MC417_MIRDY;
+	cx_write(MC417_OEN, regval);
+
+	/* Configure MC417_RWD to defaults. */
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS;
+	cx_write(MC417_RWD, regval);
+}
+
+static int mc417_wait_ready(struct cx23885_dev *dev)
+{
+	u32 mi_ready;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+	for (;;) {
+		mi_ready = cx_read(MC417_RWD) & MC417_MIRDY;
+		if (mi_ready != 0)
+			return 0;
+		if (time_after(jiffies, timeout))
+			return -1;
+		udelay(1);
+	}
+}
+
+int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
+{
+	u32 regval;
+
+	/* Enable MC417 GPIO outputs except for MC417_MIRDY,
+	 * which is an input.
+	 */
+	cx_write(MC417_OEN, MC417_MIRDY);
+
+	/* Write data byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 |
+		(value & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+
+	/* Transition CS/WR to effect write transaction across bus. */
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 |
+		((value >> 8) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 |
+		((value >> 16) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 3 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 |
+		((value >> 24) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+		(address & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+		((address >> 8) & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Indicate that this is a write. */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+		MCI_MODE_REGISTER_WRITE;
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Wait for the trans to complete (MC417_MIRDY asserted). */
+	return mc417_wait_ready(dev);
+}
+
+int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
+{
+	int retval;
+	u32 regval;
+	u32 tempval;
+	u32 dataval;
+
+	/* Enable MC417 GPIO outputs except for MC417_MIRDY,
+	 * which is an input.
+	 */
+	cx_write(MC417_OEN, MC417_MIRDY);
+
+	/* Write address byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+		((address & 0x00FF));
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+		((address >> 8) & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Indicate that this is a register read. */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+		MCI_MODE_REGISTER_READ;
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Wait for the trans to complete (MC417_MIRDY asserted). */
+	retval = mc417_wait_ready(dev);
+
+	/* switch the DAT0-7 GPIO[10:3] to input mode */
+	cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+	/* Read data byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+	cx_write(MC417_RWD, regval);
+
+	/* Transition RD to effect read transaction across bus.
+	 * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)?
+	 * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its
+	 * input only...)
+	 */
+	regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+	cx_write(MC417_RWD, regval);
+
+	/* Collect byte */
+	tempval = cx_read(MC417_RWD);
+	dataval = tempval & 0x000000FF;
+
+	/* Bring CS and RD high. */
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= ((tempval & 0x000000FF) << 8);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= ((tempval & 0x000000FF) << 16);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 3 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= ((tempval & 0x000000FF) << 24);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	*value  = dataval;
+
+	return retval;
+}
+
+int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value)
+{
+	u32 regval;
+
+	/* Enable MC417 GPIO outputs except for MC417_MIRDY,
+	 * which is an input.
+	 */
+	cx_write(MC417_OEN, MC417_MIRDY);
+
+	/* Write data byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 |
+		(value & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+
+	/* Transition CS/WR to effect write transaction across bus. */
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 |
+		((value >> 8) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 |
+		((value >> 16) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write data byte 3 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 |
+		((value >> 24) & 0x000000FF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+		MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+		((address >> 8) & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+		(address & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Wait for the trans to complete (MC417_MIRDY asserted). */
+	return mc417_wait_ready(dev);
+}
+
+int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value)
+{
+	int retval;
+	u32 regval;
+	u32 tempval;
+	u32 dataval;
+
+	/* Enable MC417 GPIO outputs except for MC417_MIRDY,
+	 * which is an input.
+	 */
+	cx_write(MC417_OEN, MC417_MIRDY);
+
+	/* Write address byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+		MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+		((address >> 8) & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Write address byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+		(address & 0xFF);
+	cx_write(MC417_RWD, regval);
+	regval |= MC417_MICS | MC417_MIWR;
+	cx_write(MC417_RWD, regval);
+
+	/* Wait for the trans to complete (MC417_MIRDY asserted). */
+	retval = mc417_wait_ready(dev);
+
+	/* switch the DAT0-7 GPIO[10:3] to input mode */
+	cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+	/* Read data byte 3 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+	cx_write(MC417_RWD, regval);
+
+	/* Transition RD to effect read transaction across bus. */
+	regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+	cx_write(MC417_RWD, regval);
+
+	/* Collect byte */
+	tempval = cx_read(MC417_RWD);
+	dataval = ((tempval & 0x000000FF) << 24);
+
+	/* Bring CS and RD high. */
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 2 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= ((tempval & 0x000000FF) << 16);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 1 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= ((tempval & 0x000000FF) << 8);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	/* Read data byte 0 */
+	regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+	cx_write(MC417_RWD, regval);
+	regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+	cx_write(MC417_RWD, regval);
+	tempval = cx_read(MC417_RWD);
+	dataval |= (tempval & 0x000000FF);
+	regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+	cx_write(MC417_RWD, regval);
+
+	*value  = dataval;
+
+	return retval;
+}
+
+void mc417_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+	u32 val;
+
+	/* Set the gpio value */
+	mc417_register_read(dev, 0x900C, &val);
+	val |= (mask & 0x000ffff);
+	mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+	u32 val;
+
+	/* Clear the gpio value */
+	mc417_register_read(dev, 0x900C, &val);
+	val &= ~(mask & 0x0000ffff);
+	mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+	u32 val;
+
+	/* Enable GPIO direction bits */
+	mc417_register_read(dev, 0x9020, &val);
+	if (asoutput)
+		val |= (mask & 0x0000ffff);
+	else
+		val &= ~(mask & 0x0000ffff);
+
+	mc417_register_write(dev, 0x9020, val);
+}
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+	switch (cmd) {
+	case CX2341X_ENC_PING_FW:
+		return  "PING_FW";
+	case CX2341X_ENC_START_CAPTURE:
+		return  "START_CAPTURE";
+	case CX2341X_ENC_STOP_CAPTURE:
+		return  "STOP_CAPTURE";
+	case CX2341X_ENC_SET_AUDIO_ID:
+		return  "SET_AUDIO_ID";
+	case CX2341X_ENC_SET_VIDEO_ID:
+		return  "SET_VIDEO_ID";
+	case CX2341X_ENC_SET_PCR_ID:
+		return  "SET_PCR_ID";
+	case CX2341X_ENC_SET_FRAME_RATE:
+		return  "SET_FRAME_RATE";
+	case CX2341X_ENC_SET_FRAME_SIZE:
+		return  "SET_FRAME_SIZE";
+	case CX2341X_ENC_SET_BIT_RATE:
+		return  "SET_BIT_RATE";
+	case CX2341X_ENC_SET_GOP_PROPERTIES:
+		return  "SET_GOP_PROPERTIES";
+	case CX2341X_ENC_SET_ASPECT_RATIO:
+		return  "SET_ASPECT_RATIO";
+	case CX2341X_ENC_SET_DNR_FILTER_MODE:
+		return  "SET_DNR_FILTER_MODE";
+	case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+		return  "SET_DNR_FILTER_PROPS";
+	case CX2341X_ENC_SET_CORING_LEVELS:
+		return  "SET_CORING_LEVELS";
+	case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+		return  "SET_SPATIAL_FILTER_TYPE";
+	case CX2341X_ENC_SET_VBI_LINE:
+		return  "SET_VBI_LINE";
+	case CX2341X_ENC_SET_STREAM_TYPE:
+		return  "SET_STREAM_TYPE";
+	case CX2341X_ENC_SET_OUTPUT_PORT:
+		return  "SET_OUTPUT_PORT";
+	case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+		return  "SET_AUDIO_PROPERTIES";
+	case CX2341X_ENC_HALT_FW:
+		return  "HALT_FW";
+	case CX2341X_ENC_GET_VERSION:
+		return  "GET_VERSION";
+	case CX2341X_ENC_SET_GOP_CLOSURE:
+		return  "SET_GOP_CLOSURE";
+	case CX2341X_ENC_GET_SEQ_END:
+		return  "GET_SEQ_END";
+	case CX2341X_ENC_SET_PGM_INDEX_INFO:
+		return  "SET_PGM_INDEX_INFO";
+	case CX2341X_ENC_SET_VBI_CONFIG:
+		return  "SET_VBI_CONFIG";
+	case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+		return  "SET_DMA_BLOCK_SIZE";
+	case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+		return  "GET_PREV_DMA_INFO_MB_10";
+	case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+		return  "GET_PREV_DMA_INFO_MB_9";
+	case CX2341X_ENC_SCHED_DMA_TO_HOST:
+		return  "SCHED_DMA_TO_HOST";
+	case CX2341X_ENC_INITIALIZE_INPUT:
+		return  "INITIALIZE_INPUT";
+	case CX2341X_ENC_SET_FRAME_DROP_RATE:
+		return  "SET_FRAME_DROP_RATE";
+	case CX2341X_ENC_PAUSE_ENCODER:
+		return  "PAUSE_ENCODER";
+	case CX2341X_ENC_REFRESH_INPUT:
+		return  "REFRESH_INPUT";
+	case CX2341X_ENC_SET_COPYRIGHT:
+		return  "SET_COPYRIGHT";
+	case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+		return  "SET_EVENT_NOTIFICATION";
+	case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+		return  "SET_NUM_VSYNC_LINES";
+	case CX2341X_ENC_SET_PLACEHOLDER:
+		return  "SET_PLACEHOLDER";
+	case CX2341X_ENC_MUTE_VIDEO:
+		return  "MUTE_VIDEO";
+	case CX2341X_ENC_MUTE_AUDIO:
+		return  "MUTE_AUDIO";
+	case CX2341X_ENC_MISC:
+		return  "MISC";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static int cx23885_mbox_func(void *priv,
+			     u32 command,
+			     int in,
+			     int out,
+			     u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct cx23885_dev *dev = priv;
+	unsigned long timeout;
+	u32 value, flag, retval = 0;
+	int i;
+
+	dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+		cmd_to_str(command));
+
+	/* this may not be 100% safe if we can't read any memory location
+	   without side effects */
+	mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+	if (value != 0x12345678) {
+		printk(KERN_ERR
+			"Firmware and/or mailbox pointer not initialized "
+			"or corrupted, signature = 0x%x, cmd = %s\n", value,
+			cmd_to_str(command));
+		return -1;
+	}
+
+	/* This read looks at 32 bits, but flag is only 8 bits.
+	 * Seems we also bail if CMD or TIMEOUT bytes are set???
+	 */
+	mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+	if (flag) {
+		printk(KERN_ERR "ERROR: Mailbox appears to be in use "
+			"(%x), cmd = %s\n", flag, cmd_to_str(command));
+		return -1;
+	}
+
+	flag |= 1; /* tell 'em we're working on it */
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	/* write command + args + fill remaining with zeros */
+	/* command code */
+	mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+	mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+		IVTV_API_STD_TIMEOUT); /* timeout */
+	for (i = 0; i < in; i++) {
+		mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+		dprintk(3, "API Input %d = %d\n", i, data[i]);
+	}
+	for (; i < CX2341X_MBOX_MAX_DATA; i++)
+		mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+	flag |= 3; /* tell 'em we're done writing */
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	/* wait for firmware to handle the API command */
+	timeout = jiffies + msecs_to_jiffies(10);
+	for (;;) {
+		mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+		if (0 != (flag & 4))
+			break;
+		if (time_after(jiffies, timeout)) {
+			printk(KERN_ERR "ERROR: API Mailbox timeout\n");
+			return -1;
+		}
+		udelay(10);
+	}
+
+	/* read output values */
+	for (i = 0; i < out; i++) {
+		mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+		dprintk(3, "API Output %d = %d\n", i, data[i]);
+	}
+
+	mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+	dprintk(3, "API result = %d\n", retval);
+
+	flag = 0;
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx23885_api_cmd(struct cx23885_dev *dev,
+			   u32 command,
+			   u32 inputcnt,
+			   u32 outputcnt,
+			   ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list vargs;
+	int i, err;
+
+	dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+	va_start(vargs, outputcnt);
+	for (i = 0; i < inputcnt; i++)
+		data[i] = va_arg(vargs, int);
+
+	err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data);
+	for (i = 0; i < outputcnt; i++) {
+		int *vptr = va_arg(vargs, int *);
+		*vptr = data[i];
+	}
+	va_end(vargs);
+
+	return err;
+}
+
+static int cx23885_find_mailbox(struct cx23885_dev *dev)
+{
+	u32 signature[4] = {
+		0x12345678, 0x34567812, 0x56781234, 0x78123456
+	};
+	int signaturecnt = 0;
+	u32 value;
+	int i;
+
+	dprintk(2, "%s()\n", __func__);
+
+	for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) {
+		mc417_memory_read(dev, i, &value);
+		if (value == signature[signaturecnt])
+			signaturecnt++;
+		else
+			signaturecnt = 0;
+		if (4 == signaturecnt) {
+			dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+			return i+1;
+		}
+	}
+	printk(KERN_ERR "Mailbox signature values not found!\n");
+	return -1;
+}
+
+static int cx23885_load_firmware(struct cx23885_dev *dev)
+{
+	static const unsigned char magic[8] = {
+		0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+	};
+	const struct firmware *firmware;
+	int i, retval = 0;
+	u32 value = 0;
+	u32 gpio_output = 0;
+	u32 gpio_value;
+	u32 checksum = 0;
+	u32 *dataptr;
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* Save GPIO settings before reset of APU */
+	retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+	retval |= mc417_memory_read(dev, 0x900C, &gpio_value);
+
+	retval  = mc417_register_write(dev,
+		IVTV_REG_VPU, 0xFFFFFFED);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_APU, 0);
+
+	if (retval != 0) {
+		printk(KERN_ERR "%s: Error with mc417_register_write\n",
+			__func__);
+		return -1;
+	}
+
+	retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME,
+				  &dev->pci->dev);
+
+	if (retval != 0) {
+		printk(KERN_ERR
+			"ERROR: Hotplug firmware request failed (%s).\n",
+			CX23885_FIRM_IMAGE_NAME);
+		printk(KERN_ERR "Please fix your hotplug setup, the board will "
+			"not work without firmware loaded!\n");
+		return -1;
+	}
+
+	if (firmware->size != CX23885_FIRM_IMAGE_SIZE) {
+		printk(KERN_ERR "ERROR: Firmware size mismatch "
+			"(have %zd, expected %d)\n",
+			firmware->size, CX23885_FIRM_IMAGE_SIZE);
+		release_firmware(firmware);
+		return -1;
+	}
+
+	if (0 != memcmp(firmware->data, magic, 8)) {
+		printk(KERN_ERR
+			"ERROR: Firmware magic mismatch, wrong file?\n");
+		release_firmware(firmware);
+		return -1;
+	}
+
+	/* transfer to the chip */
+	dprintk(2, "Loading firmware ...\n");
+	dataptr = (u32 *)firmware->data;
+	for (i = 0; i < (firmware->size >> 2); i++) {
+		value = *dataptr;
+		checksum += ~value;
+		if (mc417_memory_write(dev, i, value) != 0) {
+			printk(KERN_ERR "ERROR: Loading firmware failed!\n");
+			release_firmware(firmware);
+			return -1;
+		}
+		dataptr++;
+	}
+
+	/* read back to verify with the checksum */
+	dprintk(1, "Verifying firmware ...\n");
+	for (i--; i >= 0; i--) {
+		if (mc417_memory_read(dev, i, &value) != 0) {
+			printk(KERN_ERR "ERROR: Reading firmware failed!\n");
+			release_firmware(firmware);
+			return -1;
+		}
+		checksum -= ~value;
+	}
+	if (checksum) {
+		printk(KERN_ERR
+			"ERROR: Firmware load failed (checksum mismatch).\n");
+		release_firmware(firmware);
+		return -1;
+	}
+	release_firmware(firmware);
+	dprintk(1, "Firmware upload successful.\n");
+
+	retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+		IVTV_CMD_HW_BLOCKS_RST);
+
+	/* F/W power up disturbs the GPIOs, restore state */
+	retval |= mc417_register_write(dev, 0x9020, gpio_output);
+	retval |= mc417_register_write(dev, 0x900C, gpio_value);
+
+	retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+	retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+	/* Hardcoded GPIO's here */
+	retval |= mc417_register_write(dev, 0x9020, 0x4000);
+	retval |= mc417_register_write(dev, 0x900C, 0x4000);
+
+	mc417_register_read(dev, 0x9020, &gpio_output);
+	mc417_register_read(dev, 0x900C, &gpio_value);
+
+	if (retval < 0)
+		printk(KERN_ERR "%s: Error with mc417_register_write\n",
+			__func__);
+	return 0;
+}
+
+void cx23885_417_check_encoder(struct cx23885_dev *dev)
+{
+	u32 status, seq;
+
+	status = seq = 0;
+	cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+	dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx23885_codec_settings(struct cx23885_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	/* Dynamically change the height based on video standard */
+	if (dev->encodernorm.id & V4L2_STD_525_60)
+		dev->ts1.height = 480;
+	else
+		dev->ts1.height = 576;
+
+	/* assign frame size */
+	cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+				dev->ts1.height, dev->ts1.width);
+
+	dev->mpeg_params.width = dev->ts1.width;
+	dev->mpeg_params.height = dev->ts1.height;
+	dev->mpeg_params.is_50hz =
+		(dev->encodernorm.id & V4L2_STD_625_50) != 0;
+
+	cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params);
+
+	cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+	cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
+{
+	int version;
+	int retval;
+	u32 i, data[7];
+
+	dprintk(1, "%s()\n", __func__);
+
+	retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+	if (retval < 0) {
+		dprintk(2, "%s() PING OK\n", __func__);
+		retval = cx23885_load_firmware(dev);
+		if (retval < 0) {
+			printk(KERN_ERR "%s() f/w load failed\n", __func__);
+			return retval;
+		}
+		retval = cx23885_find_mailbox(dev);
+		if (retval < 0) {
+			printk(KERN_ERR "%s() mailbox < 0, error\n",
+				__func__);
+			return -1;
+		}
+		dev->cx23417_mailbox = retval;
+		retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+		if (retval < 0) {
+			printk(KERN_ERR
+				"ERROR: cx23417 firmware ping failed!\n");
+			return -1;
+		}
+		retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+			&version);
+		if (retval < 0) {
+			printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+				"version failed!\n");
+			return -1;
+		}
+		dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+		msleep(200);
+	}
+
+	cx23885_codec_settings(dev);
+	msleep(60);
+
+	cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+		CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115);
+	cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+		CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0);
+
+	/* Setup to capture VBI */
+	data[0] = 0x0001BD00;
+	data[1] = 1;          /* frames per interrupt */
+	data[2] = 4;          /* total bufs */
+	data[3] = 0x91559155; /* start codes */
+	data[4] = 0x206080C0; /* stop codes */
+	data[5] = 6;          /* lines */
+	data[6] = 64;         /* BPL */
+
+	cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+		data[2], data[3], data[4], data[5], data[6]);
+
+	for (i = 2; i <= 24; i++) {
+		int valid;
+
+		valid = ((i >= 19) && (i <= 21));
+		cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+				valid, 0 , 0, 0);
+		cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+				i | 0x80000000, valid, 0, 0, 0);
+	}
+
+	cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE);
+	msleep(60);
+
+	/* initialize the video input */
+	cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+	msleep(60);
+
+	/* Enable VIP style pixel invalidation so we work with scaled mode */
+	mc417_memory_write(dev, 2120, 0x00000080);
+
+	/* start capturing to the host interface */
+	if (startencoder) {
+		cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+			CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE);
+		msleep(10);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+	unsigned int *count, unsigned int *size)
+{
+	struct cx23885_fh *fh = q->priv_data;
+
+	fh->dev->ts1.ts_packet_size  = mpeglinesize;
+	fh->dev->ts1.ts_packet_count = mpeglines;
+
+	*size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+	*count = mpegbufs;
+
+	return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+	struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct cx23885_fh *fh = q->priv_data;
+	return cx23885_buf_prepare(q, &fh->dev->ts1,
+		(struct cx23885_buffer *)vb,
+		field);
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	struct cx23885_fh *fh = q->priv_data;
+	cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb);
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static struct videobuf_queue_ops cx23885_qops = {
+	.buf_setup    = bb_buf_setup,
+	.buf_prepare  = bb_buf_prepare,
+	.buf_queue    = bb_buf_queue,
+	.buf_release  = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+	cx2341x_mpeg_ctrls,
+	NULL
+};
+
+static int cx23885_queryctrl(struct cx23885_dev *dev,
+	struct v4l2_queryctrl *qctrl)
+{
+	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+	if (qctrl->id == 0)
+		return -EINVAL;
+
+	/* MPEG V4L2 controls */
+	if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+		qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+	return 0;
+}
+
+static int cx23885_querymenu(struct cx23885_dev *dev,
+	struct v4l2_querymenu *qmenu)
+{
+	struct v4l2_queryctrl qctrl;
+
+	qctrl.id = qmenu->id;
+	cx23885_queryctrl(dev, &qctrl);
+	return v4l2_ctrl_query_menu(qmenu, &qctrl,
+		cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	call_all(dev, core, g_std, id);
+
+	return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
+		if (*id & cx23885_tvnorms[i].id)
+			break;
+	if (i == ARRAY_SIZE(cx23885_tvnorms))
+		return -EINVAL;
+	dev->encodernorm = cx23885_tvnorms[i];
+
+	/* Have the drier core notify the subdevices */
+	mutex_lock(&dev->lock);
+	cx23885_set_tvnorm(dev, *id);
+	mutex_unlock(&dev->lock);
+
+	return 0;
+}
+
+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", __func__);
+	return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	return cx23885_get_input(file, priv, i);
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	if (UNSET == dev->tuner_type)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+	strcpy(t->name, "Television");
+	call_all(dev, tuner, g_tuner, t);
+
+	dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	if (UNSET == dev->tuner_type)
+		return -EINVAL;
+
+	/* Update the A/V core */
+	call_all(dev, tuner, s_tuner, t);
+
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	if (UNSET == dev->tuner_type)
+		return -EINVAL;
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency = dev->freq;
+
+	call_all(dev, tuner, g_frequency, f);
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	return cx23885_set_frequency(file, priv, f);
+}
+
+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_querycap(struct file *file, void  *priv,
+				struct v4l2_capability *cap)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+	struct cx23885_tsport  *tsport = &dev->ts1;
+
+	strlcpy(cap->driver, dev->name, sizeof(cap->driver));
+	strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
+		sizeof(cap->card));
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+	cap->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE     |
+		V4L2_CAP_STREAMING     |
+		0;
+	if (UNSET != dev->tuner_type)
+		cap->capabilities |= V4L2_CAP_TUNER;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+					struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = dev->ts1.width;
+	f->fmt.pix.height       = dev->ts1.height;
+	f->fmt.pix.field        = fh->mpegq.field;
+	dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+		dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+		dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+	return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+				struct v4l2_requestbuffers *p)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+
+	return videobuf_reqbufs(&fh->mpegq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+				struct v4l2_buffer *p)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+
+	return videobuf_querybuf(&fh->mpegq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+				struct v4l2_buffer *p)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+
+	return videobuf_qbuf(&fh->mpegq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx23885_fh  *fh  = priv;
+
+	return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+				enum v4l2_buf_type i)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+
+	return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+
+	return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+	return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+	struct cx2341x_mpeg_params p;
+	int err;
+
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	p = dev->mpeg_params;
+	err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
+
+	if (err == 0) {
+		err = cx2341x_update(dev, cx23885_mbox_func,
+			&dev->mpeg_params, &p);
+		dev->mpeg_params = p;
+	}
+	return err;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+	struct cx2341x_mpeg_params p;
+	int err;
+
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	p = dev->mpeg_params;
+	err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+	return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+	char name[32 + 2];
+
+	snprintf(name, sizeof(name), "%s/2", dev->name);
+	printk(KERN_INFO
+		"%s/2: ============  START LOG STATUS  ============\n",
+	       dev->name);
+	call_all(dev, core, log_status);
+	cx2341x_log_status(&dev->mpeg_params, name);
+	printk(KERN_INFO
+		"%s/2: =============  END LOG STATUS  =============\n",
+	       dev->name);
+	return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+				struct v4l2_querymenu *a)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+
+	return cx23885_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *c)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+
+	return cx23885_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+	struct cx23885_dev *dev = video_drvdata(file);
+	struct cx23885_fh *fh;
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (!fh)
+		return -ENOMEM;
+
+	file->private_data = fh;
+	fh->dev      = dev;
+
+	videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops,
+			    &dev->pci->dev, &dev->ts1.slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx23885_buffer),
+			    fh, NULL);
+	return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+	struct cx23885_fh  *fh  = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* FIXME: Review this crap */
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+			/* stop mpeg capture */
+			cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+				CX23885_END_NOW, CX23885_MPEG_CAPTURE,
+				CX23885_RAW_BITS_NONE);
+
+			msleep(500);
+			cx23885_417_check_encoder(dev);
+
+			cx23885_cancel_buffers(&fh->dev->ts1);
+		}
+	}
+
+	if (fh->mpegq.streaming)
+		videobuf_streamoff(&fh->mpegq);
+	if (fh->mpegq.reading)
+		videobuf_read_stop(&fh->mpegq);
+
+	videobuf_mmap_free(&fh->mpegq);
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+	size_t count, loff_t *ppos)
+{
+	struct cx23885_fh *fh = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+	/* Start mpeg encoder on first read. */
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+			if (cx23885_initialize_codec(dev, 1) < 0)
+				return -EINVAL;
+		}
+	}
+
+	return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+				    file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	struct cx23885_fh *fh = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	dprintk(2, "%s\n", __func__);
+
+	return videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cx23885_fh *fh = file->private_data;
+	struct cx23885_dev *dev = fh->dev;
+
+	dprintk(2, "%s()\n", __func__);
+
+	return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+	.owner	       = THIS_MODULE,
+	.open	       = mpeg_open,
+	.release       = mpeg_release,
+	.read	       = mpeg_read,
+	.poll          = mpeg_poll,
+	.mmap	       = mpeg_mmap,
+	.ioctl	       = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+	.vidioc_querystd	 = vidioc_g_std,
+	.vidioc_g_std		 = vidioc_g_std,
+	.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_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_g_ctrl		 = vidioc_g_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs		 = vidioc_reqbufs,
+	.vidioc_querybuf	 = vidioc_querybuf,
+	.vidioc_qbuf		 = vidioc_qbuf,
+	.vidioc_dqbuf		 = vidioc_dqbuf,
+	.vidioc_streamon	 = vidioc_streamon,
+	.vidioc_streamoff	 = vidioc_streamoff,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_log_status	 = vidioc_log_status,
+	.vidioc_querymenu	 = vidioc_querymenu,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+	.vidioc_g_chip_ident	 = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register	 = cx23885_g_register,
+	.vidioc_s_register	 = cx23885_s_register,
+#endif
+};
+
+static struct video_device cx23885_mpeg_template = {
+	.name          = "cx23885",
+	.fops          = &mpeg_fops,
+	.ioctl_ops     = &mpeg_ioctl_ops,
+	.tvnorms       = CX23885_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+void cx23885_417_unregister(struct cx23885_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	if (dev->v4l_device) {
+		if (video_is_registered(dev->v4l_device))
+			video_unregister_device(dev->v4l_device);
+		else
+			video_device_release(dev->v4l_device);
+		dev->v4l_device = NULL;
+	}
+}
+
+static struct video_device *cx23885_video_dev_alloc(
+	struct cx23885_tsport *tsport,
+	struct pci_dev *pci,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+	struct cx23885_dev *dev = tsport->dev;
+
+	dprintk(1, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+		cx23885_boards[tsport->dev->board].name, type);
+	vfd->parent  = &pci->dev;
+	vfd->release = video_device_release;
+	return vfd;
+}
+
+int cx23885_417_register(struct cx23885_dev *dev)
+{
+	/* FIXME: Port1 hardcoded here */
+	int err = -ENODEV;
+	struct cx23885_tsport *tsport = &dev->ts1;
+
+	dprintk(1, "%s()\n", __func__);
+
+	if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER)
+		return err;
+
+	/* Set default TV standard */
+	dev->encodernorm = cx23885_tvnorms[0];
+
+	if (dev->encodernorm.id & V4L2_STD_525_60)
+		tsport->height = 480;
+	else
+		tsport->height = 576;
+
+	tsport->width = 720;
+	cx2341x_fill_defaults(&dev->mpeg_params);
+
+	dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+	/* Allocate and initialize V4L video device */
+	dev->v4l_device = cx23885_video_dev_alloc(tsport,
+		dev->pci, &cx23885_mpeg_template, "mpeg");
+	video_set_drvdata(dev->v4l_device, dev);
+	err = video_register_device(dev->v4l_device,
+		VFL_TYPE_GRABBER, -1);
+	if (err < 0) {
+		printk(KERN_INFO "%s: can't register mpeg device\n", dev->name);
+		return err;
+	}
+
+	printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+	       dev->name, video_device_node_name(dev->v4l_device));
+
+	/* ST: Configure the encoder paramaters, but don't begin
+	 * encoding, this resolves an issue where the first time the
+	 * encoder is started video can be choppy.
+	 */
+	cx23885_initialize_codec(dev, 0);
+
+	return 0;
+}
+
+MODULE_FIRMWARE(CX23885_FIRM_IMAGE_NAME);
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
new file mode 100644
index 000000000000..795169237e70
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -0,0 +1,535 @@
+/*
+ *
+ *  Support for CX23885 analog audio capture
+ *
+ *    (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>
+ *    Adapted from cx88-alsa.c
+ *    (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <asm/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <sound/tlv.h>
+
+
+#include "cx23885.h"
+#include "cx23885-reg.h"
+
+#define AUDIO_SRAM_CHANNEL	SRAM_CH07
+
+#define dprintk(level, fmt, arg...)	if (audio_debug >= level) \
+	printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg)
+
+#define dprintk_core(level, fmt, arg...)	if (audio_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg)
+
+/****************************************************************************
+			Module global static vars
+ ****************************************************************************/
+
+static unsigned int disable_analog_audio;
+module_param(disable_analog_audio, int, 0644);
+MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
+
+/****************************************************************************
+			Board specific funtions
+ ****************************************************************************/
+
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1       (1 <<  0)
+#define AUD_INT_UP_RISCI1       (1 <<  1)
+#define AUD_INT_RDS_DN_RISCI1   (1 <<  2)
+#define AUD_INT_DN_RISCI2       (1 <<  4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2       (1 <<  5)
+#define AUD_INT_RDS_DN_RISCI2   (1 <<  6)
+#define AUD_INT_DN_SYNC         (1 << 12)
+#define AUD_INT_UP_SYNC         (1 << 13)
+#define AUD_INT_RDS_DN_SYNC     (1 << 14)
+#define AUD_INT_OPC_ERR         (1 << 16)
+#define AUD_INT_BER_IRQ         (1 << 20)
+#define AUD_INT_MCHG_IRQ        (1 << 21)
+#define GP_COUNT_CONTROL_RESET	0x3
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)
+{
+	struct cx23885_audio_buffer *buf = chip->buf;
+	struct cx23885_dev *dev  = chip->dev;
+	struct sram_channel *audio_ch =
+		&dev->sram_channels[AUDIO_SRAM_CHANNEL];
+
+	dprintk(1, "%s()\n", __func__);
+
+	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+	cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+	/* setup fifo + format - out channel */
+	cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,
+		buf->risc.dma);
+
+	/* sets bpl size */
+	cx_write(AUD_INT_A_LNGTH, buf->bpl);
+
+	/* This is required to get good audio (1 seems to be ok) */
+	cx_write(AUD_INT_A_MODE, 1);
+
+	/* reset counter */
+	cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+	atomic_set(&chip->count, 0);
+
+	dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+		"byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,
+		chip->num_periods, buf->bpl * chip->num_periods);
+
+	/* Enables corresponding bits at AUD_INT_STAT */
+	cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+				    AUD_INT_DN_RISCI1);
+
+	/* Clean any pending interrupt bits already set */
+	cx_write(AUDIO_INT_INT_STAT, ~0);
+
+	/* enable audio irqs */
+	cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+	/* start dma */
+	cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+	cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and
+					  RISC enable */
+	if (audio_debug)
+		cx23885_sram_channel_dump(chip->dev, audio_ch);
+
+	return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)
+{
+	struct cx23885_dev *dev = chip->dev;
+	dprintk(1, "Stopping audio DMA\n");
+
+	/* stop dma */
+	cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+	/* disable irqs */
+	cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+	cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+				    AUD_INT_DN_RISCI1);
+
+	if (audio_debug)
+		cx23885_sram_channel_dump(chip->dev,
+			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+
+	return 0;
+}
+
+/*
+ * BOARD Specific: Handles audio IRQ
+ */
+int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
+{
+	struct cx23885_audio_dev *chip = dev->audio_dev;
+
+	if (0 == (status & mask))
+		return 0;
+
+	cx_write(AUDIO_INT_INT_STAT, status);
+
+	/* risc op code error */
+	if (status & AUD_INT_OPC_ERR) {
+		printk(KERN_WARNING "%s/1: Audio risc op code error\n",
+			dev->name);
+		cx_clear(AUD_INT_DMA_CTL, 0x11);
+		cx23885_sram_channel_dump(dev,
+			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+	}
+	if (status & AUD_INT_DN_SYNC) {
+		dprintk(1, "Downstream sync error\n");
+		cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+		return 1;
+	}
+	/* risc1 downstream */
+	if (status & AUD_INT_DN_RISCI1) {
+		atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));
+		snd_pcm_period_elapsed(chip->substream);
+	}
+	/* FIXME: Any other status should deserve a special handling? */
+
+	return 1;
+}
+
+static int dsp_buffer_free(struct cx23885_audio_dev *chip)
+{
+	BUG_ON(!chip->dma_size);
+
+	dprintk(2, "Freeing buffer\n");
+	videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+	videobuf_dma_free(chip->dma_risc);
+	btcx_riscmem_free(chip->pci, &chip->buf->risc);
+	kfree(chip->buf);
+
+	chip->dma_risc = NULL;
+	chip->dma_size = 0;
+
+	return 0;
+}
+
+/****************************************************************************
+				ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE	4096
+
+static struct snd_pcm_hardware snd_cx23885_digital_hw = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* Analog audio output will be full of clicks and pops if there
+	   are not exactly four lines in the SRAM FIFO buffer.  */
+	.period_bytes_min = DEFAULT_FIFO_SIZE/4,
+	.period_bytes_max = DEFAULT_FIFO_SIZE/4,
+	.periods_min = 1,
+	.periods_max = 1024,
+	.buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	if (!chip) {
+		printk(KERN_ERR "BUG: cx23885 can't find device struct."
+				" Can't proceed with open\n");
+		return -ENODEV;
+	}
+
+	err = snd_pcm_hw_constraint_pow2(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
+		goto _error;
+
+	chip->substream = substream;
+
+	runtime->hw = snd_cx23885_digital_hw;
+
+	if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+		DEFAULT_FIFO_SIZE) {
+		unsigned int bpl = chip->dev->
+			sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;
+		bpl &= ~7; /* must be multiple of 8 */
+		runtime->hw.period_bytes_min = bpl;
+		runtime->hw.period_bytes_max = bpl;
+	}
+
+	return 0;
+_error:
+	dprintk(1, "Error opening PCM!\n");
+	return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx23885_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *hw_params)
+{
+	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct videobuf_dmabuf *dma;
+
+	struct cx23885_audio_buffer *buf;
+	int ret;
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	chip->period_size = params_period_bytes(hw_params);
+	chip->num_periods = params_periods(hw_params);
+	chip->dma_size = chip->period_size * params_periods(hw_params);
+
+	BUG_ON(!chip->dma_size);
+	BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (NULL == buf)
+		return -ENOMEM;
+
+	buf->bpl = chip->period_size;
+
+	dma = &buf->dma;
+	videobuf_dma_init(dma);
+	ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+	if (ret < 0)
+		goto error;
+
+	ret = videobuf_dma_map(&chip->pci->dev, dma);
+	if (ret < 0)
+		goto error;
+
+	ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+				   chip->period_size, chip->num_periods, 1);
+	if (ret < 0)
+		goto error;
+
+	/* Loop back to start of program */
+	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->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+	chip->buf = buf;
+	chip->dma_risc = dma;
+
+	substream->runtime->dma_area = chip->dma_risc->vaddr;
+	substream->runtime->dma_bytes = chip->dma_size;
+	substream->runtime->dma_addr = 0;
+
+	return 0;
+
+error:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx23885_hw_free(struct snd_pcm_substream *substream)
+{
+
+	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx23885_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,
+	int cmd)
+{
+	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+	int err;
+
+	/* Local interrupts are already disabled by ALSA */
+	spin_lock(&chip->lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = cx23885_start_audio_dma(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err = cx23885_stop_audio_dma(chip);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	spin_unlock(&chip->lock);
+
+	return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx23885_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	u16 count;
+
+	count = atomic_read(&chip->count);
+
+	return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx23885_page(struct snd_pcm_substream *substream,
+				unsigned long offset)
+{
+	void *pageptr = substream->runtime->dma_area + offset;
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx23885_pcm_ops = {
+	.open = snd_cx23885_pcm_open,
+	.close = snd_cx23885_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_cx23885_hw_params,
+	.hw_free = snd_cx23885_hw_free,
+	.prepare = snd_cx23885_prepare,
+	.trigger = snd_cx23885_card_trigger,
+	.pointer = snd_cx23885_pointer,
+	.page = snd_cx23885_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,
+	char *name)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = chip;
+	strcpy(pcm->name, name);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);
+
+	return 0;
+}
+
+/****************************************************************************
+			Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)
+{
+	struct snd_card *card;
+	struct cx23885_audio_dev *chip;
+	int err;
+
+	if (disable_analog_audio)
+		return NULL;
+
+	if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {
+		printk(KERN_WARNING "%s(): Missing SRAM channel configuration "
+			"for analog TV Audio\n", __func__);
+		return NULL;
+	}
+
+	err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);
+	if (err < 0)
+		goto error;
+
+	chip = (struct cx23885_audio_dev *) card->private_data;
+	chip->dev = dev;
+	chip->pci = dev->pci;
+	chip->card = card;
+	spin_lock_init(&chip->lock);
+
+	snd_card_set_dev(card, &dev->pci->dev);
+
+	err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");
+	if (err < 0)
+		goto error;
+
+	strcpy(card->driver, "CX23885");
+	sprintf(card->shortname, "Conexant CX23885");
+	sprintf(card->longname, "%s at %s", card->shortname, dev->name);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	dprintk(0, "registered ALSA audio device\n");
+
+	return chip;
+
+error:
+	snd_card_free(card);
+	printk(KERN_ERR "%s(): Failed to register analog "
+			"audio adapter\n", __func__);
+
+	return NULL;
+}
+
+/*
+ * ALSA destructor
+ */
+void cx23885_audio_unregister(struct cx23885_dev *dev)
+{
+	struct cx23885_audio_dev *chip = dev->audio_dev;
+
+	snd_card_free(chip->card);
+}
diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c
new file mode 100644
index 000000000000..134ebddd860f
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-av.c
@@ -0,0 +1,35 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  AV device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+	struct cx23885_dev *dev =
+			   container_of(work, struct cx23885_dev, cx25840_work);
+	bool handled;
+
+	v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+			 PCI_MSK_AV_CORE, &handled);
+	cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
diff --git a/drivers/media/pci/cx23885/cx23885-av.h b/drivers/media/pci/cx23885/cx23885-av.h
new file mode 100644
index 000000000000..d2915c3e53a2
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-av.h
@@ -0,0 +1,27 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  AV device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
new file mode 100644
index 000000000000..39a4a4b9ed7e
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -0,0 +1,1694 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <media/cx25840.h>
+#include <linux/firmware.h>
+#include <misc/altera.h>
+
+#include "cx23885.h"
+#include "tuner-xc2028.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "altera-ci.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "cx23888-ir.h"
+
+static unsigned int netup_card_rev = 4;
+module_param(netup_card_rev, int, 0644);
+MODULE_PARM_DESC(netup_card_rev,
+		"NetUP Dual DVB-T/C CI card revision");
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+		 "Enable integrated IR controller for supported\n"
+		 "\t\t    CX2388[57] boards that are wired for it:\n"
+		 "\t\t\tHVR-1250 (reported safe)\n"
+		 "\t\t\tTerraTec Cinergy T PCIe Dual (not well tested, appears to be safe)\n"
+		 "\t\t\tTeVii S470 (reported unsafe)\n"
+		 "\t\t    This can cause an interrupt storm with some cards.\n"
+		 "\t\t    Default: 0 [Disabled]");
+
+/* ------------------------------------------------------------------ */
+/* board config info                                                  */
+
+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,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE2,
+			.vmux   = 1,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE3,
+			.vmux   = 2,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE4,
+			.vmux   = 3,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1800lp] = {
+		.name		= "Hauppauge WinTV-HVR1800lp",
+		.portc		= CX23885_MPEG_DVB,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xff00,
+		}, {
+			.type   = CX23885_VMUX_DEBUG,
+			.vmux   = 0,
+			.gpio0  = 0xff01,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xff02,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xff02,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1800] = {
+		.name		= "Hauppauge WinTV-HVR1800",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_ENCODER,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_PHILIPS_TDA8290,
+		.tuner_addr	= 0x42, /* 0x84 >> 1 */
+		.tuner_bus	= 1,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1,
+			.amux   = CX25840_AUDIO8,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN6_CH1,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1250] = {
+		.name		= "Hauppauge WinTV-HVR1250",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+#ifdef MT2131_NO_ANALOG_SUPPORT_YET
+		.tuner_type	= TUNER_PHILIPS_TDA8290,
+		.tuner_addr	= 0x42, /* 0x84 >> 1 */
+		.tuner_bus	= 1,
+#endif
+		.force_bff	= 1,
+		.input          = {{
+#ifdef MT2131_NO_ANALOG_SUPPORT_YET
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1,
+			.amux   = CX25840_AUDIO8,
+			.gpio0  = 0xff00,
+		}, {
+#endif
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN6_CH1,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0xff02,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0xff02,
+		} },
+	},
+	[CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP] = {
+		.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",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_XC2028,
+		.tuner_addr	= 0x61, /* 0xc2 >> 1 */
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN6_CH1,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.gpio0  = 0,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1200] = {
+		.name		= "Hauppauge WinTV-HVR1200",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1700] = {
+		.name		= "Hauppauge WinTV-HVR1700",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1400] = {
+		.name		= "Hauppauge WinTV-HVR1400",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
+		.name		= "DViCO FusionHDTV7 Dual Express",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = {
+		.name		= "DViCO FusionHDTV DVB-T Dual Express",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = {
+		.name		= "Leadtek Winfast PxDVR3200 H",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
+		.name		= "Leadtek Winfast PxDVR3200 H XC4000",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_XC4000,
+		.tuner_addr	= 0x61,
+		.radio_type	= UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input		= {{
+			.type	= CX23885_VMUX_TELEVISION,
+			.vmux	= CX25840_VIN2_CH1 |
+				  CX25840_VIN5_CH2 |
+				  CX25840_NONE0_CH3,
+		}, {
+			.type	= CX23885_VMUX_COMPOSITE1,
+			.vmux	= CX25840_COMPOSITE1,
+		}, {
+			.type	= CX23885_VMUX_SVIDEO,
+			.vmux	= CX25840_SVIDEO_LUMA3 |
+				  CX25840_SVIDEO_CHROMA4,
+		}, {
+			.type	= CX23885_VMUX_COMPONENT,
+			.vmux	= CX25840_VIN7_CH1 |
+				  CX25840_VIN6_CH2 |
+				  CX25840_VIN8_CH3 |
+				  CX25840_COMPONENT_ON,
+		} },
+	},
+	[CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+		.name		= "Compro VideoMate E650F",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_TBS_6920] = {
+		.name		= "TurboSight TBS 6920",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_TEVII_S470] = {
+		.name		= "TeVii S470",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBWORLD_2005] = {
+		.name		= "DVBWorld DVB-S2 2005",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
+		.ci_type	= 1,
+		.name		= "NetUP Dual DVB-S2 CI",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1270] = {
+		.name		= "Hauppauge WinTV-HVR1270",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1275] = {
+		.name		= "Hauppauge WinTV-HVR1275",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1255] = {
+		.name		= "Hauppauge WinTV-HVR1255",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= 0x42, /* 0x84 >> 1 */
+		.force_bff	= 1,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1 |
+					CX25840_DIF_ON,
+			.amux   = CX25840_AUDIO8,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN6_CH1,
+			.amux   = CX25840_AUDIO7,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.amux   = CX25840_AUDIO7,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1255_22111] = {
+		.name		= "Hauppauge WinTV-HVR1255",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= 0x42, /* 0x84 >> 1 */
+		.force_bff	= 1,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1 |
+					CX25840_DIF_ON,
+			.amux   = CX25840_AUDIO8,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.amux   = CX25840_AUDIO7,
+		} },
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1210] = {
+		.name		= "Hauppauge WinTV-HVR1210",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_MYGICA_X8506] = {
+		.name		= "Mygica X8506 DMB-TH",
+		.tuner_type = TUNER_XC5000,
+		.tuner_addr = 0x61,
+		.tuner_bus	= 1,
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_DVB,
+		.input		= {
+			{
+				.type   = CX23885_VMUX_TELEVISION,
+				.vmux   = CX25840_COMPOSITE2,
+			},
+			{
+				.type   = CX23885_VMUX_COMPOSITE1,
+				.vmux   = CX25840_COMPOSITE8,
+			},
+			{
+				.type   = CX23885_VMUX_SVIDEO,
+				.vmux   = CX25840_SVIDEO_LUMA3 |
+						CX25840_SVIDEO_CHROMA4,
+			},
+			{
+				.type   = CX23885_VMUX_COMPONENT,
+				.vmux   = CX25840_COMPONENT_ON |
+					CX25840_VIN1_CH1 |
+					CX25840_VIN6_CH2 |
+					CX25840_VIN7_CH3,
+			},
+		},
+	},
+	[CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
+		.name		= "Magic-Pro ProHDTV Extreme 2",
+		.tuner_type = TUNER_XC5000,
+		.tuner_addr = 0x61,
+		.tuner_bus	= 1,
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_DVB,
+		.input		= {
+			{
+				.type   = CX23885_VMUX_TELEVISION,
+				.vmux   = CX25840_COMPOSITE2,
+			},
+			{
+				.type   = CX23885_VMUX_COMPOSITE1,
+				.vmux   = CX25840_COMPOSITE8,
+			},
+			{
+				.type   = CX23885_VMUX_SVIDEO,
+				.vmux   = CX25840_SVIDEO_LUMA3 |
+						CX25840_SVIDEO_CHROMA4,
+			},
+			{
+				.type   = CX23885_VMUX_COMPONENT,
+				.vmux   = CX25840_COMPONENT_ON |
+					CX25840_VIN1_CH1 |
+					CX25840_VIN6_CH2 |
+					CX25840_VIN7_CH3,
+			},
+		},
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1850] = {
+		.name		= "Hauppauge WinTV-HVR1850",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_ENCODER,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_ABSENT,
+		.tuner_addr	= 0x42, /* 0x84 >> 1 */
+		.force_bff	= 1,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN5_CH2 |
+					CX25840_VIN2_CH1 |
+					CX25840_DIF_ON,
+			.amux   = CX25840_AUDIO8,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN6_CH1,
+			.amux   = CX25840_AUDIO7,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   =	CX25840_VIN7_CH3 |
+					CX25840_VIN4_CH2 |
+					CX25840_VIN8_CH1 |
+					CX25840_SVIDEO_ON,
+			.amux   = CX25840_AUDIO7,
+		} },
+	},
+	[CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
+		.name		= "Compro VideoMate E800",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_HAUPPAUGE_HVR1290] = {
+		.name		= "Hauppauge WinTV-HVR1290",
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_MYGICA_X8558PRO] = {
+		.name		= "Mygica X8558 PRO DMB-TH",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = {
+		.name           = "LEADTEK WinFast PxTV1200",
+		.porta          = CX23885_ANALOG_VIDEO,
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.tuner_bus	= 1,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   = CX25840_VIN2_CH1 |
+				  CX25840_VIN5_CH2 |
+				  CX25840_NONE0_CH3,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   = CX25840_COMPOSITE1,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   = CX25840_SVIDEO_LUMA3 |
+				  CX25840_SVIDEO_CHROMA4,
+		}, {
+			.type   = CX23885_VMUX_COMPONENT,
+			.vmux   = CX25840_VIN7_CH1 |
+				  CX25840_VIN6_CH2 |
+				  CX25840_VIN8_CH3 |
+				  CX25840_COMPONENT_ON,
+		} },
+	},
+	[CX23885_BOARD_GOTVIEW_X5_3D_HYBRID] = {
+		.name		= "GoTView X5 3D Hybrid",
+		.tuner_type	= TUNER_XC5000,
+		.tuner_addr	= 0x64,
+		.tuner_bus	= 1,
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_DVB,
+		.input          = {{
+			.type   = CX23885_VMUX_TELEVISION,
+			.vmux   = CX25840_VIN2_CH1 |
+				  CX25840_VIN5_CH2,
+			.gpio0	= 0x02,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   = CX23885_VMUX_COMPOSITE1,
+		}, {
+			.type   = CX23885_VMUX_SVIDEO,
+			.vmux   = CX25840_SVIDEO_LUMA3 |
+				  CX25840_SVIDEO_CHROMA4,
+		} },
+	},
+	[CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = {
+		.ci_type	= 2,
+		.name		= "NetUP Dual DVB-T/C-CI RF",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+		.num_fds_portb	= 2,
+		.num_fds_portc	= 2,
+		.tuner_type	= TUNER_XC5000,
+		.tuner_addr	= 0x64,
+		.input          = { {
+				.type   = CX23885_VMUX_TELEVISION,
+				.vmux   = CX25840_COMPOSITE1,
+		} },
+	},
+	[CX23885_BOARD_MPX885] = {
+		.name		= "MPX-885",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.input          = {{
+			.type   = CX23885_VMUX_COMPOSITE1,
+			.vmux   = CX25840_COMPOSITE1,
+			.amux   = CX25840_AUDIO6,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE2,
+			.vmux   = CX25840_COMPOSITE2,
+			.amux   = CX25840_AUDIO6,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE3,
+			.vmux   = CX25840_COMPOSITE3,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0,
+		}, {
+			.type   = CX23885_VMUX_COMPOSITE4,
+			.vmux   = CX25840_COMPOSITE4,
+			.amux   = CX25840_AUDIO7,
+			.gpio0  = 0,
+		} },
+	},
+	[CX23885_BOARD_MYGICA_X8507] = {
+		.name		= "Mygica X8507",
+		.tuner_type = TUNER_XC5000,
+		.tuner_addr = 0x61,
+		.tuner_bus	= 1,
+		.porta		= CX23885_ANALOG_VIDEO,
+		.input		= {
+			{
+				.type   = CX23885_VMUX_TELEVISION,
+				.vmux   = CX25840_COMPOSITE2,
+				.amux   = CX25840_AUDIO8,
+			},
+			{
+				.type   = CX23885_VMUX_COMPOSITE1,
+				.vmux   = CX25840_COMPOSITE8,
+			},
+			{
+				.type   = CX23885_VMUX_SVIDEO,
+				.vmux   = CX25840_SVIDEO_LUMA3 |
+						CX25840_SVIDEO_CHROMA4,
+			},
+			{
+				.type   = CX23885_VMUX_COMPONENT,
+				.vmux   = CX25840_COMPONENT_ON |
+					CX25840_VIN1_CH1 |
+					CX25840_VIN6_CH2 |
+					CX25840_VIN7_CH3,
+			},
+		},
+	},
+	[CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL] = {
+		.name		= "TerraTec Cinergy T PCIe Dual",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_TEVII_S471] = {
+		.name		= "TeVii S471",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_PROF_8000] = {
+		.name		= "Prof Revolution DVB-S2 8000",
+		.portb		= CX23885_MPEG_DVB,
+	}
+};
+const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs                                                  */
+
+struct cx23885_subid cx23885_subids[] = {
+	{
+		.subvendor = 0x0070,
+		.subdevice = 0x3400,
+		.card      = CX23885_BOARD_UNKNOWN,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x7600,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1800lp,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x7800,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1800,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x7801,
+		.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,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x71d1,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1200,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x71d3,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1200,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8101,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1700,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8010,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1400,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xd618,
+		.card      = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb78,
+		.card      = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6681,
+		.card      = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f39,
+		.card	   = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
+	}, {
+		.subvendor = 0x185b,
+		.subdevice = 0xe800,
+		.card      = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
+	}, {
+		.subvendor = 0x6920,
+		.subdevice = 0x8888,
+		.card      = CX23885_BOARD_TBS_6920,
+	}, {
+		.subvendor = 0xd470,
+		.subdevice = 0x9022,
+		.card      = CX23885_BOARD_TEVII_S470,
+	}, {
+		.subvendor = 0x0001,
+		.subdevice = 0x2005,
+		.card      = CX23885_BOARD_DVBWORLD_2005,
+	}, {
+		.subvendor = 0x1b55,
+		.subdevice = 0x2a2c,
+		.card      = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2211,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1270,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2215,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1275,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x221d,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1275,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2251,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1255,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2259,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1255_22111,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2291,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2295,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x2299,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x229d,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f0,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f1,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1255,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f2,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1275,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f3,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f4,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x22f5,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8651,
+		.card      = CX23885_BOARD_MYGICA_X8506,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8657,
+		.card      = CX23885_BOARD_MAGICPRO_PROHDTVE2,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8541,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1850,
+	}, {
+		.subvendor = 0x1858,
+		.subdevice = 0xe800,
+		.card      = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8551,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR1290,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8578,
+		.card      = CX23885_BOARD_MYGICA_X8558PRO,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f22,
+		.card      = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200,
+	}, {
+		.subvendor = 0x5654,
+		.subdevice = 0x2390,
+		.card      = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID,
+	}, {
+		.subvendor = 0x1b55,
+		.subdevice = 0xe2e4,
+		.card      = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8502,
+		.card      = CX23885_BOARD_MYGICA_X8507,
+	}, {
+		.subvendor = 0x153b,
+		.subdevice = 0x117e,
+		.card      = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
+	}, {
+		.subvendor = 0xd471,
+		.subdevice = 0x9022,
+		.card      = CX23885_BOARD_TEVII_S471,
+	}, {
+		.subvendor = 0x8000,
+		.subdevice = 0x3034,
+		.card      = CX23885_BOARD_PROF_8000,
+	},
+};
+const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
+
+void cx23885_card_list(struct cx23885_dev *dev)
+{
+	int i;
+
+	if (0 == dev->pci->subsystem_vendor &&
+	    0 == dev->pci->subsystem_device) {
+		printk(KERN_INFO
+			"%s: Board has no valid PCIe Subsystem ID and can't\n"
+		       "%s: be autodetected. Pass card=<n> insmod option\n"
+		       "%s: to workaround that. Redirect complaints to the\n"
+		       "%s: vendor of the TV card.  Best regards,\n"
+		       "%s:         -- tux\n",
+		       dev->name, dev->name, dev->name, dev->name, dev->name);
+	} else {
+		printk(KERN_INFO
+			"%s: Your board isn't known (yet) to the driver.\n"
+		       "%s: Try to pick one of the existing card configs via\n"
+		       "%s: card=<n> insmod option.  Updating to the latest\n"
+		       "%s: version might help as well.\n",
+		       dev->name, dev->name, dev->name, dev->name);
+	}
+	printk(KERN_INFO "%s: Here is a list of valid choices for the card=<n> insmod option:\n",
+	       dev->name);
+	for (i = 0; i < cx23885_bcount; i++)
+		printk(KERN_INFO "%s:    card=%d -> %s\n",
+		       dev->name, i, cx23885_boards[i].name);
+}
+
+static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
+{
+	struct tveeprom tv;
+
+	tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
+		eeprom_data);
+
+	/* Make sure we support the board model */
+	switch (tv.model) {
+	case 22001:
+		/* WinTV-HVR1270 (PCIe, Retail, half height)
+		 * ATSC/QAM and basic analog, IR Blast */
+	case 22009:
+		/* WinTV-HVR1210 (PCIe, Retail, half height)
+		 * DVB-T and basic analog, IR Blast */
+	case 22011:
+		/* WinTV-HVR1270 (PCIe, Retail, half height)
+		 * ATSC/QAM and basic analog, IR Recv */
+	case 22019:
+		/* WinTV-HVR1210 (PCIe, Retail, half height)
+		 * DVB-T and basic analog, IR Recv */
+	case 22021:
+		/* WinTV-HVR1275 (PCIe, Retail, half height)
+		 * ATSC/QAM and basic analog, IR Recv */
+	case 22029:
+		/* WinTV-HVR1210 (PCIe, Retail, half height)
+		 * DVB-T and basic analog, IR Recv */
+	case 22101:
+		/* WinTV-HVR1270 (PCIe, Retail, full height)
+		 * ATSC/QAM and basic analog, IR Blast */
+	case 22109:
+		/* WinTV-HVR1210 (PCIe, Retail, full height)
+		 * DVB-T and basic analog, IR Blast */
+	case 22111:
+		/* WinTV-HVR1270 (PCIe, Retail, full height)
+		 * ATSC/QAM and basic analog, IR Recv */
+	case 22119:
+		/* WinTV-HVR1210 (PCIe, Retail, full height)
+		 * DVB-T and basic analog, IR Recv */
+	case 22121:
+		/* WinTV-HVR1275 (PCIe, Retail, full height)
+		 * ATSC/QAM and basic analog, IR Recv */
+	case 22129:
+		/* WinTV-HVR1210 (PCIe, Retail, full height)
+		 * DVB-T and basic analog, IR Recv */
+	case 71009:
+		/* WinTV-HVR1200 (PCIe, Retail, full height)
+		 * DVB-T and basic analog */
+	case 71359:
+		/* WinTV-HVR1200 (PCIe, OEM, half height)
+		 * DVB-T and basic analog */
+	case 71439:
+		/* WinTV-HVR1200 (PCIe, OEM, half height)
+		 * DVB-T and basic analog */
+	case 71449:
+		/* WinTV-HVR1200 (PCIe, OEM, full height)
+		 * DVB-T and basic analog */
+	case 71939:
+		/* WinTV-HVR1200 (PCIe, OEM, half height)
+		 * DVB-T and basic analog */
+	case 71949:
+		/* WinTV-HVR1200 (PCIe, OEM, full height)
+		 * DVB-T and basic analog */
+	case 71959:
+		/* WinTV-HVR1200 (PCIe, OEM, full height)
+		 * DVB-T and basic analog */
+	case 71979:
+		/* WinTV-HVR1200 (PCIe, OEM, half height)
+		 * DVB-T and basic analog */
+	case 71999:
+		/* WinTV-HVR1200 (PCIe, OEM, full height)
+		 * DVB-T and basic analog */
+	case 76601:
+		/* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual
+			channel ATSC and MPEG2 HW Encoder */
+	case 77001:
+		/* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC
+			and Basic analog */
+	case 77011:
+		/* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC
+			and Basic analog */
+	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 79501:
+		/* WinTV-HVR1250 (PCIe, No IR, half height,
+			ATSC [at least] and Basic analog) */
+	case 79561:
+		/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+			ATSC and Basic analog */
+	case 79571:
+		/* WinTV-HVR1250 (PCIe, OEM, No IR, full height,
+		 ATSC and Basic analog */
+	case 79671:
+		/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+			ATSC and Basic analog */
+	case 80019:
+		/* WinTV-HVR1400 (Express Card, Retail, IR,
+		 * DVB-T and Basic analog */
+	case 81509:
+		/* WinTV-HVR1700 (PCIe, OEM, No IR, half height)
+		 * DVB-T and MPEG2 HW Encoder */
+	case 81519:
+		/* WinTV-HVR1700 (PCIe, OEM, No IR, full height)
+		 * DVB-T and MPEG2 HW Encoder */
+		break;
+	case 85021:
+		/* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
+			Dual channel ATSC and MPEG2 HW Encoder */
+		break;
+	case 85721:
+		/* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
+			Dual channel ATSC and Basic analog */
+		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 cx23885_tuner_callback(void *priv, int component, int command, int arg)
+{
+	struct cx23885_tsport *port = priv;
+	struct cx23885_dev *dev = port->dev;
+	u32 bitmask = 0;
+
+	if ((command == XC2028_RESET_CLK) || (command == XC2028_I2C_FLUSH))
+		return 0;
+
+	if (command != 0) {
+		printk(KERN_ERR "%s(): Unknown command 0x%x.\n",
+			__func__, command);
+		return -EINVAL;
+	}
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+		/* Tuner Reset Command */
+		bitmask = 0x04;
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+		/* Two identical tuners on two different i2c buses,
+		 * we need to reset the correct gpio. */
+		if (port->nr == 1)
+			bitmask = 0x01;
+		else if (port->nr == 2)
+			bitmask = 0x04;
+		break;
+	case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+		/* Tuner Reset Command */
+		bitmask = 0x02;
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+		altera_ci_tuner_reset(dev, port->nr);
+		break;
+	}
+
+	if (bitmask) {
+		/* Drive the tuner into reset and back out */
+		cx_clear(GP0_IO, bitmask);
+		mdelay(200);
+		cx_set(GP0_IO, bitmask);
+	}
+
+	return 0;
+}
+
+void cx23885_gpio_setup(struct cx23885_dev *dev)
+{
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		/* 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 */
+		/* GPIO-2 8295A Reset */
+		/* GPIO-3-10 cx23417 data0-7 */
+		/* GPIO-11-14 cx23417 addr0-3 */
+		/* GPIO-15-18 cx23417 READY, CS, RD, WR */
+		/* GPIO-19 IR_RX */
+
+		/* CX23417 GPIO's */
+		/* EIO15 Zilog Reset */
+		/* EIO14 S5H1409/CX24227 Reset */
+		mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1);
+
+		/* Put the demod into reset and protect the eeprom */
+		mc417_gpio_clear(dev, GPIO_15 | GPIO_14);
+		mdelay(100);
+
+		/* Bring the demod and blaster out of reset */
+		mc417_gpio_set(dev, GPIO_15 | GPIO_14);
+		mdelay(100);
+
+		/* Force the TDA8295A into reset and back */
+		cx23885_gpio_enable(dev, GPIO_2, 1);
+		cx23885_gpio_set(dev, GPIO_2);
+		mdelay(20);
+		cx23885_gpio_clear(dev, GPIO_2);
+		mdelay(20);
+		cx23885_gpio_set(dev, GPIO_2);
+		mdelay(20);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1200:
+		/* GPIO-0 tda10048 demodulator reset */
+		/* GPIO-2 tda18271 tuner reset */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x00050000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x00000005);
+		mdelay(20);
+		cx_set(GP0_IO, 0x00050005);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1700:
+		/* GPIO-0 TDA10048 demodulator reset */
+		/* GPIO-2 TDA8295A Reset */
+		/* GPIO-3-10 cx23417 data0-7 */
+		/* GPIO-11-14 cx23417 addr0-3 */
+		/* GPIO-15-18 cx23417 READY, CS, RD, WR */
+
+		/* The following GPIO's are on the interna AVCore (cx25840) */
+		/* GPIO-19 IR_RX */
+		/* GPIO-20 IR_TX 416/DVBT Select */
+		/* GPIO-21 IIS DAT */
+		/* GPIO-22 IIS WCLK */
+		/* GPIO-23 IIS BCLK */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x00050000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x00000005);
+		mdelay(20);
+		cx_set(GP0_IO, 0x00050005);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+		/* GPIO-0  Dibcom7000p demodulator reset */
+		/* GPIO-2  xc3028L tuner reset */
+		/* GPIO-13 LED */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x00050000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x00000005);
+		mdelay(20);
+		cx_set(GP0_IO, 0x00050005);
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+		/* GPIO-0 xc5000 tuner reset i2c bus 0 */
+		/* GPIO-1 s5h1409 demod reset i2c bus 0 */
+		/* GPIO-2 xc5000 tuner reset i2c bus 1 */
+		/* GPIO-3 s5h1409 demod reset i2c bus 0 */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x000f0000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x0000000f);
+		mdelay(20);
+		cx_set(GP0_IO, 0x000f000f);
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+		/* GPIO-0 portb xc3028 reset */
+		/* GPIO-1 portb zl10353 reset */
+		/* GPIO-2 portc xc3028 reset */
+		/* GPIO-3 portc zl10353 reset */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x000f0000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x0000000f);
+		mdelay(20);
+		cx_set(GP0_IO, 0x000f000f);
+		break;
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+		/* GPIO-2  xc3028 tuner reset */
+
+		/* The following GPIO's are on the internal AVCore (cx25840) */
+		/* GPIO-?  zl10353 demod reset */
+
+		/* Put the parts into reset and back */
+		cx_set(GP0_IO, 0x00040000);
+		mdelay(20);
+		cx_clear(GP0_IO, 0x00000004);
+		mdelay(20);
+		cx_set(GP0_IO, 0x00040004);
+		break;
+	case CX23885_BOARD_TBS_6920:
+	case CX23885_BOARD_PROF_8000:
+		cx_write(MC417_CTL, 0x00000036);
+		cx_write(MC417_OEN, 0x00001000);
+		cx_set(MC417_RWD, 0x00000002);
+		mdelay(200);
+		cx_clear(MC417_RWD, 0x00000800);
+		mdelay(200);
+		cx_set(MC417_RWD, 0x00000800);
+		mdelay(200);
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		/* GPIO-0 INTA from CiMax1
+		   GPIO-1 INTB from CiMax2
+		   GPIO-2 reset chips
+		   GPIO-3 to GPIO-10 data/addr for CA
+		   GPIO-11 ~CS0 to CiMax1
+		   GPIO-12 ~CS1 to CiMax2
+		   GPIO-13 ADL0 load LSB addr
+		   GPIO-14 ADL1 load MSB addr
+		   GPIO-15 ~RDY from CiMax
+		   GPIO-17 ~RD to CiMax
+		   GPIO-18 ~WR to CiMax
+		 */
+		cx_set(GP0_IO, 0x00040000); /* GPIO as out */
+		/* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
+		cx_clear(GP0_IO, 0x00030004);
+		mdelay(100);/* reset delay */
+		cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		/* GPIO-15 IN as ~ACK, rest as OUT */
+		cx_write(MC417_OEN, 0x00001000);
+		/* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+		cx_write(MC417_RWD, 0x0000c300);
+		/* enable irq */
+		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1210:
+		/* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */
+		/* GPIO-6 I2C Gate which can isolate the demod from the bus */
+		/* GPIO-9 Demod reset */
+
+		/* Put the parts into reset and back */
+		cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1);
+		cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5);
+		cx23885_gpio_clear(dev, GPIO_9);
+		mdelay(20);
+		cx23885_gpio_set(dev, GPIO_9);
+		break;
+	case CX23885_BOARD_MYGICA_X8506:
+	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+	case CX23885_BOARD_MYGICA_X8507:
+		/* GPIO-0 (0)Analog / (1)Digital TV */
+		/* GPIO-1 reset XC5000 */
+		/* GPIO-2 reset LGS8GL5 / LGS8G75 */
+		cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
+		cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
+		mdelay(100);
+		cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
+		mdelay(100);
+		break;
+	case CX23885_BOARD_MYGICA_X8558PRO:
+		/* GPIO-0 reset first ATBM8830 */
+		/* GPIO-1 reset second ATBM8830 */
+		cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+		cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
+		mdelay(100);
+		cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
+		mdelay(100);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		/* GPIO-0 656_CLK */
+		/* GPIO-1 656_D0 */
+		/* GPIO-2 Wake# */
+		/* GPIO-3-10 cx23417 data0-7 */
+		/* GPIO-11-14 cx23417 addr0-3 */
+		/* GPIO-15-18 cx23417 READY, CS, RD, WR */
+		/* GPIO-19 IR_RX */
+		/* GPIO-20 C_IR_TX */
+		/* GPIO-21 I2S DAT */
+		/* GPIO-22 I2S WCLK */
+		/* GPIO-23 I2S BCLK */
+		/* ALT GPIO: EXP GPIO LATCH */
+
+		/* CX23417 GPIO's */
+		/* GPIO-14 S5H1411/CX24228 Reset */
+		/* GPIO-13 EEPROM write protect */
+		mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1);
+
+		/* Put the demod into reset and protect the eeprom */
+		mc417_gpio_clear(dev, GPIO_14 | GPIO_13);
+		mdelay(100);
+
+		/* Bring the demod out of reset */
+		mc417_gpio_set(dev, GPIO_14);
+		mdelay(100);
+
+		/* CX24228 GPIO */
+		/* Connected to IF / Mux */
+		break;
+	case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+		cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+		/* GPIO-0 ~INT in
+		   GPIO-1 TMS out
+		   GPIO-2 ~reset chips out
+		   GPIO-3 to GPIO-10 data/addr for CA in/out
+		   GPIO-11 ~CS out
+		   GPIO-12 ADDR out
+		   GPIO-13 ~WR out
+		   GPIO-14 ~RD out
+		   GPIO-15 ~RDY in
+		   GPIO-16 TCK out
+		   GPIO-17 TDO in
+		   GPIO-18 TDI out
+		 */
+		cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */
+		/* GPIO-0 as INT, reset & TMS low */
+		cx_clear(GP0_IO, 0x00010006);
+		mdelay(100);/* reset delay */
+		cx_set(GP0_IO, 0x00000004); /* reset high */
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */
+		/* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */
+		cx_write(MC417_OEN, 0x00005000);
+		/* ~RD, ~WR high; ADDR low; ~CS high */
+		cx_write(MC417_RWD, 0x00000d00);
+		/* enable irq */
+		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+		break;
+	}
+}
+
+int cx23885_ir_init(struct cx23885_dev *dev)
+{
+	static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+		{
+			.flags	  = V4L2_SUBDEV_IO_PIN_INPUT,
+			.pin	  = CX23885_PIN_IR_RX_GPIO19,
+			.function = CX23885_PAD_IR_RX,
+			.value	  = 0,
+			.strength = CX25840_PIN_DRIVE_MEDIUM,
+		}, {
+			.flags	  = V4L2_SUBDEV_IO_PIN_OUTPUT,
+			.pin	  = CX23885_PIN_IR_TX_GPIO20,
+			.function = CX23885_PAD_IR_TX,
+			.value	  = 0,
+			.strength = CX25840_PIN_DRIVE_MEDIUM,
+		}
+	};
+	const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+	static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+		{
+			.flags	  = V4L2_SUBDEV_IO_PIN_INPUT,
+			.pin	  = CX23885_PIN_IR_RX_GPIO19,
+			.function = CX23885_PAD_IR_RX,
+			.value	  = 0,
+			.strength = CX25840_PIN_DRIVE_MEDIUM,
+		}
+	};
+	const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+	struct v4l2_subdev_ir_parameters params;
+	int ret = 0;
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1200:
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1210:
+		/* FIXME: Implement me */
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+		ret = cx23888_ir_probe(dev);
+		if (ret)
+			break;
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		ret = cx23888_ir_probe(dev);
+		if (ret)
+			break;
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+		/*
+		 * For these boards we need to invert the Tx output via the
+		 * IR controller to have the LED off while idle
+		 */
+		v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
+		params.enable = false;
+		params.shutdown = false;
+		params.invert_level = true;
+		v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+		params.shutdown = true;
+		v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_TEVII_S470:
+		if (!enable_885_ir)
+			break;
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+		if (dev->sd_ir == NULL) {
+			ret = -ENODEV;
+			break;
+		}
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		if (!enable_885_ir)
+			break;
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+		if (dev->sd_ir == NULL) {
+			ret = -ENODEV;
+			break;
+		}
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+		request_module("ir-kbd-i2c");
+		break;
+	}
+
+	return ret;
+}
+
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		cx23885_irq_remove(dev, PCI_MSK_IR);
+		cx23888_ir_remove(dev);
+		dev->sd_ir = NULL;
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_TEVII_S470:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+		/* sd_ir is a duplicate pointer to the AV Core, just clear it */
+		dev->sd_ir = NULL;
+		break;
+	}
+}
+
+int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
+{
+	int data;
+	int tdo = 0;
+	struct cx23885_dev *dev = (struct cx23885_dev *)device;
+	/*TMS*/
+	data = ((cx_read(GP0_IO)) & (~0x00000002));
+	data |= (tms ? 0x00020002 : 0x00020000);
+	cx_write(GP0_IO, data);
+
+	/*TDI*/
+	data = ((cx_read(MC417_RWD)) & (~0x0000a000));
+	data |= (tdi ? 0x00008000 : 0);
+	cx_write(MC417_RWD, data);
+	if (read_tdo)
+		tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/
+
+	cx_write(MC417_RWD, data | 0x00002000);
+	udelay(1);
+	/*TCK*/
+	cx_write(MC417_RWD, data);
+
+	return tdo;
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		if (dev->sd_ir)
+			cx23885_irq_add_enable(dev, PCI_MSK_IR);
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_TEVII_S470:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		if (dev->sd_ir)
+			cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
+		break;
+	}
+}
+
+void cx23885_card_setup(struct cx23885_dev *dev)
+{
+	struct cx23885_tsport *ts1 = &dev->ts1;
+	struct cx23885_tsport *ts2 = &dev->ts2;
+
+	static u8 eeprom[256];
+
+	if (dev->i2c_bus[0].i2c_rc == 0) {
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client,
+			      eeprom, sizeof(eeprom));
+	}
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		if (dev->i2c_bus[0].i2c_rc == 0) {
+			if (eeprom[0x80] != 0x84)
+				hauppauge_eeprom(dev, eeprom+0xc0);
+			else
+				hauppauge_eeprom(dev, eeprom+0x80);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+		if (dev->i2c_bus[0].i2c_rc == 0)
+			hauppauge_eeprom(dev, eeprom+0x80);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+	case CX23885_BOARD_HAUPPAUGE_HVR1200:
+	case CX23885_BOARD_HAUPPAUGE_HVR1700:
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1210:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		if (dev->i2c_bus[0].i2c_rc == 0)
+			hauppauge_eeprom(dev, eeprom+0xc0);
+		break;
+	}
+
+	switch (dev->board) {
+	case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		/* break omitted intentionally */
+	case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+		ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800:
+		/* Defaults for VID B - Analog encoder */
+		/* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */
+		ts1->gen_ctrl_val    = 0x10e;
+		ts1->ts_clk_en_val   = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val     = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+
+		/* APB_TSVALERR_POL (active low)*/
+		ts1->vld_misc_val    = 0x2000;
+		ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc);
+		cx_write(0x130184, 0xc);
+
+		/* Defaults for VID C */
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_TBS_6920:
+		ts1->gen_ctrl_val  = 0x4; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_TEVII_S470:
+	case CX23885_BOARD_TEVII_S471:
+	case CX23885_BOARD_DVBWORLD_2005:
+	case CX23885_BOARD_PROF_8000:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+		ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_MYGICA_X8506:
+	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_MYGICA_X8558PRO:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+	case CX23885_BOARD_HAUPPAUGE_HVR1200:
+	case CX23885_BOARD_HAUPPAUGE_HVR1700:
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1210:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+	default:
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+	}
+
+	/* Certain boards support analog, or require the avcore to be
+	 * loaded, ensure this happens.
+	 */
+	switch (dev->board) {
+	case CX23885_BOARD_TEVII_S470:
+		/* Currently only enabled for the integrated IR controller */
+		if (!enable_885_ir)
+			break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+	case CX23885_BOARD_HAUPPAUGE_HVR1700:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_MYGICA_X8506:
+	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+	case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+	case CX23885_BOARD_MPX885:
+	case CX23885_BOARD_MYGICA_X8507:
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+		dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_bus[2].i2c_adap,
+				"cx25840", 0x88 >> 1, NULL);
+		if (dev->sd_cx25840) {
+			dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+			v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+		}
+		break;
+	}
+
+	/* AUX-PLL 27MHz CLK */
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		netup_initialize(dev);
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+		int ret;
+		const struct firmware *fw;
+		const char *filename = "dvb-netup-altera-01.fw";
+		char *action = "configure";
+		static struct netup_card_info cinfo;
+		struct altera_config netup_config = {
+			.dev = dev,
+			.action = action,
+			.jtag_io = netup_jtag_io,
+		};
+
+		netup_initialize(dev);
+
+		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+		if (netup_card_rev)
+			cinfo.rev = netup_card_rev;
+
+		switch (cinfo.rev) {
+		case 0x4:
+			filename = "dvb-netup-altera-04.fw";
+			break;
+		default:
+			filename = "dvb-netup-altera-01.fw";
+			break;
+		}
+		printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
+				cinfo.rev, filename);
+
+		ret = request_firmware(&fw, filename, &dev->pci->dev);
+		if (ret != 0)
+			printk(KERN_ERR "did not find the firmware file. (%s) "
+			"Please see linux/Documentation/dvb/ for more details "
+			"on firmware-problems.", filename);
+		else
+			altera_init(&netup_config, fw);
+
+		release_firmware(fw);
+		break;
+	}
+	}
+}
+
+/* ------------------------------------------------------------------ */
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
new file mode 100644
index 000000000000..697728f09430
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -0,0 +1,2234 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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/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 <asm/div64.h>
+#include <linux/firmware.h>
+
+#include "cx23885.h"
+#include "cimax2.h"
+#include "altera-ci.h"
+#include "cx23888-ir.h"
+#include "cx23885-ir.h"
+#include "cx23885-av.h"
+#include "cx23885-input.h"
+
+MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX23885_VERSION);
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+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...)\
+	do { if (debug >= level)\
+		printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+	} while (0)
+
+static unsigned int cx23885_devcount;
+
+#define NO_SYNC_LINE (-1U)
+
+/* FIXME, these allocations will change when
+ * analog arrives. The be reviewed.
+ * CX23887 Assumptions
+ * 1 line = 16 bytes of CDT
+ * cmds size = 80
+ * cdt size = 16 * linesize
+ * iqsize = 64
+ * maxlines = 6
+ *
+ * Address Space:
+ * 0x00000000 0x00008fff FIFO clusters
+ * 0x00010000 0x000104af Channel Management Data Structures
+ * 0x000104b0 0x000104ff Free
+ * 0x00010500 0x000108bf 15 channels * iqsize
+ * 0x000108c0 0x000108ff Free
+ * 0x00010900 0x00010e9f IQ's + Cluster Descriptor Tables
+ *                       15 channels * (iqsize + (maxlines * linesize))
+ * 0x00010ea0 0x00010xxx Free
+ */
+
+static struct sram_channel cx23885_sram_channels[] = {
+	[SRAM_CH01] = {
+		.name		= "VID A",
+		.cmds_start	= 0x10000,
+		.ctrl_start	= 0x10380,
+		.cdt		= 0x104c0,
+		.fifo_start	= 0x40,
+		.fifo_size	= 0x2800,
+		.ptr1_reg	= DMA1_PTR1,
+		.ptr2_reg	= DMA1_PTR2,
+		.cnt1_reg	= DMA1_CNT1,
+		.cnt2_reg	= DMA1_CNT2,
+	},
+	[SRAM_CH02] = {
+		.name		= "ch2",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA2_PTR1,
+		.ptr2_reg	= DMA2_PTR2,
+		.cnt1_reg	= DMA2_CNT1,
+		.cnt2_reg	= DMA2_CNT2,
+	},
+	[SRAM_CH03] = {
+		.name		= "TS1 B",
+		.cmds_start	= 0x100A0,
+		.ctrl_start	= 0x10400,
+		.cdt		= 0x10580,
+		.fifo_start	= 0x5000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA3_PTR1,
+		.ptr2_reg	= DMA3_PTR2,
+		.cnt1_reg	= DMA3_CNT1,
+		.cnt2_reg	= DMA3_CNT2,
+	},
+	[SRAM_CH04] = {
+		.name		= "ch4",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA4_PTR1,
+		.ptr2_reg	= DMA4_PTR2,
+		.cnt1_reg	= DMA4_CNT1,
+		.cnt2_reg	= DMA4_CNT2,
+	},
+	[SRAM_CH05] = {
+		.name		= "ch5",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA5_PTR1,
+		.ptr2_reg	= DMA5_PTR2,
+		.cnt1_reg	= DMA5_CNT1,
+		.cnt2_reg	= DMA5_CNT2,
+	},
+	[SRAM_CH06] = {
+		.name		= "TS2 C",
+		.cmds_start	= 0x10140,
+		.ctrl_start	= 0x10440,
+		.cdt		= 0x105e0,
+		.fifo_start	= 0x6000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA5_PTR1,
+		.ptr2_reg	= DMA5_PTR2,
+		.cnt1_reg	= DMA5_CNT1,
+		.cnt2_reg	= DMA5_CNT2,
+	},
+	[SRAM_CH07] = {
+		.name		= "TV Audio",
+		.cmds_start	= 0x10190,
+		.ctrl_start	= 0x10480,
+		.cdt		= 0x10a00,
+		.fifo_start	= 0x7000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA6_PTR1,
+		.ptr2_reg	= DMA6_PTR2,
+		.cnt1_reg	= DMA6_CNT1,
+		.cnt2_reg	= DMA6_CNT2,
+	},
+	[SRAM_CH08] = {
+		.name		= "ch8",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA7_PTR1,
+		.ptr2_reg	= DMA7_PTR2,
+		.cnt1_reg	= DMA7_CNT1,
+		.cnt2_reg	= DMA7_CNT2,
+	},
+	[SRAM_CH09] = {
+		.name		= "ch9",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA8_PTR1,
+		.ptr2_reg	= DMA8_PTR2,
+		.cnt1_reg	= DMA8_CNT1,
+		.cnt2_reg	= DMA8_CNT2,
+	},
+};
+
+static struct sram_channel cx23887_sram_channels[] = {
+	[SRAM_CH01] = {
+		.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,
+		.cnt2_reg	= DMA1_CNT2,
+	},
+	[SRAM_CH02] = {
+		.name		= "VID A (VBI)",
+		.cmds_start	= 0x10050,
+		.ctrl_start	= 0x105F0,
+		.cdt		= 0x10810,
+		.fifo_start	= 0x3000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA2_PTR1,
+		.ptr2_reg	= DMA2_PTR2,
+		.cnt1_reg	= DMA2_CNT1,
+		.cnt2_reg	= DMA2_CNT2,
+	},
+	[SRAM_CH03] = {
+		.name		= "TS1 B",
+		.cmds_start	= 0x100A0,
+		.ctrl_start	= 0x10630,
+		.cdt		= 0x10870,
+		.fifo_start	= 0x5000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA3_PTR1,
+		.ptr2_reg	= DMA3_PTR2,
+		.cnt1_reg	= DMA3_CNT1,
+		.cnt2_reg	= DMA3_CNT2,
+	},
+	[SRAM_CH04] = {
+		.name		= "ch4",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA4_PTR1,
+		.ptr2_reg	= DMA4_PTR2,
+		.cnt1_reg	= DMA4_CNT1,
+		.cnt2_reg	= DMA4_CNT2,
+	},
+	[SRAM_CH05] = {
+		.name		= "ch5",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA5_PTR1,
+		.ptr2_reg	= DMA5_PTR2,
+		.cnt1_reg	= DMA5_CNT1,
+		.cnt2_reg	= DMA5_CNT2,
+	},
+	[SRAM_CH06] = {
+		.name		= "TS2 C",
+		.cmds_start	= 0x10140,
+		.ctrl_start	= 0x10670,
+		.cdt		= 0x108d0,
+		.fifo_start	= 0x6000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA5_PTR1,
+		.ptr2_reg	= DMA5_PTR2,
+		.cnt1_reg	= DMA5_CNT1,
+		.cnt2_reg	= DMA5_CNT2,
+	},
+	[SRAM_CH07] = {
+		.name		= "TV Audio",
+		.cmds_start	= 0x10190,
+		.ctrl_start	= 0x106B0,
+		.cdt		= 0x10930,
+		.fifo_start	= 0x7000,
+		.fifo_size	= 0x1000,
+		.ptr1_reg	= DMA6_PTR1,
+		.ptr2_reg	= DMA6_PTR2,
+		.cnt1_reg	= DMA6_CNT1,
+		.cnt2_reg	= DMA6_CNT2,
+	},
+	[SRAM_CH08] = {
+		.name		= "ch8",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA7_PTR1,
+		.ptr2_reg	= DMA7_PTR2,
+		.cnt1_reg	= DMA7_CNT1,
+		.cnt2_reg	= DMA7_CNT2,
+	},
+	[SRAM_CH09] = {
+		.name		= "ch9",
+		.cmds_start	= 0x0,
+		.ctrl_start	= 0x0,
+		.cdt		= 0x0,
+		.fifo_start	= 0x0,
+		.fifo_size	= 0x0,
+		.ptr1_reg	= DMA8_PTR1,
+		.ptr2_reg	= DMA8_PTR2,
+		.cnt1_reg	= DMA8_CNT1,
+		.cnt2_reg	= DMA8_CNT2,
+	},
+};
+
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	dev->pci_irqmask |= mask;
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	dev->pci_irqmask |= mask;
+	cx_set(PCI_INT_MSK, mask);
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+	u32 v;
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	v = mask & dev->pci_irqmask;
+	if (v)
+		cx_set(PCI_INT_MSK, v);
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+	cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	cx_clear(PCI_INT_MSK, mask);
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+	cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	dev->pci_irqmask &= ~mask;
+	cx_clear(PCI_INT_MSK, mask);
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+	u32 v;
+	unsigned long flags;
+	spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+	v = cx_read(PCI_INT_MSK);
+
+	spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+	return v;
+}
+
+static int cx23885_risc_decode(u32 risc)
+{
+	static char *instr[16] = {
+		[RISC_SYNC    >> 28] = "sync",
+		[RISC_WRITE   >> 28] = "write",
+		[RISC_WRITEC  >> 28] = "writec",
+		[RISC_READ    >> 28] = "read",
+		[RISC_READC   >> 28] = "readc",
+		[RISC_JUMP    >> 28] = "jump",
+		[RISC_SKIP    >> 28] = "skip",
+		[RISC_WRITERM >> 28] = "writerm",
+		[RISC_WRITECM >> 28] = "writecm",
+		[RISC_WRITECR >> 28] = "writecr",
+	};
+	static int incr[16] = {
+		[RISC_WRITE   >> 28] = 3,
+		[RISC_JUMP    >> 28] = 3,
+		[RISC_SKIP    >> 28] = 1,
+		[RISC_SYNC    >> 28] = 1,
+		[RISC_WRITERM >> 28] = 3,
+		[RISC_WRITECM >> 28] = 3,
+		[RISC_WRITECR >> 28] = 4,
+	};
+	static char *bits[] = {
+		"12",   "13",   "14",   "resync",
+		"cnt0", "cnt1", "18",   "19",
+		"20",   "21",   "22",   "23",
+		"irq1", "irq2", "eol",  "sol",
+	};
+	int i;
+
+	printk("0x%08x [ %s", risc,
+	       instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+	for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--)
+		if (risc & (1 << (i + 12)))
+			printk(" %s", bits[i]);
+	printk(" count=%d ]\n", risc & 0xfff);
+	return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+void cx23885_wakeup(struct cx23885_tsport *port,
+			   struct cx23885_dmaqueue *q, u32 count)
+{
+	struct cx23885_dev *dev = port->dev;
+	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_WARNING "%s: %d buffers handled (should be 1)\n",
+		       __func__, bc);
+}
+
+int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+				      struct sram_channel *ch,
+				      unsigned int bpl, u32 risc)
+{
+	unsigned int i, lines;
+	u32 cdt;
+
+	if (ch->cmds_start == 0) {
+		dprintk(1, "%s() Erasing channel [%s]\n", __func__,
+			ch->name);
+		cx_write(ch->ptr1_reg, 0);
+		cx_write(ch->ptr2_reg, 0);
+		cx_write(ch->cnt2_reg, 0);
+		cx_write(ch->cnt1_reg, 0);
+		return 0;
+	} else {
+		dprintk(1, "%s() Configuring channel [%s]\n", __func__,
+			ch->name);
+	}
+
+	bpl   = (bpl + 7) & ~7; /* alignment */
+	cdt   = ch->cdt;
+	lines = ch->fifo_size / bpl;
+	if (lines > 6)
+		lines = 6;
+	BUG_ON(lines < 2);
+
+	cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+	cx_write(8 + 4, 8);
+	cx_write(8 + 8, 0);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++) {
+		dprintk(2, "%s() 0x%08x <- 0x%08x\n", __func__, cdt + 16*i,
+			ch->fifo_start + bpl*i);
+		cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
+		cx_write(cdt + 16*i +  4, 0);
+		cx_write(cdt + 16*i +  8, 0);
+		cx_write(cdt + 16*i + 12, 0);
+	}
+
+	/* write CMDS */
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 0, 8);
+	else
+		cx_write(ch->cmds_start + 0, risc);
+	cx_write(ch->cmds_start +  4, 0); /* 64 bits 63-32 */
+	cx_write(ch->cmds_start +  8, cdt);
+	cx_write(ch->cmds_start + 12, (lines*16) >> 3);
+	cx_write(ch->cmds_start + 16, ch->ctrl_start);
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+	else
+		cx_write(ch->cmds_start + 20, 64 >> 2);
+	for (i = 24; i < 80; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt2_reg, (lines*16) >> 3);
+	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+	dprintk(2, "[bridge %d] sram setup %s: bpl=%d lines=%d\n",
+		dev->bridge,
+		ch->name,
+		bpl,
+		lines);
+
+	return 0;
+}
+
+void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+				      struct sram_channel *ch)
+{
+	static char *name[] = {
+		"init risc lo",
+		"init risc hi",
+		"cdt base",
+		"cdt size",
+		"iq base",
+		"iq size",
+		"risc pc lo",
+		"risc pc hi",
+		"iq wr ptr",
+		"iq rd ptr",
+		"cdt current",
+		"pci target lo",
+		"pci target hi",
+		"line / byte",
+	};
+	u32 risc;
+	unsigned int i, j, n;
+
+	printk(KERN_WARNING "%s: %s - dma channel status dump\n",
+	       dev->name, ch->name);
+	for (i = 0; i < ARRAY_SIZE(name); i++)
+		printk(KERN_WARNING "%s:   cmds: %-15s: 0x%08x\n",
+		       dev->name, name[i],
+		       cx_read(ch->cmds_start + 4*i));
+
+	for (i = 0; i < 4; i++) {
+		risc = cx_read(ch->cmds_start + 4 * (i + 14));
+		printk(KERN_WARNING "%s:   risc%d: ", dev->name, i);
+		cx23885_risc_decode(risc);
+	}
+	for (i = 0; i < (64 >> 2); i += n) {
+		risc = cx_read(ch->ctrl_start + 4 * i);
+		/* No consideration for bits 63-32 */
+
+		printk(KERN_WARNING "%s:   (0x%08x) iq %x: ", dev->name,
+		       ch->ctrl_start + 4 * i, i);
+		n = cx23885_risc_decode(risc);
+		for (j = 1; j < n; j++) {
+			risc = cx_read(ch->ctrl_start + 4 * (i + j));
+			printk(KERN_WARNING "%s:   iq %x: 0x%08x [ arg #%d ]\n",
+			       dev->name, i+j, risc, j);
+		}
+	}
+
+	printk(KERN_WARNING "%s: fifo: 0x%08x -> 0x%x\n",
+	       dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
+	printk(KERN_WARNING "%s: ctrl: 0x%08x -> 0x%x\n",
+	       dev->name, ch->ctrl_start, ch->ctrl_start + 6*16);
+	printk(KERN_WARNING "%s:   ptr1_reg: 0x%08x\n",
+	       dev->name, cx_read(ch->ptr1_reg));
+	printk(KERN_WARNING "%s:   ptr2_reg: 0x%08x\n",
+	       dev->name, cx_read(ch->ptr2_reg));
+	printk(KERN_WARNING "%s:   cnt1_reg: 0x%08x\n",
+	       dev->name, cx_read(ch->cnt1_reg));
+	printk(KERN_WARNING "%s:   cnt2_reg: 0x%08x\n",
+	       dev->name, cx_read(ch->cnt2_reg));
+}
+
+static void cx23885_risc_disasm(struct cx23885_tsport *port,
+				struct btcx_riscmem *risc)
+{
+	struct cx23885_dev *dev = port->dev;
+	unsigned int i, j, n;
+
+	printk(KERN_INFO "%s: risc disasm: %p [dma=0x%08lx]\n",
+	       dev->name, risc->cpu, (unsigned long)risc->dma);
+	for (i = 0; i < (risc->size >> 2); i += n) {
+		printk(KERN_INFO "%s:   %04d: ", dev->name, i);
+		n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i]));
+		for (j = 1; j < n; j++)
+			printk(KERN_INFO "%s:   %04d: 0x%08x [ arg #%d ]\n",
+			       dev->name, i + j, risc->cpu[i + j], j);
+		if (risc->cpu[i] == cpu_to_le32(RISC_JUMP))
+			break;
+	}
+}
+
+static void cx23885_shutdown(struct cx23885_dev *dev)
+{
+	/* disable RISC controller */
+	cx_write(DEV_CNTRL2, 0);
+
+	/* Disable all IR activity */
+	cx_write(IR_CNTRL_REG, 0);
+
+	/* Disable Video A/B activity */
+	cx_write(VID_A_DMA_CTL, 0);
+	cx_write(VID_B_DMA_CTL, 0);
+	cx_write(VID_C_DMA_CTL, 0);
+
+	/* Disable Audio activity */
+	cx_write(AUD_INT_DMA_CTL, 0);
+	cx_write(AUD_EXT_DMA_CTL, 0);
+
+	/* Disable Serial port */
+	cx_write(UART_CTL, 0);
+
+	/* Disable Interrupts */
+	cx23885_irq_disable_all(dev);
+	cx_write(VID_A_INT_MSK, 0);
+	cx_write(VID_B_INT_MSK, 0);
+	cx_write(VID_C_INT_MSK, 0);
+	cx_write(AUDIO_INT_INT_MSK, 0);
+	cx_write(AUDIO_EXT_INT_MSK, 0);
+
+}
+
+static void cx23885_reset(struct cx23885_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	cx23885_shutdown(dev);
+
+	cx_write(PCI_INT_STAT, 0xffffffff);
+	cx_write(VID_A_INT_STAT, 0xffffffff);
+	cx_write(VID_B_INT_STAT, 0xffffffff);
+	cx_write(VID_C_INT_STAT, 0xffffffff);
+	cx_write(AUDIO_INT_INT_STAT, 0xffffffff);
+	cx_write(AUDIO_EXT_INT_STAT, 0xffffffff);
+	cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
+	cx_write(PAD_CTRL, 0x00500300);
+
+	mdelay(100);
+
+	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);
+}
+
+
+static int cx23885_pci_quirks(struct cx23885_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	/* The cx23885 bridge has a weird bug which causes NMI to be asserted
+	 * when DMA begins if RDR_TLCTL0 bit4 is not cleared. It does not
+	 * occur on the cx23887 bridge.
+	 */
+	if (dev->bridge == CX23885_BRIDGE_885)
+		cx_clear(RDR_TLCTL0, 1 << 4);
+
+	return 0;
+}
+
+static int get_resources(struct cx23885_dev *dev)
+{
+	if (request_mem_region(pci_resource_start(dev->pci, 0),
+			       pci_resource_len(dev->pci, 0),
+			       dev->name))
+		return 0;
+
+	printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+		dev->name, (unsigned long long)pci_resource_start(dev->pci, 0));
+
+	return -EBUSY;
+}
+
+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);
+
+static int cx23885_init_tsport(struct cx23885_dev *dev,
+	struct cx23885_tsport *port, int portno)
+{
+	dprintk(1, "%s(portno=%d)\n", __func__, portno);
+
+	/* Transport bus init dma queue  - Common settings */
+	port->dma_ctl_val        = 0x11; /* Enable RISC controller and Fifo */
+	port->ts_int_msk_val     = 0x1111; /* TS port bits for RISC */
+	port->vld_misc_val       = 0x0;
+	port->hw_sop_ctrl_val    = (0x47 << 16 | 188 << 4);
+
+	spin_lock_init(&port->slock);
+	port->dev = dev;
+	port->nr = portno;
+
+	INIT_LIST_HEAD(&port->mpegq.active);
+	INIT_LIST_HEAD(&port->mpegq.queued);
+	port->mpegq.timeout.function = cx23885_timeout;
+	port->mpegq.timeout.data = (unsigned long)port;
+	init_timer(&port->mpegq.timeout);
+
+	mutex_init(&port->frontends.lock);
+	INIT_LIST_HEAD(&port->frontends.felist);
+	port->frontends.active_fe_id = 0;
+
+	/* This should be hardcoded allow a single frontend
+	 * attachment to this tsport, keeping the -dvb.c
+	 * code clean and safe.
+	 */
+	if (!port->num_frontends)
+		port->num_frontends = 1;
+
+	switch (portno) {
+	case 1:
+		port->reg_gpcnt          = VID_B_GPCNT;
+		port->reg_gpcnt_ctl      = VID_B_GPCNT_CTL;
+		port->reg_dma_ctl        = VID_B_DMA_CTL;
+		port->reg_lngth          = VID_B_LNGTH;
+		port->reg_hw_sop_ctrl    = VID_B_HW_SOP_CTL;
+		port->reg_gen_ctrl       = VID_B_GEN_CTL;
+		port->reg_bd_pkt_status  = VID_B_BD_PKT_STATUS;
+		port->reg_sop_status     = VID_B_SOP_STATUS;
+		port->reg_fifo_ovfl_stat = VID_B_FIFO_OVFL_STAT;
+		port->reg_vld_misc       = VID_B_VLD_MISC;
+		port->reg_ts_clk_en      = VID_B_TS_CLK_EN;
+		port->reg_src_sel        = VID_B_SRC_SEL;
+		port->reg_ts_int_msk     = VID_B_INT_MSK;
+		port->reg_ts_int_stat    = VID_B_INT_STAT;
+		port->sram_chno          = SRAM_CH03; /* VID_B */
+		port->pci_irqmask        = 0x02; /* VID_B bit1 */
+		break;
+	case 2:
+		port->reg_gpcnt          = VID_C_GPCNT;
+		port->reg_gpcnt_ctl      = VID_C_GPCNT_CTL;
+		port->reg_dma_ctl        = VID_C_DMA_CTL;
+		port->reg_lngth          = VID_C_LNGTH;
+		port->reg_hw_sop_ctrl    = VID_C_HW_SOP_CTL;
+		port->reg_gen_ctrl       = VID_C_GEN_CTL;
+		port->reg_bd_pkt_status  = VID_C_BD_PKT_STATUS;
+		port->reg_sop_status     = VID_C_SOP_STATUS;
+		port->reg_fifo_ovfl_stat = VID_C_FIFO_OVFL_STAT;
+		port->reg_vld_misc       = VID_C_VLD_MISC;
+		port->reg_ts_clk_en      = VID_C_TS_CLK_EN;
+		port->reg_src_sel        = 0;
+		port->reg_ts_int_msk     = VID_C_INT_MSK;
+		port->reg_ts_int_stat    = VID_C_INT_STAT;
+		port->sram_chno          = SRAM_CH06; /* VID_C */
+		port->pci_irqmask        = 0x04; /* VID_C bit2 */
+		break;
+	default:
+		BUG();
+	}
+
+	cx23885_risc_stopper(dev->pci, &port->mpegq.stopper,
+		     port->reg_dma_ctl, port->dma_ctl_val, 0x00);
+
+	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/14Z */
+		dev->hwrevision = 0xb0;
+		break;
+	case 0x03:
+		if (dev->pci->device == 0x8880) {
+			/* CX23888-21Z/22Z */
+			dev->hwrevision = 0xc0;
+		} else {
+			/* CX23885-14Z */
+			dev->hwrevision = 0xa4;
+		}
+		break;
+	case 0x04:
+		if (dev->pci->device == 0x8880) {
+			/* CX23888-31Z */
+			dev->hwrevision = 0xd0;
+		} else {
+			/* CX23885-15Z, CX23888-31Z */
+			dev->hwrevision = 0xa5;
+		}
+		break;
+	case 0x0e:
+		/* CX23887-15Z */
+		dev->hwrevision = 0xc0;
+		break;
+	case 0x0f:
+		/* CX23887-14Z */
+		dev->hwrevision = 0xb1;
+		break;
+	default:
+		printk(KERN_ERR "%s() New hardware revision found 0x%x\n",
+			__func__, dev->hwrevision);
+	}
+	if (dev->hwrevision)
+		printk(KERN_INFO "%s() Hardware revision = 0x%02x\n",
+			__func__, dev->hwrevision);
+	else
+		printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n",
+			__func__, dev->hwrevision);
+}
+
+/* Find the first v4l2_subdev member of the group id in hw */
+struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw)
+{
+	struct v4l2_subdev *result = NULL;
+	struct v4l2_subdev *sd;
+
+	spin_lock(&dev->v4l2_dev.lock);
+	v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) {
+		if (sd->grp_id == hw) {
+			result = sd;
+			break;
+		}
+	}
+	spin_unlock(&dev->v4l2_dev.lock);
+	return result;
+}
+
+static int cx23885_dev_setup(struct cx23885_dev *dev)
+{
+	int i;
+
+	spin_lock_init(&dev->pci_irqmask_lock);
+
+	mutex_init(&dev->lock);
+	mutex_init(&dev->gpio_lock);
+
+	atomic_inc(&dev->refcount);
+
+	dev->nr = cx23885_devcount++;
+	sprintf(dev->name, "cx23885[%d]", dev->nr);
+
+	/* Configure the internal memory */
+	if (dev->pci->device == 0x8880) {
+		/* Could be 887 or 888, assume a default */
+		dev->bridge = CX23885_BRIDGE_887;
+		/* Apply a sensible clock frequency for the PCIe bridge */
+		dev->clk_freq = 25000000;
+		dev->sram_channels = cx23887_sram_channels;
+	} else
+	if (dev->pci->device == 0x8852) {
+		dev->bridge = CX23885_BRIDGE_885;
+		/* Apply a sensible clock frequency for the PCIe bridge */
+		dev->clk_freq = 28000000;
+		dev->sram_channels = cx23885_sram_channels;
+	} else
+		BUG();
+
+	dprintk(1, "%s() Memory configured for PCIe bridge type %d\n",
+		__func__, dev->bridge);
+
+	/* board config */
+	dev->board = UNSET;
+	if (card[dev->nr] < cx23885_bcount)
+		dev->board = card[dev->nr];
+	for (i = 0; UNSET == dev->board  &&  i < cx23885_idcount; i++)
+		if (dev->pci->subsystem_vendor == cx23885_subids[i].subvendor &&
+		    dev->pci->subsystem_device == cx23885_subids[i].subdevice)
+			dev->board = cx23885_subids[i].card;
+	if (UNSET == dev->board) {
+		dev->board = CX23885_BOARD_UNKNOWN;
+		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);
+	cx23885_irq_add(dev, 0x001f00);
+
+	/* External Master 1 Bus */
+	dev->i2c_bus[0].nr = 0;
+	dev->i2c_bus[0].dev = dev;
+	dev->i2c_bus[0].reg_stat  = I2C1_STAT;
+	dev->i2c_bus[0].reg_ctrl  = I2C1_CTRL;
+	dev->i2c_bus[0].reg_addr  = I2C1_ADDR;
+	dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
+	dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
+	dev->i2c_bus[0].i2c_period = (0x9d << 24); /* 100kHz */
+
+	/* External Master 2 Bus */
+	dev->i2c_bus[1].nr = 1;
+	dev->i2c_bus[1].dev = dev;
+	dev->i2c_bus[1].reg_stat  = I2C2_STAT;
+	dev->i2c_bus[1].reg_ctrl  = I2C2_CTRL;
+	dev->i2c_bus[1].reg_addr  = I2C2_ADDR;
+	dev->i2c_bus[1].reg_rdata = I2C2_RDATA;
+	dev->i2c_bus[1].reg_wdata = I2C2_WDATA;
+	dev->i2c_bus[1].i2c_period = (0x9d << 24); /* 100kHz */
+
+	/* Internal Master 3 Bus */
+	dev->i2c_bus[2].nr = 2;
+	dev->i2c_bus[2].dev = dev;
+	dev->i2c_bus[2].reg_stat  = I2C3_STAT;
+	dev->i2c_bus[2].reg_ctrl  = I2C3_CTRL;
+	dev->i2c_bus[2].reg_addr  = I2C3_ADDR;
+	dev->i2c_bus[2].reg_rdata = I2C3_RDATA;
+	dev->i2c_bus[2].reg_wdata = I2C3_WDATA;
+	dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */
+
+	if ((cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) ||
+		(cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER))
+		cx23885_init_tsport(dev, &dev->ts1, 1);
+
+	if ((cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) ||
+		(cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER))
+		cx23885_init_tsport(dev, &dev->ts2, 2);
+
+	if (get_resources(dev) < 0) {
+		printk(KERN_ERR "CORE %s No more PCIe resources for "
+		       "subsystem: %04x:%04x\n",
+		       dev->name, dev->pci->subsystem_vendor,
+		       dev->pci->subsystem_device);
+
+		cx23885_devcount--;
+		return -ENODEV;
+	}
+
+	/* PCIe stuff */
+	dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
+			     pci_resource_len(dev->pci, 0));
+
+	dev->bmmio = (u8 __iomem *)dev->lmmio;
+
+	printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+	       dev->name, dev->pci->subsystem_vendor,
+	       dev->pci->subsystem_device, cx23885_boards[dev->board].name,
+	       dev->board, card[dev->nr] == dev->board ?
+	       "insmod option" : "autodetected");
+
+	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->tuner_bus = cx23885_boards[dev->board].tuner_bus;
+	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 tuner_bus = %d\n",
+		__func__, dev->tuner_type, dev->tuner_addr, dev->tuner_bus);
+	dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
+		__func__, dev->radio_type, dev->radio_addr);
+
+	/* The cx23417 encoder has GPIO's that need to be initialised
+	 * before DVB, so that demodulators and tuners are out of
+	 * reset before DVB uses them.
+	 */
+	if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ||
+		(cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER))
+			cx23885_mc417_init(dev);
+
+	/* init hardware */
+	cx23885_reset(dev);
+
+	cx23885_i2c_register(&dev->i2c_bus[0]);
+	cx23885_i2c_register(&dev->i2c_bus[1]);
+	cx23885_i2c_register(&dev->i2c_bus[2]);
+	cx23885_card_setup(dev);
+	call_all(dev, core, s_power, 0);
+	cx23885_ir_init(dev);
+
+	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", __func__);
+		}
+	}
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+		if (cx23885_boards[dev->board].num_fds_portb)
+			dev->ts1.num_frontends =
+				cx23885_boards[dev->board].num_fds_portb;
+		if (cx23885_dvb_register(&dev->ts1) < 0) {
+			printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
+			       __func__);
+		}
+	} else
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+		if (cx23885_417_register(dev) < 0) {
+			printk(KERN_ERR
+				"%s() Failed to register 417 on VID_B\n",
+			       __func__);
+		}
+	}
+
+	if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+		if (cx23885_boards[dev->board].num_fds_portc)
+			dev->ts2.num_frontends =
+				cx23885_boards[dev->board].num_fds_portc;
+		if (cx23885_dvb_register(&dev->ts2) < 0) {
+			printk(KERN_ERR
+				"%s() Failed to register dvb on VID_C\n",
+			       __func__);
+		}
+	} else
+	if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) {
+		if (cx23885_417_register(dev) < 0) {
+			printk(KERN_ERR
+				"%s() Failed to register 417 on VID_C\n",
+			       __func__);
+		}
+	}
+
+	cx23885_dev_checkrevision(dev);
+
+	/* disable MSI for NetUP cards, otherwise CI is not working */
+	if (cx23885_boards[dev->board].ci_type > 0)
+		cx_clear(RDR_RDRCTL1, 1 << 8);
+
+	switch (dev->board) {
+	case CX23885_BOARD_TEVII_S470:
+	case CX23885_BOARD_TEVII_S471:
+		cx_clear(RDR_RDRCTL1, 1 << 8);
+		break;
+	}
+
+	return 0;
+}
+
+static void cx23885_dev_unregister(struct cx23885_dev *dev)
+{
+	release_mem_region(pci_resource_start(dev->pci, 0),
+			   pci_resource_len(dev->pci, 0));
+
+	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);
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+		cx23885_417_unregister(dev);
+
+	if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+		cx23885_dvb_unregister(&dev->ts2);
+
+	if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)
+		cx23885_417_unregister(dev);
+
+	cx23885_i2c_unregister(&dev->i2c_bus[2]);
+	cx23885_i2c_unregister(&dev->i2c_bus[1]);
+	cx23885_i2c_unregister(&dev->i2c_bus[0]);
+
+	iounmap(dev->lmmio);
+}
+
+static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
+			       unsigned int offset, u32 sync_line,
+			       unsigned int bpl, unsigned int padding,
+			       unsigned int lines,  unsigned int lpi)
+{
+	struct scatterlist *sg;
+	unsigned int line, todo, sol;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < lines; line++) {
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+
+		if (lpi && line > 0 && !(line % lpi))
+			sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+		else
+			sol = RISC_SOL;
+
+		if (bpl <= sg_dma_len(sg)-offset) {
+			/* fits into current chunk */
+			*(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
+			*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+			offset += bpl;
+		} else {
+			/* scanline needs to be split */
+			todo = bpl;
+			*(rp++) = cpu_to_le32(RISC_WRITE|sol|
+					    (sg_dma_len(sg)-offset));
+			*(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
+			*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+			todo -= (sg_dma_len(sg)-offset);
+			offset = 0;
+			sg++;
+			while (todo > sg_dma_len(sg)) {
+				*(rp++) = cpu_to_le32(RISC_WRITE|
+						    sg_dma_len(sg));
+				*(rp++) = cpu_to_le32(sg_dma_address(sg));
+				*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+			*(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg));
+			*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+			offset += todo;
+		}
+		offset += padding;
+	}
+
+	return rp;
+}
+
+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)
+{
+	u32 instructions, fields;
+	__le32 *rp;
+	int rc;
+
+	fields = 0;
+	if (UNSET != top_offset)
+		fields++;
+	if (UNSET != bottom_offset)
+		fields++;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Padding
+	   can cause next bpl to start close to a page border.  First DMA
+	   region may be smaller than PAGE_SIZE */
+	/* write and jump need and extra dword */
+	instructions  = fields * (1 + ((bpl + padding) * lines)
+		/ PAGE_SIZE + lines);
+	instructions += 2;
+	rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	if (UNSET != top_offset)
+		rp = cx23885_risc_field(rp, sglist, top_offset, 0,
+					bpl, padding, lines, 0);
+	if (UNSET != bottom_offset)
+		rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
+					bpl, padding, lines, 0);
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+
+int cx23885_risc_databuffer(struct pci_dev *pci,
+				   struct btcx_riscmem *risc,
+				   struct scatterlist *sglist,
+				   unsigned int bpl,
+				   unsigned int lines, unsigned int lpi)
+{
+	u32 instructions;
+	__le32 *rp;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Here
+	   there is no padding and no sync.  First DMA region may be smaller
+	   than PAGE_SIZE */
+	/* Jump and write need an extra dword */
+	instructions  = 1 + (bpl * lines) / PAGE_SIZE + lines;
+	instructions += 1;
+
+	rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE,
+				bpl, 0, lines, lpi);
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+
+int cx23885_risc_vbibuffer(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)
+{
+	u32 instructions, fields;
+	__le32 *rp;
+	int rc;
+
+	fields = 0;
+	if (UNSET != top_offset)
+		fields++;
+	if (UNSET != bottom_offset)
+		fields++;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Padding
+	   can cause next bpl to start close to a page border.  First DMA
+	   region may be smaller than PAGE_SIZE */
+	/* write and jump need and extra dword */
+	instructions  = fields * (1 + ((bpl + padding) * lines)
+		/ PAGE_SIZE + lines);
+	instructions += 2;
+	rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+	if (rc < 0)
+		return rc;
+	/* write risc instructions */
+	rp = risc->cpu;
+
+	/* Sync to line 6, so US CC line 21 will appear in line '12'
+	 * in the userland vbi payload */
+	if (UNSET != top_offset)
+		rp = cx23885_risc_field(rp, sglist, top_offset, 6,
+					bpl, padding, lines, 0);
+
+	if (UNSET != bottom_offset)
+		rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207,
+					bpl, padding, lines, 0);
+
+
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+
+
+int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+				u32 reg, u32 mask, u32 value)
+{
+	__le32 *rp;
+	int rc;
+
+	rc = btcx_riscmem_alloc(pci, risc, 4*16);
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(RISC_WRITECR  | RISC_IRQ2);
+	*(rp++) = cpu_to_le32(reg);
+	*(rp++) = cpu_to_le32(value);
+	*(rp++) = cpu_to_le32(mask);
+	*(rp++) = cpu_to_le32(RISC_JUMP);
+	*(rp++) = cpu_to_le32(risc->dma);
+	*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+	return 0;
+}
+
+void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
+{
+	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+	BUG_ON(in_interrupt());
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_dma_unmap(q->dev, dma);
+	videobuf_dma_free(dma);
+	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+	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", __func__);
+	dprintk(1, "%s() DEV_CNTRL2               0x%08X\n", __func__,
+		cx_read(DEV_CNTRL2));
+	dprintk(1, "%s() PCI_INT_MSK              0x%08X\n", __func__,
+		cx23885_irq_get_mask(dev));
+	dprintk(1, "%s() AUD_INT_INT_MSK          0x%08X\n", __func__,
+		cx_read(AUDIO_INT_INT_MSK));
+	dprintk(1, "%s() AUD_INT_DMA_CTL          0x%08X\n", __func__,
+		cx_read(AUD_INT_DMA_CTL));
+	dprintk(1, "%s() AUD_EXT_INT_MSK          0x%08X\n", __func__,
+		cx_read(AUDIO_EXT_INT_MSK));
+	dprintk(1, "%s() AUD_EXT_DMA_CTL          0x%08X\n", __func__,
+		cx_read(AUD_EXT_DMA_CTL));
+	dprintk(1, "%s() PAD_CTRL                 0x%08X\n", __func__,
+		cx_read(PAD_CTRL));
+	dprintk(1, "%s() ALT_PIN_OUT_SEL          0x%08X\n", __func__,
+		cx_read(ALT_PIN_OUT_SEL));
+	dprintk(1, "%s() GPIO2                    0x%08X\n", __func__,
+		cx_read(GPIO2));
+	dprintk(1, "%s() gpcnt(0x%08X)          0x%08X\n", __func__,
+		port->reg_gpcnt, cx_read(port->reg_gpcnt));
+	dprintk(1, "%s() gpcnt_ctl(0x%08X)      0x%08x\n", __func__,
+		port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl));
+	dprintk(1, "%s() dma_ctl(0x%08X)        0x%08x\n", __func__,
+		port->reg_dma_ctl, cx_read(port->reg_dma_ctl));
+	if (port->reg_src_sel)
+		dprintk(1, "%s() src_sel(0x%08X)        0x%08x\n", __func__,
+			port->reg_src_sel, cx_read(port->reg_src_sel));
+	dprintk(1, "%s() lngth(0x%08X)          0x%08x\n", __func__,
+		port->reg_lngth, cx_read(port->reg_lngth));
+	dprintk(1, "%s() hw_sop_ctrl(0x%08X)    0x%08x\n", __func__,
+		port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl));
+	dprintk(1, "%s() gen_ctrl(0x%08X)       0x%08x\n", __func__,
+		port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl));
+	dprintk(1, "%s() bd_pkt_status(0x%08X)  0x%08x\n", __func__,
+		port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status));
+	dprintk(1, "%s() sop_status(0x%08X)     0x%08x\n", __func__,
+		port->reg_sop_status, cx_read(port->reg_sop_status));
+	dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __func__,
+		port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat));
+	dprintk(1, "%s() vld_misc(0x%08X)       0x%08x\n", __func__,
+		port->reg_vld_misc, cx_read(port->reg_vld_misc));
+	dprintk(1, "%s() ts_clk_en(0x%08X)      0x%08x\n", __func__,
+		port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
+	dprintk(1, "%s() ts_int_msk(0x%08X)     0x%08x\n", __func__,
+		port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
+}
+
+static int cx23885_start_dma(struct cx23885_tsport *port,
+			     struct cx23885_dmaqueue *q,
+			     struct cx23885_buffer   *buf)
+{
+	struct cx23885_dev *dev = port->dev;
+	u32 reg;
+
+	dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__,
+		buf->vb.width, buf->vb.height, buf->vb.field);
+
+	/* Stop the fifo and risc engine for this port */
+	cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+
+	/* setup fifo + format */
+	cx23885_sram_channel_setup(dev,
+				   &dev->sram_channels[port->sram_chno],
+				   port->ts_packet_size, buf->risc.dma);
+	if (debug > 5) {
+		cx23885_sram_channel_dump(dev,
+			&dev->sram_channels[port->sram_chno]);
+		cx23885_risc_disasm(port, &buf->risc);
+	}
+
+	/* write TS length to chip */
+	cx_write(port->reg_lngth, buf->vb.width);
+
+	if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) &&
+		(!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) {
+		printk("%s() Unsupported .portb/c (0x%08x)/(0x%08x)\n",
+			__func__,
+			cx23885_boards[dev->board].portb,
+			cx23885_boards[dev->board].portc);
+		return -EINVAL;
+	}
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+		cx23885_av_clk(dev, 0);
+
+	udelay(100);
+
+	/* If the port supports SRC SELECT, configure it */
+	if (port->reg_src_sel)
+		cx_write(port->reg_src_sel, port->src_sel_val);
+
+	cx_write(port->reg_hw_sop_ctrl, port->hw_sop_ctrl_val);
+	cx_write(port->reg_ts_clk_en, port->ts_clk_en_val);
+	cx_write(port->reg_vld_misc, port->vld_misc_val);
+	cx_write(port->reg_gen_ctrl, port->gen_ctrl_val);
+	udelay(100);
+
+	/* NOTE: this is 2 (reserved) for portb, does it matter? */
+	/* reset counter to zero */
+	cx_write(port->reg_gpcnt_ctl, 3);
+	q->count = 1;
+
+	/* Set VIDB pins to input */
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+		reg = cx_read(PAD_CTRL);
+		reg &= ~0x3; /* Clear TS1_OE & TS1_SOP_OE */
+		cx_write(PAD_CTRL, reg);
+	}
+
+	/* Set VIDC pins to input */
+	if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+		reg = cx_read(PAD_CTRL);
+		reg &= ~0x4; /* Clear TS2_SOP_OE */
+		cx_write(PAD_CTRL, reg);
+	}
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+
+		reg = cx_read(PAD_CTRL);
+		reg = reg & ~0x1;    /* Clear TS1_OE */
+
+		/* FIXME, bit 2 writing here is questionable */
+		/* set TS1_SOP_OE and TS1_OE_HI */
+		reg = reg | 0xa;
+		cx_write(PAD_CTRL, reg);
+
+		/* FIXME and these two registers should be documented. */
+		cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011);
+		cx_write(ALT_PIN_OUT_SEL, 0x10100045);
+	}
+
+	switch (dev->bridge) {
+	case CX23885_BRIDGE_885:
+	case CX23885_BRIDGE_887:
+	case CX23885_BRIDGE_888:
+		/* enable irqs */
+		dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
+		cx_set(port->reg_ts_int_msk,  port->ts_int_msk_val);
+		cx_set(port->reg_dma_ctl, port->dma_ctl_val);
+		cx23885_irq_add(dev, port->pci_irqmask);
+		cx23885_irq_enable_all(dev);
+		break;
+	default:
+		BUG();
+	}
+
+	cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+		cx23885_av_clk(dev, 1);
+
+	if (debug > 4)
+		cx23885_tsport_reg_dump(port);
+
+	return 0;
+}
+
+static int cx23885_stop_dma(struct cx23885_tsport *port)
+{
+	struct cx23885_dev *dev = port->dev;
+	u32 reg;
+
+	dprintk(1, "%s()\n", __func__);
+
+	/* Stop interrupts and DMA */
+	cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val);
+	cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+
+		reg = cx_read(PAD_CTRL);
+
+		/* Set TS1_OE */
+		reg = reg | 0x1;
+
+		/* clear TS1_SOP_OE and TS1_OE_HI */
+		reg = reg & ~0xa;
+		cx_write(PAD_CTRL, reg);
+		cx_write(port->reg_src_sel, 0);
+		cx_write(port->reg_gen_ctrl, 8);
+
+	}
+
+	if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+		cx23885_av_clk(dev, 0);
+
+	return 0;
+}
+
+int cx23885_restart_queue(struct cx23885_tsport *port,
+				struct cx23885_dmaqueue *q)
+{
+	struct cx23885_dev *dev = port->dev;
+	struct cx23885_buffer *buf;
+
+	dprintk(5, "%s()\n", __func__);
+	if (list_empty(&q->active)) {
+		struct cx23885_buffer *prev;
+		prev = NULL;
+
+		dprintk(5, "%s() queue is empty\n", __func__);
+
+		for (;;) {
+			if (list_empty(&q->queued))
+				return 0;
+			buf = list_entry(q->queued.next, struct cx23885_buffer,
+					 vb.queue);
+			if (NULL == prev) {
+				list_del(&buf->vb.queue);
+				list_add_tail(&buf->vb.queue, &q->active);
+				cx23885_start_dma(port, q, buf);
+				buf->vb.state = VIDEOBUF_ACTIVE;
+				buf->count    = q->count++;
+				mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+				dprintk(5, "[%p/%d] restart_queue - f/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, &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(5, "[%p/%d] restart_queue - m/active\n",
+					buf, buf->vb.i);
+			} else {
+				return 0;
+			}
+			prev = buf;
+		}
+		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_dma(port, q, buf);
+	list_for_each_entry(buf, &q->active, vb.queue)
+		buf->count = q->count++;
+	mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
+			struct cx23885_buffer *buf, enum v4l2_field field)
+{
+	struct cx23885_dev *dev = port->dev;
+	int size = port->ts_packet_size * port->ts_packet_count;
+	int rc;
+
+	dprintk(1, "%s: %p\n", __func__, buf);
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	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;
+		buf->vb.field  = field /*V4L2_FIELD_TOP*/;
+
+		rc = videobuf_iolock(q, &buf->vb, NULL);
+		if (0 != rc)
+			goto fail;
+		cx23885_risc_databuffer(dev->pci, &buf->risc,
+					videobuf_to_dma(&buf->vb)->sglist,
+					buf->vb.width, buf->vb.height, 0);
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+ fail:
+	cx23885_free_buffer(q, buf);
+	return rc;
+}
+
+void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
+{
+	struct cx23885_buffer    *prev;
+	struct cx23885_dev *dev = port->dev;
+	struct cx23885_dmaqueue  *cx88q = &port->mpegq;
+
+	/* 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(cx88q->stopper.dma);
+	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+	if (list_empty(&cx88q->active)) {
+		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 = VIDEOBUF_ACTIVE;
+		buf->count    = cx88q->count++;
+		mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT);
+		dprintk(1, "[%p/%d] %s - first active\n",
+			buf, buf->vb.i, __func__);
+	} else {
+		dprintk(1, "queue is not empty - append to active\n");
+		prev = list_entry(cx88q->active.prev, struct cx23885_buffer,
+				  vb.queue);
+		list_add_tail(&buf->vb.queue, &cx88q->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 */
+		dprintk(1, "[%p/%d] %s - append to active\n",
+			 buf, buf->vb.i, __func__);
+	}
+}
+
+/* ----------------------------------------------------------- */
+
+static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
+			      int restart)
+{
+	struct cx23885_dev *dev = port->dev;
+	struct cx23885_dmaqueue *q = &port->mpegq;
+	struct cx23885_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->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);
+		dprintk(1, "[%p/%d] %s - dma=0x%08lx\n",
+			buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+	}
+	if (restart) {
+		dprintk(1, "restarting queue\n");
+		cx23885_restart_queue(port, q);
+	}
+	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", __func__);
+	del_timer_sync(&q->timeout);
+	cx23885_stop_dma(port);
+	do_cancel_buffers(port, "cancel", 0);
+}
+
+static void cx23885_timeout(unsigned long data)
+{
+	struct cx23885_tsport *port = (struct cx23885_tsport *)data;
+	struct cx23885_dev *dev = port->dev;
+
+	dprintk(1, "%s()\n", __func__);
+
+	if (debug > 5)
+		cx23885_sram_channel_dump(dev,
+			&dev->sram_channels[port->sram_chno]);
+
+	cx23885_stop_dma(port);
+	do_cancel_buffers(port, "timeout", 1);
+}
+
+int cx23885_irq_417(struct cx23885_dev *dev, u32 status)
+{
+	/* FIXME: port1 assumption here. */
+	struct cx23885_tsport *port = &dev->ts1;
+	int count = 0;
+	int handled = 0;
+
+	if (status == 0)
+		return handled;
+
+	count = cx_read(port->reg_gpcnt);
+	dprintk(7, "status: 0x%08x  mask: 0x%08x count: 0x%x\n",
+		status, cx_read(port->reg_ts_int_msk), count);
+
+	if ((status & VID_B_MSK_BAD_PKT)         ||
+		(status & VID_B_MSK_OPC_ERR)     ||
+		(status & VID_B_MSK_VBI_OPC_ERR) ||
+		(status & VID_B_MSK_SYNC)        ||
+		(status & VID_B_MSK_VBI_SYNC)    ||
+		(status & VID_B_MSK_OF)          ||
+		(status & VID_B_MSK_VBI_OF)) {
+		printk(KERN_ERR "%s: V4L mpeg risc op code error, status "
+			"= 0x%x\n", dev->name, status);
+		if (status & VID_B_MSK_BAD_PKT)
+			dprintk(1, "        VID_B_MSK_BAD_PKT\n");
+		if (status & VID_B_MSK_OPC_ERR)
+			dprintk(1, "        VID_B_MSK_OPC_ERR\n");
+		if (status & VID_B_MSK_VBI_OPC_ERR)
+			dprintk(1, "        VID_B_MSK_VBI_OPC_ERR\n");
+		if (status & VID_B_MSK_SYNC)
+			dprintk(1, "        VID_B_MSK_SYNC\n");
+		if (status & VID_B_MSK_VBI_SYNC)
+			dprintk(1, "        VID_B_MSK_VBI_SYNC\n");
+		if (status & VID_B_MSK_OF)
+			dprintk(1, "        VID_B_MSK_OF\n");
+		if (status & VID_B_MSK_VBI_OF)
+			dprintk(1, "        VID_B_MSK_VBI_OF\n");
+
+		cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+		cx23885_sram_channel_dump(dev,
+			&dev->sram_channels[port->sram_chno]);
+		cx23885_417_check_encoder(dev);
+	} else if (status & VID_B_MSK_RISCI1) {
+		dprintk(7, "        VID_B_MSK_RISCI1\n");
+		spin_lock(&port->slock);
+		cx23885_wakeup(port, &port->mpegq, count);
+		spin_unlock(&port->slock);
+	} else if (status & VID_B_MSK_RISCI2) {
+		dprintk(7, "        VID_B_MSK_RISCI2\n");
+		spin_lock(&port->slock);
+		cx23885_restart_queue(port, &port->mpegq);
+		spin_unlock(&port->slock);
+	}
+	if (status) {
+		cx_write(port->reg_ts_int_stat, status);
+		handled = 1;
+	}
+
+	return handled;
+}
+
+static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status)
+{
+	struct cx23885_dev *dev = port->dev;
+	int handled = 0;
+	u32 count;
+
+	if ((status & VID_BC_MSK_OPC_ERR) ||
+		(status & VID_BC_MSK_BAD_PKT) ||
+		(status & VID_BC_MSK_SYNC) ||
+		(status & VID_BC_MSK_OF)) {
+
+		if (status & VID_BC_MSK_OPC_ERR)
+			dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+				VID_BC_MSK_OPC_ERR);
+
+		if (status & VID_BC_MSK_BAD_PKT)
+			dprintk(7, " (VID_BC_MSK_BAD_PKT 0x%08x)\n",
+				VID_BC_MSK_BAD_PKT);
+
+		if (status & VID_BC_MSK_SYNC)
+			dprintk(7, " (VID_BC_MSK_SYNC    0x%08x)\n",
+				VID_BC_MSK_SYNC);
+
+		if (status & VID_BC_MSK_OF)
+			dprintk(7, " (VID_BC_MSK_OF      0x%08x)\n",
+				VID_BC_MSK_OF);
+
+		printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name);
+
+		cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+		cx23885_sram_channel_dump(dev,
+			&dev->sram_channels[port->sram_chno]);
+
+	} else if (status & VID_BC_MSK_RISCI1) {
+
+		dprintk(7, " (RISCI1            0x%08x)\n", VID_BC_MSK_RISCI1);
+
+		spin_lock(&port->slock);
+		count = cx_read(port->reg_gpcnt);
+		cx23885_wakeup(port, &port->mpegq, count);
+		spin_unlock(&port->slock);
+
+	} else if (status & VID_BC_MSK_RISCI2) {
+
+		dprintk(7, " (RISCI2            0x%08x)\n", VID_BC_MSK_RISCI2);
+
+		spin_lock(&port->slock);
+		cx23885_restart_queue(port, &port->mpegq);
+		spin_unlock(&port->slock);
+
+	}
+	if (status) {
+		cx_write(port->reg_ts_int_stat, status);
+		handled = 1;
+	}
+
+	return handled;
+}
+
+static irqreturn_t cx23885_irq(int irq, void *dev_id)
+{
+	struct cx23885_dev *dev = 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 audint_status, audint_mask;
+	u32 ts1_status, ts1_mask;
+	u32 ts2_status, ts2_mask;
+	int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+	int audint_count = 0;
+	bool subdev_handled;
+
+	pci_status = cx_read(PCI_INT_STAT);
+	pci_mask = cx23885_irq_get_mask(dev);
+	vida_status = cx_read(VID_A_INT_STAT);
+	vida_mask = cx_read(VID_A_INT_MSK);
+	audint_status = cx_read(AUDIO_INT_INT_STAT);
+	audint_mask = cx_read(AUDIO_INT_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);
+	ts2_mask = cx_read(VID_C_INT_MSK);
+
+	if ((pci_status == 0) && (ts2_status == 0) && (ts1_status == 0))
+		goto out;
+
+	vida_count = cx_read(VID_A_GPCNT);
+	audint_count = cx_read(AUD_INT_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, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
+		vida_status, vida_mask, vida_count);
+	dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n",
+		audint_status, audint_mask, audint_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_MSK_RISC_WR |
+			  PCI_MSK_AL_RD   | PCI_MSK_AL_WR   | PCI_MSK_APB_DMA |
+			  PCI_MSK_VID_C   | PCI_MSK_VID_B   | PCI_MSK_VID_A   |
+			  PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
+			  PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
+			  PCI_MSK_AV_CORE | PCI_MSK_IR)) {
+
+		if (pci_status & PCI_MSK_RISC_RD)
+			dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
+				PCI_MSK_RISC_RD);
+
+		if (pci_status & PCI_MSK_RISC_WR)
+			dprintk(7, " (PCI_MSK_RISC_WR   0x%08x)\n",
+				PCI_MSK_RISC_WR);
+
+		if (pci_status & PCI_MSK_AL_RD)
+			dprintk(7, " (PCI_MSK_AL_RD     0x%08x)\n",
+				PCI_MSK_AL_RD);
+
+		if (pci_status & PCI_MSK_AL_WR)
+			dprintk(7, " (PCI_MSK_AL_WR     0x%08x)\n",
+				PCI_MSK_AL_WR);
+
+		if (pci_status & PCI_MSK_APB_DMA)
+			dprintk(7, " (PCI_MSK_APB_DMA   0x%08x)\n",
+				PCI_MSK_APB_DMA);
+
+		if (pci_status & PCI_MSK_VID_C)
+			dprintk(7, " (PCI_MSK_VID_C     0x%08x)\n",
+				PCI_MSK_VID_C);
+
+		if (pci_status & PCI_MSK_VID_B)
+			dprintk(7, " (PCI_MSK_VID_B     0x%08x)\n",
+				PCI_MSK_VID_B);
+
+		if (pci_status & PCI_MSK_VID_A)
+			dprintk(7, " (PCI_MSK_VID_A     0x%08x)\n",
+				PCI_MSK_VID_A);
+
+		if (pci_status & PCI_MSK_AUD_INT)
+			dprintk(7, " (PCI_MSK_AUD_INT   0x%08x)\n",
+				PCI_MSK_AUD_INT);
+
+		if (pci_status & PCI_MSK_AUD_EXT)
+			dprintk(7, " (PCI_MSK_AUD_EXT   0x%08x)\n",
+				PCI_MSK_AUD_EXT);
+
+		if (pci_status & PCI_MSK_GPIO0)
+			dprintk(7, " (PCI_MSK_GPIO0     0x%08x)\n",
+				PCI_MSK_GPIO0);
+
+		if (pci_status & PCI_MSK_GPIO1)
+			dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
+				PCI_MSK_GPIO1);
+
+		if (pci_status & PCI_MSK_AV_CORE)
+			dprintk(7, " (PCI_MSK_AV_CORE   0x%08x)\n",
+				PCI_MSK_AV_CORE);
+
+		if (pci_status & PCI_MSK_IR)
+			dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
+				PCI_MSK_IR);
+	}
+
+	if (cx23885_boards[dev->board].ci_type == 1 &&
+			(pci_status & (PCI_MSK_GPIO1 | PCI_MSK_GPIO0)))
+		handled += netup_ci_slot_status(dev, pci_status);
+
+	if (cx23885_boards[dev->board].ci_type == 2 &&
+			(pci_status & PCI_MSK_GPIO0))
+		handled += altera_ci_irq(dev);
+
+	if (ts1_status) {
+		if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+			handled += cx23885_irq_ts(ts1, ts1_status);
+		else
+		if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+			handled += cx23885_irq_417(dev, ts1_status);
+	}
+
+	if (ts2_status) {
+		if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+			handled += cx23885_irq_ts(ts2, ts2_status);
+		else
+		if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)
+			handled += cx23885_irq_417(dev, ts2_status);
+	}
+
+	if (vida_status)
+		handled += cx23885_video_irq(dev, vida_status);
+
+	if (audint_status)
+		handled += cx23885_audio_irq(dev, audint_status, audint_mask);
+
+	if (pci_status & PCI_MSK_IR) {
+		subdev_handled = false;
+		v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+				 pci_status, &subdev_handled);
+		if (subdev_handled)
+			handled++;
+	}
+
+	if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+		cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+		if (!schedule_work(&dev->cx25840_work))
+			printk(KERN_ERR "%s: failed to set up deferred work for"
+			       " AV Core/IR interrupt. Interrupt is disabled"
+			       " and won't be re-enabled\n", dev->name);
+		handled++;
+	}
+
+	if (handled)
+		cx_write(PCI_INT_STAT, pci_status);
+out:
+	return IRQ_RETVAL(handled);
+}
+
+static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
+				    unsigned int notification, void *arg)
+{
+	struct cx23885_dev *dev;
+
+	if (sd == NULL)
+		return;
+
+	dev = to_cx23885(sd->v4l2_dev);
+
+	switch (notification) {
+	case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
+		if (sd == dev->sd_ir)
+			cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
+		break;
+	case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
+		if (sd == dev->sd_ir)
+			cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
+		break;
+	}
+}
+
+static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
+{
+	INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
+	INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
+	INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
+	dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
+}
+
+static inline int encoder_on_portb(struct cx23885_dev *dev)
+{
+	return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER;
+}
+
+static inline int encoder_on_portc(struct cx23885_dev *dev)
+{
+	return cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER;
+}
+
+/* Mask represents 32 different GPIOs, GPIO's are split into multiple
+ * registers depending on the board configuration (and whether the
+ * 417 encoder (wi it's own GPIO's) are present. Each GPIO bit will
+ * be pushed into the correct hardware register, regardless of the
+ * physical location. Certain registers are shared so we sanity check
+ * and report errors if we think we're tampering with a GPIo that might
+ * be assigned to the encoder (and used for the host bus).
+ *
+ * GPIO  2 thru  0 - On the cx23885 bridge
+ * GPIO 18 thru  3 - On the cx23417 host bus interface
+ * GPIO 23 thru 19 - On the cx25840 a/v core
+ */
+void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+	if (mask & 0x7)
+		cx_set(GP0_IO, mask & 0x7);
+
+	if (mask & 0x0007fff8) {
+		if (encoder_on_portb(dev) || encoder_on_portc(dev))
+			printk(KERN_ERR
+				"%s: Setting GPIO on encoder ports\n",
+				dev->name);
+		cx_set(MC417_RWD, (mask & 0x0007fff8) >> 3);
+	}
+
+	/* TODO: 23-19 */
+	if (mask & 0x00f80000)
+		printk(KERN_INFO "%s: Unsupported\n", dev->name);
+}
+
+void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+	if (mask & 0x00000007)
+		cx_clear(GP0_IO, mask & 0x7);
+
+	if (mask & 0x0007fff8) {
+		if (encoder_on_portb(dev) || encoder_on_portc(dev))
+			printk(KERN_ERR
+				"%s: Clearing GPIO moving on encoder ports\n",
+				dev->name);
+		cx_clear(MC417_RWD, (mask & 0x7fff8) >> 3);
+	}
+
+	/* TODO: 23-19 */
+	if (mask & 0x00f80000)
+		printk(KERN_INFO "%s: Unsupported\n", dev->name);
+}
+
+u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask)
+{
+	if (mask & 0x00000007)
+		return (cx_read(GP0_IO) >> 8) & mask & 0x7;
+
+	if (mask & 0x0007fff8) {
+		if (encoder_on_portb(dev) || encoder_on_portc(dev))
+			printk(KERN_ERR
+				"%s: Reading GPIO moving on encoder ports\n",
+				dev->name);
+		return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3;
+	}
+
+	/* TODO: 23-19 */
+	if (mask & 0x00f80000)
+		printk(KERN_INFO "%s: Unsupported\n", dev->name);
+
+	return 0;
+}
+
+void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+	if ((mask & 0x00000007) && asoutput)
+		cx_set(GP0_IO, (mask & 0x7) << 16);
+	else if ((mask & 0x00000007) && !asoutput)
+		cx_clear(GP0_IO, (mask & 0x7) << 16);
+
+	if (mask & 0x0007fff8) {
+		if (encoder_on_portb(dev) || encoder_on_portc(dev))
+			printk(KERN_ERR
+				"%s: Enabling GPIO on encoder ports\n",
+				dev->name);
+	}
+
+	/* MC417_OEN is active low for output, write 1 for an input */
+	if ((mask & 0x0007fff8) && asoutput)
+		cx_clear(MC417_OEN, (mask & 0x7fff8) >> 3);
+
+	else if ((mask & 0x0007fff8) && !asoutput)
+		cx_set(MC417_OEN, (mask & 0x7fff8) >> 3);
+
+	/* TODO: 23-19 */
+}
+
+static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
+				     const struct pci_device_id *pci_id)
+{
+	struct cx23885_dev *dev;
+	int err;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+
+	err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+	if (err < 0)
+		goto fail_free;
+
+	/* Prepare to handle notifications from subdevices */
+	cx23885_v4l2_dev_notify_init(dev);
+
+	/* pci init */
+	dev->pci = pci_dev;
+	if (pci_enable_device(pci_dev)) {
+		err = -EIO;
+		goto fail_unreg;
+	}
+
+	if (cx23885_dev_setup(dev) < 0) {
+		err = -EINVAL;
+		goto fail_unreg;
+	}
+
+	/* print pci info */
+	dev->pci_rev = pci_dev->revision;
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+	printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", dev->name,
+	       pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+	       dev->pci_lat,
+		(unsigned long long)pci_resource_start(pci_dev, 0));
+
+	pci_set_master(pci_dev);
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+		err = -EIO;
+		goto fail_irq;
+	}
+
+	err = request_irq(pci_dev->irq, cx23885_irq,
+			  IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+	if (err < 0) {
+		printk(KERN_ERR "%s: can't get IRQ %d\n",
+		       dev->name, pci_dev->irq);
+		goto fail_irq;
+	}
+
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0);
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+		cx23885_irq_add_enable(dev, PCI_MSK_GPIO0);
+		break;
+	}
+
+	/*
+	 * The CX2388[58] IR controller can start firing interrupts when
+	 * enabled, so these have to take place after the cx23885_irq() handler
+	 * is hooked up by the call to request_irq() above.
+	 */
+	cx23885_ir_pci_int_enable(dev);
+	cx23885_input_init(dev);
+
+	return 0;
+
+fail_irq:
+	cx23885_dev_unregister(dev);
+fail_unreg:
+	v4l2_device_unregister(&dev->v4l2_dev);
+fail_free:
+	kfree(dev);
+	return err;
+}
+
+static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct cx23885_dev *dev = to_cx23885(v4l2_dev);
+
+	cx23885_input_fini(dev);
+	cx23885_ir_fini(dev);
+
+	cx23885_shutdown(dev);
+
+	pci_disable_device(pci_dev);
+
+	/* unregister stuff */
+	free_irq(pci_dev->irq, dev);
+
+	cx23885_dev_unregister(dev);
+	v4l2_device_unregister(v4l2_dev);
+	kfree(dev);
+}
+
+static struct pci_device_id cx23885_pci_tbl[] = {
+	{
+		/* CX23885 */
+		.vendor       = 0x14f1,
+		.device       = 0x8852,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	}, {
+		/* CX23887 Rev 2 */
+		.vendor       = 0x14f1,
+		.device       = 0x8880,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	}, {
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, cx23885_pci_tbl);
+
+static struct pci_driver cx23885_pci_driver = {
+	.name     = "cx23885",
+	.id_table = cx23885_pci_tbl,
+	.probe    = cx23885_initdev,
+	.remove   = __devexit_p(cx23885_finidev),
+	/* TODO */
+	.suspend  = NULL,
+	.resume   = NULL,
+};
+
+static int __init cx23885_init(void)
+{
+	printk(KERN_INFO "cx23885 driver version %s loaded\n",
+		CX23885_VERSION);
+	return pci_register_driver(&cx23885_pci_driver);
+}
+
+static void __exit cx23885_fini(void)
+{
+	pci_unregister_driver(&cx23885_pci_driver);
+}
+
+module_init(cx23885_init);
+module_exit(cx23885_fini);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
new file mode 100644
index 000000000000..4379d8a6dad5
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -0,0 +1,1412 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+
+#include "dvb_ca_en50221.h"
+#include "s5h1409.h"
+#include "s5h1411.h"
+#include "mt2131.h"
+#include "tda8290.h"
+#include "tda18271.h"
+#include "lgdt330x.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "max2165.h"
+#include "tda10048.h"
+#include "tuner-xc2028.h"
+#include "tuner-simple.h"
+#include "dib7000p.h"
+#include "dibx000_common.h"
+#include "zl10353.h"
+#include "stv0900.h"
+#include "stv0900_reg.h"
+#include "stv6110.h"
+#include "lnbh24.h"
+#include "cx24116.h"
+#include "cimax2.h"
+#include "lgs8gxx.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "lgdt3305.h"
+#include "atbm8830.h"
+#include "ds3000.h"
+#include "cx23885-f300.h"
+#include "altera-ci.h"
+#include "stv0367.h"
+#include "drxk.h"
+#include "mt2063.h"
+#include "stv090x.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+
+static unsigned int debug;
+
+#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");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+			 unsigned int *count, unsigned int *size)
+{
+	struct cx23885_tsport *port = q->priv_data;
+
+	port->ts_packet_size  = 188 * 4;
+	port->ts_packet_count = 32;
+
+	*size  = port->ts_packet_size * port->ts_packet_count;
+	*count = 32;
+	return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+			   struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct cx23885_tsport *port = q->priv_data;
+	return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct cx23885_tsport *port = q->priv_data;
+	cx23885_buf_queue(port, (struct cx23885_buffer *)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+			    struct videobuf_buffer *vb)
+{
+	cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe);
+
+static void cx23885_dvb_gate_ctrl(struct cx23885_tsport  *port, int open)
+{
+	struct videobuf_dvb_frontends *f;
+	struct videobuf_dvb_frontend *fe;
+
+	f = &port->frontends;
+
+	if (f->gate <= 1) /* undefined or fe0 */
+		fe = videobuf_dvb_get_frontend(f, 1);
+	else
+		fe = videobuf_dvb_get_frontend(f, f->gate);
+
+	if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+		fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+
+	/*
+	 * FIXME: Improve this path to avoid calling the
+	 * cx23885_dvb_set_frontend() every time it passes here.
+	 */
+	cx23885_dvb_set_frontend(fe->dvb.frontend);
+}
+
+static struct videobuf_queue_ops dvb_qops = {
+	.buf_setup    = dvb_buf_setup,
+	.buf_prepare  = dvb_buf_prepare,
+	.buf_queue    = dvb_buf_queue,
+	.buf_release  = dvb_buf_release,
+};
+
+static struct s5h1409_config hauppauge_generic_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 tda10048_config hauppauge_hvr1200_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_SERIAL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3800,
+	.dtv8_if_freq_khz = TDA10048_IF_4300,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+};
+
+static struct tda10048_config hauppauge_hvr1210_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_SERIAL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3500,
+	.dtv8_if_freq_khz = TDA10048_IF_4000,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+};
+
+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,
+	.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,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct mt2131_config hauppauge_generic_tunerconfig = {
+	0x61
+};
+
+static struct lgdt330x_config fusionhdtv_5_express = {
+	.demod_address = 0x0e,
+	.demod_chip = LGDT3303,
+	.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 s5h1409_config dvico_s5h1409_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 s5h1411_config dvico_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_ON,
+	.qam_if        = S5H1411_IF_44000,
+	.vsb_if        = S5H1411_IF_44000,
+	.inversion     = S5H1411_INVERSION_OFF,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config hcw_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.vsb_if        = S5H1411_IF_44000,
+	.qam_if        = S5H1411_IF_4000,
+	.inversion     = S5H1411_INVERSION_ON,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
+	.i2c_address      = 0x61,
+	.if_khz           = 5380,
+};
+
+static struct xc5000_config dvico_xc5000_tunerconfig = {
+	.i2c_address      = 0x64,
+	.if_khz           = 5380,
+};
+
+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, .agc_mode = 3, .std = 3,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = {
+	.dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_7   = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_8   = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+	.std_map = &hauppauge_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1200_tuner_config = {
+	.std_map = &hauppauge_hvr1200_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1210_tuner_config = {
+	.gate    = TDA18271_GATE_DIGITAL,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map hauppauge_hvr127x_std_map = {
+	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x58 },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x58 },
+};
+
+static struct tda18271_config hauppauge_hvr127x_config = {
+	.std_map = &hauppauge_hvr127x_std_map,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+	.i2c_addr           = 0x0e,
+	.mpeg_mode          = LGDT3305_MPEG_SERIAL,
+	.tpclk_edge         = LGDT3305_TPCLK_FALLING_EDGE,
+	.tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
+	.deny_i2c_rptr      = 1,
+	.spectral_inversion = 1,
+	.qam_if_khz         = 4000,
+	.vsb_if_khz         = 3250,
+};
+
+static struct dibx000_agc_config xc3028_agc_config = {
+	BAND_VHF | BAND_UHF,	/* band_caps */
+
+	/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0,
+	 * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+	 * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0,
+	 * P_agc_nb_est=2, P_agc_write=0
+	 */
+	(0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) |
+		(3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */
+
+	712,	/* inv_gain */
+	21,	/* time_stabiliz */
+
+	0,	/* alpha_level */
+	118,	/* thlock */
+
+	0,	/* wbd_inv */
+	2867,	/* wbd_ref */
+	0,	/* wbd_sel */
+	2,	/* wbd_alpha */
+
+	0,	/* agc1_max */
+	0,	/* agc1_min */
+	39718,	/* agc2_max */
+	9930,	/* agc2_min */
+	0,	/* agc1_pt1 */
+	0,	/* agc1_pt2 */
+	0,	/* agc1_pt3 */
+	0,	/* agc1_slope1 */
+	0,	/* agc1_slope2 */
+	0,	/* agc2_pt1 */
+	128,	/* agc2_pt2 */
+	29,	/* agc2_slope1 */
+	29,	/* agc2_slope2 */
+
+	17,	/* alpha_mant */
+	27,	/* alpha_exp */
+	23,	/* beta_mant */
+	51,	/* beta_exp */
+
+	1,	/* perform_agc_softsplit */
+};
+
+/* PLL Configuration for COFDM BW_MHz = 8.000000
+ * With external clock = 30.000000 */
+static struct dibx000_bandwidth_config xc3028_bw_config = {
+	60000,	/* internal */
+	30000,	/* sampling */
+	1,	/* pll_cfg: prediv */
+	8,	/* pll_cfg: ratio */
+	3,	/* pll_cfg: range */
+	1,	/* pll_cfg: reset */
+	0,	/* pll_cfg: bypass */
+	0,	/* misc: refdiv */
+	0,	/* misc: bypclk_div */
+	1,	/* misc: IO_CLK_en_core */
+	1,	/* misc: ADClkSrc */
+	0,	/* misc: modulo */
+	(3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
+	(1 << 25) | 5816102, /* ifreq = 5.200000 MHz */
+	20452225, /* timf */
+	30000000  /* xtal_hz */
+};
+
+static struct dib7000p_config hauppauge_hvr1400_dib7000_config = {
+	.output_mpeg2_in_188_bytes = 1,
+	.hostbus_diversity = 1,
+	.tuner_is_baseband = 0,
+	.update_lna  = NULL,
+
+	.agc_config_count = 1,
+	.agc = &xc3028_agc_config,
+	.bw  = &xc3028_bw_config,
+
+	.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+	.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+	.gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+	.pwm_freq_div = 0,
+	.agc_control  = NULL,
+	.spur_protect = 0,
+
+	.output_mode = OUTMODE_MPEG2_SERIAL,
+};
+
+static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+	.demod_address = 0x0f,
+	.if2           = 45600,
+	.no_tuner      = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static struct stv0900_reg stv0900_ts_regs[] = {
+	{ R0900_TSGENERAL, 0x00 },
+	{ R0900_P1_TSSPEED, 0x40 },
+	{ R0900_P2_TSSPEED, 0x40 },
+	{ R0900_P1_TSCFGM, 0xc0 },
+	{ R0900_P2_TSCFGM, 0xc0 },
+	{ R0900_P1_TSCFGH, 0xe0 },
+	{ R0900_P2_TSCFGH, 0xe0 },
+	{ R0900_P1_TSCFGL, 0x20 },
+	{ R0900_P2_TSCFGL, 0x20 },
+	{ 0xffff, 0xff }, /* terminate */
+};
+
+static struct stv0900_config netup_stv0900_config = {
+	.demod_address = 0x68,
+	.demod_mode = 1, /* dual */
+	.xtal = 8000000,
+	.clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+	.diseqc_mode = 2,/* 2/3 PWM */
+	.ts_config_regs = stv0900_ts_regs,
+	.tun1_maddress = 0,/* 0x60 */
+	.tun2_maddress = 3,/* 0x63 */
+	.tun1_adc = 1,/* 1 Vpp */
+	.tun2_adc = 1,/* 1 Vpp */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_a = {
+	.i2c_address = 0x60,
+	.mclk = 16000000,
+	.clk_div = 1,
+	.gain = 8, /* +16 dB  - maximum gain */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_b = {
+	.i2c_address = 0x63,
+	.mclk = 16000000,
+	.clk_div = 1,
+	.gain = 8, /* +16 dB  - maximum gain */
+};
+
+static struct cx24116_config tbs_cx24116_config = {
+	.demod_address = 0x55,
+};
+
+static struct ds3000_config tevii_ds3000_config = {
+	.demod_address = 0x68,
+};
+
+static struct cx24116_config dvbworld_cx24116_config = {
+	.demod_address = 0x05,
+};
+
+static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = {
+	.prod = LGS8GXX_PROD_LGS8GL5,
+	.demod_address = 0x19,
+	.serial_ts = 0,
+	.ts_clk_pol = 1,
+	.ts_clk_gated = 1,
+	.if_clk_freq = 30400, /* 30.4 MHz */
+	.if_freq = 5380, /* 5.38 MHz */
+	.if_neg_center = 1,
+	.ext_adc = 0,
+	.adc_signed = 0,
+	.if_neg_edge = 0,
+};
+
+static struct xc5000_config mygica_x8506_xc5000_config = {
+	.i2c_address = 0x61,
+	.if_khz = 5380,
+};
+
+static struct stv090x_config prof_8000_stv090x_config = {
+        .device                 = STV0903,
+        .demod_mode             = STV090x_SINGLE,
+        .clk_mode               = STV090x_CLK_EXT,
+        .xtal                   = 27000000,
+        .address                = 0x6A,
+        .ts1_mode               = STV090x_TSMODE_PARALLEL_PUNCTURED,
+        .repeater_level         = STV090x_RPTLEVEL_64,
+        .adc1_range             = STV090x_ADC_2Vpp,
+        .diseqc_envelope_mode   = false,
+
+        .tuner_get_frequency    = stb6100_get_frequency,
+        .tuner_set_frequency    = stb6100_set_frequency,
+        .tuner_set_bandwidth    = stb6100_set_bandwidth,
+        .tuner_get_bandwidth    = stb6100_get_bandwidth,
+};
+
+static struct stb6100_config prof_8000_stb6100_config = {
+	.tuner_address = 0x60,
+	.refclock = 27000000,
+};
+
+static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+
+	if (voltage == SEC_VOLTAGE_18)
+		cx_write(MC417_RWD, 0x00001e00);
+	else if (voltage == SEC_VOLTAGE_13)
+		cx_write(MC417_RWD, 0x00001a00);
+	else
+		cx_write(MC417_RWD, 0x00001800);
+	return 0;
+}
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+		switch (p->modulation) {
+		case VSB_8:
+			cx23885_gpio_clear(dev, GPIO_5);
+			break;
+		case QAM_64:
+		case QAM_256:
+		default:
+			cx23885_gpio_set(dev, GPIO_5);
+			break;
+		}
+		break;
+	case CX23885_BOARD_MYGICA_X8506:
+	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+		/* Select Digital TV */
+		cx23885_gpio_set(dev, GPIO_0);
+		break;
+	}
+	return 0;
+}
+
+static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
+	.prod = LGS8GXX_PROD_LGS8G75,
+	.demod_address = 0x19,
+	.serial_ts = 0,
+	.ts_clk_pol = 1,
+	.ts_clk_gated = 1,
+	.if_clk_freq = 30400, /* 30.4 MHz */
+	.if_freq = 6500, /* 6.50 MHz */
+	.if_neg_center = 1,
+	.ext_adc = 0,
+	.adc_signed = 1,
+	.adc_vpp = 2, /* 1.6 Vpp */
+	.if_neg_edge = 1,
+};
+
+static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
+	.i2c_address = 0x61,
+	.if_khz = 6500,
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
+	.prod = ATBM8830_PROD_8830,
+	.demod_address = 0x44,
+	.serial_ts = 0,
+	.ts_sampling_edge = 1,
+	.ts_clk_gated = 0,
+	.osc_clk_freq = 30400, /* in kHz */
+	.if_freq = 0, /* zero IF */
+	.zif_swap_iq = 1,
+	.agc_min = 0x2E,
+	.agc_max = 0xFF,
+	.agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
+	.i2c_address = 0x60,
+	.osc_clk = 20
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
+	.prod = ATBM8830_PROD_8830,
+	.demod_address = 0x44,
+	.serial_ts = 1,
+	.ts_sampling_edge = 1,
+	.ts_clk_gated = 0,
+	.osc_clk_freq = 30400, /* in kHz */
+	.if_freq = 0, /* zero IF */
+	.zif_swap_iq = 1,
+	.agc_min = 0x2E,
+	.agc_max = 0xFF,
+	.agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
+	.i2c_address = 0x60,
+	.osc_clk = 20
+};
+static struct stv0367_config netup_stv0367_config[] = {
+	{
+		.demod_address = 0x1c,
+		.xtal = 27000000,
+		.if_khz = 4500,
+		.if_iq_mode = 0,
+		.ts_mode = 1,
+		.clk_pol = 0,
+	}, {
+		.demod_address = 0x1d,
+		.xtal = 27000000,
+		.if_khz = 4500,
+		.if_iq_mode = 0,
+		.ts_mode = 1,
+		.clk_pol = 0,
+	},
+};
+
+static struct xc5000_config netup_xc5000_config[] = {
+	{
+		.i2c_address = 0x61,
+		.if_khz = 4500,
+	}, {
+		.i2c_address = 0x64,
+		.if_khz = 4500,
+	},
+};
+
+static struct drxk_config terratec_drxk_config[] = {
+	{
+		.adr = 0x29,
+		.no_i2c_bridge = 1,
+	}, {
+		.adr = 0x2a,
+		.no_i2c_bridge = 1,
+	},
+};
+
+static struct mt2063_config terratec_mt2063_config[] = {
+	{
+		.tuner_address = 0x60,
+	}, {
+		.tuner_address = 0x67,
+	},
+};
+
+int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+{
+	struct cx23885_dev *dev = (struct cx23885_dev *)device;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1);
+	uint32_t mem = 0;
+
+	mem = cx_read(MC417_RWD);
+	if (read)
+		cx_set(MC417_OEN, ALT_DATA);
+	else {
+		cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */
+		mem &= ~ALT_DATA;
+		mem |= (data & ALT_DATA);
+	}
+
+	if (flag)
+		mem |= ALT_AD_RG;
+	else
+		mem &= ~ALT_AD_RG;
+
+	mem &= ~ALT_CS;
+	if (read)
+		mem = (mem & ~ALT_RD) | ALT_WR;
+	else
+		mem = (mem & ~ALT_WR) | ALT_RD;
+
+	cx_write(MC417_RWD, mem);  /* start RW cycle */
+
+	for (;;) {
+		mem = cx_read(MC417_RWD);
+		if ((mem & ALT_RDY) == 0)
+			break;
+		if (time_after(jiffies, timeout))
+			break;
+		udelay(1);
+	}
+
+	cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
+	if (read)
+		return mem & ALT_DATA;
+
+	return 0;
+};
+
+static int dvb_register(struct cx23885_tsport *port)
+{
+	struct cx23885_dev *dev = port->dev;
+	struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
+	struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+	int mfe_shared = 0; /* bus not shared by default */
+	int ret;
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	/* init struct videobuf_dvb */
+	fe0->dvb.name = dev->name;
+
+	/* multi-frontend gate control is undefined or defaults to fe0 */
+	port->frontends.gate = 0;
+
+	/* Sets the gate control callback to be used by i2c command calls */
+	port->gate_ctrl = cx23885_dvb_gate_ctrl;
+
+	/* init frontend */
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&hauppauge_generic_config,
+						&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(mt2131_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap,
+				   &hauppauge_generic_tunerconfig, 0);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1275:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+					       &hauppauge_lgdt3305_config,
+					       &i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_bus[1].i2c_adap,
+				   &hauppauge_hvr127x_config);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+					       &hcw_s5h1411_config,
+					       &i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_bus[1].i2c_adap,
+				   &hauppauge_tda18271_config);
+		}
+
+		tda18271_attach(&dev->ts1.analog_fe,
+			0x60, &dev->i2c_bus[1].i2c_adap,
+			&hauppauge_tda18271_config);
+
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1800:
+		i2c_bus = &dev->i2c_bus[0];
+		switch (alt_tuner) {
+		case 1:
+			fe0->dvb.frontend =
+				dvb_attach(s5h1409_attach,
+					   &hauppauge_ezqam_config,
+					   &i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				dvb_attach(tda829x_attach, fe0->dvb.frontend,
+					   &dev->i2c_bus[1].i2c_adap, 0x42,
+					   &tda829x_no_probe);
+				dvb_attach(tda18271_attach, fe0->dvb.frontend,
+					   0x60, &dev->i2c_bus[1].i2c_adap,
+					   &hauppauge_tda18271_config);
+			}
+			break;
+		case 0:
+		default:
+			fe0->dvb.frontend =
+				dvb_attach(s5h1409_attach,
+					   &hauppauge_generic_config,
+					   &i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL)
+				dvb_attach(mt2131_attach, fe0->dvb.frontend,
+					   &i2c_bus->i2c_adap,
+					   &hauppauge_generic_tunerconfig, 0);
+			break;
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&hauppauge_hvr1800lp_config,
+						&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(mt2131_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap,
+				   &hauppauge_generic_tunerconfig, 0);
+		}
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+						&fusionhdtv_5_express,
+						&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap, 0x61,
+				   TUNER_LG_TDVS_H06XF);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+		i2c_bus = &dev->i2c_bus[1];
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&hauppauge_hvr1500q_config,
+						&dev->i2c_bus[0].i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap,
+				   &hauppauge_hvr1500q_tunerconfig);
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1500:
+		i2c_bus = &dev->i2c_bus[1];
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&hauppauge_hvr1500_config,
+						&dev->i2c_bus[0].i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap  = &i2c_bus->i2c_adap,
+				.i2c_addr  = 0x61,
+			};
+			static struct xc2028_ctrl ctl = {
+				.fname       = XC2028_DEFAULT_FIRMWARE,
+				.max_len     = 64,
+				.demod       = XC3028_FE_OREN538,
+			};
+
+			fe = dvb_attach(xc2028_attach,
+					fe0->dvb.frontend, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctl);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1200:
+	case CX23885_BOARD_HAUPPAUGE_HVR1700:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
+			&hauppauge_hvr1200_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				&dev->i2c_bus[1].i2c_adap, 0x42,
+				&tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				0x60, &dev->i2c_bus[1].i2c_adap,
+				&hauppauge_hvr1200_tuner_config);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1210:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
+			&hauppauge_hvr1210_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				0x60, &dev->i2c_bus[1].i2c_adap,
+				&hauppauge_hvr1210_tuner_config);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1400:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(dib7000p_attach,
+			&i2c_bus->i2c_adap,
+			0x12, &hauppauge_hvr1400_dib7000_config);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap  = &dev->i2c_bus[1].i2c_adap,
+				.i2c_addr  = 0x64,
+			};
+			static struct xc2028_ctrl ctl = {
+				.fname   = XC3028L_DEFAULT_FIRMWARE,
+				.max_len = 64,
+				.demod   = XC3028_FE_DIBCOM52,
+				/* This is true for all demods with
+					v36 firmware? */
+				.type    = XC2028_D2633,
+			};
+
+			fe = dvb_attach(xc2028_attach,
+					fe0->dvb.frontend, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctl);
+		}
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+		i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&dvico_s5h1409_config,
+						&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend == NULL)
+			fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+							&dvico_s5h1411_config,
+							&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap,
+				   &dvico_xc5000_tunerconfig);
+		break;
+	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
+		i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_xc3028,
+					       &i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend      *fe;
+			struct xc2028_config	  cfg = {
+				.i2c_adap  = &i2c_bus->i2c_adap,
+				.i2c_addr  = 0x61,
+			};
+			static struct xc2028_ctrl ctl = {
+				.fname       = XC2028_DEFAULT_FIRMWARE,
+				.max_len     = 64,
+				.demod       = XC3028_FE_ZARLINK456,
+			};
+
+			fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+					&cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctl);
+		}
+		break;
+	}
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+		i2c_bus = &dev->i2c_bus[0];
+
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+			&dvico_fusionhdtv_xc3028,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend      *fe;
+			struct xc2028_config	  cfg = {
+				.i2c_adap  = &dev->i2c_bus[1].i2c_adap,
+				.i2c_addr  = 0x61,
+			};
+			static struct xc2028_ctrl ctl = {
+				.fname       = XC2028_DEFAULT_FIRMWARE,
+				.max_len     = 64,
+				.demod       = XC3028_FE_ZARLINK456,
+			};
+
+			fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+				&cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctl);
+		}
+		break;
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+		i2c_bus = &dev->i2c_bus[0];
+
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_xc3028,
+					       &i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend	*fe;
+			struct xc4000_config	cfg = {
+				.i2c_address	  = 0x61,
+				.default_pm	  = 0,
+				.dvb_amplitude	  = 134,
+				.set_smoothedcvbs = 1,
+				.if_khz		  = 4560
+			};
+
+			fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
+					&dev->i2c_bus[1].i2c_adap, &cfg);
+			if (!fe) {
+				printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+				       dev->name);
+				goto frontend_detach;
+			}
+		}
+		break;
+	case CX23885_BOARD_TBS_6920:
+		i2c_bus = &dev->i2c_bus[1];
+
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+					&tbs_cx24116_config,
+					&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+		break;
+	case CX23885_BOARD_TEVII_S470:
+		i2c_bus = &dev->i2c_bus[1];
+
+		fe0->dvb.frontend = dvb_attach(ds3000_attach,
+					&tevii_ds3000_config,
+					&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+		break;
+	case CX23885_BOARD_DVBWORLD_2005:
+		i2c_bus = &dev->i2c_bus[1];
+
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+			&dvbworld_cx24116_config,
+			&i2c_bus->i2c_adap);
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		i2c_bus = &dev->i2c_bus[0];
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			fe0->dvb.frontend = dvb_attach(stv0900_attach,
+							&netup_stv0900_config,
+							&i2c_bus->i2c_adap, 0);
+			if (fe0->dvb.frontend != NULL) {
+				if (dvb_attach(stv6110_attach,
+						fe0->dvb.frontend,
+						&netup_stv6110_tunerconfig_a,
+						&i2c_bus->i2c_adap)) {
+					if (!dvb_attach(lnbh24_attach,
+							fe0->dvb.frontend,
+							&i2c_bus->i2c_adap,
+							LNBH24_PCL | LNBH24_TTX,
+							LNBH24_TEN, 0x09))
+						printk(KERN_ERR
+							"No LNBH24 found!\n");
+
+				}
+			}
+			break;
+		/* port C */
+		case 2:
+			fe0->dvb.frontend = dvb_attach(stv0900_attach,
+							&netup_stv0900_config,
+							&i2c_bus->i2c_adap, 1);
+			if (fe0->dvb.frontend != NULL) {
+				if (dvb_attach(stv6110_attach,
+						fe0->dvb.frontend,
+						&netup_stv6110_tunerconfig_b,
+						&i2c_bus->i2c_adap)) {
+					if (!dvb_attach(lnbh24_attach,
+							fe0->dvb.frontend,
+							&i2c_bus->i2c_adap,
+							LNBH24_PCL | LNBH24_TTX,
+							LNBH24_TEN, 0x0a))
+						printk(KERN_ERR
+							"No LNBH24 found!\n");
+
+				}
+			}
+			break;
+		}
+		break;
+	case CX23885_BOARD_MYGICA_X8506:
+		i2c_bus = &dev->i2c_bus[0];
+		i2c_bus2 = &dev->i2c_bus[1];
+		fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+			&mygica_x8506_lgs8gl5_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(xc5000_attach,
+				fe0->dvb.frontend,
+				&i2c_bus2->i2c_adap,
+				&mygica_x8506_xc5000_config);
+		}
+		break;
+	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+		i2c_bus = &dev->i2c_bus[0];
+		i2c_bus2 = &dev->i2c_bus[1];
+		fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+			&magicpro_prohdtve2_lgs8g75_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(xc5000_attach,
+				fe0->dvb.frontend,
+				&i2c_bus2->i2c_adap,
+				&magicpro_prohdtve2_xc5000_config);
+		}
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+			&hcw_s5h1411_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				0x60, &dev->i2c_bus[0].i2c_adap,
+				&hauppauge_tda18271_config);
+
+		tda18271_attach(&dev->ts1.analog_fe,
+			0x60, &dev->i2c_bus[1].i2c_adap,
+			&hauppauge_tda18271_config);
+
+		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+			&hcw_s5h1411_config,
+			&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				0x60, &dev->i2c_bus[0].i2c_adap,
+				&hauppauge_tda18271_config);
+		break;
+	case CX23885_BOARD_MYGICA_X8558PRO:
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			i2c_bus = &dev->i2c_bus[0];
+			fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+				&mygica_x8558pro_atbm8830_cfg1,
+				&i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				dvb_attach(max2165_attach,
+					fe0->dvb.frontend,
+					&i2c_bus->i2c_adap,
+					&mygic_x8558pro_max2165_cfg1);
+			}
+			break;
+		/* port C */
+		case 2:
+			i2c_bus = &dev->i2c_bus[1];
+			fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+				&mygica_x8558pro_atbm8830_cfg2,
+				&i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				dvb_attach(max2165_attach,
+					fe0->dvb.frontend,
+					&i2c_bus->i2c_adap,
+					&mygic_x8558pro_max2165_cfg2);
+			}
+			break;
+		}
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+		i2c_bus = &dev->i2c_bus[0];
+		mfe_shared = 1;/* MFE */
+		port->frontends.gate = 0;/* not clear for me yet */
+		/* ports B, C */
+		/* MFE frontend 1 DVB-T */
+		fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
+					&netup_stv0367_config[port->nr - 1],
+					&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (NULL == dvb_attach(xc5000_attach,
+					fe0->dvb.frontend,
+					&i2c_bus->i2c_adap,
+					&netup_xc5000_config[port->nr - 1]))
+				goto frontend_detach;
+			/* load xc5000 firmware */
+			fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
+		}
+		/* MFE frontend 2 */
+		fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
+		if (fe1 == NULL)
+			goto frontend_detach;
+		/* DVB-C init */
+		fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
+					&netup_stv0367_config[port->nr - 1],
+					&i2c_bus->i2c_adap);
+		if (fe1->dvb.frontend != NULL) {
+			fe1->dvb.frontend->id = 1;
+			if (NULL == dvb_attach(xc5000_attach,
+					fe1->dvb.frontend,
+					&i2c_bus->i2c_adap,
+					&netup_xc5000_config[port->nr - 1]))
+				goto frontend_detach;
+		}
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+		i2c_bus = &dev->i2c_bus[0];
+		i2c_bus2 = &dev->i2c_bus[1];
+
+		switch (port->nr) {
+		/* port b */
+		case 1:
+			fe0->dvb.frontend = dvb_attach(drxk_attach,
+					&terratec_drxk_config[0],
+					&i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				if (!dvb_attach(mt2063_attach,
+						fe0->dvb.frontend,
+						&terratec_mt2063_config[0],
+						&i2c_bus2->i2c_adap))
+					goto frontend_detach;
+			}
+			break;
+		/* port c */
+		case 2:
+			fe0->dvb.frontend = dvb_attach(drxk_attach,
+					&terratec_drxk_config[1],
+					&i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				if (!dvb_attach(mt2063_attach,
+						fe0->dvb.frontend,
+						&terratec_mt2063_config[1],
+						&i2c_bus2->i2c_adap))
+					goto frontend_detach;
+			}
+			break;
+		}
+		break;
+	case CX23885_BOARD_TEVII_S471:
+		i2c_bus = &dev->i2c_bus[1];
+
+		fe0->dvb.frontend = dvb_attach(ds3000_attach,
+					&tevii_ds3000_config,
+					&i2c_bus->i2c_adap);
+		break;
+	case CX23885_BOARD_PROF_8000:
+		i2c_bus = &dev->i2c_bus[0];
+
+		fe0->dvb.frontend = dvb_attach(stv090x_attach,
+						&prof_8000_stv090x_config,
+						&i2c_bus->i2c_adap,
+						STV090x_DEMODULATOR_0);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(stb6100_attach,
+					fe0->dvb.frontend,
+					&prof_8000_stb6100_config,
+					&i2c_bus->i2c_adap))
+				goto frontend_detach;
+
+			fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage;
+		}
+		break;
+	default:
+		printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
+			" isn't supported yet\n",
+		       dev->name);
+		break;
+	}
+
+	if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) {
+		printk(KERN_ERR "%s: frontend initialization failed\n",
+		       dev->name);
+		goto frontend_detach;
+	}
+
+	/* define general-purpose callback pointer */
+	fe0->dvb.frontend->callback = cx23885_tuner_callback;
+	if (fe1)
+		fe1->dvb.frontend->callback = cx23885_tuner_callback;
+#if 0
+	/* Ensure all frontends negotiate bus access */
+	fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+	if (fe1)
+		fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+#endif
+
+	/* Put the analog decoder in standby to keep it quiet */
+	call_all(dev, core, s_power, 0);
+
+	if (fe0->dvb.frontend->ops.analog_ops.standby)
+		fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
+
+	/* register everything */
+	ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+					&dev->pci->dev, adapter_nr, mfe_shared);
+	if (ret)
+		goto frontend_detach;
+
+	/* init CI & MAC */
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
+		static struct netup_card_info cinfo;
+
+		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+		memcpy(port->frontends.adapter.proposed_mac,
+				cinfo.port[port->nr - 1].mac, 6);
+		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
+			port->nr, port->frontends.adapter.proposed_mac);
+
+		netup_ci_init(port);
+		break;
+		}
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+		struct altera_ci_config netup_ci_cfg = {
+			.dev = dev,/* magic number to identify*/
+			.adapter = &port->frontends.adapter,/* for CI */
+			.demux = &fe0->dvb.demux,/* for hw pid filter */
+			.fpga_rw = netup_altera_fpga_rw,
+		};
+
+		altera_ci_init(&netup_ci_cfg, port->nr);
+		break;
+		}
+	case CX23885_BOARD_TEVII_S470: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if (port->nr != 1)
+			break;
+
+		/* Read entire EEPROM */
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
+		break;
+		}
+	}
+
+	return ret;
+
+frontend_detach:
+	port->gate_ctrl = NULL;
+	videobuf_dvb_dealloc_frontends(&port->frontends);
+	return -EINVAL;
+}
+
+int cx23885_dvb_register(struct cx23885_tsport *port)
+{
+
+	struct videobuf_dvb_frontend *fe0;
+	struct cx23885_dev *dev = port->dev;
+	int err, i;
+
+	/* Here we need to allocate the correct number of frontends,
+	 * as reflected in the cards struct. The reality is that currently
+	 * no cx23885 boards support this - yet. But, if we don't modify this
+	 * code then the second frontend would never be allocated (later)
+	 * and fail with error before the attach in dvb_register().
+	 * Without these changes we risk an OOPS later. The changes here
+	 * are for safety, and should provide a good foundation for the
+	 * future addition of any multi-frontend cx23885 based boards.
+	 */
+	printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+		port->num_frontends);
+
+	for (i = 1; i <= port->num_frontends; i++) {
+		if (videobuf_dvb_alloc_frontend(
+			&port->frontends, i) == NULL) {
+			printk(KERN_ERR "%s() failed to alloc\n", __func__);
+			return -ENOMEM;
+		}
+
+		fe0 = videobuf_dvb_get_frontend(&port->frontends, i);
+		if (!fe0)
+			err = -EINVAL;
+
+		dprintk(1, "%s\n", __func__);
+		dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n",
+			dev->board,
+			dev->name,
+			dev->pci_bus,
+			dev->pci_slot);
+
+		err = -ENODEV;
+
+		/* dvb stuff */
+		/* We have to init the queue for each frontend on a port. */
+		printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name);
+		videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
+			    &dev->pci->dev, &port->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
+			    sizeof(struct cx23885_buffer), port, NULL);
+	}
+	err = dvb_register(port);
+	if (err != 0)
+		printk(KERN_ERR "%s() dvb_register failed err = %d\n",
+			__func__, err);
+
+	return err;
+}
+
+int cx23885_dvb_unregister(struct cx23885_tsport *port)
+{
+	struct videobuf_dvb_frontend *fe0;
+
+	/* FIXME: in an error condition where the we have
+	 * an expected number of frontends (attach problem)
+	 * then this might not clean up correctly, if 1
+	 * is invalid.
+	 * This comment only applies to future boards IF they
+	 * implement MFE support.
+	 */
+	fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+	if (fe0 && fe0->dvb.frontend)
+		videobuf_dvb_unregister_bus(&port->frontends);
+
+	switch (port->dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		netup_ci_exit(port);
+		break;
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+		altera_ci_release(port->dev, port->nr);
+		break;
+	}
+
+	port->gate_ctrl = NULL;
+
+	return 0;
+}
+
diff --git a/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c
new file mode 100644
index 000000000000..93998f220986
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-f300.c
@@ -0,0 +1,177 @@
+/*
+ * Driver for Silicon Labs C8051F300 microcontroller.
+ *
+ * It is used for LNB power control in TeVii S470,
+ * TBS 6920 PCIe DVB-S2 cards.
+ *
+ * Microcontroller connected to cx23885 GPIO pins:
+ * GPIO0 - data		- P0.3 F300
+ * GPIO1 - reset	- P0.2 F300
+ * GPIO2 - clk		- P0.1 F300
+ * GPIO3 - busy		- P0.0 F300
+ *
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by>
+ *
+ * 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 "cx23885.h"
+
+#define F300_DATA	GPIO_0
+#define F300_RESET	GPIO_1
+#define F300_CLK	GPIO_2
+#define F300_BUSY	GPIO_3
+
+static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl)
+{
+	cx23885_gpio_enable(dev, line, 1);
+	if (lvl == 1)
+		cx23885_gpio_set(dev, line);
+	else
+		cx23885_gpio_clear(dev, line);
+}
+
+static u8 f300_get_line(struct cx23885_dev *dev, u32 line)
+{
+	cx23885_gpio_enable(dev, line, 0);
+
+	return cx23885_gpio_get(dev, line);
+}
+
+static void f300_send_byte(struct cx23885_dev *dev, u8 dta)
+{
+	u8 i;
+
+	for (i = 0; i < 8; i++) {
+		f300_set_line(dev, F300_CLK, 0);
+		udelay(30);
+		f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */
+		udelay(30);
+		dta <<= 1;
+		f300_set_line(dev, F300_CLK, 1);
+		udelay(30);
+	}
+}
+
+static u8 f300_get_byte(struct cx23885_dev *dev)
+{
+	u8 i, dta = 0;
+
+	for (i = 0; i < 8; i++) {
+		f300_set_line(dev, F300_CLK, 0);
+		udelay(30);
+		dta <<= 1;
+		f300_set_line(dev, F300_CLK, 1);
+		udelay(30);
+		dta |= f300_get_line(dev, F300_DATA);/* msb first */
+
+	}
+
+	return dta;
+}
+
+static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+	u8 i, temp, ret = 0;
+
+	temp = buf[0];
+	for (i = 0; i < buf[0]; i++)
+		temp += buf[i + 1];
+	temp = (~temp + 1);/* get check sum */
+	buf[1 + buf[0]] = temp;
+
+	f300_set_line(dev, F300_RESET, 1);
+	f300_set_line(dev, F300_CLK, 1);
+	udelay(30);
+	f300_set_line(dev, F300_DATA, 1);
+	msleep(1);
+
+	/* question: */
+	f300_set_line(dev, F300_RESET, 0);/* begin to send data */
+	msleep(1);
+
+	f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */
+	msleep(1);
+
+	temp = buf[0];
+	temp += 2;
+	for (i = 0; i < temp; i++)
+		f300_send_byte(dev, buf[i]);
+
+	f300_set_line(dev, F300_RESET, 1);/* sent data over */
+	f300_set_line(dev, F300_DATA, 1);
+
+	/* answer: */
+	temp = 0;
+	for (i = 0; ((i < 8) & (temp == 0)); i++) {
+		msleep(1);
+		if (f300_get_line(dev, F300_BUSY) == 0)
+			temp = 1;
+	}
+
+	if (i > 7) {
+		printk(KERN_ERR "%s: timeout, the slave no response\n",
+								__func__);
+		ret = 1; /* timeout, the slave no response */
+	} else { /* the slave not busy, prepare for getting data */
+		f300_set_line(dev, F300_RESET, 0);/*ready...*/
+		msleep(1);
+		f300_send_byte(dev, 0xe1);/* 0xe1 is Read */
+		msleep(1);
+		temp = f300_get_byte(dev);/*get the data length */
+		if (temp > 14)
+			temp = 14;
+
+		for (i = 0; i < (temp + 1); i++)
+			f300_get_byte(dev);/* get data to empty buffer */
+
+		f300_set_line(dev, F300_RESET, 1);/* received data over */
+		f300_set_line(dev, F300_DATA, 1);
+	}
+
+	return ret;
+}
+
+int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	u8 buf[16];
+
+	buf[0] = 0x05;
+	buf[1] = 0x38;/* write port */
+	buf[2] = 0x01;/* A port, lnb power */
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		buf[3] = 0x01;/* power on */
+		buf[4] = 0x02;/* B port, H/V */
+		buf[5] = 0x00;/*13V v*/
+		break;
+	case SEC_VOLTAGE_18:
+		buf[3] = 0x01;
+		buf[4] = 0x02;
+		buf[5] = 0x01;/* 18V h*/
+		break;
+	case SEC_VOLTAGE_OFF:
+		buf[3] = 0x00;/* power off */
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		break;
+	}
+
+	return f300_xfer(fe, buf);
+}
diff --git a/drivers/media/pci/cx23885/cx23885-f300.h b/drivers/media/pci/cx23885/cx23885-f300.h
new file mode 100644
index 000000000000..e73344c94963
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-f300.h
@@ -0,0 +1,2 @@
+extern int f300_set_voltage(struct dvb_frontend *fe,
+				fe_sec_voltage_t voltage);
diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c
new file mode 100644
index 000000000000..4887314339cb
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-i2c.c
@@ -0,0 +1,396 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "cx23885.h"
+
+#include <media/v4l2-common.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#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
+
+#define I2C_EXTEND  (1 << 3)
+#define I2C_NOSTOP  (1 << 4)
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+	struct cx23885_i2c *bus = i2c_adap->algo_data;
+	struct cx23885_dev *dev = bus->dev;
+	return cx_read(bus->reg_stat) & 0x01;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+	struct cx23885_i2c *bus = i2c_adap->algo_data;
+	struct cx23885_dev *dev = bus->dev;
+	return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+	int count;
+
+	for (count = 0; count < I2C_WAIT_RETRY; count++) {
+		if (!i2c_is_busy(i2c_adap))
+			break;
+		udelay(I2C_WAIT_DELAY);
+	}
+
+	if (I2C_WAIT_RETRY == count)
+		return 0;
+
+	return 1;
+}
+
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+			 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;
+
+	if (joined_rlen)
+		dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__,
+			msg->len, joined_rlen);
+	else
+		dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+	/* Deal with i2c probe functions with zero payload */
+	if (msg->len == 0) {
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
+		if (!i2c_wait_done(i2c_adap))
+			return -EIO;
+		if (!i2c_slave_did_ack(i2c_adap))
+			return -ENXIO;
+
+		dprintk(1, "%s() returns 0\n", __func__);
+		return 0;
+	}
+
+
+	/* dev, reg + first byte */
+	addr = (msg->addr << 25) | msg->buf[0];
+	wdata = msg->buf[0];
+	ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+	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);
+	cx_write(bus->reg_ctrl, ctrl);
+
+	if (!i2c_wait_done(i2c_adap))
+		goto eio;
+	if (i2c_debug) {
+		printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+		if (!(ctrl & I2C_NOSTOP))
+			printk(" >\n");
+	}
+
+	for (cnt = 1; cnt < msg->len; cnt++) {
+		/* following bytes */
+		wdata = msg->buf[cnt];
+		ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+		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);
+		cx_write(bus->reg_ctrl, ctrl);
+
+		if (!i2c_wait_done(i2c_adap))
+			goto eio;
+		if (i2c_debug) {
+			dprintk(1, " %02x", msg->buf[cnt]);
+			if (!(ctrl & I2C_NOSTOP))
+				dprintk(1, " >\n");
+		}
+	}
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+	if (i2c_debug)
+		printk(KERN_ERR " ERR: %d\n", retval);
+	return retval;
+}
+
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+			 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;
+
+
+	if (i2c_debug && !joined)
+		dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+	/* Deal with i2c probe functions with zero payload */
+	if (msg->len == 0) {
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
+		if (!i2c_wait_done(i2c_adap))
+			return -EIO;
+		if (!i2c_slave_did_ack(i2c_adap))
+			return -ENXIO;
+
+
+		dprintk(1, "%s() returns 0\n", __func__);
+		return 0;
+	}
+
+	if (i2c_debug) {
+		if (joined)
+			dprintk(1, " R");
+		else
+			dprintk(1, " <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)
+			ctrl |= I2C_NOSTOP | I2C_EXTEND;
+
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, ctrl);
+
+		if (!i2c_wait_done(i2c_adap))
+			goto eio;
+		msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
+		if (i2c_debug) {
+			dprintk(1, " %02x", msg->buf[cnt]);
+			if (!(ctrl & I2C_NOSTOP))
+				dprintk(1, " >\n");
+		}
+	}
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+	if (i2c_debug)
+		printk(KERN_ERR " ERR: %d\n", retval);
+	return retval;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+		    struct i2c_msg *msgs, int num)
+{
+	struct cx23885_i2c *bus = i2c_adap->algo_data;
+	struct cx23885_dev *dev = bus->dev;
+	int i, retval = 0;
+
+	dprintk(1, "%s(num = %d)\n", __func__, num);
+
+	for (i = 0 ; i < num; i++) {
+		dprintk(1, "%s(num = %d) addr = 0x%02x  len = 0x%x\n",
+			__func__, num, msgs[i].addr, msgs[i].len);
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read */
+			retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+		} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+			   msgs[i].addr == msgs[i + 1].addr) {
+			/* write then read from same address */
+			retval = i2c_sendbytes(i2c_adap, &msgs[i],
+					       msgs[i + 1].len);
+			if (retval < 0)
+				goto err;
+			i++;
+			retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+		} else {
+			/* write */
+			retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+		}
+		if (retval < 0)
+			goto err;
+	}
+	return num;
+
+ err:
+	return retval;
+}
+
+static u32 cx23885_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm cx23885_i2c_algo_template = {
+	.master_xfer	= i2c_xfer,
+	.functionality	= cx23885_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter cx23885_i2c_adap_template = {
+	.name              = "cx23885",
+	.owner             = THIS_MODULE,
+	.algo              = &cx23885_i2c_algo_template,
+};
+
+static struct i2c_client cx23885_i2c_client_template = {
+	.name	= "cx23885 internal",
+};
+
+static char *i2c_devs[128] = {
+	[0x10 >> 1] = "tda10048",
+	[0x12 >> 1] = "dib7000pc",
+	[0x1c >> 1] = "lgdt3303",
+	[0x86 >> 1] = "tda9887",
+	[0x32 >> 1] = "cx24227",
+	[0x88 >> 1] = "cx25837",
+	[0x84 >> 1] = "tda8295",
+	[0x98 >> 1] = "flatiron",
+	[0xa0 >> 1] = "eeprom",
+	[0xc0 >> 1] = "tuner/mt2131/tda8275",
+	[0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028",
+	[0xc8 >> 1] = "tuner/xc3028L",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+	unsigned char buf;
+	int i, rc;
+
+	for (i = 0; i < 128; i++) {
+		c->addr = i;
+		rc = i2c_master_recv(c, &buf, 0);
+		if (rc < 0)
+			continue;
+		printk(KERN_INFO "%s: i2c scan: found device @ 0x%x  [%s]\n",
+		       name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+	}
+}
+
+/* init + register i2c adapter */
+int cx23885_i2c_register(struct cx23885_i2c *bus)
+{
+	struct cx23885_dev *dev = bus->dev;
+
+	dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
+
+	bus->i2c_adap = cx23885_i2c_adap_template;
+	bus->i2c_client = cx23885_i2c_client_template;
+	bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+	strlcpy(bus->i2c_adap.name, bus->dev->name,
+		sizeof(bus->i2c_adap.name));
+
+	bus->i2c_adap.algo_data = bus;
+	i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+	i2c_add_adapter(&bus->i2c_adap);
+
+	bus->i2c_client.adapter = &bus->i2c_adap;
+
+	if (0 == bus->i2c_rc) {
+		dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr);
+		if (i2c_scan) {
+			printk(KERN_INFO "%s: scan bus %d:\n",
+					dev->name, bus->nr);
+			do_i2c_scan(dev->name, &bus->i2c_client);
+		}
+	} else
+		printk(KERN_WARNING "%s: i2c bus %d register FAILED\n",
+			dev->name, bus->nr);
+
+	/* Instantiate the IR receiver device, if present */
+	if (0 == bus->i2c_rc) {
+		struct i2c_board_info info;
+		const unsigned short addr_list[] = {
+			0x6b, I2C_CLIENT_END
+		};
+
+		memset(&info, 0, sizeof(struct i2c_board_info));
+		strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+		/* Use quick read command for probe, some IR chips don't
+		 * support writes */
+		i2c_new_probed_device(&bus->i2c_adap, &info, addr_list,
+				      i2c_probe_func_quick_read);
+	}
+
+	return bus->i2c_rc;
+}
+
+int cx23885_i2c_unregister(struct cx23885_i2c *bus)
+{
+	i2c_del_adapter(&bus->i2c_adap);
+	return 0;
+}
+
+void cx23885_av_clk(struct cx23885_dev *dev, int enable)
+{
+	/* write 0 to bus 2 addr 0x144 via i2x_xfer() */
+	char buffer[3];
+	struct i2c_msg msg;
+	dprintk(1, "%s(enabled = %d)\n", __func__, enable);
+
+	/* Register 0x144 */
+	buffer[0] = 0x01;
+	buffer[1] = 0x44;
+	if (enable == 1)
+		buffer[2] = 0x05;
+	else
+		buffer[2] = 0x00;
+
+	msg.addr = 0x44;
+	msg.flags = I2C_M_TEN;
+	msg.len = 3;
+	msg.buf = buffer;
+
+	i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
new file mode 100644
index 000000000000..2c925f77cf2a
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -0,0 +1,365 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared remote control input device
+ *
+ *  Most of this file is
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
+ *
+ *  However, the cx23885_input_{init,fini} functions contained herein are
+ *  derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+ *
+ *  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ *  Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ *		       Markus Rechberger <mrechberger@gmail.com>
+ *		       Mauro Carvalho Chehab <mchehab@infradead.org>
+ *		       Sascha Sommer <saschasommer@freenet.de>
+ *  Copyright (C) 2004, 2005 Chris Pascoe
+ *  Copyright (C) 2003, 2004 Gerd Knorr
+ *  Copyright (C) 2003 Pavel Machek
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <media/v4l2-subdev.h>
+
+#include "cx23885.h"
+
+#define MODULE_NAME "cx23885"
+
+static void cx23885_input_process_measurements(struct cx23885_dev *dev,
+					       bool overrun)
+{
+	struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
+
+	ssize_t num;
+	int count, i;
+	bool handle = false;
+	struct ir_raw_event ir_core_event[64];
+
+	do {
+		num = 0;
+		v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+				 sizeof(ir_core_event), &num);
+
+		count = num / sizeof(struct ir_raw_event);
+
+		for (i = 0; i < count; i++) {
+			ir_raw_event_store(kernel_ir->rc,
+					   &ir_core_event[i]);
+			handle = true;
+		}
+	} while (num != 0);
+
+	if (overrun)
+		ir_raw_event_reset(kernel_ir->rc);
+	else if (handle)
+		ir_raw_event_handle(kernel_ir->rc);
+}
+
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+{
+	struct v4l2_subdev_ir_parameters params;
+	int overrun, data_available;
+
+	if (dev->sd_ir == NULL || events == 0)
+		return;
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_TEVII_S470:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		/*
+		 * The only boards we handle right now.  However other boards
+		 * using the CX2388x integrated IR controller should be similar
+		 */
+		break;
+	default:
+		return;
+	}
+
+	overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+			    V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+
+	data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+				   V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+
+	if (overrun) {
+		/* If there was a FIFO overrun, stop the device */
+		v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+		params.enable = false;
+		/* Mitigate race with cx23885_input_ir_stop() */
+		params.shutdown = atomic_read(&dev->ir_input_stopping);
+		v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+	}
+
+	if (data_available)
+		cx23885_input_process_measurements(dev, overrun);
+
+	if (overrun) {
+		/* If there was a FIFO overrun, clear & restart the device */
+		params.enable = true;
+		/* Mitigate race with cx23885_input_ir_stop() */
+		params.shutdown = atomic_read(&dev->ir_input_stopping);
+		v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+	}
+}
+
+static int cx23885_input_ir_start(struct cx23885_dev *dev)
+{
+	struct v4l2_subdev_ir_parameters params;
+
+	if (dev->sd_ir == NULL)
+		return -ENODEV;
+
+	atomic_set(&dev->ir_input_stopping, 0);
+
+	v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		/*
+		 * The IR controller on this board only returns pulse widths.
+		 * Any other mode setting will fail to set up the device.
+		*/
+		params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+		params.enable = true;
+		params.interrupt_enable = true;
+		params.shutdown = false;
+
+		/* Setup for baseband compatible with both RC-5 and RC-6A */
+		params.modulation = false;
+		/* RC-5:  2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+		/* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+		params.max_pulse_width = 3333333; /* ns */
+		/* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+		/* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+		params.noise_filter_min_width = 333333; /* ns */
+		/*
+		 * This board has inverted receive sense:
+		 * mark is received as low logic level;
+		 * falling edges are detected as rising edges; etc.
+		 */
+		params.invert_level = true;
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_TEVII_S470:
+		/*
+		 * The IR controller on this board only returns pulse widths.
+		 * Any other mode setting will fail to set up the device.
+		 */
+		params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+		params.enable = true;
+		params.interrupt_enable = true;
+		params.shutdown = false;
+
+		/* Setup for a standard NEC protocol */
+		params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+		params.carrier_range_lower = 33000; /* Hz */
+		params.carrier_range_upper = 43000; /* Hz */
+		params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+		/*
+		 * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+		 * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+		 */
+		params.max_pulse_width = 12378022; /* ns */
+
+		/*
+		 * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+		 * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+		 */
+		params.noise_filter_min_width = 351648; /* ns */
+
+		params.modulation = false;
+		params.invert_level = true;
+		break;
+	}
+	v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+	return 0;
+}
+
+static int cx23885_input_ir_open(struct rc_dev *rc)
+{
+	struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+	if (kernel_ir->cx == NULL)
+		return -ENODEV;
+
+	return cx23885_input_ir_start(kernel_ir->cx);
+}
+
+static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+{
+	struct v4l2_subdev_ir_parameters params;
+
+	if (dev->sd_ir == NULL)
+		return;
+
+	/*
+	 * Stop the sd_ir subdevice from generating notifications and
+	 * scheduling work.
+	 * It is shutdown this way in order to mitigate a race with
+	 * cx23885_input_rx_work_handler() in the overrun case, which could
+	 * re-enable the subdevice.
+	 */
+	atomic_set(&dev->ir_input_stopping, 1);
+	v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+	while (params.shutdown == false) {
+		params.enable = false;
+		params.interrupt_enable = false;
+		params.shutdown = true;
+		v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+		v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+	}
+	flush_work(&dev->cx25840_work);
+	flush_work(&dev->ir_rx_work);
+	flush_work(&dev->ir_tx_work);
+}
+
+static void cx23885_input_ir_close(struct rc_dev *rc)
+{
+	struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+	if (kernel_ir->cx != NULL)
+		cx23885_input_ir_stop(kernel_ir->cx);
+}
+
+int cx23885_input_init(struct cx23885_dev *dev)
+{
+	struct cx23885_kernel_ir *kernel_ir;
+	struct rc_dev *rc;
+	char *rc_map;
+	enum rc_driver_type driver_type;
+	unsigned long allowed_protos;
+
+	int ret;
+
+	/*
+	 * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+	 * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+	 */
+	if (dev->sd_ir == NULL)
+		return -ENODEV;
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+		/* Integrated CX2388[58] IR controller */
+		driver_type = RC_DRIVER_IR_RAW;
+		allowed_protos = RC_TYPE_ALL;
+		/* The grey Hauppauge RC-5 remote */
+		rc_map = RC_MAP_HAUPPAUGE;
+		break;
+	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+		/* Integrated CX23885 IR controller */
+		driver_type = RC_DRIVER_IR_RAW;
+		allowed_protos = RC_TYPE_NEC;
+		/* The grey Terratec remote with orange buttons */
+		rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS;
+		break;
+	case CX23885_BOARD_TEVII_S470:
+		/* Integrated CX23885 IR controller */
+		driver_type = RC_DRIVER_IR_RAW;
+		allowed_protos = RC_TYPE_ALL;
+		/* A guess at the remote */
+		rc_map = RC_MAP_TEVII_NEC;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	/* cx23885 board instance kernel IR state */
+	kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
+	if (kernel_ir == NULL)
+		return -ENOMEM;
+
+	kernel_ir->cx = dev;
+	kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
+				    cx23885_boards[dev->board].name);
+	kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
+				    pci_name(dev->pci));
+
+	/* input device */
+	rc = rc_allocate_device();
+	if (!rc) {
+		ret = -ENOMEM;
+		goto err_out_free;
+	}
+
+	kernel_ir->rc = rc;
+	rc->input_name = kernel_ir->name;
+	rc->input_phys = kernel_ir->phys;
+	rc->input_id.bustype = BUS_PCI;
+	rc->input_id.version = 1;
+	if (dev->pci->subsystem_vendor) {
+		rc->input_id.vendor  = dev->pci->subsystem_vendor;
+		rc->input_id.product = dev->pci->subsystem_device;
+	} else {
+		rc->input_id.vendor  = dev->pci->vendor;
+		rc->input_id.product = dev->pci->device;
+	}
+	rc->dev.parent = &dev->pci->dev;
+	rc->driver_type = driver_type;
+	rc->allowed_protos = allowed_protos;
+	rc->priv = kernel_ir;
+	rc->open = cx23885_input_ir_open;
+	rc->close = cx23885_input_ir_close;
+	rc->map_name = rc_map;
+	rc->driver_name = MODULE_NAME;
+
+	/* Go */
+	dev->kernel_ir = kernel_ir;
+	ret = rc_register_device(rc);
+	if (ret)
+		goto err_out_stop;
+
+	return 0;
+
+err_out_stop:
+	cx23885_input_ir_stop(dev);
+	dev->kernel_ir = NULL;
+	rc_free_device(rc);
+err_out_free:
+	kfree(kernel_ir->phys);
+	kfree(kernel_ir->name);
+	kfree(kernel_ir);
+	return ret;
+}
+
+void cx23885_input_fini(struct cx23885_dev *dev)
+{
+	/* Always stop the IR hardware from generating interrupts */
+	cx23885_input_ir_stop(dev);
+
+	if (dev->kernel_ir == NULL)
+		return;
+	rc_unregister_device(dev->kernel_ir->rc);
+	kfree(dev->kernel_ir->phys);
+	kfree(dev->kernel_ir->name);
+	kfree(dev->kernel_ir);
+	dev->kernel_ir = NULL;
+}
diff --git a/drivers/media/pci/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h
new file mode 100644
index 000000000000..75ef15d3f523
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-input.h
@@ -0,0 +1,30 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared remote control input device
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_INPUT_H_
+#define _CX23885_INPUT_H_
+int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events);
+
+int cx23885_input_init(struct cx23885_dev *dev);
+void cx23885_input_fini(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c
new file mode 100644
index 000000000000..44812ca78899
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.c
@@ -0,0 +1,208 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Various common ioctl() support functions
+ *
+ *  Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+#include <media/v4l2-chip-ident.h>
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+			 struct v4l2_dbg_chip_ident *chip)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+	int err = 0;
+	u8 rev;
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	switch (chip->match.type) {
+	case V4L2_CHIP_MATCH_HOST:
+		switch (chip->match.addr) {
+		case 0:
+			rev = cx_read(RDR_CFG2) & 0xff;
+			switch (dev->pci->device) {
+			case 0x8852:
+				/* rev 0x04 could be '885 or '888. Pick '888. */
+				if (rev == 0x04)
+					chip->ident = V4L2_IDENT_CX23888;
+				else
+					chip->ident = V4L2_IDENT_CX23885;
+				break;
+			case 0x8880:
+				if (rev == 0x0e || rev == 0x0f)
+					chip->ident = V4L2_IDENT_CX23887;
+				else
+					chip->ident = V4L2_IDENT_CX23888;
+				break;
+			default:
+				chip->ident = V4L2_IDENT_UNKNOWN;
+				break;
+			}
+			chip->revision = (dev->pci->device << 16) | (rev << 8) |
+					 (dev->hwrevision & 0xff);
+			break;
+		case 1:
+			if (dev->v4l_device != NULL) {
+				chip->ident = V4L2_IDENT_CX23417;
+				chip->revision = 0;
+			}
+			break;
+		case 2:
+			/*
+			 * The integrated IR controller on the CX23888 is
+			 * host chip 2.  It may not be used/initialized or sd_ir
+			 * may be pointing at the cx25840 subdevice for the
+			 * IR controller on the CX23885.  Thus we find it
+			 * without using the dev->sd_ir pointer.
+			 */
+			call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
+				chip);
+			break;
+		default:
+			err = -EINVAL; /* per V4L2 spec */
+			break;
+		}
+		break;
+	case V4L2_CHIP_MATCH_I2C_DRIVER:
+		/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+		call_all(dev, core, g_chip_ident, chip);
+		break;
+	case V4L2_CHIP_MATCH_I2C_ADDR:
+		/*
+		 * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+		 * to look if a chip is at the address with no driver.  That's a
+		 * dangerous thing to do with EEPROMs anyway.
+		 */
+		call_all(dev, core, g_chip_ident, chip);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23885_g_host_register(struct cx23885_dev *dev,
+				   struct v4l2_dbg_register *reg)
+{
+	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+		return -EINVAL;
+
+	reg->size = 4;
+	reg->val = cx_read(reg->reg);
+	return 0;
+}
+
+static int cx23417_g_register(struct cx23885_dev *dev,
+			      struct v4l2_dbg_register *reg)
+{
+	u32 value;
+
+	if (dev->v4l_device == NULL)
+		return -EINVAL;
+
+	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+		return -EINVAL;
+
+	if (mc417_register_read(dev, (u16) reg->reg, &value))
+		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+	reg->size = 4;
+	reg->val = value;
+	return 0;
+}
+
+int cx23885_g_register(struct file *file, void *fh,
+		       struct v4l2_dbg_register *reg)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+		switch (reg->match.addr) {
+		case 0:
+			return cx23885_g_host_register(dev, reg);
+		case 1:
+			return cx23417_g_register(dev, reg);
+		default:
+			break;
+		}
+	}
+
+	/* FIXME - any error returns should not be ignored */
+	call_all(dev, core, g_register, reg);
+	return 0;
+}
+
+static int cx23885_s_host_register(struct cx23885_dev *dev,
+				   struct v4l2_dbg_register *reg)
+{
+	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+		return -EINVAL;
+
+	reg->size = 4;
+	cx_write(reg->reg, reg->val);
+	return 0;
+}
+
+static int cx23417_s_register(struct cx23885_dev *dev,
+			      struct v4l2_dbg_register *reg)
+{
+	if (dev->v4l_device == NULL)
+		return -EINVAL;
+
+	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+		return -EINVAL;
+
+	if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
+		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+	reg->size = 4;
+	return 0;
+}
+
+int cx23885_s_register(struct file *file, void *fh,
+		       struct v4l2_dbg_register *reg)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+		switch (reg->match.addr) {
+		case 0:
+			return cx23885_s_host_register(dev, reg);
+		case 1:
+			return cx23417_s_register(dev, reg);
+		default:
+			break;
+		}
+	}
+
+	/* FIXME - any error returns should not be ignored */
+	call_all(dev, core, s_register, reg);
+	return 0;
+}
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h
new file mode 100644
index 000000000000..315be0ca5a04
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.h
@@ -0,0 +1,39 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Various common ioctl() support functions
+ *
+ *  Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX23885_IOCTL_H_
+#define _CX23885_IOCTL_H_
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+			 struct v4l2_dbg_chip_ident *chip);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx23885_g_register(struct file *file, void *fh,
+		       struct v4l2_dbg_register *reg);
+
+
+int cx23885_s_register(struct file *file, void *fh,
+		       struct v4l2_dbg_register *reg);
+
+#endif
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c
new file mode 100644
index 000000000000..7125247dd255
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-ir.c
@@ -0,0 +1,117 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <media/v4l2-device.h>
+
+#include "cx23885.h"
+#include "cx23885-input.h"
+
+#define CX23885_IR_RX_FIFO_SERVICE_REQ		0
+#define CX23885_IR_RX_END_OF_RX_DETECTED	1
+#define CX23885_IR_RX_HW_FIFO_OVERRUN		2
+#define CX23885_IR_RX_SW_FIFO_OVERRUN		3
+
+#define CX23885_IR_TX_FIFO_SERVICE_REQ		0
+
+
+void cx23885_ir_rx_work_handler(struct work_struct *work)
+{
+	struct cx23885_dev *dev =
+			     container_of(work, struct cx23885_dev, ir_rx_work);
+	u32 events = 0;
+	unsigned long *notifications = &dev->ir_rx_notifications;
+
+	if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
+		events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+	if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
+		events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+	if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
+		events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+	if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
+		events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+	if (events == 0)
+		return;
+
+	if (dev->kernel_ir)
+		cx23885_input_rx_work_handler(dev, events);
+}
+
+void cx23885_ir_tx_work_handler(struct work_struct *work)
+{
+	struct cx23885_dev *dev =
+			     container_of(work, struct cx23885_dev, ir_tx_work);
+	u32 events = 0;
+	unsigned long *notifications = &dev->ir_tx_notifications;
+
+	if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
+		events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+
+	if (events == 0)
+		return;
+
+}
+
+/* Possibly called in an IRQ context */
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+	unsigned long *notifications = &dev->ir_rx_notifications;
+
+	if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
+		set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
+	if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
+		set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
+	if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
+		set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
+	if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
+		set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
+
+	/*
+	 * For the integrated AV core, we are already in a workqueue context.
+	 * For the CX23888 integrated IR, we are in an interrupt context.
+	 */
+	if (sd == dev->sd_cx25840)
+		cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+	else
+		schedule_work(&dev->ir_rx_work);
+}
+
+/* Possibly called in an IRQ context */
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+	unsigned long *notifications = &dev->ir_tx_notifications;
+
+	if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
+		set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
+
+	/*
+	 * For the integrated AV core, we are already in a workqueue context.
+	 * For the CX23888 integrated IR, we are in an interrupt context.
+	 */
+	if (sd == dev->sd_cx25840)
+		cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+	else
+		schedule_work(&dev->ir_tx_work);
+}
diff --git a/drivers/media/pci/cx23885/cx23885-ir.h b/drivers/media/pci/cx23885/cx23885-ir.h
new file mode 100644
index 000000000000..0c9d8bda9e28
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-ir.h
@@ -0,0 +1,31 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_IR_H_
+#define _CX23885_IR_H_
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+
+void cx23885_ir_rx_work_handler(struct work_struct *work);
+void cx23885_ir_tx_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h
new file mode 100644
index 000000000000..a99936e0cbc2
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-reg.h
@@ -0,0 +1,452 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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 _CX23885_REG_H_
+#define _CX23885_REG_H_
+
+/*
+Address Map
+0x00000000 -> 0x00009000   TX SRAM  (Fifos)
+0x00010000 -> 0x00013c00   RX SRAM  CMDS + CDT
+
+EACH CMDS struct is 0x80 bytes long
+
+DMAx_PTR1 = 0x03040 address of first cluster
+DMAx_PTR2 = 0x10600 address of the CDT
+DMAx_CNT1 = cluster size in (bytes >> 4) -1
+DMAx_CNT2 = total cdt size for all entries >> 3
+
+Cluster Descriptor entry = 4 DWORDS
+ DWORD 0 -> ptr to cluster
+ DWORD 1 Reserved
+ DWORD 2 Reserved
+ DWORD 3 Reserved
+
+Channel manager Data Structure entry = 20 DWORD
+  0  IntialProgramCounterLow
+  1  IntialProgramCounterHigh
+  2  ClusterDescriptorTableBase
+  3  ClusterDescriptorTableSize
+  4  InstructionQueueBase
+  5  InstructionQueueSize
+...  Reserved
+ 19  Reserved
+*/
+
+/* Risc Instructions */
+#define RISC_CNT_INC		 0x00010000
+#define RISC_CNT_RESET		 0x00030000
+#define RISC_IRQ1		 0x01000000
+#define RISC_IRQ2		 0x02000000
+#define RISC_EOL		 0x04000000
+#define RISC_SOL		 0x08000000
+#define RISC_WRITE		 0x10000000
+#define RISC_SKIP		 0x20000000
+#define RISC_JUMP		 0x70000000
+#define RISC_SYNC		 0x80000000
+#define RISC_RESYNC		 0x80008000
+#define RISC_READ		 0x90000000
+#define RISC_WRITERM		 0xB0000000
+#define RISC_WRITECM		 0xC0000000
+#define RISC_WRITECR		 0xD0000000
+#define RISC_WRITEC		 0x50000000
+#define RISC_READC		 0xA0000000
+
+
+/* Audio and Video Core */
+#define HOST_REG1		0x00000000
+#define HOST_REG2		0x00000001
+#define HOST_REG3		0x00000002
+
+/* Chip Configuration Registers */
+#define CHIP_CTRL		0x00000100
+#define AFE_CTRL		0x00000104
+#define VID_PLL_INT_POST	0x00000108
+#define VID_PLL_FRAC		0x0000010C
+#define AUX_PLL_INT_POST	0x00000110
+#define AUX_PLL_FRAC		0x00000114
+#define SYS_PLL_INT_POST	0x00000118
+#define SYS_PLL_FRAC		0x0000011C
+#define PIN_CTRL		0x00000120
+#define AUD_IO_CTRL		0x00000124
+#define AUD_LOCK1		0x00000128
+#define AUD_LOCK2		0x0000012C
+#define POWER_CTRL		0x00000130
+#define AFE_DIAG_CTRL1		0x00000134
+#define AFE_DIAG_CTRL3		0x0000013C
+#define PLL_DIAG_CTRL		0x00000140
+#define AFE_CLK_OUT_CTRL	0x00000144
+#define DLL1_DIAG_CTRL		0x0000015C
+
+/* GPIO[23:19] Output Enable */
+#define GPIO2_OUT_EN_REG	0x00000160
+/* GPIO[23:19] Data Registers */
+#define GPIO2			0x00000164
+
+#define IFADC_CTRL		0x00000180
+
+/* Infrared Remote Registers */
+#define IR_CNTRL_REG	0x00000200
+#define IR_TXCLK_REG	0x00000204
+#define IR_RXCLK_REG	0x00000208
+#define IR_CDUTY_REG	0x0000020C
+#define IR_STAT_REG	0x00000210
+#define IR_IRQEN_REG	0x00000214
+#define IR_FILTR_REG	0x00000218
+#define IR_FIFO_REG	0x0000023C
+
+/* Video Decoder Registers */
+#define MODE_CTRL		0x00000400
+#define OUT_CTRL1		0x00000404
+#define OUT_CTRL2		0x00000408
+#define GEN_STAT		0x0000040C
+#define INT_STAT_MASK		0x00000410
+#define LUMA_CTRL		0x00000414
+#define HSCALE_CTRL		0x00000418
+#define VSCALE_CTRL		0x0000041C
+#define CHROMA_CTRL		0x00000420
+#define VBI_LINE_CTRL1		0x00000424
+#define VBI_LINE_CTRL2		0x00000428
+#define VBI_LINE_CTRL3		0x0000042C
+#define VBI_LINE_CTRL4		0x00000430
+#define VBI_LINE_CTRL5		0x00000434
+#define VBI_FC_CFG		0x00000438
+#define VBI_MISC_CFG1		0x0000043C
+#define VBI_MISC_CFG2		0x00000440
+#define VBI_PAY1		0x00000444
+#define VBI_PAY2		0x00000448
+#define VBI_CUST1_CFG1		0x0000044C
+#define VBI_CUST1_CFG2		0x00000450
+#define VBI_CUST1_CFG3		0x00000454
+#define VBI_CUST2_CFG1		0x00000458
+#define VBI_CUST2_CFG2		0x0000045C
+#define VBI_CUST2_CFG3		0x00000460
+#define VBI_CUST3_CFG1		0x00000464
+#define VBI_CUST3_CFG2		0x00000468
+#define VBI_CUST3_CFG3		0x0000046C
+#define HORIZ_TIM_CTRL		0x00000470
+#define VERT_TIM_CTRL		0x00000474
+#define SRC_COMB_CFG		0x00000478
+#define CHROMA_VBIOFF_CFG	0x0000047C
+#define FIELD_COUNT		0x00000480
+#define MISC_TIM_CTRL		0x00000484
+#define DFE_CTRL1		0x00000488
+#define DFE_CTRL2		0x0000048C
+#define DFE_CTRL3		0x00000490
+#define PLL_CTRL		0x00000494
+#define HTL_CTRL		0x00000498
+#define COMB_CTRL		0x0000049C
+#define CRUSH_CTRL		0x000004A0
+#define SOFT_RST_CTRL		0x000004A4
+#define CX885_VERSION		0x000004B4
+#define VBI_PASS_CTRL		0x000004BC
+
+/* Audio Decoder Registers */
+/* 8051 Configuration */
+#define DL_CTL		0x00000800
+#define STD_DET_STATUS	0x00000804
+#define STD_DET_CTL	0x00000808
+#define DW8051_INT	0x0000080C
+#define GENERAL_CTL	0x00000810
+#define AAGC_CTL	0x00000814
+#define DEMATRIX_CTL	0x000008CC
+#define PATH1_CTL1	0x000008D0
+#define PATH1_VOL_CTL	0x000008D4
+#define PATH1_EQ_CTL	0x000008D8
+#define PATH1_SC_CTL	0x000008DC
+#define PATH2_CTL1	0x000008E0
+#define PATH2_VOL_CTL	0x000008E4
+#define PATH2_EQ_CTL	0x000008E8
+#define PATH2_SC_CTL	0x000008EC
+
+/* Sample Rate Converter */
+#define SRC_CTL		0x000008F0
+#define SRC_LF_COEF	0x000008F4
+#define SRC1_CTL	0x000008F8
+#define SRC2_CTL	0x000008FC
+#define SRC3_CTL	0x00000900
+#define SRC4_CTL	0x00000904
+#define SRC5_CTL	0x00000908
+#define SRC6_CTL	0x0000090C
+#define BAND_OUT_SEL	0x00000910
+#define I2S_N_CTL	0x00000914
+#define I2S_OUT_CTL	0x00000918
+#define AUTOCONFIG_REG	0x000009C4
+
+/* Audio ADC Registers */
+#define DSM_CTRL1	0x00000000
+#define DSM_CTRL2	0x00000001
+#define CHP_EN_CTRL	0x00000002
+#define CHP_CLK_CTRL1	0x00000004
+#define CHP_CLK_CTRL2	0x00000005
+#define BG_REF_CTRL	0x00000006
+#define SD2_SW_CTRL1	0x00000008
+#define SD2_SW_CTRL2	0x00000009
+#define SD2_BIAS_CTRL	0x0000000A
+#define AMP_BIAS_CTRL	0x0000000C
+#define CH_PWR_CTRL1	0x0000000E
+#define FLD_CH_SEL      (1 << 3)
+#define CH_PWR_CTRL2	0x0000000F
+#define DSM_STATUS1	0x00000010
+#define DSM_STATUS2	0x00000011
+#define DIG_CTL1	0x00000012
+#define DIG_CTL2	0x00000013
+#define I2S_TX_CFG	0x0000001A
+
+#define DEV_CNTRL2	0x00040000
+
+#define PCI_MSK_IR        (1 << 28)
+#define PCI_MSK_AV_CORE   (1 << 27)
+#define PCI_MSK_GPIO1     (1 << 24)
+#define PCI_MSK_GPIO0     (1 << 23)
+#define PCI_MSK_APB_DMA   (1 << 12)
+#define PCI_MSK_AL_WR     (1 << 11)
+#define PCI_MSK_AL_RD     (1 << 10)
+#define PCI_MSK_RISC_WR   (1 <<  9)
+#define PCI_MSK_RISC_RD   (1 <<  8)
+#define PCI_MSK_AUD_EXT   (1 <<  4)
+#define PCI_MSK_AUD_INT   (1 <<  3)
+#define PCI_MSK_VID_C     (1 <<  2)
+#define PCI_MSK_VID_B     (1 <<  1)
+#define PCI_MSK_VID_A      1
+#define PCI_INT_MSK	0x00040010
+
+#define PCI_INT_STAT	0x00040014
+#define PCI_INT_MSTAT	0x00040018
+
+#define VID_A_INT_MSK	0x00040020
+#define VID_A_INT_STAT	0x00040024
+#define VID_A_INT_MSTAT	0x00040028
+#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
+
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_SYNC    (1 << 12)
+#define VID_B_MSK_OF      (1 <<  8)
+#define VID_B_MSK_RISCI2  (1 <<  4)
+#define VID_B_MSK_RISCI1   1
+
+#define VID_C_MSK_BAD_PKT (1 << 20)
+#define VID_C_MSK_OPC_ERR (1 << 16)
+#define VID_C_MSK_SYNC    (1 << 12)
+#define VID_C_MSK_OF      (1 <<  8)
+#define VID_C_MSK_RISCI2  (1 <<  4)
+#define VID_C_MSK_RISCI1   1
+
+/* A superset for testing purposes */
+#define VID_BC_MSK_BAD_PKT (1 << 20)
+#define VID_BC_MSK_OPC_ERR (1 << 16)
+#define VID_BC_MSK_SYNC    (1 << 12)
+#define VID_BC_MSK_OF      (1 <<  8)
+#define VID_BC_MSK_VBI_RISCI2 (1 <<  5)
+#define VID_BC_MSK_RISCI2  (1 <<  4)
+#define VID_BC_MSK_VBI_RISCI1 (1 <<  1)
+#define VID_BC_MSK_RISCI1   1
+
+#define VID_C_INT_MSK	0x00040040
+#define VID_C_INT_STAT	0x00040044
+#define VID_C_INT_MSTAT	0x00040048
+#define VID_C_INT_SSTAT	0x0004004C
+
+#define AUDIO_INT_INT_MSK	0x00040050
+#define AUDIO_INT_INT_STAT	0x00040054
+#define AUDIO_INT_INT_MSTAT	0x00040058
+#define AUDIO_INT_INT_SSTAT	0x0004005C
+
+#define AUDIO_EXT_INT_MSK	0x00040060
+#define AUDIO_EXT_INT_STAT	0x00040064
+#define AUDIO_EXT_INT_MSTAT	0x00040068
+#define AUDIO_EXT_INT_SSTAT	0x0004006C
+
+#define RDR_CFG0	0x00050000
+#define RDR_CFG1	0x00050004
+#define RDR_CFG2	0x00050008
+#define RDR_RDRCTL1	0x0005030c
+#define RDR_TLCTL0	0x00050318
+
+/* APB DMAC Current Buffer Pointer */
+#define DMA1_PTR1	0x00100000
+#define DMA2_PTR1	0x00100004
+#define DMA3_PTR1	0x00100008
+#define DMA4_PTR1	0x0010000C
+#define DMA5_PTR1	0x00100010
+#define DMA6_PTR1	0x00100014
+#define DMA7_PTR1	0x00100018
+#define DMA8_PTR1	0x0010001C
+
+/* APB DMAC Current Table Pointer */
+#define DMA1_PTR2	0x00100040
+#define DMA2_PTR2	0x00100044
+#define DMA3_PTR2	0x00100048
+#define DMA4_PTR2	0x0010004C
+#define DMA5_PTR2	0x00100050
+#define DMA6_PTR2	0x00100054
+#define DMA7_PTR2	0x00100058
+#define DMA8_PTR2	0x0010005C
+
+/* APB DMAC Buffer Limit */
+#define DMA1_CNT1	0x00100080
+#define DMA2_CNT1	0x00100084
+#define DMA3_CNT1	0x00100088
+#define DMA4_CNT1	0x0010008C
+#define DMA5_CNT1	0x00100090
+#define DMA6_CNT1	0x00100094
+#define DMA7_CNT1	0x00100098
+#define DMA8_CNT1	0x0010009C
+
+/* APB DMAC Table Size */
+#define DMA1_CNT2	0x001000C0
+#define DMA2_CNT2	0x001000C4
+#define DMA3_CNT2	0x001000C8
+#define DMA4_CNT2	0x001000CC
+#define DMA5_CNT2	0x001000D0
+#define DMA6_CNT2	0x001000D4
+#define DMA7_CNT2	0x001000D8
+#define DMA8_CNT2	0x001000DC
+
+/* Timer Counters */
+#define TM_CNT_LDW	0x00110000
+#define TM_CNT_UW	0x00110004
+#define TM_LMT_LDW	0x00110008
+#define TM_LMT_UW	0x0011000C
+
+/* GPIO */
+#define GP0_IO		0x00110010
+#define GPIO_ISM	0x00110014
+#define SOFT_RESET	0x0011001C
+
+/* GPIO (417 Microsoftcontroller) RW Data */
+#define MC417_RWD	0x00110020
+
+/* 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
+
+/* Video A Interface */
+#define VID_A_GPCNT		0x00130020
+#define VBI_A_GPCNT		0x00130024
+#define VID_A_GPCNT_CTL		0x00130030
+#define VBI_A_GPCNT_CTL		0x00130034
+#define VID_A_DMA_CTL		0x00130040
+#define VID_A_VIP_CTRL		0x00130080
+#define VID_A_PIXEL_FRMT	0x00130084
+#define VID_A_VBI_CTRL		0x00130088
+
+/* Video B Interface */
+#define VID_B_DMA		0x00130100
+#define VBI_B_DMA		0x00130108
+#define VID_B_GPCNT		0x00130120
+#define VBI_B_GPCNT		0x00130124
+#define VID_B_GPCNT_CTL		0x00130134
+#define VBI_B_GPCNT_CTL		0x00130138
+#define VID_B_DMA_CTL		0x00130140
+#define VID_B_SRC_SEL		0x00130144
+#define VID_B_LNGTH		0x00130150
+#define VID_B_HW_SOP_CTL	0x00130154
+#define VID_B_GEN_CTL		0x00130158
+#define VID_B_BD_PKT_STATUS	0x0013015C
+#define VID_B_SOP_STATUS	0x00130160
+#define VID_B_FIFO_OVFL_STAT	0x00130164
+#define VID_B_VLD_MISC		0x00130168
+#define VID_B_TS_CLK_EN		0x0013016C
+#define VID_B_VIP_CTRL		0x00130180
+#define VID_B_PIXEL_FRMT	0x00130184
+
+/* Video C Interface */
+#define VID_C_GPCNT		0x00130220
+#define VID_C_GPCNT_CTL		0x00130230
+#define VBI_C_GPCNT_CTL		0x00130234
+#define VID_C_DMA_CTL		0x00130240
+#define VID_C_LNGTH		0x00130250
+#define VID_C_HW_SOP_CTL	0x00130254
+#define VID_C_GEN_CTL		0x00130258
+#define VID_C_BD_PKT_STATUS	0x0013025C
+#define VID_C_SOP_STATUS	0x00130260
+#define VID_C_FIFO_OVFL_STAT	0x00130264
+#define VID_C_VLD_MISC		0x00130268
+#define VID_C_TS_CLK_EN		0x0013026C
+
+/* Internal Audio Interface */
+#define AUD_INT_A_GPCNT		0x00140020
+#define AUD_INT_B_GPCNT		0x00140024
+#define AUD_INT_A_GPCNT_CTL	0x00140030
+#define AUD_INT_B_GPCNT_CTL	0x00140034
+#define AUD_INT_DMA_CTL		0x00140040
+#define AUD_INT_A_LNGTH		0x00140050
+#define AUD_INT_B_LNGTH		0x00140054
+#define AUD_INT_A_MODE		0x00140058
+#define AUD_INT_B_MODE		0x0014005C
+
+/* External Audio Interface */
+#define AUD_EXT_DMA		0x00140100
+#define AUD_EXT_GPCNT		0x00140120
+#define AUD_EXT_GPCNT_CTL	0x00140130
+#define AUD_EXT_DMA_CTL		0x00140140
+#define AUD_EXT_LNGTH		0x00140150
+#define AUD_EXT_A_MODE		0x00140158
+
+/* I2C Bus 1 */
+#define I2C1_ADDR	0x00180000
+#define I2C1_WDATA	0x00180004
+#define I2C1_CTRL	0x00180008
+#define I2C1_RDATA	0x0018000C
+#define I2C1_STAT	0x00180010
+
+/* I2C Bus 2 */
+#define I2C2_ADDR	0x00190000
+#define I2C2_WDATA	0x00190004
+#define I2C2_CTRL	0x00190008
+#define I2C2_RDATA	0x0019000C
+#define I2C2_STAT	0x00190010
+
+/* I2C Bus 3 */
+#define I2C3_ADDR	0x001A0000
+#define I2C3_WDATA	0x001A0004
+#define I2C3_CTRL	0x001A0008
+#define I2C3_RDATA	0x001A000C
+#define I2C3_STAT	0x001A0010
+
+/* UART */
+#define UART_CTL	0x001B0000
+#define UART_BRD	0x001B0004
+#define UART_ISR	0x001B000C
+#define UART_CNT	0x001B0010
+
+#endif /* _CX23885_REG_H_ */
diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c
new file mode 100644
index 000000000000..a1154f035bc1
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-vbi.c
@@ -0,0 +1,295 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2007 Steven Toth <stoth@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/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.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)
+
+/* ------------------------------------------------------------------ */
+
+#define VBI_LINE_LENGTH 1440
+#define NTSC_VBI_START_LINE 10        /* line 10 - 21 */
+#define NTSC_VBI_END_LINE   21
+#define NTSC_VBI_LINES      (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1)
+
+
+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.samples_per_line = VBI_LINE_LENGTH;
+		f->fmt.vbi.sampling_rate = 27000000;
+		f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+		f->fmt.vbi.offset = 0;
+		f->fmt.vbi.flags = 0;
+		f->fmt.vbi.start[0] = 10;
+		f->fmt.vbi.count[0] = 17;
+		f->fmt.vbi.start[1] = 263 + 10 + 1;
+		f->fmt.vbi.count[1] = 17;
+	} 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;
+}
+
+/* We're given the Video Interrupt status register.
+ * The cx23885_video_irq() func has already validated
+ * the potential error bits, we just need to
+ * deal with vbi payload and return indication if
+ * we actually processed any payload.
+ */
+int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
+{
+	u32 count;
+	int handled = 0;
+
+	if (status & VID_BC_MSK_VBI_RISCI1) {
+		dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__);
+		spin_lock(&dev->slock);
+		count = cx_read(VID_A_GPCNT);
+		cx23885_video_wakeup(dev, &dev->vbiq, count);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+
+	if (status & VID_BC_MSK_VBI_RISCI2) {
+		dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__);
+		dprintk(2, "stopper vbi\n");
+		spin_lock(&dev->slock);
+		cx23885_restart_vbi_queue(dev, &dev->vbiq);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+
+	return handled;
+}
+
+static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
+			 struct cx23885_dmaqueue *q,
+			 struct cx23885_buffer   *buf)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	/* setup fifo + format */
+	cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
+				buf->vb.width, buf->risc.dma);
+
+	/* reset counter */
+	cx_write(VID_A_GPCNT_CTL, 3);
+	cx_write(VID_A_VBI_CTRL, 3);
+	cx_write(VBI_A_GPCNT_CTL, 3);
+	q->count = 1;
+
+	/* enable irq */
+	cx23885_irq_add_enable(dev, 0x01);
+	cx_set(VID_A_INT_MSK, 0x000022);
+
+	/* start dma */
+	cx_set(DEV_CNTRL2, (1<<5));
+	cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */
+
+	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 / 30));
+	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;
+
+	/* Stop the VBI engine */
+	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 1440
+#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_vbibuffer(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 / 30));
+		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/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
new file mode 100644
index 000000000000..8c4a9a5f9a50
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -0,0 +1,1926 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2007 Steven Toth <stoth@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/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>
+#include <media/v4l2-ioctl.h>
+#include "cx23885-ioctl.h"
+#include "tuner-xc2028.h"
+
+#include <media/cx25840.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+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: " fmt, dev->name, ## arg);\
+	} while (0)
+
+/* ------------------------------------------------------------------- */
+/* static data                                                         */
+
+#define FORMAT_FLAGS_PACKED       0x01
+#if 0
+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,
+	},
+};
+#else
+static struct cx23885_fmt formats[] = {
+	{
+#if 0
+		.name     = "4:2:2, packed, UYVY",
+		.fourcc   = V4L2_PIX_FMT_UYVY,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	}, {
+#endif
+		.name     = "4:2:2, packed, YUYV",
+		.fourcc   = V4L2_PIX_FMT_YUYV,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	}
+};
+#endif
+
+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(%c%c%c%c) NOT FOUND\n", __func__,
+		(fourcc & 0xff),
+		((fourcc >> 8) & 0xff),
+		((fourcc >> 16) & 0xff),
+		((fourcc >> 24) & 0xff)
+		);
+	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       = 0x7f,
+			.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       = -127,
+			.maximum       = 128,
+			.step          = 1,
+			.default_value = 0x0,
+			.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       = 0x7f,
+			.step          = 1,
+			.default_value = 0x3f,
+			.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       = 65535,
+			.step          = 65535 / 100,
+			.default_value = 65535,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.reg                   = PATH1_VOL_CTL,
+		.mask                  = 0xff,
+		.shift                 = 0,
+	}
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+/* Must be sorted from low to high control ID! */
+static 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
+};
+
+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",
+			__func__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+	dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+		__func__,
+		(unsigned int)norm,
+		v4l2_norm_to_name(norm));
+
+	dev->tvnorm = norm;
+
+	call_all(dev, core, s_std, norm);
+
+	return 0;
+}
+
+static 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", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->release = video_device_release;
+	snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+		 cx23885_boards[dev->board].name, type);
+	video_set_drvdata(vfd, dev);
+	return vfd;
+}
+
+static 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;
+}
+
+/* ------------------------------------------------------------------- */
+/* resource management                                                 */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+	unsigned int bit)
+{
+	dprintk(1, "%s()\n", __func__);
+	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", __func__);
+
+	mutex_lock(&dev->lock);
+	fh->resources  &= ~bits;
+	dev->resources &= ~bits;
+	dprintk(1, "res: put %d\n", bits);
+	mutex_unlock(&dev->lock);
+}
+
+static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
+{
+	/* 8 bit registers, 8 bit values */
+	u8 buf[] = { reg, data };
+
+	struct i2c_msg msg = { .addr = 0x98 >> 1,
+		.flags = 0, .buf = buf, .len = 2 };
+
+	return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
+{
+	/* 8 bit registers, 8 bit values */
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+
+	struct i2c_msg msg[] = {
+		{ .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+	};
+
+	ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
+	if (ret != 2)
+		printk(KERN_ERR "%s() error\n", __func__);
+
+	return b1[0];
+}
+
+static void cx23885_flatiron_dump(struct cx23885_dev *dev)
+{
+	int i;
+	dprintk(1, "Flatiron dump\n");
+	for (i = 0; i < 0x24; i++) {
+		dprintk(1, "FI[%02x] = %02x\n", i,
+			cx23885_flatiron_read(dev, i));
+	}
+}
+
+static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
+{
+	u8 val;
+	dprintk(1, "%s(input = %d)\n", __func__, input);
+
+	if (input == 1)
+		val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
+	else if (input == 2)
+		val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
+	else
+		return -EINVAL;
+
+	val |= 0x20; /* Enable clock to delta-sigma and dec filter */
+
+	cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
+
+	/* Wake up */
+	cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
+
+	if (video_debug)
+		cx23885_flatiron_dump(dev);
+
+	return 0;
+}
+
+static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+	dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+		__func__,
+		input, INPUT(input)->vmux,
+		INPUT(input)->gpio0, INPUT(input)->gpio1,
+		INPUT(input)->gpio2, INPUT(input)->gpio3);
+	dev->input = input;
+
+	if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
+		dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 ||
+		dev->board == CX23885_BOARD_MYGICA_X8507) {
+		/* Select Analog TV */
+		if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
+			cx23885_gpio_clear(dev, GPIO_0);
+	}
+
+	/* Tell the internal A/V decoder */
+	v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
+			INPUT(input)->vmux, 0, 0);
+
+	if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
+		(dev->board == CX23885_BOARD_MPX885) ||
+		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) ||
+		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
+		(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) {
+		/* Configure audio routing */
+		v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
+			INPUT(input)->amux, 0, 0);
+
+		if (INPUT(input)->amux == CX25840_AUDIO7)
+			cx23885_flatiron_mux(dev, 1);
+		else if (INPUT(input)->amux == CX25840_AUDIO6)
+			cx23885_flatiron_mux(dev, 2);
+	}
+
+	return 0;
+}
+
+static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
+{
+	dprintk(1, "%s(input=%d)\n", __func__, input);
+
+	/* The baseband video core of the cx23885 has two audio inputs.
+	 * LR1 and LR2. In almost every single case so far only HVR1xxx
+	 * cards we've only ever supported LR1. Time to support LR2,
+	 * which is available via the optional white breakout header on
+	 * the board.
+	 * We'll use a could of existing enums in the card struct to allow
+	 * devs to specify which baseband input they need, or just default
+	 * to what we've always used.
+	 */
+	if (INPUT(input)->amux == CX25840_AUDIO7)
+		cx23885_flatiron_mux(dev, 1);
+	else if (INPUT(input)->amux == CX25840_AUDIO6)
+		cx23885_flatiron_mux(dev, 2);
+	else {
+		/* Not specifically defined, assume the default. */
+		cx23885_flatiron_mux(dev, 1);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+			   struct cx23885_dmaqueue *q,
+			   struct cx23885_buffer *buf)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	/* Stop the dma/fifo before we tamper with it's risc programs */
+	cx_clear(VID_A_DMA_CTL, 0x11);
+
+	/* setup fifo + format */
+	cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+				buf->bpl, buf->risc.dma);
+
+	/* reset counter */
+	cx_write(VID_A_GPCNT_CTL, 3);
+	q->count = 1;
+
+	/* enable irq */
+	cx23885_irq_add_enable(dev, 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", __func__);
+
+	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;
+	if (*size * *count > vid_limit * 1024 * 1024)
+		*count = (vid_limit * 1024 * 1024) / *size;
+	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);
+	int field_tff;
+
+	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)
+				/* NTSC or  */
+				field_tff = 1;
+			else
+				field_tff = 0;
+
+			if (cx23885_boards[dev->board].force_bff)
+				/* PAL / SECAM OR 888 in NTSC MODE */
+				field_tff = 0;
+
+			if (field_tff) {
+				/* cx25840 transmits NTSC bottom field first */
+				dprintk(1, "%s() Creating TFF/NTSC risc\n",
+					__func__);
+				line0_offset = buf->bpl;
+				line1_offset = 0;
+			} else {
+				/* All other formats are top field first */
+				dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
+					__func__);
+				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 file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx23885_dev *dev = video_drvdata(file);
+	struct cx23885_fh *fh;
+	enum v4l2_buf_type type = 0;
+	int radio = 0;
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		break;
+	case VFL_TYPE_VBI:
+		type = V4L2_BUF_TYPE_VBI_CAPTURE;
+		break;
+	case VFL_TYPE_RADIO:
+		radio = 1;
+		break;
+	}
+
+	dprintk(1, "open dev=%s radio=%d type=%s\n",
+		video_device_node_name(vdev), 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_YUYV);
+
+	videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx23885_buffer),
+			    fh, NULL);
+
+	videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
+		&dev->pci->dev, &dev->slock,
+		V4L2_BUF_TYPE_VBI_CAPTURE,
+		V4L2_FIELD_SEQ_TB,
+		sizeof(struct cx23885_buffer),
+		fh, NULL);
+
+
+	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;
+	unsigned int rc = POLLERR;
+
+	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);
+	}
+
+	mutex_lock(&fh->vidq.vb_lock);
+	if (res_check(fh, RESOURCE_VIDEO)) {
+		/* streaming capture */
+		if (list_empty(&fh->vidq.stream))
+			goto done;
+		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)
+			goto done;
+	}
+	poll_wait(file, &buf->vb.done, wait);
+	if (buf->vb.state == VIDEOBUF_DONE ||
+	    buf->vb.state == VIDEOBUF_ERROR)
+		rc =  POLLIN|POLLRDNORM;
+	else
+		rc = 0;
+done:
+	mutex_unlock(&fh->vidq.vb_lock);
+	return rc;
+}
+
+static int video_release(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);
+	videobuf_mmap_free(&fh->vbiq);
+
+	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", __func__);
+	call_all(dev, core, g_ctrl, ctl);
+	return 0;
+}
+
+int cx23885_set_control(struct cx23885_dev *dev,
+	struct v4l2_control *ctl)
+{
+	dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
+	call_all(dev, core, s_ctrl, ctl);
+
+	return 0;
+}
+
+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_vid_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_vid_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;
+	v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+			      &f->fmt.pix.height, 32, maxh, 0, 0);
+	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_vid_cap(struct file *file, void *priv,
+	struct v4l2_format *f)
+{
+	struct cx23885_fh *fh = priv;
+	struct cx23885_dev *dev  = ((struct cx23885_fh *)priv)->dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int err;
+
+	dprintk(2, "%s()\n", __func__);
+	err = vidioc_try_fmt_vid_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", __func__,
+		fh->width, fh->height, fh->vidq.field);
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+	call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+	v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+	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->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_vid_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;
+}
+
+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", __func__);
+
+	if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+	if (unlikely(i != fh->type))
+		return -EINVAL;
+
+	if (unlikely(!res_get(dev, fh, get_resource(fh))))
+		return -EBUSY;
+
+	/* Don't start VBI streaming unless vida streaming
+	 * has already started.
+	 */
+	if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
+		((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
+		return -EINVAL;
+
+	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", __func__);
+
+	if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(fh->type != V4L2_BUF_TYPE_VBI_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_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+	dprintk(1, "%s()\n", __func__);
+
+	call_all(dev, core, g_std, id);
+
+	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", __func__);
+
+	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_COMPONENT]  = "Component",
+		[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", __func__);
+
+	n = i->index;
+	if (n >= MAX_CX23885_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 ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+		(CX23885_VMUX_CABLE == INPUT(n)->type)) {
+		i->type = V4L2_INPUT_TYPE_TUNER;
+		i->std = CX23885_NORMS;
+	}
+
+	/* Two selectable audio inputs for non-tv inputs */
+	if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
+		i->audioset = 0x3;
+
+	if (dev->input == n) {
+		/* enum'd input matches our configured input.
+		 * Ask the video decoder to process the call
+		 * and give it an oppertunity to update the
+		 * status field.
+		 */
+		call_all(dev, video, g_input_status, &i->status);
+	}
+
+	return 0;
+}
+
+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", __func__);
+	return cx23885_enum_input(dev, i);
+}
+
+int cx23885_get_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", __func__, *i);
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	return cx23885_get_input(file, priv, i);
+}
+
+int cx23885_set_input(struct file *file, void *priv, unsigned int i)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+	dprintk(1, "%s(%d)\n", __func__, i);
+
+	if (i >= MAX_CX23885_INPUT) {
+		dprintk(1, "%s() -EINVAL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (INPUT(i)->type == 0)
+		return -EINVAL;
+
+	mutex_lock(&dev->lock);
+	cx23885_video_mux(dev, i);
+
+	/* By default establish the default audio input for the card also */
+	/* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
+	cx23885_audio_mux(dev, i);
+	mutex_unlock(&dev->lock);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	struct cx23885_fh  *fh  = priv;
+	struct cx23885_dev *dev = fh->dev;
+
+	printk(KERN_INFO
+		"%s/0: ============  START LOG STATUS  ============\n",
+		dev->name);
+	call_all(dev, core, log_status);
+	printk(KERN_INFO
+		"%s/0: =============  END LOG STATUS  =============\n",
+		dev->name);
+	return 0;
+}
+
+static int cx23885_query_audinput(struct file *file, void *priv,
+	struct v4l2_audio *i)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+	static const char *iname[] = {
+		[0] = "Baseband L/R 1",
+		[1] = "Baseband L/R 2",
+	};
+	unsigned int n;
+	dprintk(1, "%s()\n", __func__);
+
+	n = i->index;
+	if (n >= 2)
+		return -EINVAL;
+
+	memset(i, 0, sizeof(*i));
+	i->index = n;
+	strcpy(i->name, iname[n]);
+	i->capability  = V4L2_AUDCAP_STEREO;
+	i->mode  = V4L2_AUDMODE_AVL;
+	return 0;
+
+}
+
+static int vidioc_enum_audinput(struct file *file, void *priv,
+				struct v4l2_audio *i)
+{
+	return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_g_audinput(struct file *file, void *priv,
+	struct v4l2_audio *i)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+	i->index = dev->audinput;
+	dprintk(1, "%s(input=%d)\n", __func__, i->index);
+
+	return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_s_audinput(struct file *file, void *priv,
+	const struct v4l2_audio *i)
+{
+	struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+	if (i->index >= 2)
+		return -EINVAL;
+
+	dprintk(1, "%s(%d)\n", __func__, i->index);
+
+	dev->audinput = i->index;
+
+	/* Skip the audio defaults from the cards struct, caller wants
+	 * directly touch the audio mux hardware. */
+	cx23885_flatiron_mux(dev, dev->audinput + 1);
+	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");
+
+	call_all(dev, tuner, g_tuner, t);
+	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;
+	/* Update the A/V core */
+	call_all(dev, tuner, s_tuner, t);
+
+	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;
+
+	call_all(dev, tuner, g_frequency, f);
+
+	return 0;
+}
+
+static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+	struct v4l2_control ctrl;
+
+	if (unlikely(UNSET == dev->tuner_type))
+		return -EINVAL;
+	if (unlikely(f->tuner != 0))
+		return -EINVAL;
+
+	mutex_lock(&dev->lock);
+	dev->freq = f->frequency;
+
+	/* I need to mute audio here */
+	ctrl.id = V4L2_CID_AUDIO_MUTE;
+	ctrl.value = 1;
+	cx23885_set_control(dev, &ctrl);
+
+	call_all(dev, tuner, s_frequency, f);
+
+	/* When changing channels it is required to reset TVAUDIO */
+	msleep(100);
+
+	/* I need to unmute audio here */
+	ctrl.value = 0;
+	cx23885_set_control(dev, &ctrl);
+
+	mutex_unlock(&dev->lock);
+
+	return 0;
+}
+
+static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
+	struct v4l2_frequency *f)
+{
+	struct v4l2_control ctrl;
+	struct videobuf_dvb_frontend *vfe;
+	struct dvb_frontend *fe;
+
+	struct analog_parameters params = {
+		.mode      = V4L2_TUNER_ANALOG_TV,
+		.audmode   = V4L2_TUNER_MODE_STEREO,
+		.std       = dev->tvnorm,
+		.frequency = f->frequency
+	};
+
+	mutex_lock(&dev->lock);
+	dev->freq = f->frequency;
+
+	/* I need to mute audio here */
+	ctrl.id = V4L2_CID_AUDIO_MUTE;
+	ctrl.value = 1;
+	cx23885_set_control(dev, &ctrl);
+
+	/* If HVR1850 */
+	dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
+		params.frequency, f->tuner, params.std);
+
+	vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
+	if (!vfe) {
+		mutex_unlock(&dev->lock);
+		return -EINVAL;
+	}
+
+	fe = vfe->dvb.frontend;
+
+	if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
+	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+	    (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111))
+		fe = &dev->ts1.analog_fe;
+
+	if (fe && fe->ops.tuner_ops.set_analog_params) {
+		call_all(dev, core, s_std, dev->tvnorm);
+		fe->ops.tuner_ops.set_analog_params(fe, &params);
+	}
+	else
+		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+	/* When changing channels it is required to reset TVAUDIO */
+	msleep(100);
+
+	/* I need to unmute audio here */
+	ctrl.value = 0;
+	cx23885_set_control(dev, &ctrl);
+
+	mutex_unlock(&dev->lock);
+
+	return 0;
+}
+
+int cx23885_set_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct cx23885_fh *fh = priv;
+	struct cx23885_dev *dev = fh->dev;
+	int ret;
+
+	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1255:
+	case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+	case CX23885_BOARD_HAUPPAUGE_HVR1850:
+		ret = cx23885_set_freq_via_ops(dev, f);
+		break;
+	default:
+		ret = cx23885_set_freq(dev, f);
+	}
+
+	return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	return cx23885_set_frequency(file, priv, f);
+}
+
+/* ----------------------------------------------------------- */
+
+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;
+
+	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: [%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);
+
+	/* risc op code error, fifo overflow or line sync detection error */
+	if ((status & VID_BC_MSK_OPC_ERR) ||
+		(status & VID_BC_MSK_SYNC) ||
+		(status & VID_BC_MSK_OF)) {
+
+		if (status & VID_BC_MSK_OPC_ERR) {
+			dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+				VID_BC_MSK_OPC_ERR);
+			printk(KERN_WARNING "%s: video risc op code error\n",
+				dev->name);
+			cx23885_sram_channel_dump(dev,
+				&dev->sram_channels[SRAM_CH01]);
+		}
+
+		if (status & VID_BC_MSK_SYNC)
+			dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
+				"video lines miss-match\n",
+				VID_BC_MSK_SYNC);
+
+		if (status & VID_BC_MSK_OF)
+			dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
+				VID_BC_MSK_OF);
+
+	}
+
+	/* Video */
+	if (status & VID_BC_MSK_RISCI1) {
+		spin_lock(&dev->slock);
+		count = cx_read(VID_A_GPCNT);
+		cx23885_video_wakeup(dev, &dev->vidq, count);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+	if (status & VID_BC_MSK_RISCI2) {
+		dprintk(2, "stopper video\n");
+		spin_lock(&dev->slock);
+		cx23885_restart_video_queue(dev, &dev->vidq);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+
+	/* Allow the VBI framework to process it's payload */
+	handled += cx23885_vbi_irq(dev, status);
+
+	return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff                                              */
+
+static const struct v4l2_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,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+	.vidioc_g_fmt_vbi_cap     = cx23885_vbi_fmt,
+	.vidioc_try_fmt_vbi_cap   = cx23885_vbi_fmt,
+	.vidioc_s_fmt_vbi_cap     = 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_g_std         = vidioc_g_std,
+	.vidioc_querystd      = vidioc_g_std,
+	.vidioc_enum_input    = vidioc_enum_input,
+	.vidioc_g_input       = vidioc_g_input,
+	.vidioc_s_input       = vidioc_s_input,
+	.vidioc_log_status    = vidioc_log_status,
+	.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,
+	.vidioc_g_chip_ident  = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register    = cx23885_g_register,
+	.vidioc_s_register    = cx23885_s_register,
+#endif
+	.vidioc_enumaudio     = vidioc_enum_audinput,
+	.vidioc_g_audio       = vidioc_g_audinput,
+	.vidioc_s_audio       = vidioc_s_audinput,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+	.name                 = "cx23885-video",
+	.fops                 = &video_fops,
+	.ioctl_ops 	      = &video_ioctl_ops,
+	.tvnorms              = CX23885_NORMS,
+	.current_norm         = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+	.owner         = THIS_MODULE,
+	.open          = video_open,
+	.release       = video_release,
+	.ioctl         = video_ioctl2,
+};
+
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+	cx23885_irq_remove(dev, 0x01);
+
+	if (dev->vbi_dev) {
+		if (video_is_registered(dev->vbi_dev))
+			video_unregister_device(dev->vbi_dev);
+		else
+			video_device_release(dev->vbi_dev);
+		dev->vbi_dev = NULL;
+		btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+	}
+	if (dev->video_dev) {
+		if (video_is_registered(dev->video_dev))
+			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);
+	}
+
+	if (dev->audio_dev)
+		cx23885_audio_unregister(dev);
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+	int err;
+
+	dprintk(1, "%s()\n", __func__);
+	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");
+
+	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);
+
+	/* init vbi dma queues */
+	INIT_LIST_HEAD(&dev->vbiq.active);
+	INIT_LIST_HEAD(&dev->vbiq.queued);
+	dev->vbiq.timeout.function = cx23885_vbi_timeout;
+	dev->vbiq.timeout.data = (unsigned long)dev;
+	init_timer(&dev->vbiq.timeout);
+	cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+		VID_A_DMA_CTL, 0x22, 0x00);
+
+	cx23885_irq_add_enable(dev, 0x01);
+
+	if ((TUNER_ABSENT != dev->tuner_type) &&
+			((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
+		struct v4l2_subdev *sd = NULL;
+
+		if (dev->tuner_addr)
+			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_bus[dev->tuner_bus].i2c_adap,
+				"tuner", dev->tuner_addr, NULL);
+		else
+			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_bus[dev->tuner_bus].i2c_adap,
+				"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
+		if (sd) {
+			struct tuner_setup tun_setup;
+
+			memset(&tun_setup, 0, sizeof(tun_setup));
+			tun_setup.mode_mask = T_ANALOG_TV;
+			tun_setup.type = dev->tuner_type;
+			tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+			tun_setup.tuner_callback = cx23885_tuner_callback;
+
+			v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
+
+			if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
+				struct xc2028_ctrl ctrl = {
+					.fname = XC2028_DEFAULT_FIRMWARE,
+					.max_len = 64
+				};
+				struct v4l2_priv_tun_config cfg = {
+					.tuner = dev->tuner_type,
+					.priv = &ctrl
+				};
+				v4l2_subdev_call(sd, tuner, s_config, &cfg);
+			}
+		}
+	}
+
+	/* register Video device */
+	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: registered device %s [v4l2]\n",
+	       dev->name, video_device_node_name(dev->video_dev));
+
+	/* register VBI device */
+	dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+		&cx23885_vbi_template, "vbi");
+	err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+				    vbi_nr[dev->nr]);
+	if (err < 0) {
+		printk(KERN_INFO "%s: can't register vbi device\n",
+			dev->name);
+		goto fail_unreg;
+	}
+	printk(KERN_INFO "%s: registered device %s\n",
+	       dev->name, video_device_node_name(dev->vbi_dev));
+
+	/* Register ALSA audio device */
+	dev->audio_dev = cx23885_audio_register(dev);
+
+	/* initial device configuration */
+	mutex_lock(&dev->lock);
+	cx23885_set_tvnorm(dev, dev->tvnorm);
+	init_controls(dev);
+	cx23885_video_mux(dev, 0);
+	cx23885_audio_mux(dev, 0);
+	mutex_unlock(&dev->lock);
+
+	return 0;
+
+fail_unreg:
+	cx23885_video_unregister(dev);
+	return err;
+}
+
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
new file mode 100644
index 000000000000..67f40d31450b
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -0,0 +1,654 @@
+/*
+ *  Driver for the Conexant CX23885 PCIe bridge
+ *
+ *  Copyright (c) 2006 Steven Toth <stoth@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/pci.h>
+#include <linux/i2c.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <media/rc-core.h>
+
+#include "btcx-risc.h"
+#include "cx23885-reg.h"
+#include "media/cx2341x.h"
+
+#include <linux/mutex.h>
+
+#define CX23885_VERSION "0.0.3"
+
+#define UNSET (-1U)
+
+#define CX23885_MAXBOARDS 8
+
+/* 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 */
+
+#define CX23885_BOARD_NOAUTO               UNSET
+#define CX23885_BOARD_UNKNOWN                  0
+#define CX23885_BOARD_HAUPPAUGE_HVR1800lp      1
+#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
+#define CX23885_BOARD_HAUPPAUGE_HVR1200        7
+#define CX23885_BOARD_HAUPPAUGE_HVR1700        8
+#define CX23885_BOARD_HAUPPAUGE_HVR1400        9
+#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
+#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F   13
+#define CX23885_BOARD_TBS_6920                 14
+#define CX23885_BOARD_TEVII_S470               15
+#define CX23885_BOARD_DVBWORLD_2005            16
+#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI      17
+#define CX23885_BOARD_HAUPPAUGE_HVR1270        18
+#define CX23885_BOARD_HAUPPAUGE_HVR1275        19
+#define CX23885_BOARD_HAUPPAUGE_HVR1255        20
+#define CX23885_BOARD_HAUPPAUGE_HVR1210        21
+#define CX23885_BOARD_MYGICA_X8506             22
+#define CX23885_BOARD_MAGICPRO_PROHDTVE2       23
+#define CX23885_BOARD_HAUPPAUGE_HVR1850        24
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E800    25
+#define CX23885_BOARD_HAUPPAUGE_HVR1290        26
+#define CX23885_BOARD_MYGICA_X8558PRO          27
+#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
+#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID     29
+#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
+#define CX23885_BOARD_MPX885                   32
+#define CX23885_BOARD_MYGICA_X8507             33
+#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
+#define CX23885_BOARD_TEVII_S471               35
+#define CX23885_BOARD_HAUPPAUGE_HVR1255_22111  36
+#define CX23885_BOARD_PROF_8000                37
+
+#define GPIO_0 0x00000001
+#define GPIO_1 0x00000002
+#define GPIO_2 0x00000004
+#define GPIO_3 0x00000008
+#define GPIO_4 0x00000010
+#define GPIO_5 0x00000020
+#define GPIO_6 0x00000040
+#define GPIO_7 0x00000080
+#define GPIO_8 0x00000100
+#define GPIO_9 0x00000200
+#define GPIO_10 0x00000400
+#define GPIO_11 0x00000800
+#define GPIO_12 0x00001000
+#define GPIO_13 0x00002000
+#define GPIO_14 0x00004000
+#define GPIO_15 0x00008000
+
+/* 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,
+	CX23885_VMUX_COMPOSITE2,
+	CX23885_VMUX_COMPOSITE3,
+	CX23885_VMUX_COMPOSITE4,
+	CX23885_VMUX_SVIDEO,
+	CX23885_VMUX_COMPONENT,
+	CX23885_VMUX_TELEVISION,
+	CX23885_VMUX_CABLE,
+	CX23885_VMUX_DVB,
+	CX23885_VMUX_DEBUG,
+	CX23885_RADIO,
+};
+
+enum cx23885_src_sel_type {
+	CX23885_SRC_SEL_EXT_656_VIDEO = 0,
+	CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO
+};
+
+/* buffer for one video frame */
+struct cx23885_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer vb;
+
+	/* cx23885 specific */
+	unsigned int           bpl;
+	struct btcx_riscmem    risc;
+	struct cx23885_fmt     *fmt;
+	u32                    count;
+};
+
+struct cx23885_input {
+	enum cx23885_itype type;
+	unsigned int    vmux;
+	unsigned int    amux;
+	u32             gpio0, gpio1, gpio2, gpio3;
+};
+
+typedef enum {
+	CX23885_MPEG_UNDEFINED = 0,
+	CX23885_MPEG_DVB,
+	CX23885_ANALOG_VIDEO,
+	CX23885_MPEG_ENCODER,
+} port_t;
+
+struct cx23885_board {
+	char                    *name;
+	port_t			porta, portb, portc;
+	int		num_fds_portb, num_fds_portc;
+	unsigned int		tuner_type;
+	unsigned int		radio_type;
+	unsigned char		tuner_addr;
+	unsigned char		radio_addr;
+	unsigned int		tuner_bus;
+
+	/* Vendors can and do run the PCIe bridge at different
+	 * clock rates, driven physically by crystals on the PCBs.
+	 * The core has to accommodate 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];
+	int			ci_type; /* for NetUP */
+	/* Force bottom field first during DMA (888 workaround) */
+	u32                     force_bff;
+};
+
+struct cx23885_subid {
+	u16     subvendor;
+	u16     subdevice;
+	u32     card;
+};
+
+struct cx23885_i2c {
+	struct cx23885_dev *dev;
+
+	int                        nr;
+
+	/* i2c i/o */
+	struct i2c_adapter         i2c_adap;
+	struct i2c_client          i2c_client;
+	u32                        i2c_rc;
+
+	/* 885 registers used for raw addess */
+	u32                        i2c_period;
+	u32                        reg_ctrl;
+	u32                        reg_stat;
+	u32                        reg_addr;
+	u32                        reg_rdata;
+	u32                        reg_wdata;
+};
+
+struct cx23885_dmaqueue {
+	struct list_head       active;
+	struct list_head       queued;
+	struct timer_list      timeout;
+	struct btcx_riscmem    stopper;
+	u32                    count;
+};
+
+struct cx23885_tsport {
+	struct cx23885_dev *dev;
+
+	int                        nr;
+	int                        sram_chno;
+
+	struct videobuf_dvb_frontends frontends;
+
+	/* dma queues */
+	struct cx23885_dmaqueue    mpegq;
+	u32                        ts_packet_size;
+	u32                        ts_packet_count;
+
+	int                        width;
+	int                        height;
+
+	spinlock_t                 slock;
+
+	/* registers */
+	u32                        reg_gpcnt;
+	u32                        reg_gpcnt_ctl;
+	u32                        reg_dma_ctl;
+	u32                        reg_lngth;
+	u32                        reg_hw_sop_ctrl;
+	u32                        reg_gen_ctrl;
+	u32                        reg_bd_pkt_status;
+	u32                        reg_sop_status;
+	u32                        reg_fifo_ovfl_stat;
+	u32                        reg_vld_misc;
+	u32                        reg_ts_clk_en;
+	u32                        reg_ts_int_msk;
+	u32                        reg_ts_int_stat;
+	u32                        reg_src_sel;
+
+	/* Default register vals */
+	int                        pci_irqmask;
+	u32                        dma_ctl_val;
+	u32                        ts_int_msk_val;
+	u32                        gen_ctrl_val;
+	u32                        ts_clk_en_val;
+	u32                        src_sel_val;
+	u32                        vld_misc_val;
+	u32                        hw_sop_ctrl_val;
+
+	/* Allow a single tsport to have multiple frontends */
+	u32                        num_frontends;
+	void                (*gate_ctrl)(struct cx23885_tsport *port, int open);
+	void                       *port_priv;
+
+	/* Workaround for a temp dvb_frontend that the tuner can attached to */
+	struct dvb_frontend analog_fe;
+};
+
+struct cx23885_kernel_ir {
+	struct cx23885_dev	*cx;
+	char			*name;
+	char			*phys;
+
+	struct rc_dev		*rc;
+};
+
+struct cx23885_audio_buffer {
+	unsigned int		bpl;
+	struct btcx_riscmem	risc;
+	struct videobuf_dmabuf	dma;
+};
+
+struct cx23885_audio_dev {
+	struct cx23885_dev	*dev;
+
+	struct pci_dev		*pci;
+
+	struct snd_card		*card;
+
+	spinlock_t		lock;
+
+	atomic_t		count;
+
+	unsigned int		dma_size;
+	unsigned int		period_size;
+	unsigned int		num_periods;
+
+	struct videobuf_dmabuf	*dma_risc;
+
+	struct cx23885_audio_buffer   *buf;
+
+	struct snd_pcm_substream *substream;
+};
+
+struct cx23885_dev {
+	atomic_t                   refcount;
+	struct v4l2_device 	   v4l2_dev;
+
+	/* pci stuff */
+	struct pci_dev             *pci;
+	unsigned char              pci_rev, pci_lat;
+	int                        pci_bus, pci_slot;
+	u32                        __iomem *lmmio;
+	u8                         __iomem *bmmio;
+	int                        pci_irqmask;
+	spinlock_t		   pci_irqmask_lock; /* protects mask reg too */
+	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];
+
+	int                        nr;
+	struct mutex               lock;
+	struct mutex               gpio_lock;
+
+	/* board details */
+	unsigned int               board;
+	char                       name[32];
+
+	struct cx23885_tsport      ts1, ts2;
+
+	/* sram configuration */
+	struct sram_channel        *sram_channels;
+
+	enum {
+		CX23885_BRIDGE_UNDEFINED = 0,
+		CX23885_BRIDGE_885 = 885,
+		CX23885_BRIDGE_887 = 887,
+		CX23885_BRIDGE_888 = 888,
+	} bridge;
+
+	/* Analog video */
+	u32                        resources;
+	unsigned int               input;
+	unsigned int               audinput; /* Selectable audio input */
+	u32                        tvaudio;
+	v4l2_std_id                tvnorm;
+	unsigned int               tuner_type;
+	unsigned char              tuner_addr;
+	unsigned int               tuner_bus;
+	unsigned int               radio_type;
+	unsigned char              radio_addr;
+	unsigned int               has_radio;
+	struct v4l2_subdev 	   *sd_cx25840;
+	struct work_struct	   cx25840_work;
+
+	/* Infrared */
+	struct v4l2_subdev         *sd_ir;
+	struct work_struct	   ir_rx_work;
+	unsigned long		   ir_rx_notifications;
+	struct work_struct	   ir_tx_work;
+	unsigned long		   ir_tx_notifications;
+
+	struct cx23885_kernel_ir   *kernel_ir;
+	atomic_t		   ir_input_stopping;
+
+	/* 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;
+
+	/* MPEG Encoder ONLY settings */
+	u32                        cx23417_mailbox;
+	struct cx2341x_mpeg_params mpeg_params;
+	struct video_device        *v4l_device;
+	atomic_t                   v4l_reader_count;
+	struct cx23885_tvnorm      encodernorm;
+
+	/* Analog raw audio */
+	struct cx23885_audio_dev   *audio_dev;
+
+};
+
+static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev);
+}
+
+#define call_all(dev, o, f, args...) \
+	v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+#define CX23885_HW_888_IR  (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
+
+#define call_hw(dev, grpid, o, f, args...) \
+	v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
+
+extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
+
+#define SRAM_CH01  0 /* Video A */
+#define SRAM_CH02  1 /* VBI A */
+#define SRAM_CH03  2 /* Video B */
+#define SRAM_CH04  3 /* Transport via B */
+#define SRAM_CH05  4 /* VBI B */
+#define SRAM_CH06  5 /* Video C */
+#define SRAM_CH07  6 /* Transport via C */
+#define SRAM_CH08  7 /* Audio Internal A */
+#define SRAM_CH09  8 /* Audio Internal B */
+#define SRAM_CH10  9 /* Audio External */
+#define SRAM_CH11 10 /* COMB_3D_N */
+#define SRAM_CH12 11 /* Comb 3D N1 */
+#define SRAM_CH13 12 /* Comb 3D N2 */
+#define SRAM_CH14 13 /* MOE Vid */
+#define SRAM_CH15 14 /* MOE RSLT */
+
+struct sram_channel {
+	char *name;
+	u32  cmds_start;
+	u32  ctrl_start;
+	u32  cdt;
+	u32  fifo_start;
+	u32  fifo_size;
+	u32  ptr1_reg;
+	u32  ptr2_reg;
+	u32  cnt1_reg;
+	u32  cnt2_reg;
+	u32  jumponly;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg)             readl(dev->lmmio + ((reg)>>2))
+#define cx_write(reg, value)     writel((value), dev->lmmio + ((reg)>>2))
+
+#define cx_andor(reg, mask, value) \
+  writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+  ((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#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);
+
+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);
+
+extern int cx23885_risc_vbibuffer(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);
+
+extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
+	int asoutput);
+
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
+
+/* ----------------------------------------------------------- */
+/* 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 component,
+	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_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
+extern void cx23885_gpio_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
+
+extern int cx23885_dvb_register(struct cx23885_tsport *port);
+extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
+
+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);
+extern void cx23885_video_wakeup(struct cx23885_dev *dev,
+	struct cx23885_dmaqueue *q, u32 count);
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
+int cx23885_set_input(struct file *file, void *priv, unsigned int i);
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
+int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
+
+/* ----------------------------------------------------------- */
+/* 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;
+extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+	struct cx23885_dmaqueue *q);
+extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
+
+/* cx23885-i2c.c                                                */
+extern int cx23885_i2c_register(struct cx23885_i2c *bus);
+extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
+extern void cx23885_av_clk(struct cx23885_dev *dev, int enable);
+
+/* ----------------------------------------------------------- */
+/* cx23885-417.c                                               */
+extern int cx23885_417_register(struct cx23885_dev *dev);
+extern void cx23885_417_unregister(struct cx23885_dev *dev);
+extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status);
+extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
+extern void cx23885_mc417_init(struct cx23885_dev *dev);
+extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
+extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern int mc417_register_read(struct cx23885_dev *dev,
+				u16 address, u32 *value);
+extern int mc417_register_write(struct cx23885_dev *dev,
+				u16 address, u32 value);
+extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
+
+/* ----------------------------------------------------------- */
+/* cx23885-alsa.c                                             */
+extern struct cx23885_audio_dev *cx23885_audio_register(
+					struct cx23885_dev *dev);
+extern void cx23885_audio_unregister(struct cx23885_dev *dev);
+extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
+extern int cx23885_risc_databuffer(struct pci_dev *pci,
+				   struct btcx_riscmem *risc,
+				   struct scatterlist *sglist,
+				   unsigned int bpl,
+				   unsigned int lines,
+				   unsigned int lpi);
+
+/* ----------------------------------------------------------- */
+/* 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;
+}
diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
new file mode 100644
index 000000000000..c2bc39c58f82
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
@@ -0,0 +1,1271 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  CX23888 Integrated Consumer Infrared Controller
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/rc-core.h>
+
+#include "cx23885.h"
+
+static unsigned int ir_888_debug;
+module_param(ir_888_debug, int, 0644);
+MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
+
+#define CX23888_IR_REG_BASE 	0x170000
+/*
+ * These CX23888 register offsets have a straightforward one to one mapping
+ * to the CX23885 register offsets of 0x200 through 0x218
+ */
+#define CX23888_IR_CNTRL_REG	0x170000
+#define CNTRL_WIN_3_3	0x00000000
+#define CNTRL_WIN_4_3	0x00000001
+#define CNTRL_WIN_3_4	0x00000002
+#define CNTRL_WIN_4_4	0x00000003
+#define CNTRL_WIN	0x00000003
+#define CNTRL_EDG_NONE	0x00000000
+#define CNTRL_EDG_FALL	0x00000004
+#define CNTRL_EDG_RISE	0x00000008
+#define CNTRL_EDG_BOTH	0x0000000C
+#define CNTRL_EDG	0x0000000C
+#define CNTRL_DMD	0x00000010
+#define CNTRL_MOD	0x00000020
+#define CNTRL_RFE	0x00000040
+#define CNTRL_TFE	0x00000080
+#define CNTRL_RXE	0x00000100
+#define CNTRL_TXE	0x00000200
+#define CNTRL_RIC	0x00000400
+#define CNTRL_TIC	0x00000800
+#define CNTRL_CPL	0x00001000
+#define CNTRL_LBM	0x00002000
+#define CNTRL_R		0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO	0x00008000
+
+#define CX23888_IR_TXCLK_REG	0x170004
+#define TXCLK_TCD	0x0000FFFF
+
+#define CX23888_IR_RXCLK_REG	0x170008
+#define RXCLK_RCD	0x0000FFFF
+
+#define CX23888_IR_CDUTY_REG	0x17000C
+#define CDUTY_CDC	0x0000000F
+
+#define CX23888_IR_STATS_REG	0x170010
+#define STATS_RTO	0x00000001
+#define STATS_ROR	0x00000002
+#define STATS_RBY	0x00000004
+#define STATS_TBY	0x00000008
+#define STATS_RSR	0x00000010
+#define STATS_TSR	0x00000020
+
+#define CX23888_IR_IRQEN_REG	0x170014
+#define IRQEN_RTE	0x00000001
+#define IRQEN_ROE	0x00000002
+#define IRQEN_RSE	0x00000010
+#define IRQEN_TSE	0x00000020
+
+#define CX23888_IR_FILTR_REG	0x170018
+#define FILTR_LPF	0x0000FFFF
+
+/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */
+#define CX23888_IR_FIFO_REG	0x170040
+#define FIFO_RXTX	0x0000FFFF
+#define FIFO_RXTX_LVL	0x00010000
+#define FIFO_RXTX_RTO	0x0001FFFF
+#define FIFO_RX_NDV	0x00020000
+#define FIFO_RX_DEPTH	8
+#define FIFO_TX_DEPTH	8
+
+/* CX23888 unique registers */
+#define CX23888_IR_SEEDP_REG	0x17001C
+#define CX23888_IR_TIMOL_REG	0x170020
+#define CX23888_IR_WAKE0_REG	0x170024
+#define CX23888_IR_WAKE1_REG	0x170028
+#define CX23888_IR_WAKE2_REG	0x17002C
+#define CX23888_IR_MASK0_REG	0x170030
+#define CX23888_IR_MASK1_REG	0x170034
+#define CX23888_IR_MAKS2_REG	0x170038
+#define CX23888_IR_DPIPG_REG	0x17003C
+#define CX23888_IR_LEARN_REG	0x170044
+
+#define CX23888_VIDCLK_FREQ	108000000 /* 108 MHz, BT.656 */
+#define CX23888_IR_REFCLK_FREQ	(CX23888_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx23888_ir_fifo_rec {
+	u32 hw_fifo_data;
+	struct ir_raw_event ir_core_data;
+};
+
+#define CX23888_IR_RX_KFIFO_SIZE    (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_TX_KFIFO_SIZE    (256 * sizeof(union cx23888_ir_fifo_rec))
+
+struct cx23888_ir_state {
+	struct v4l2_subdev sd;
+	struct cx23885_dev *dev;
+	u32 id;
+	u32 rev;
+
+	struct v4l2_subdev_ir_parameters rx_params;
+	struct mutex rx_params_lock;
+	atomic_t rxclk_divider;
+	atomic_t rx_invert;
+
+	struct kfifo rx_kfifo;
+	spinlock_t rx_kfifo_lock;
+
+	struct v4l2_subdev_ir_parameters tx_params;
+	struct mutex tx_params_lock;
+	atomic_t txclk_divider;
+};
+
+static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd)
+{
+	return v4l2_get_subdevdata(sd);
+}
+
+/*
+ * IR register block read and write functions
+ */
+static
+inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value)
+{
+	cx_write(addr, value);
+	return 0;
+}
+
+static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr)
+{
+	return cx_read(addr);
+}
+
+static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr,
+				     u32 and_mask, u32 or_value)
+{
+	cx_andor(addr, ~and_mask, or_value);
+	return 0;
+}
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * 	(0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+	if (d > RXCLK_RCD + 1)
+		d = RXCLK_RCD;
+	else if (d < 2)
+		d = 1;
+	else
+		d--;
+	return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+	return count_to_clock_divider(
+		DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+	/* Period of the Rx or Tx clock in ns */
+	return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+				 CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+	return count_to_clock_divider(
+			  DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+	return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+					unsigned int rollovers)
+{
+	return count_to_clock_divider(
+		   DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+						 unsigned int rollovers)
+{
+	return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ,
+				 (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 	0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+	if (d > FILTR_LPF)
+		d = FILTR_LPF;
+	else if (d < 4)
+		d = 0;
+	return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+	return count_to_lpf_count(
+		DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+	/* Duration of the Low Pass Filter rejection window in ns */
+	return DIV_ROUND_CLOSEST(count * 1000,
+				 CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+	/* Duration of the Low Pass Filter rejection window in us */
+	return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+	/*
+	 * Resolution is the duration of 1 tick of the readable portion of
+	 * of the pulse width counter as read from the FIFO.  The two lsb's are
+	 * not readable, hence the << 2.  This function returns ns.
+	 */
+	return DIV_ROUND_CLOSEST((1 << 2)  * ((u32) divider + 1) * 1000,
+				 CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+	u64 n;
+	u32 rem;
+
+	/*
+	 * The 2 lsb's of the pulse width timer count are not readable, hence
+	 * the (count << 2) | 0x3
+	 */
+	n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+	rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000);     /* / MHz => ns */
+	if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+		n++;
+	return n;
+}
+
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+	u64 n;
+	u32 rem;
+
+	/*
+	 * The 2 lsb's of the pulse width timer count are not readable, hence
+	 * the (count << 2) | 0x3
+	 */
+	n = (((u64) count << 2) | 0x3) * (divider + 1);    /* cycles      */
+	rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+	if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+		n++;
+	return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+	u64 clocks;
+	u32 rem;
+	clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles  */
+	rem = do_div(clocks, 1000);                         /* /1000 = cycles */
+	if (rem >= 1000 / 2)
+		clocks++;
+	return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+	do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+	/* net result needs to be rounded down and decremented by 1 */
+	if (count > RXCLK_RCD + 1)
+		count = RXCLK_RCD;
+	else if (count < 2)
+		count = 1;
+	else
+		count--;
+	return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+	TX_FIFO_HALF_EMPTY = 0,
+	TX_FIFO_EMPTY      = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+	RX_FIFO_HALF_FULL = 0,
+	RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct cx23885_dev *dev,
+					    enum tx_fifo_watermark level)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct cx23885_dev *dev,
+					    enum rx_fifo_watermark level)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct cx23885_dev *dev, bool enable)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+			   enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct cx23885_dev *dev, bool enable)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+			   enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct cx23885_dev *dev,
+						bool enable)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD,
+			   enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct cx23885_dev *dev,
+						  bool enable)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD,
+			   enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct cx23885_dev *dev,
+					       u32 edge_types)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+			   edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct cx23885_dev *dev,
+					unsigned int carrier,
+					unsigned int *carrier_range_low,
+					unsigned int *carrier_range_high)
+{
+	u32 v;
+	unsigned int c16 = carrier * 16;
+
+	if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+		v = CNTRL_WIN_3_4;
+		*carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+	} else {
+		v = CNTRL_WIN_3_3;
+		*carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+	}
+
+	if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+		v |= CNTRL_WIN_4_3;
+		*carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+	} else {
+		v |= CNTRL_WIN_3_3;
+		*carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+	}
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
+					      bool invert)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL,
+			   invert ? CNTRL_CPL : 0);
+}
+
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+					  bool invert)
+{
+	cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+			   invert ? CNTRL_IVO : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev,
+				       unsigned int freq,
+				       u16 *divider)
+{
+	*divider = carrier_freq_to_clock_divider(freq);
+	cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+	return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev,
+				       unsigned int freq,
+				       u16 *divider)
+{
+	*divider = carrier_freq_to_clock_divider(freq);
+	cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+	return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+				      u16 *divider)
+{
+	u64 pulse_clocks;
+
+	if (ns > IR_MAX_DURATION)
+		ns = IR_MAX_DURATION;
+	pulse_clocks = ns_to_pulse_clocks(ns);
+	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
+	cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+	return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+				      u16 *divider)
+{
+	u64 pulse_clocks;
+
+	if (ns > IR_MAX_DURATION)
+		ns = IR_MAX_DURATION;
+	pulse_clocks = ns_to_pulse_clocks(ns);
+	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
+	cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+	return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev,
+					  unsigned int duty_cycle)
+{
+	u32 n;
+	n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+	if (n != 0)
+		n--;
+	if (n > 15)
+		n = 15;
+	cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n);
+	return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns)
+{
+	u32 count = ns_to_lpf_count(min_width_ns);
+	cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count);
+	return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask)
+{
+	mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+	cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG,
+			   ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask)
+{
+	mask &= IRQEN_TSE;
+	cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
+				  bool *handled)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+	unsigned long flags;
+
+	u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+	u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+	u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+
+	union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+	unsigned int i, j, k;
+	u32 events, v;
+	int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+
+	tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+	rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+	rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+	ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+	tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+	rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+	rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+	roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+	*handled = false;
+	v4l2_dbg(2, ir_888_debug, sd, "IRQ Status:  %s %s %s %s %s %s\n",
+		 tsr ? "tsr" : "   ", rsr ? "rsr" : "   ",
+		 rto ? "rto" : "   ", ror ? "ror" : "   ",
+		 stats & STATS_TBY ? "tby" : "   ",
+		 stats & STATS_RBY ? "rby" : "   ");
+
+	v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n",
+		 tse ? "tse" : "   ", rse ? "rse" : "   ",
+		 rte ? "rte" : "   ", roe ? "roe" : "   ");
+
+	/*
+	 * Transmitter interrupt service
+	 */
+	if (tse && tsr) {
+		/*
+		 * TODO:
+		 * Check the watermark threshold setting
+		 * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+		 * Push the data to the hardware FIFO.
+		 * If there was nothing more to send in the tx_kfifo, disable
+		 *	the TSR IRQ and notify the v4l2_device.
+		 * If there was something in the tx_kfifo, check the tx_kfifo
+		 *      level and notify the v4l2_device, if it is low.
+		 */
+		/* For now, inhibit TSR interrupt until Tx is implemented */
+		irqenable_tx(dev, 0);
+		events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+		*handled = true;
+	}
+
+	/*
+	 * Receiver interrupt service
+	 */
+	kror = 0;
+	if ((rse && rsr) || (rte && rto)) {
+		/*
+		 * Receive data on RSR to clear the STATS_RSR.
+		 * Receive data on RTO, since we may not have yet hit the RSR
+		 * watermark when we receive the RTO.
+		 */
+		for (i = 0, v = FIFO_RX_NDV;
+		     (v & FIFO_RX_NDV) && !kror; i = 0) {
+			for (j = 0;
+			     (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+				v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
+				rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+				i++;
+			}
+			if (i == 0)
+				break;
+			j = i * sizeof(union cx23888_ir_fifo_rec);
+			k = kfifo_in_locked(&state->rx_kfifo,
+				      (unsigned char *) rx_data, j,
+				      &state->rx_kfifo_lock);
+			if (k != j)
+				kror++; /* rx_kfifo over run */
+		}
+		*handled = true;
+	}
+
+	events = 0;
+	v = 0;
+	if (kror) {
+		events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+		v4l2_err(sd, "IR receiver software FIFO overrun\n");
+	}
+	if (roe && ror) {
+		/*
+		 * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+		 * the Rx FIFO Over Run status (STATS_ROR)
+		 */
+		v |= CNTRL_RFE;
+		events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+		v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+	}
+	if (rte && rto) {
+		/*
+		 * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+		 * the Rx Pulse Width Timer Time Out (STATS_RTO)
+		 */
+		v |= CNTRL_RXE;
+		events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+	}
+	if (v) {
+		/* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+		cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
+		cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
+		*handled = true;
+	}
+
+	spin_lock_irqsave(&state->rx_kfifo_lock, flags);
+	if (kfifo_len(&state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2)
+		events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+	spin_unlock_irqrestore(&state->rx_kfifo_lock, flags);
+
+	if (events)
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+	return 0;
+}
+
+/* Receiver */
+static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+			      ssize_t *num)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	bool invert = (bool) atomic_read(&state->rx_invert);
+	u16 divider = (u16) atomic_read(&state->rxclk_divider);
+
+	unsigned int i, n;
+	union cx23888_ir_fifo_rec *p;
+	unsigned u, v, w;
+
+	n = count / sizeof(union cx23888_ir_fifo_rec)
+		* sizeof(union cx23888_ir_fifo_rec);
+	if (n == 0) {
+		*num = 0;
+		return 0;
+	}
+
+	n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
+
+	n /= sizeof(union cx23888_ir_fifo_rec);
+	*num = n * sizeof(union cx23888_ir_fifo_rec);
+
+	for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+		if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+			/* Assume RTO was because of no IR light input */
+			u = 0;
+			w = 1;
+		} else {
+			u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+			if (invert)
+				u = u ? 0 : 1;
+			w = 0;
+		}
+
+		v = (unsigned) pulse_width_count_to_ns(
+				  (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+		if (v > IR_MAX_DURATION)
+			v = IR_MAX_DURATION;
+
+		init_ir_raw_event(&p->ir_core_data);
+		p->ir_core_data.pulse = u;
+		p->ir_core_data.duration = v;
+		p->ir_core_data.timeout = w;
+
+		v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns  %s  %s\n",
+			 v, u ? "mark" : "space", w ? "(timed out)" : "");
+		if (w)
+			v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+	}
+	return 0;
+}
+
+static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_ir_parameters *p)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	mutex_lock(&state->rx_params_lock);
+	memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters));
+	mutex_unlock(&state->rx_params_lock);
+	return 0;
+}
+
+static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+
+	mutex_lock(&state->rx_params_lock);
+
+	/* Disable or slow down all IR Rx circuits and counters */
+	irqenable_rx(dev, 0);
+	control_rx_enable(dev, false);
+	control_rx_demodulation_enable(dev, false);
+	control_rx_s_edge_detection(dev, CNTRL_EDG_NONE);
+	filter_rx_s_min_width(dev, 0);
+	cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD);
+
+	state->rx_params.shutdown = true;
+
+	mutex_unlock(&state->rx_params_lock);
+	return 0;
+}
+
+static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_ir_parameters *p)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+	struct v4l2_subdev_ir_parameters *o = &state->rx_params;
+	u16 rxclk_divider;
+
+	if (p->shutdown)
+		return cx23888_ir_rx_shutdown(sd);
+
+	if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+		return -ENOSYS;
+
+	mutex_lock(&state->rx_params_lock);
+
+	o->shutdown = p->shutdown;
+
+	o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+	o->bytes_per_data_element = p->bytes_per_data_element
+				  = sizeof(union cx23888_ir_fifo_rec);
+
+	/* Before we tweak the hardware, we have to disable the receiver */
+	irqenable_rx(dev, 0);
+	control_rx_enable(dev, false);
+
+	control_rx_demodulation_enable(dev, p->modulation);
+	o->modulation = p->modulation;
+
+	if (p->modulation) {
+		p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq,
+						     &rxclk_divider);
+
+		o->carrier_freq = p->carrier_freq;
+
+		o->duty_cycle = p->duty_cycle = 50;
+
+		control_rx_s_carrier_window(dev, p->carrier_freq,
+					    &p->carrier_range_lower,
+					    &p->carrier_range_upper);
+		o->carrier_range_lower = p->carrier_range_lower;
+		o->carrier_range_upper = p->carrier_range_upper;
+
+		p->max_pulse_width =
+			(u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+	} else {
+		p->max_pulse_width =
+			    rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
+						       &rxclk_divider);
+	}
+	o->max_pulse_width = p->max_pulse_width;
+	atomic_set(&state->rxclk_divider, rxclk_divider);
+
+	p->noise_filter_min_width =
+			  filter_rx_s_min_width(dev, p->noise_filter_min_width);
+	o->noise_filter_min_width = p->noise_filter_min_width;
+
+	p->resolution = clock_divider_to_resolution(rxclk_divider);
+	o->resolution = p->resolution;
+
+	/* FIXME - make this dependent on resolution for better performance */
+	control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL);
+
+	control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
+
+	o->invert_level = p->invert_level;
+	atomic_set(&state->rx_invert, p->invert_level);
+
+	o->interrupt_enable = p->interrupt_enable;
+	o->enable = p->enable;
+	if (p->enable) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&state->rx_kfifo_lock, flags);
+		kfifo_reset(&state->rx_kfifo);
+		/* reset tx_fifo too if there is one... */
+		spin_unlock_irqrestore(&state->rx_kfifo_lock, flags);
+		if (p->interrupt_enable)
+			irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+		control_rx_enable(dev, p->enable);
+	}
+
+	mutex_unlock(&state->rx_params_lock);
+	return 0;
+}
+
+/* Transmitter */
+static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+			       ssize_t *num)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+	/* For now enable the Tx FIFO Service interrupt & pretend we did work */
+	irqenable_tx(dev, IRQEN_TSE);
+	*num = count;
+	return 0;
+}
+
+static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_ir_parameters *p)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	mutex_lock(&state->tx_params_lock);
+	memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters));
+	mutex_unlock(&state->tx_params_lock);
+	return 0;
+}
+
+static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+
+	mutex_lock(&state->tx_params_lock);
+
+	/* Disable or slow down all IR Tx circuits and counters */
+	irqenable_tx(dev, 0);
+	control_tx_enable(dev, false);
+	control_tx_modulation_enable(dev, false);
+	cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD);
+
+	state->tx_params.shutdown = true;
+
+	mutex_unlock(&state->tx_params_lock);
+	return 0;
+}
+
+static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_ir_parameters *p)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+	struct v4l2_subdev_ir_parameters *o = &state->tx_params;
+	u16 txclk_divider;
+
+	if (p->shutdown)
+		return cx23888_ir_tx_shutdown(sd);
+
+	if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+		return -ENOSYS;
+
+	mutex_lock(&state->tx_params_lock);
+
+	o->shutdown = p->shutdown;
+
+	o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+	o->bytes_per_data_element = p->bytes_per_data_element
+				  = sizeof(union cx23888_ir_fifo_rec);
+
+	/* Before we tweak the hardware, we have to disable the transmitter */
+	irqenable_tx(dev, 0);
+	control_tx_enable(dev, false);
+
+	control_tx_modulation_enable(dev, p->modulation);
+	o->modulation = p->modulation;
+
+	if (p->modulation) {
+		p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq,
+						     &txclk_divider);
+		o->carrier_freq = p->carrier_freq;
+
+		p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
+		o->duty_cycle = p->duty_cycle;
+
+		p->max_pulse_width =
+			(u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+	} else {
+		p->max_pulse_width =
+			    txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
+						       &txclk_divider);
+	}
+	o->max_pulse_width = p->max_pulse_width;
+	atomic_set(&state->txclk_divider, txclk_divider);
+
+	p->resolution = clock_divider_to_resolution(txclk_divider);
+	o->resolution = p->resolution;
+
+	/* FIXME - make this dependent on resolution for better performance */
+	control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
+
+	control_tx_polarity_invert(dev, p->invert_carrier_sense);
+	o->invert_carrier_sense = p->invert_carrier_sense;
+
+	control_tx_level_invert(dev, p->invert_level);
+	o->invert_level = p->invert_level;
+
+	o->interrupt_enable = p->interrupt_enable;
+	o->enable = p->enable;
+	if (p->enable) {
+		if (p->interrupt_enable)
+			irqenable_tx(dev, IRQEN_TSE);
+		control_tx_enable(dev, p->enable);
+	}
+
+	mutex_unlock(&state->tx_params_lock);
+	return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops
+ */
+static int cx23888_ir_log_status(struct v4l2_subdev *sd)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	struct cx23885_dev *dev = state->dev;
+	char *s;
+	int i, j;
+
+	u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+	u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD;
+	u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD;
+	u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC;
+	u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+	u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+	u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF;
+
+	v4l2_info(sd, "IR Receiver:\n");
+	v4l2_info(sd, "\tEnabled:                           %s\n",
+		  cntrl & CNTRL_RXE ? "yes" : "no");
+	v4l2_info(sd, "\tDemodulation from a carrier:       %s\n",
+		  cntrl & CNTRL_DMD ? "enabled" : "disabled");
+	v4l2_info(sd, "\tFIFO:                              %s\n",
+		  cntrl & CNTRL_RFE ? "enabled" : "disabled");
+	switch (cntrl & CNTRL_EDG) {
+	case CNTRL_EDG_NONE:
+		s = "disabled";
+		break;
+	case CNTRL_EDG_FALL:
+		s = "falling edge";
+		break;
+	case CNTRL_EDG_RISE:
+		s = "rising edge";
+		break;
+	case CNTRL_EDG_BOTH:
+		s = "rising & falling edges";
+		break;
+	default:
+		s = "??? edge";
+		break;
+	}
+	v4l2_info(sd, "\tPulse timers' start/stop trigger:  %s\n", s);
+	v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+		  cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+		  cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+	v4l2_info(sd, "\tLoopback mode:                     %s\n",
+		  cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+	if (cntrl & CNTRL_DMD) {
+		v4l2_info(sd, "\tExpected carrier (16 clocks):      %u Hz\n",
+			  clock_divider_to_carrier_freq(rxclk));
+		switch (cntrl & CNTRL_WIN) {
+		case CNTRL_WIN_3_3:
+			i = 3;
+			j = 3;
+			break;
+		case CNTRL_WIN_4_3:
+			i = 4;
+			j = 3;
+			break;
+		case CNTRL_WIN_3_4:
+			i = 3;
+			j = 4;
+			break;
+		case CNTRL_WIN_4_4:
+			i = 4;
+			j = 4;
+			break;
+		default:
+			i = 0;
+			j = 0;
+			break;
+		}
+		v4l2_info(sd, "\tNext carrier edge window:          16 clocks "
+			  "-%1d/+%1d, %u to %u Hz\n", i, j,
+			  clock_divider_to_freq(rxclk, 16 + j),
+			  clock_divider_to_freq(rxclk, 16 - i));
+	}
+	v4l2_info(sd, "\tMax measurable pulse width:        %u us, %llu ns\n",
+		  pulse_width_count_to_us(FIFO_RXTX, rxclk),
+		  pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+	v4l2_info(sd, "\tLow pass filter:                   %s\n",
+		  filtr ? "enabled" : "disabled");
+	if (filtr)
+		v4l2_info(sd, "\tMin acceptable pulse width (LPF):  %u us, "
+			  "%u ns\n",
+			  lpf_count_to_us(filtr),
+			  lpf_count_to_ns(filtr));
+	v4l2_info(sd, "\tPulse width timer timed-out:       %s\n",
+		  stats & STATS_RTO ? "yes" : "no");
+	v4l2_info(sd, "\tPulse width timer time-out intr:   %s\n",
+		  irqen & IRQEN_RTE ? "enabled" : "disabled");
+	v4l2_info(sd, "\tFIFO overrun:                      %s\n",
+		  stats & STATS_ROR ? "yes" : "no");
+	v4l2_info(sd, "\tFIFO overrun interrupt:            %s\n",
+		  irqen & IRQEN_ROE ? "enabled" : "disabled");
+	v4l2_info(sd, "\tBusy:                              %s\n",
+		  stats & STATS_RBY ? "yes" : "no");
+	v4l2_info(sd, "\tFIFO service requested:            %s\n",
+		  stats & STATS_RSR ? "yes" : "no");
+	v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+		  irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+	v4l2_info(sd, "IR Transmitter:\n");
+	v4l2_info(sd, "\tEnabled:                           %s\n",
+		  cntrl & CNTRL_TXE ? "yes" : "no");
+	v4l2_info(sd, "\tModulation onto a carrier:         %s\n",
+		  cntrl & CNTRL_MOD ? "enabled" : "disabled");
+	v4l2_info(sd, "\tFIFO:                              %s\n",
+		  cntrl & CNTRL_TFE ? "enabled" : "disabled");
+	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+		  cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+	v4l2_info(sd, "\tOutput pin level inversion         %s\n",
+		  cntrl & CNTRL_IVO ? "yes" : "no");
+	v4l2_info(sd, "\tCarrier polarity:                  %s\n",
+		  cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+				    : "space:noburst mark:burst");
+	if (cntrl & CNTRL_MOD) {
+		v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
+			  clock_divider_to_carrier_freq(txclk));
+		v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
+			  cduty + 1);
+	}
+	v4l2_info(sd, "\tMax pulse width:                   %u us, %llu ns\n",
+		  pulse_width_count_to_us(FIFO_RXTX, txclk),
+		  pulse_width_count_to_ns(FIFO_RXTX, txclk));
+	v4l2_info(sd, "\tBusy:                              %s\n",
+		  stats & STATS_TBY ? "yes" : "no");
+	v4l2_info(sd, "\tFIFO service requested:            %s\n",
+		  stats & STATS_TSR ? "yes" : "no");
+	v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+		  irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+	return 0;
+}
+
+static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match)
+{
+	return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2;
+}
+
+static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd,
+				   struct v4l2_dbg_chip_ident *chip)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+
+	if (cx23888_ir_dbg_match(&chip->match)) {
+		chip->ident = state->id;
+		chip->revision = state->rev;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23888_ir_g_register(struct v4l2_subdev *sd,
+				 struct v4l2_dbg_register *reg)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+	if (!cx23888_ir_dbg_match(&reg->match))
+		return -EINVAL;
+	if ((addr & 0x3) != 0)
+		return -EINVAL;
+	if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	reg->size = 4;
+	reg->val = cx23888_ir_read4(state->dev, addr);
+	return 0;
+}
+
+static int cx23888_ir_s_register(struct v4l2_subdev *sd,
+				 struct v4l2_dbg_register *reg)
+{
+	struct cx23888_ir_state *state = to_state(sd);
+	u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+	if (!cx23888_ir_dbg_match(&reg->match))
+		return -EINVAL;
+	if ((addr & 0x3) != 0)
+		return -EINVAL;
+	if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	cx23888_ir_write4(state->dev, addr, reg->val);
+	return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
+	.g_chip_ident = cx23888_ir_g_chip_ident,
+	.log_status = cx23888_ir_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = cx23888_ir_g_register,
+	.s_register = cx23888_ir_s_register,
+#endif
+	.interrupt_service_routine = cx23888_ir_irq_handler,
+};
+
+static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
+	.rx_read = cx23888_ir_rx_read,
+	.rx_g_parameters = cx23888_ir_rx_g_parameters,
+	.rx_s_parameters = cx23888_ir_rx_s_parameters,
+
+	.tx_write = cx23888_ir_tx_write,
+	.tx_g_parameters = cx23888_ir_tx_g_parameters,
+	.tx_s_parameters = cx23888_ir_tx_s_parameters,
+};
+
+static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
+	.core = &cx23888_ir_core_ops,
+	.ir = &cx23888_ir_ir_ops,
+};
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+	.bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
+	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+	.enable = false,
+	.interrupt_enable = false,
+	.shutdown = true,
+
+	.modulation = true,
+	.carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */
+
+	/* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+	/* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+	.noise_filter_min_width = 333333, /* ns */
+	.carrier_range_lower = 35000,
+	.carrier_range_upper = 37000,
+	.invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+	.bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
+	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+	.enable = false,
+	.interrupt_enable = false,
+	.shutdown = true,
+
+	.modulation = true,
+	.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+	.duty_cycle = 25,      /* 25 %   - RC-5 carrier */
+	.invert_level = false,
+	.invert_carrier_sense = false,
+};
+
+int cx23888_ir_probe(struct cx23885_dev *dev)
+{
+	struct cx23888_ir_state *state;
+	struct v4l2_subdev *sd;
+	struct v4l2_subdev_ir_parameters default_params;
+	int ret;
+
+	state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL);
+	if (state == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&state->rx_kfifo_lock);
+	if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL))
+		return -ENOMEM;
+
+	state->dev = dev;
+	state->id = V4L2_IDENT_CX23888_IR;
+	state->rev = 0;
+	sd = &state->sd;
+
+	v4l2_subdev_init(sd, &cx23888_ir_controller_ops);
+	v4l2_set_subdevdata(sd, state);
+	/* FIXME - fix the formatting of dev->v4l2_dev.name and use it */
+	snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name);
+	sd->grp_id = CX23885_HW_888_IR;
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd);
+	if (ret == 0) {
+		/*
+		 * Ensure no interrupts arrive from '888 specific conditions,
+		 * since we ignore them in this driver to have commonality with
+		 * similar IR controller cores.
+		 */
+		cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0);
+
+		mutex_init(&state->rx_params_lock);
+		memcpy(&default_params, &default_rx_params,
+		       sizeof(struct v4l2_subdev_ir_parameters));
+		v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+		mutex_init(&state->tx_params_lock);
+		memcpy(&default_params, &default_tx_params,
+		       sizeof(struct v4l2_subdev_ir_parameters));
+		v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+	} else {
+		kfifo_free(&state->rx_kfifo);
+	}
+	return ret;
+}
+
+int cx23888_ir_remove(struct cx23885_dev *dev)
+{
+	struct v4l2_subdev *sd;
+	struct cx23888_ir_state *state;
+
+	sd = cx23885_find_hw(dev, CX23885_HW_888_IR);
+	if (sd == NULL)
+		return -ENODEV;
+
+	cx23888_ir_rx_shutdown(sd);
+	cx23888_ir_tx_shutdown(sd);
+
+	state = to_state(sd);
+	v4l2_device_unregister_subdev(sd);
+	kfifo_free(&state->rx_kfifo);
+	kfree(state);
+	/* Nothing more to free() as state held the actual v4l2_subdev object */
+	return 0;
+}
diff --git a/drivers/media/pci/cx23885/cx23888-ir.h b/drivers/media/pci/cx23885/cx23888-ir.h
new file mode 100644
index 000000000000..d2de41caaf1d
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23888-ir.h
@@ -0,0 +1,28 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  CX23888 Integrated Consumer Infrared Controller
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23888_IR_H_
+#define _CX23888_IR_H_
+int cx23888_ir_probe(struct cx23885_dev *dev);
+int cx23888_ir_remove(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/pci/cx23885/netup-eeprom.c b/drivers/media/pci/cx23885/netup-eeprom.c
new file mode 100644
index 000000000000..98a48f500684
--- /dev/null
+++ b/drivers/media/pci/cx23885/netup-eeprom.c
@@ -0,0 +1,107 @@
+
+/*
+ * netup-eeprom.c
+ *
+ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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 "cx23885.h"
+#include "netup-eeprom.h"
+
+#define EEPROM_I2C_ADDR 0x50
+
+int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr)
+{
+	int ret;
+	unsigned char buf[2];
+
+	/* Read from EEPROM */
+	struct i2c_msg msg[] = {
+		{
+			.addr	= EEPROM_I2C_ADDR,
+			.flags	= 0,
+			.buf	= &buf[0],
+			.len	= 1
+		}, {
+			.addr	= EEPROM_I2C_ADDR,
+			.flags	= I2C_M_RD,
+			.buf	= &buf[1],
+			.len	= 1
+		}
+
+	};
+
+	buf[0] = addr;
+	buf[1] = 0x0;
+
+	ret = i2c_transfer(i2c_adap, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "eeprom i2c read error, status=%d\n", ret);
+		return -1;
+	}
+
+	return buf[1];
+};
+
+int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data)
+{
+	int ret;
+	unsigned char bufw[2];
+
+	/* Write into EEPROM */
+	struct i2c_msg msg[] = {
+		{
+			.addr	= EEPROM_I2C_ADDR,
+			.flags	= 0,
+			.buf	= &bufw[0],
+			.len	= 2
+		}
+	};
+
+	bufw[0] = addr;
+	bufw[1] = data;
+
+	ret = i2c_transfer(i2c_adap, msg, 1);
+
+	if (ret != 1) {
+		printk(KERN_ERR "eeprom i2c write error, status=%d\n", ret);
+		return -1;
+	}
+
+	mdelay(10); /* prophylactic delay, datasheet write cycle time = 5 ms */
+	return 0;
+};
+
+void netup_get_card_info(struct i2c_adapter *i2c_adap,
+				struct netup_card_info *cinfo)
+{
+	int i, j;
+
+	cinfo->rev =  netup_eeprom_read(i2c_adap, 63);
+
+	for (i = 64, j = 0; i < 70; i++, j++)
+		cinfo->port[0].mac[j] =  netup_eeprom_read(i2c_adap, i);
+
+	for (i = 70, j = 0; i < 76; i++, j++)
+		cinfo->port[1].mac[j] =  netup_eeprom_read(i2c_adap, i);
+};
diff --git a/drivers/media/pci/cx23885/netup-eeprom.h b/drivers/media/pci/cx23885/netup-eeprom.h
new file mode 100644
index 000000000000..13926e18feba
--- /dev/null
+++ b/drivers/media/pci/cx23885/netup-eeprom.h
@@ -0,0 +1,42 @@
+/*
+ * netup-eeprom.h
+ *
+ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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 NETUP_EEPROM_H
+#define NETUP_EEPROM_H
+
+struct netup_port_info {
+	u8 mac[6];/* card MAC address */
+};
+
+struct netup_card_info {
+	struct netup_port_info port[2];/* ports - 1,2 */
+	u8 rev;/* card revision */
+};
+
+extern int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr);
+extern int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data);
+extern void netup_get_card_info(struct i2c_adapter *i2c_adap,
+				struct netup_card_info *cinfo);
+
+#endif
diff --git a/drivers/media/pci/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c
new file mode 100644
index 000000000000..f4893e69cd89
--- /dev/null
+++ b/drivers/media/pci/cx23885/netup-init.c
@@ -0,0 +1,125 @@
+/*
+ * netup-init.c
+ *
+ * NetUP Dual DVB-S2 CI driver
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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 "cx23885.h"
+
+static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val)
+{
+	int ret;
+	u8 buf[3];
+	struct i2c_msg msg = {
+		.addr	= 0x88 >> 1,
+		.flags	= 0,
+		.buf	= buf,
+		.len	= 3
+	};
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+
+	if (ret != 1)
+		printk(KERN_ERR "%s: i2c write error!\n", __func__);
+}
+
+static void i2c_av_write4(struct i2c_adapter *i2c, u16 reg, u32 val)
+{
+	int ret;
+	u8 buf[6];
+	struct i2c_msg msg = {
+		.addr	= 0x88 >> 1,
+		.flags	= 0,
+		.buf	= buf,
+		.len	= 6
+	};
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val & 0xff;
+	buf[3] = (val >> 8) & 0xff;
+	buf[4] = (val >> 16) & 0xff;
+	buf[5] = val >> 24;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+
+	if (ret != 1)
+		printk(KERN_ERR "%s: i2c write error!\n", __func__);
+}
+
+static u8 i2c_av_read(struct i2c_adapter *i2c, u16 reg)
+{
+	int ret;
+	u8 buf[2];
+	struct i2c_msg msg = {
+		.addr	= 0x88 >> 1,
+		.flags	= 0,
+		.buf	= buf,
+		.len	= 2
+	};
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+
+	if (ret != 1)
+		printk(KERN_ERR "%s: i2c write error!\n", __func__);
+
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+
+	if (ret != 1)
+		printk(KERN_ERR "%s: i2c read error!\n", __func__);
+
+	return buf[0];
+}
+
+static void i2c_av_and_or(struct i2c_adapter *i2c, u16 reg, unsigned and_mask,
+								u8 or_value)
+{
+	i2c_av_write(i2c, reg, (i2c_av_read(i2c, reg) & and_mask) | or_value);
+}
+/* set 27MHz on AUX_CLK */
+void netup_initialize(struct cx23885_dev *dev)
+{
+	struct cx23885_i2c *i2c_bus = &dev->i2c_bus[2];
+	struct i2c_adapter *i2c = &i2c_bus->i2c_adap;
+
+	/* Stop microcontroller */
+	i2c_av_and_or(i2c, 0x803, ~0x10, 0x00);
+
+	/* Aux PLL frac for 27 MHz */
+	i2c_av_write4(i2c, 0x114, 0xea0eb3);
+
+	/* Aux PLL int for 27 MHz */
+	i2c_av_write4(i2c, 0x110, 0x090319);
+
+	/* start microcontroller */
+	i2c_av_and_or(i2c, 0x803, ~0x10, 0x10);
+}
diff --git a/drivers/media/pci/cx23885/netup-init.h b/drivers/media/pci/cx23885/netup-init.h
new file mode 100644
index 000000000000..d26ae4b1590e
--- /dev/null
+++ b/drivers/media/pci/cx23885/netup-init.h
@@ -0,0 +1,25 @@
+/*
+ * netup-init.h
+ *
+ * NetUP Dual DVB-S2 CI driver
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * 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.
+ */
+extern void netup_initialize(struct cx23885_dev *dev);
diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig
new file mode 100644
index 000000000000..5f6b54213713
--- /dev/null
+++ b/drivers/media/pci/cx25821/Kconfig
@@ -0,0 +1,34 @@
+config VIDEO_CX25821
+	tristate "Conexant cx25821 support"
+	depends on DVB_CORE && VIDEO_DEV && PCI && I2C
+	select I2C_ALGOBIT
+	select VIDEO_BTCX
+	select VIDEO_TVEEPROM
+	depends on RC_CORE
+	select VIDEOBUF_DVB
+	select VIDEOBUF_DMA_SG
+	select VIDEO_CX25840
+	select VIDEO_CX2341X
+	---help---
+	  This is a video4linux driver for Conexant 25821 based
+	  TV cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx25821
+
+config VIDEO_CX25821_ALSA
+	tristate "Conexant 25821 DMA audio support"
+	depends on VIDEO_CX25821 && SND && EXPERIMENTAL
+	select SND_PCM
+	---help---
+	  This is a video4linux driver for direct (DMA) audio on
+	  Conexant 25821 based capture cards using ALSA.
+
+	  It only works with boards with function 01 enabled.
+	  To check if your board supports, use lspci -n.
+	  If supported, you should see 14f1:8801 or 14f1:8811
+	  PCI device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx25821-alsa.
+
diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile
new file mode 100644
index 000000000000..5bf3ea4c1556
--- /dev/null
+++ b/drivers/media/pci/cx25821/Makefile
@@ -0,0 +1,13 @@
+cx25821-y   := cx25821-core.o cx25821-cards.o cx25821-i2c.o \
+		       cx25821-gpio.o cx25821-medusa-video.o \
+		       cx25821-video.o cx25821-video-upstream.o \
+		       cx25821-video-upstream-ch2.o \
+		       cx25821-audio-upstream.o
+
+obj-$(CONFIG_VIDEO_CX25821) += cx25821.o
+obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
new file mode 100644
index 000000000000..1858a45dd081
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -0,0 +1,784 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *	Based on SAA713x ALSA driver and CX88 driver
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, version 2
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "cx25821.h"
+#include "cx25821-reg.h"
+
+#define AUDIO_SRAM_CHANNEL	SRAM_CH08
+
+#define dprintk(level, fmt, arg...)				\
+do {								\
+	if (debug >= level)					\
+		pr_info("%s/1: " fmt, chip->dev->name, ##arg);	\
+} while (0)
+#define dprintk_core(level, fmt, arg...)				\
+do {									\
+	if (debug >= level)						\
+		printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name, ##arg); \
+} while (0)
+
+/****************************************************************************
+	Data type declarations - Can be moded to a header file later
+ ****************************************************************************/
+
+static struct snd_card *snd_cx25821_cards[SNDRV_CARDS];
+static int devno;
+
+struct cx25821_audio_buffer {
+	unsigned int bpl;
+	struct btcx_riscmem risc;
+	struct videobuf_dmabuf dma;
+};
+
+struct cx25821_audio_dev {
+	struct cx25821_dev *dev;
+	struct cx25821_dmaqueue q;
+
+	/* pci i/o */
+	struct pci_dev *pci;
+
+	/* audio controls */
+	int irq;
+
+	struct snd_card *card;
+
+	unsigned long iobase;
+	spinlock_t reg_lock;
+	atomic_t count;
+
+	unsigned int dma_size;
+	unsigned int period_size;
+	unsigned int num_periods;
+
+	struct videobuf_dmabuf *dma_risc;
+
+	struct cx25821_audio_buffer *buf;
+
+	struct snd_pcm_substream *substream;
+};
+
+
+/****************************************************************************
+			Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static bool enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 };
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s).");
+
+/****************************************************************************
+				Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards");
+MODULE_AUTHOR("Hiep Huynh");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Conexant,25821}");	/* "{{Conexant,23881}," */
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+/****************************************************************************
+			Module specific funtions
+ ****************************************************************************/
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1       (1 <<  0)
+#define AUD_INT_UP_RISCI1       (1 <<  1)
+#define AUD_INT_RDS_DN_RISCI1   (1 <<  2)
+#define AUD_INT_DN_RISCI2       (1 <<  4)	/* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2       (1 <<  5)
+#define AUD_INT_RDS_DN_RISCI2   (1 <<  6)
+#define AUD_INT_DN_SYNC         (1 << 12)
+#define AUD_INT_UP_SYNC         (1 << 13)
+#define AUD_INT_RDS_DN_SYNC     (1 << 14)
+#define AUD_INT_OPC_ERR         (1 << 16)
+#define AUD_INT_BER_IRQ         (1 << 20)
+#define AUD_INT_MCHG_IRQ        (1 << 21)
+#define GP_COUNT_CONTROL_RESET	0x3
+
+#define PCI_MSK_AUD_EXT   (1 <<  4)
+#define PCI_MSK_AUD_INT   (1 <<  3)
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip)
+{
+	struct cx25821_audio_buffer *buf = chip->buf;
+	struct cx25821_dev *dev = chip->dev;
+	struct sram_channel *audio_ch =
+	    &cx25821_sram_channels[AUDIO_SRAM_CHANNEL];
+	u32 tmp = 0;
+
+	/* enable output on the GPIO 0 for the MCLK ADC (Audio) */
+	cx25821_set_gpiopin_direction(chip->dev, 0, 0);
+
+	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+	cx_clear(AUD_INT_DMA_CTL,
+		 FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+
+	/* setup fifo + format - out channel */
+	cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl,
+					 buf->risc.dma);
+
+	/* sets bpl size */
+	cx_write(AUD_A_LNGTH, buf->bpl);
+
+	/* reset counter */
+	/* GP_COUNT_CONTROL_RESET = 0x3 */
+	cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+	atomic_set(&chip->count, 0);
+
+	/* Set the input mode to 16-bit */
+	tmp = cx_read(AUD_A_CFG);
+	cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE |
+		 FLD_AUD_CLK_ENABLE);
+
+	/*
+	pr_info("DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d byte buffer\n",
+		buf->bpl, audio_ch->cmds_start,
+		cx_read(audio_ch->cmds_start + 12)>>1,
+		chip->num_periods, buf->bpl * chip->num_periods);
+	*/
+
+	/* Enables corresponding bits at AUD_INT_STAT */
+	cx_write(AUD_A_INT_MSK, FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF |
+		 FLD_AUD_DST_SYNC | FLD_AUD_DST_OPC_ERR);
+
+	/* Clean any pending interrupt bits already set */
+	cx_write(AUD_A_INT_STAT, ~0);
+
+	/* enable audio irqs */
+	cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+	/* Turn on audio downstream fifo and risc enable 0x101 */
+	tmp = cx_read(AUD_INT_DMA_CTL);
+	cx_set(AUD_INT_DMA_CTL, tmp |
+	       (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN));
+
+	mdelay(100);
+	return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip)
+{
+	struct cx25821_dev *dev = chip->dev;
+
+	/* stop dma */
+	cx_clear(AUD_INT_DMA_CTL,
+		 FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+
+	/* disable irqs */
+	cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+	cx_clear(AUD_A_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+		 AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+	return 0;
+}
+
+#define MAX_IRQ_LOOP 50
+
+/*
+ * BOARD Specific: IRQ dma bits
+ */
+static char *cx25821_aud_irqs[32] = {
+	"dn_risci1", "up_risci1", "rds_dn_risc1",	/* 0-2 */
+	NULL,						/* reserved */
+	"dn_risci2", "up_risci2", "rds_dn_risc2",	/* 4-6 */
+	NULL,						/* reserved */
+	"dnf_of", "upf_uf", "rds_dnf_uf",		/* 8-10 */
+	NULL,						/* reserved */
+	"dn_sync", "up_sync", "rds_dn_sync",		/* 12-14 */
+	NULL,						/* reserved */
+	"opc_err", "par_err", "rip_err",		/* 16-18 */
+	"pci_abort", "ber_irq", "mchg_irq"		/* 19-21 */
+};
+
+/*
+ * BOARD Specific: Threats IRQ audio specific calls
+ */
+static void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status,
+			    u32 mask)
+{
+	struct cx25821_dev *dev = chip->dev;
+
+	if (0 == (status & mask))
+		return;
+
+	cx_write(AUD_A_INT_STAT, status);
+	if (debug > 1 || (status & mask & ~0xff))
+		cx25821_print_irqbits(dev->name, "irq aud", cx25821_aud_irqs,
+				ARRAY_SIZE(cx25821_aud_irqs), status, mask);
+
+	/* risc op code error */
+	if (status & AUD_INT_OPC_ERR) {
+		pr_warn("WARNING %s/1: Audio risc op code error\n", dev->name);
+
+		cx_clear(AUD_INT_DMA_CTL,
+			 FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+		cx25821_sram_channel_dump_audio(dev,
+				&cx25821_sram_channels[AUDIO_SRAM_CHANNEL]);
+	}
+	if (status & AUD_INT_DN_SYNC) {
+		pr_warn("WARNING %s: Downstream sync error!\n", dev->name);
+		cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+		return;
+	}
+
+	/* risc1 downstream */
+	if (status & AUD_INT_DN_RISCI1) {
+		atomic_set(&chip->count, cx_read(AUD_A_GPCNT));
+		snd_pcm_period_elapsed(chip->substream);
+	}
+}
+
+/*
+ * BOARD Specific: Handles IRQ calls
+ */
+static irqreturn_t cx25821_irq(int irq, void *dev_id)
+{
+	struct cx25821_audio_dev *chip = dev_id;
+	struct cx25821_dev *dev = chip->dev;
+	u32 status, pci_status;
+	u32 audint_status, audint_mask;
+	int loop, handled = 0;
+
+	audint_status = cx_read(AUD_A_INT_STAT);
+	audint_mask = cx_read(AUD_A_INT_MSK);
+	status = cx_read(PCI_INT_STAT);
+
+	for (loop = 0; loop < 1; loop++) {
+		status = cx_read(PCI_INT_STAT);
+		if (0 == status) {
+			status = cx_read(PCI_INT_STAT);
+			audint_status = cx_read(AUD_A_INT_STAT);
+			audint_mask = cx_read(AUD_A_INT_MSK);
+
+			if (status) {
+				handled = 1;
+				cx_write(PCI_INT_STAT, status);
+
+				cx25821_aud_irq(chip, audint_status,
+						audint_mask);
+				break;
+			} else {
+				goto out;
+			}
+		}
+
+		handled = 1;
+		cx_write(PCI_INT_STAT, status);
+
+		cx25821_aud_irq(chip, audint_status, audint_mask);
+	}
+
+	pci_status = cx_read(PCI_INT_STAT);
+
+	if (handled)
+		cx_write(PCI_INT_STAT, pci_status);
+
+out:
+	return IRQ_RETVAL(handled);
+}
+
+static int dsp_buffer_free(struct cx25821_audio_dev *chip)
+{
+	BUG_ON(!chip->dma_size);
+
+	dprintk(2, "Freeing buffer\n");
+	videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+	videobuf_dma_free(chip->dma_risc);
+	btcx_riscmem_free(chip->pci, &chip->buf->risc);
+	kfree(chip->buf);
+
+	chip->dma_risc = NULL;
+	chip->dma_size = 0;
+
+	return 0;
+}
+
+/****************************************************************************
+				ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE	384
+static struct snd_pcm_hardware snd_cx25821_digital_hw = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* Analog audio output will be full of clicks and pops if there
+	   are not exactly four lines in the SRAM FIFO buffer.  */
+	.period_bytes_min = DEFAULT_FIFO_SIZE / 3,
+	.period_bytes_max = DEFAULT_FIFO_SIZE / 3,
+	.periods_min = 1,
+	.periods_max = AUDIO_LINE_SIZE,
+	/* 128 * 128 = 16384 = 1024 * 16 */
+	.buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+	unsigned int bpl = 0;
+
+	if (!chip) {
+		pr_err("DEBUG: cx25821 can't find device struct. Can't proceed with open\n");
+		return -ENODEV;
+	}
+
+	err = snd_pcm_hw_constraint_pow2(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
+		goto _error;
+
+	chip->substream = substream;
+
+	runtime->hw = snd_cx25821_digital_hw;
+
+	if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+	    DEFAULT_FIFO_SIZE) {
+		/* since there are 3 audio Clusters */
+		bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3;
+		bpl &= ~7;	/* must be multiple of 8 */
+
+		if (bpl > AUDIO_LINE_SIZE)
+			bpl = AUDIO_LINE_SIZE;
+
+		runtime->hw.period_bytes_min = bpl;
+		runtime->hw.period_bytes_max = bpl;
+	}
+
+	return 0;
+_error:
+	dprintk(1, "Error opening PCM!\n");
+	return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx25821_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx25821_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct videobuf_dmabuf *dma;
+
+	struct cx25821_audio_buffer *buf;
+	int ret;
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	chip->period_size = params_period_bytes(hw_params);
+	chip->num_periods = params_periods(hw_params);
+	chip->dma_size = chip->period_size * params_periods(hw_params);
+
+	BUG_ON(!chip->dma_size);
+	BUG_ON(chip->num_periods & (chip->num_periods - 1));
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (NULL == buf)
+		return -ENOMEM;
+
+	if (chip->period_size > AUDIO_LINE_SIZE)
+		chip->period_size = AUDIO_LINE_SIZE;
+
+	buf->bpl = chip->period_size;
+
+	dma = &buf->dma;
+	videobuf_dma_init(dma);
+	ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+	if (ret < 0)
+		goto error;
+
+	ret = videobuf_dma_map(&chip->pci->dev, dma);
+	if (ret < 0)
+		goto error;
+
+	ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist,
+			chip->period_size, chip->num_periods, 1);
+	if (ret < 0) {
+		pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n");
+		goto error;
+	}
+
+	/* Loop back to start of program */
+	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->risc.jmp[2] = cpu_to_le32(0);	/* bits 63-32 */
+
+	chip->buf = buf;
+	chip->dma_risc = dma;
+
+	substream->runtime->dma_area = chip->dma_risc->vaddr;
+	substream->runtime->dma_bytes = chip->dma_size;
+	substream->runtime->dma_addr = 0;
+
+	return 0;
+
+error:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx25821_hw_free(struct snd_pcm_substream *substream)
+{
+	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx25821_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+	int err = 0;
+
+	/* Local interrupts are already disabled by ALSA */
+	spin_lock(&chip->reg_lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = _cx25821_start_audio_dma(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err = _cx25821_stop_audio_dma(chip);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	spin_unlock(&chip->reg_lock);
+
+	return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream
+					     *substream)
+{
+	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	u16 count;
+
+	count = atomic_read(&chip->count);
+
+	return runtime->period_size * (count & (runtime->periods - 1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx25821_page(struct snd_pcm_substream *substream,
+				     unsigned long offset)
+{
+	void *pageptr = substream->runtime->dma_area + offset;
+
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx25821_pcm_ops = {
+	.open = snd_cx25821_pcm_open,
+	.close = snd_cx25821_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_cx25821_hw_params,
+	.hw_free = snd_cx25821_hw_free,
+	.prepare = snd_cx25821_prepare,
+	.trigger = snd_cx25821_card_trigger,
+	.pointer = snd_cx25821_pointer,
+	.page = snd_cx25821_page,
+};
+
+/*
+ * ALSA create a PCM device:  Called when initializing the board.
+ * Sets up the name and hooks up the callbacks
+ */
+static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device,
+			   char *name)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+	if (err < 0) {
+		pr_info("ERROR: FAILED snd_pcm_new() in %s\n", __func__);
+		return err;
+	}
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, name);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops);
+
+	return 0;
+}
+
+/****************************************************************************
+			Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
+ * Only boards with eeprom and byte 1 at eeprom=1 have it
+ */
+
+static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = {
+	{0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl);
+
+/*
+ * Not used in the function snd_cx25821_dev_free so removing
+ * from the file.
+ */
+/*
+static int snd_cx25821_free(struct cx25821_audio_dev *chip)
+{
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+
+	cx25821_dev_unregister(chip->dev);
+	pci_disable_device(chip->pci);
+
+	return 0;
+}
+*/
+
+/*
+ * Component Destructor
+ */
+static void snd_cx25821_dev_free(struct snd_card *card)
+{
+	struct cx25821_audio_dev *chip = card->private_data;
+
+	/* snd_cx25821_free(chip); */
+	snd_card_free(chip->card);
+}
+
+/*
+ * Alsa Constructor - Component probe
+ */
+static int cx25821_audio_initdev(struct cx25821_dev *dev)
+{
+	struct snd_card *card;
+	struct cx25821_audio_dev *chip;
+	int err;
+
+	if (devno >= SNDRV_CARDS) {
+		pr_info("DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!enable[devno]) {
+		++devno;
+		pr_info("DEBUG ERROR: !enable[devno] %s\n", __func__);
+		return -ENOENT;
+	}
+
+	err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+			sizeof(struct cx25821_audio_dev), &card);
+	if (err < 0) {
+		pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n",
+			__func__);
+		return err;
+	}
+
+	strcpy(card->driver, "cx25821");
+
+	/* Card "creation" */
+	card->private_free = snd_cx25821_dev_free;
+	chip = card->private_data;
+	spin_lock_init(&chip->reg_lock);
+
+	chip->dev = dev;
+	chip->card = card;
+	chip->pci = dev->pci;
+	chip->iobase = pci_resource_start(dev->pci, 0);
+
+	chip->irq = dev->pci->irq;
+
+	err = request_irq(dev->pci->irq, cx25821_irq,
+			  IRQF_SHARED, chip->dev->name, chip);
+
+	if (err < 0) {
+		pr_err("ERROR %s: can't get IRQ %d for ALSA\n", chip->dev->name,
+			dev->pci->irq);
+		goto error;
+	}
+
+	err = snd_cx25821_pcm(chip, 0, "cx25821 Digital");
+	if (err < 0) {
+		pr_info("DEBUG ERROR: cannot create snd_cx25821_pcm %s\n",
+			__func__);
+		goto error;
+	}
+
+	snd_card_set_dev(card, &chip->pci->dev);
+
+	strcpy(card->shortname, "cx25821");
+	sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name,
+		chip->iobase, chip->irq);
+	strcpy(card->mixername, "CX25821");
+
+	pr_info("%s/%i: ALSA support for cx25821 boards\n", card->driver,
+		devno);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		pr_info("DEBUG ERROR: cannot register sound card %s\n",
+			__func__);
+		goto error;
+	}
+
+	snd_cx25821_cards[devno] = card;
+
+	devno++;
+	return 0;
+
+error:
+	snd_card_free(card);
+	return err;
+}
+
+/****************************************************************************
+				LINUX MODULE INIT
+ ****************************************************************************/
+static void cx25821_audio_fini(void)
+{
+	snd_card_free(snd_cx25821_cards[0]);
+}
+
+/*
+ * Module initializer
+ *
+ * Loops through present saa7134 cards, and assigns an ALSA device
+ * to each one
+ *
+ */
+static int cx25821_alsa_init(void)
+{
+	struct cx25821_dev *dev = NULL;
+	struct list_head *list;
+
+	mutex_lock(&cx25821_devlist_mutex);
+	list_for_each(list, &cx25821_devlist) {
+		dev = list_entry(list, struct cx25821_dev, devlist);
+		cx25821_audio_initdev(dev);
+	}
+	mutex_unlock(&cx25821_devlist_mutex);
+
+	if (dev == NULL)
+		pr_info("ERROR ALSA: no cx25821 cards found\n");
+
+	return 0;
+
+}
+
+late_initcall(cx25821_alsa_init);
+module_exit(cx25821_audio_fini);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
new file mode 100644
index 000000000000..8b2a99975c23
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
@@ -0,0 +1,778 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-audio-upstream.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF |
+			FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR;
+
+int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
+					      struct sram_channel *ch,
+					      unsigned int bpl, u32 risc)
+{
+	unsigned int i, lines;
+	u32 cdt;
+
+	if (ch->cmds_start == 0) {
+		cx_write(ch->ptr1_reg, 0);
+		cx_write(ch->ptr2_reg, 0);
+		cx_write(ch->cnt2_reg, 0);
+		cx_write(ch->cnt1_reg, 0);
+		return 0;
+	}
+
+	bpl = (bpl + 7) & ~7;	/* alignment */
+	cdt = ch->cdt;
+	lines = ch->fifo_size / bpl;
+
+	if (lines > 3)
+		lines = 3;
+
+	BUG_ON(lines < 2);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++) {
+		cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+		cx_write(cdt + 16 * i + 4, 0);
+		cx_write(cdt + 16 * i + 8, 0);
+		cx_write(cdt + 16 * i + 12, 0);
+	}
+
+	/* write CMDS */
+	cx_write(ch->cmds_start + 0, risc);
+
+	cx_write(ch->cmds_start + 4, 0);
+	cx_write(ch->cmds_start + 8, cdt);
+	cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW);
+	cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+	/* IQ size */
+	cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW);
+
+	for (i = 24; i < 80; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW);
+	cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1);
+
+	return 0;
+}
+
+static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev,
+						 __le32 *rp,
+						 dma_addr_t databuf_phys_addr,
+						 unsigned int bpl,
+						 int fifo_enable)
+{
+	unsigned int line;
+	struct sram_channel *sram_ch =
+		dev->channels[dev->_audio_upstream_channel].sram_channels;
+	int offset = 0;
+
+	/* scan lines */
+	for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) {
+		*(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+		*(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+		*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+
+		/* Check if we need to enable the FIFO
+		 * after the first 3 lines.
+		 * For the upstream audio channel,
+		 * the risc engine will enable the FIFO */
+		if (fifo_enable && line == 2) {
+			*(rp++) = RISC_WRITECR;
+			*(rp++) = sram_ch->dma_ctl;
+			*(rp++) = sram_ch->fld_aud_fifo_en;
+			*(rp++) = 0x00000020;
+		}
+
+		offset += AUDIO_LINE_SIZE;
+	}
+
+	return rp;
+}
+
+int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev,
+				       struct pci_dev *pci,
+				       unsigned int bpl, unsigned int lines)
+{
+	__le32 *rp;
+	int fifo_enable = 0;
+	int frame = 0, i = 0;
+	int frame_size = AUDIO_DATA_BUF_SZ;
+	int databuf_offset = 0;
+	int risc_flag = RISC_CNT_INC;
+	dma_addr_t risc_phys_jump_addr;
+
+	/* Virtual address of Risc buffer program */
+	rp = dev->_risc_virt_addr;
+
+	/* sync instruction */
+	*(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE);
+
+	for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) {
+		databuf_offset = frame_size * frame;
+
+		if (frame == 0) {
+			fifo_enable = 1;
+			risc_flag = RISC_CNT_RESET;
+		} else {
+			fifo_enable = 0;
+			risc_flag = RISC_CNT_INC;
+		}
+
+		/* Calculate physical jump address */
+		if ((frame + 1) == NUM_AUDIO_FRAMES) {
+			risc_phys_jump_addr =
+			    dev->_risc_phys_start_addr +
+			    RISC_SYNC_INSTRUCTION_SIZE;
+		} else {
+			risc_phys_jump_addr =
+			    dev->_risc_phys_start_addr +
+			    RISC_SYNC_INSTRUCTION_SIZE +
+			    AUDIO_RISC_DMA_BUF_SIZE * (frame + 1);
+		}
+
+		rp = cx25821_risc_field_upstream_audio(dev, rp,
+				dev->_audiodata_buf_phys_addr + databuf_offset,
+				bpl, fifo_enable);
+
+		if (USE_RISC_NOOP_AUDIO) {
+			for (i = 0; i < NUM_NO_OPS; i++)
+				*(rp++) = cpu_to_le32(RISC_NOOP);
+		}
+
+		/* Loop to (Nth)FrameRISC or to Start of Risc program &
+		 * generate IRQ */
+		*(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+		*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+		*(rp++) = cpu_to_le32(0);
+
+		/* Recalculate virtual address based on frame index */
+		rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 +
+			(AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4);
+	}
+
+	return 0;
+}
+
+void cx25821_free_memory_audio(struct cx25821_dev *dev)
+{
+	if (dev->_risc_virt_addr) {
+		pci_free_consistent(dev->pci, dev->_audiorisc_size,
+				    dev->_risc_virt_addr, dev->_risc_phys_addr);
+		dev->_risc_virt_addr = NULL;
+	}
+
+	if (dev->_audiodata_buf_virt_addr) {
+		pci_free_consistent(dev->pci, dev->_audiodata_buf_size,
+				    dev->_audiodata_buf_virt_addr,
+				    dev->_audiodata_buf_phys_addr);
+		dev->_audiodata_buf_virt_addr = NULL;
+	}
+}
+
+void cx25821_stop_upstream_audio(struct cx25821_dev *dev)
+{
+	struct sram_channel *sram_ch =
+		dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels;
+	u32 tmp = 0;
+
+	if (!dev->_audio_is_running) {
+		printk(KERN_DEBUG
+		       pr_fmt("No audio file is currently running so return!\n"));
+		return;
+	}
+	/* Disable RISC interrupts */
+	cx_write(sram_ch->int_msk, 0);
+
+	/* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_write(sram_ch->dma_ctl,
+		 tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en));
+
+	/* Clear data buffer memory */
+	if (dev->_audiodata_buf_virt_addr)
+		memset(dev->_audiodata_buf_virt_addr, 0,
+		       dev->_audiodata_buf_size);
+
+	dev->_audio_is_running = 0;
+	dev->_is_first_audio_frame = 0;
+	dev->_audioframe_count = 0;
+	dev->_audiofile_status = END_OF_FILE;
+
+	kfree(dev->_irq_audio_queues);
+	dev->_irq_audio_queues = NULL;
+
+	kfree(dev->_audiofilename);
+}
+
+void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev)
+{
+	if (dev->_audio_is_running)
+		cx25821_stop_upstream_audio(dev);
+
+	cx25821_free_memory_audio(dev);
+}
+
+int cx25821_get_audio_data(struct cx25821_dev *dev,
+			   struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int frame_index_temp = dev->_audioframe_index;
+	int i = 0;
+	int line_size = AUDIO_LINE_SIZE;
+	int frame_size = AUDIO_DATA_BUF_SZ;
+	int frame_offset = frame_size * frame_index_temp;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t file_offset = dev->_audioframe_count * frame_size;
+	loff_t pos;
+	mm_segment_t old_fs;
+
+	if (dev->_audiofile_status == END_OF_FILE)
+		return 0;
+
+	myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0);
+
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+		       __func__, dev->_audiofilename, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+				__func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!\n",
+				__func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (i = 0; i < dev->_audio_lines_count; i++) {
+			pos = file_offset;
+
+			vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+									&pos);
+
+			if (vfs_read_retval > 0 && vfs_read_retval == line_size
+			    && dev->_audiodata_buf_virt_addr != NULL) {
+				memcpy((void *)(dev->_audiodata_buf_virt_addr +
+						frame_offset / 4), mybuf,
+					vfs_read_retval);
+			}
+
+			file_offset += vfs_read_retval;
+			frame_offset += vfs_read_retval;
+
+			if (vfs_read_retval < line_size) {
+				pr_info("Done: exit %s() since no more bytes to read from Audio file\n",
+					__func__);
+				break;
+			}
+		}
+
+		if (i > 0)
+			dev->_audioframe_count++;
+
+		dev->_audiofile_status = (vfs_read_retval == line_size) ?
+						IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+static void cx25821_audioups_handler(struct work_struct *work)
+{
+	struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+			_audio_work_entry);
+
+	if (!dev) {
+		pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+			__func__);
+		return;
+	}
+
+	cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel].
+			sram_channels);
+}
+
+int cx25821_openfile_audio(struct cx25821_dev *dev,
+			   struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int i = 0, j = 0;
+	int line_size = AUDIO_LINE_SIZE;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t pos;
+	loff_t offset = (unsigned long)0;
+	mm_segment_t old_fs;
+
+	myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0);
+
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+			__func__, dev->_audiofilename, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+				__func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!\n",
+				__func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (j = 0; j < NUM_AUDIO_FRAMES; j++) {
+			for (i = 0; i < dev->_audio_lines_count; i++) {
+				pos = offset;
+
+				vfs_read_retval = vfs_read(myfile, mybuf,
+						line_size, &pos);
+
+				if (vfs_read_retval > 0 &&
+				    vfs_read_retval == line_size &&
+				    dev->_audiodata_buf_virt_addr != NULL) {
+					memcpy((void *)(dev->
+							_audiodata_buf_virt_addr
+							+ offset / 4), mybuf,
+					       vfs_read_retval);
+				}
+
+				offset += vfs_read_retval;
+
+				if (vfs_read_retval < line_size) {
+					pr_info("Done: exit %s() since no more bytes to read from Audio file\n",
+						__func__);
+					break;
+				}
+			}
+
+			if (i > 0)
+				dev->_audioframe_count++;
+
+			if (vfs_read_retval < line_size)
+				break;
+		}
+
+		dev->_audiofile_status = (vfs_read_retval == line_size) ?
+						IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		myfile->f_pos = 0;
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev,
+						 struct sram_channel *sram_ch,
+						 int bpl)
+{
+	int ret = 0;
+	dma_addr_t dma_addr;
+	dma_addr_t data_dma_addr;
+
+	cx25821_free_memory_audio(dev);
+
+	dev->_risc_virt_addr = pci_alloc_consistent(dev->pci,
+			dev->audio_upstream_riscbuf_size, &dma_addr);
+	dev->_risc_virt_start_addr = dev->_risc_virt_addr;
+	dev->_risc_phys_start_addr = dma_addr;
+	dev->_risc_phys_addr = dma_addr;
+	dev->_audiorisc_size = dev->audio_upstream_riscbuf_size;
+
+	if (!dev->_risc_virt_addr) {
+		printk(KERN_DEBUG
+			pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n"));
+		return -ENOMEM;
+	}
+	/* Clear out memory at address */
+	memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size);
+
+	/* For Audio Data buffer allocation */
+	dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci,
+			dev->audio_upstream_databuf_size, &data_dma_addr);
+	dev->_audiodata_buf_phys_addr = data_dma_addr;
+	dev->_audiodata_buf_size = dev->audio_upstream_databuf_size;
+
+	if (!dev->_audiodata_buf_virt_addr) {
+		printk(KERN_DEBUG
+			pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n"));
+		return -ENOMEM;
+	}
+	/* Clear out memory at address */
+	memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size);
+
+	ret = cx25821_openfile_audio(dev, sram_ch);
+	if (ret < 0)
+		return ret;
+
+	/* Creating RISC programs */
+	ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl,
+						dev->_audio_lines_count);
+	if (ret < 0) {
+		printk(KERN_DEBUG
+			pr_fmt("ERROR creating audio upstream RISC programs!\n"));
+		goto error;
+	}
+
+	return 0;
+
+error:
+	return ret;
+}
+
+int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
+			       u32 status)
+{
+	int i = 0;
+	u32 int_msk_tmp;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	dma_addr_t risc_phys_jump_addr;
+	__le32 *rp;
+
+	if (status & FLD_AUD_SRC_RISCI1) {
+		/* Get interrupt_index of the program that interrupted */
+		u32 prog_cnt = cx_read(channel->gpcnt);
+
+		/* Since we've identified our IRQ, clear our bits from the
+		 * interrupt mask and interrupt status registers */
+		cx_write(channel->int_msk, 0);
+		cx_write(channel->int_stat, cx_read(channel->int_stat));
+
+		spin_lock(&dev->slock);
+
+		while (prog_cnt != dev->_last_index_irq) {
+			/* Update _last_index_irq */
+			if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1))
+				dev->_last_index_irq++;
+			else
+				dev->_last_index_irq = 0;
+
+			dev->_audioframe_index = dev->_last_index_irq;
+
+			queue_work(dev->_irq_audio_queues,
+				   &dev->_audio_work_entry);
+		}
+
+		if (dev->_is_first_audio_frame) {
+			dev->_is_first_audio_frame = 0;
+
+			if (dev->_risc_virt_start_addr != NULL) {
+				risc_phys_jump_addr =
+					dev->_risc_phys_start_addr +
+					RISC_SYNC_INSTRUCTION_SIZE +
+					AUDIO_RISC_DMA_BUF_SIZE;
+
+				rp = cx25821_risc_field_upstream_audio(dev,
+						dev->_risc_virt_start_addr + 1,
+						dev->_audiodata_buf_phys_addr,
+						AUDIO_LINE_SIZE, FIFO_DISABLE);
+
+				if (USE_RISC_NOOP_AUDIO) {
+					for (i = 0; i < NUM_NO_OPS; i++) {
+						*(rp++) =
+						    cpu_to_le32(RISC_NOOP);
+					}
+				}
+				/* Jump to 2nd Audio Frame */
+				*(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 |
+						RISC_CNT_RESET);
+				*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+				*(rp++) = cpu_to_le32(0);
+			}
+		}
+
+		spin_unlock(&dev->slock);
+	} else {
+		if (status & FLD_AUD_SRC_OF)
+			pr_warn("%s(): Audio Received Overflow Error Interrupt!\n",
+				__func__);
+
+		if (status & FLD_AUD_SRC_SYNC)
+			pr_warn("%s(): Audio Received Sync Error Interrupt!\n",
+				__func__);
+
+		if (status & FLD_AUD_SRC_OPC_ERR)
+			pr_warn("%s(): Audio Received OpCode Error Interrupt!\n",
+				__func__);
+
+		/* Read and write back the interrupt status register to clear
+		 * our bits */
+		cx_write(channel->int_stat, cx_read(channel->int_stat));
+	}
+
+	if (dev->_audiofile_status == END_OF_FILE) {
+		pr_warn("EOF Channel Audio Framecount = %d\n",
+			dev->_audioframe_count);
+		return -1;
+	}
+	/* ElSE, set the interrupt mask register, re-enable irq. */
+	int_msk_tmp = cx_read(channel->int_msk);
+	cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+	return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
+{
+	struct cx25821_dev *dev = dev_id;
+	u32 audio_status;
+	int handled = 0;
+	struct sram_channel *sram_ch;
+
+	if (!dev)
+		return -1;
+
+	sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels;
+
+	audio_status = cx_read(sram_ch->int_stat);
+
+	/* Only deal with our interrupt */
+	if (audio_status) {
+		handled = cx25821_audio_upstream_irq(dev,
+				dev->_audio_upstream_channel, audio_status);
+	}
+
+	if (handled < 0)
+		cx25821_stop_upstream_audio(dev);
+	else
+		handled += handled;
+
+	return IRQ_RETVAL(handled);
+}
+
+static void cx25821_wait_fifo_enable(struct cx25821_dev *dev,
+				     struct sram_channel *sram_ch)
+{
+	int count = 0;
+	u32 tmp;
+
+	do {
+		/* Wait 10 microsecond before checking to see if the FIFO is
+		 * turned ON. */
+		udelay(10);
+
+		tmp = cx_read(sram_ch->dma_ctl);
+
+		/* 10 millisecond timeout */
+		if (count++ > 1000) {
+			pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n",
+				__func__);
+			return;
+		}
+
+	} while (!(tmp & sram_ch->fld_aud_fifo_en));
+
+}
+
+int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
+				     struct sram_channel *sram_ch)
+{
+	u32 tmp = 0;
+	int err = 0;
+
+	/* Set the physical start address of the RISC program in the initial
+	 * program counter(IPC) member of the CMDS. */
+	cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr);
+	/* Risc IPC High 64 bits 63-32 */
+	cx_write(sram_ch->cmds_start + 4, 0);
+
+	/* reset counter */
+	cx_write(sram_ch->gpcnt_ctl, 3);
+
+	/* Set the line length       (It looks like we do not need to set the
+	 * line length) */
+	cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH);
+
+	/* Set the input mode to 16-bit */
+	tmp = cx_read(sram_ch->aud_cfg);
+	tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE |
+		FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D |
+		FLD_AUD_SONY_MODE;
+	cx_write(sram_ch->aud_cfg, tmp);
+
+	/* Read and write back the interrupt status register to clear it */
+	tmp = cx_read(sram_ch->int_stat);
+	cx_write(sram_ch->int_stat, tmp);
+
+	/* Clear our bits from the interrupt status register. */
+	cx_write(sram_ch->int_stat, _intr_msk);
+
+	/* Set the interrupt mask register, enable irq. */
+	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+	tmp = cx_read(sram_ch->int_msk);
+	cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+	err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio,
+			IRQF_SHARED, dev->name, dev);
+	if (err < 0) {
+		pr_err("%s: can't get upstream IRQ %d\n", dev->name,
+				dev->pci->irq);
+		goto fail_irq;
+	}
+
+	/* Start the DMA  engine */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en);
+
+	dev->_audio_is_running = 1;
+	dev->_is_first_audio_frame = 1;
+
+	/* The fifo_en bit turns on by the first Risc program */
+	cx25821_wait_fifo_enable(dev, sram_ch);
+
+	return 0;
+
+fail_irq:
+	cx25821_dev_unregister(dev);
+	return err;
+}
+
+int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
+{
+	struct sram_channel *sram_ch;
+	int retval = 0;
+	int err = 0;
+	int str_length = 0;
+
+	if (dev->_audio_is_running) {
+		pr_warn("Audio Channel is still running so return!\n");
+		return 0;
+	}
+
+	dev->_audio_upstream_channel = channel_select;
+	sram_ch = dev->channels[channel_select].sram_channels;
+
+	/* Work queue */
+	INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler);
+	dev->_irq_audio_queues =
+	    create_singlethread_workqueue("cx25821_audioworkqueue");
+
+	if (!dev->_irq_audio_queues) {
+		printk(KERN_DEBUG
+			pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n"));
+		return -ENOMEM;
+	}
+
+	dev->_last_index_irq = 0;
+	dev->_audio_is_running = 0;
+	dev->_audioframe_count = 0;
+	dev->_audiofile_status = RESET_STATUS;
+	dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER;
+	_line_size = AUDIO_LINE_SIZE;
+
+	if (dev->input_audiofilename) {
+		str_length = strlen(dev->input_audiofilename);
+		dev->_audiofilename = kmemdup(dev->input_audiofilename,
+					      str_length + 1, GFP_KERNEL);
+
+		if (!dev->_audiofilename)
+			goto error;
+
+		/* Default if filename is empty string */
+		if (strcmp(dev->input_audiofilename, "") == 0)
+			dev->_audiofilename = "/root/audioGOOD.wav";
+	} else {
+		str_length = strlen(_defaultAudioName);
+		dev->_audiofilename = kmemdup(_defaultAudioName,
+					      str_length + 1, GFP_KERNEL);
+
+		if (!dev->_audiofilename)
+			goto error;
+	}
+
+	retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch,
+							_line_size, 0);
+
+	dev->audio_upstream_riscbuf_size =
+		AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS +
+		RISC_SYNC_INSTRUCTION_SIZE;
+	dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;
+
+	/* Allocating buffers and prepare RISC program */
+	retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch,
+							_line_size);
+	if (retval < 0) {
+		pr_err("%s: Failed to set up Audio upstream buffers!\n",
+			dev->name);
+		goto error;
+	}
+	/* Start RISC engine */
+	cx25821_start_audio_dma_upstream(dev, sram_ch);
+
+	return 0;
+
+error:
+	cx25821_dev_unregister(dev);
+
+	return err;
+}
diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.h b/drivers/media/pci/cx25821/cx25821-audio-upstream.h
new file mode 100644
index 000000000000..af2ae7c5815a
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.h
@@ -0,0 +1,62 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.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/mutex.h>
+#include <linux/workqueue.h>
+
+#define NUM_AUDIO_PROGS       8
+#define NUM_AUDIO_FRAMES      8
+#define END_OF_FILE           0
+#define IN_PROGRESS           1
+#define RESET_STATUS          -1
+#define FIFO_DISABLE          0
+#define FIFO_ENABLE           1
+#define NUM_NO_OPS            4
+
+#define RISC_READ_INSTRUCTION_SIZE      12
+#define RISC_JUMP_INSTRUCTION_SIZE      12
+#define RISC_WRITECR_INSTRUCTION_SIZE   16
+#define RISC_SYNC_INSTRUCTION_SIZE      4
+#define DWORD_SIZE                      4
+#define AUDIO_SYNC_LINE                 4
+
+#define LINES_PER_AUDIO_BUFFER      15
+#define AUDIO_LINE_SIZE             128
+#define AUDIO_DATA_BUF_SZ           (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER)
+
+#define USE_RISC_NOOP_AUDIO   1
+
+#ifdef USE_RISC_NOOP_AUDIO
+#define AUDIO_RISC_DMA_BUF_SIZE						\
+	(LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE +		\
+	 RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE +	\
+	 RISC_JUMP_INSTRUCTION_SIZE)
+#endif
+
+#ifndef USE_RISC_NOOP_AUDIO
+#define AUDIO_RISC_DMA_BUF_SIZE						\
+	(LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE +		\
+	 RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#endif
+
+static int _line_size;
+char *_defaultAudioName = "/root/audioGOOD.wav";
diff --git a/drivers/media/pci/cx25821/cx25821-audio.h b/drivers/media/pci/cx25821/cx25821-audio.h
new file mode 100644
index 000000000000..1fc2d24f5110
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-audio.h
@@ -0,0 +1,62 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 __CX25821_AUDIO_H__
+#define __CX25821_AUDIO_H__
+
+#define USE_RISC_NOOP		1
+#define LINES_PER_BUFFER	15
+#define AUDIO_LINE_SIZE		128
+
+/* Number of buffer programs to use at once. */
+#define NUMBER_OF_PROGRAMS	8
+
+/*
+ * Max size of the RISC program for a buffer. - worst case is 2 writes per line
+ * Space is also added for the 4 no-op instructions added on the end.
+ */
+#ifndef USE_RISC_NOOP
+#define MAX_BUFFER_PROGRAM_SIZE						\
+	(2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE +		\
+	 RISC_WRITECR_INSTRUCTION_SIZE * 4)
+#endif
+
+/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */
+#ifdef USE_RISC_NOOP
+#define MAX_BUFFER_PROGRAM_SIZE						\
+	(2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE +		\
+	 RISC_NOOP_INSTRUCTION_SIZE * 4)
+#endif
+
+/* Sizes of various instructions in bytes.  Used when adding instructions. */
+#define RISC_WRITE_INSTRUCTION_SIZE	12
+#define RISC_JUMP_INSTRUCTION_SIZE	12
+#define RISC_SKIP_INSTRUCTION_SIZE	4
+#define RISC_SYNC_INSTRUCTION_SIZE	4
+#define RISC_WRITECR_INSTRUCTION_SIZE	16
+#define RISC_NOOP_INSTRUCTION_SIZE	4
+
+#define MAX_AUDIO_DMA_BUFFER_SIZE					\
+	(MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS +			\
+	 RISC_SYNC_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-biffuncs.h b/drivers/media/pci/cx25821/cx25821-biffuncs.h
new file mode 100644
index 000000000000..9326a7c729ec
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-biffuncs.h
@@ -0,0 +1,45 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 _BITFUNCS_H
+#define _BITFUNCS_H
+
+#define SetBit(Bit)  (1 << Bit)
+
+inline u8 getBit(u32 sample, u8 index)
+{
+	return (u8) ((sample >> index) & 1);
+}
+
+inline u32 clearBitAtPos(u32 value, u8 bit)
+{
+	return value & ~(1 << bit);
+}
+
+inline u32 setBitAtPos(u32 sample, u8 bit)
+{
+	sample |= (1 << bit);
+	return sample;
+
+}
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-cards.c b/drivers/media/pci/cx25821/cx25821-cards.c
new file mode 100644
index 000000000000..99988c988095
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-cards.c
@@ -0,0 +1,72 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *	Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <media/cx25840.h>
+
+#include "cx25821.h"
+#include "tuner-xc2028.h"
+
+/* board config info */
+
+struct cx25821_board cx25821_boards[] = {
+	[UNKNOWN_BOARD] = {
+		.name = "UNKNOWN/GENERIC",
+		/* Ensure safe default for unknown boards */
+		.clk_freq = 0,
+	},
+
+	[CX25821_BOARD] = {
+		.name = "CX25821",
+		.portb = CX25821_RAW,
+		.portc = CX25821_264,
+		.input[0].type = CX25821_VMUX_COMPOSITE,
+	},
+
+};
+
+const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
+
+struct cx25821_subid cx25821_subids[] = {
+	{
+		.subvendor = 0x14f1,
+		.subdevice = 0x0920,
+		.card = CX25821_BOARD,
+	},
+};
+
+void cx25821_card_setup(struct cx25821_dev *dev)
+{
+	static u8 eeprom[256];
+
+	if (dev->i2c_bus[0].i2c_rc == 0) {
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+				sizeof(eeprom));
+	}
+}
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
new file mode 100644
index 000000000000..f11f6f07e915
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -0,0 +1,1502 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include "cx25821.h"
+#include "cx25821-sram.h"
+#include "cx25821-video.h"
+
+MODULE_DESCRIPTION("Driver for Athena cards");
+MODULE_AUTHOR("Shu Lin - Hiep Huynh");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+static unsigned int cx25821_devcount;
+
+DEFINE_MUTEX(cx25821_devlist_mutex);
+EXPORT_SYMBOL(cx25821_devlist_mutex);
+LIST_HEAD(cx25821_devlist);
+EXPORT_SYMBOL(cx25821_devlist);
+
+struct sram_channel cx25821_sram_channels[] = {
+	[SRAM_CH00] = {
+		.i = SRAM_CH00,
+		.name = "VID A",
+		.cmds_start = VID_A_DOWN_CMDS,
+		.ctrl_start = VID_A_IQ,
+		.cdt = VID_A_CDT,
+		.fifo_start = VID_A_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA1_PTR1,
+		.ptr2_reg = DMA1_PTR2,
+		.cnt1_reg = DMA1_CNT1,
+		.cnt2_reg = DMA1_CNT2,
+		.int_msk = VID_A_INT_MSK,
+		.int_stat = VID_A_INT_STAT,
+		.int_mstat = VID_A_INT_MSTAT,
+		.dma_ctl = VID_DST_A_DMA_CTL,
+		.gpcnt_ctl = VID_DST_A_GPCNT_CTL,
+		.gpcnt = VID_DST_A_GPCNT,
+		.vip_ctl = VID_DST_A_VIP_CTL,
+		.pix_frmt = VID_DST_A_PIX_FRMT,
+	},
+
+	[SRAM_CH01] = {
+		.i = SRAM_CH01,
+		.name = "VID B",
+		.cmds_start = VID_B_DOWN_CMDS,
+		.ctrl_start = VID_B_IQ,
+		.cdt = VID_B_CDT,
+		.fifo_start = VID_B_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA2_PTR1,
+		.ptr2_reg = DMA2_PTR2,
+		.cnt1_reg = DMA2_CNT1,
+		.cnt2_reg = DMA2_CNT2,
+		.int_msk = VID_B_INT_MSK,
+		.int_stat = VID_B_INT_STAT,
+		.int_mstat = VID_B_INT_MSTAT,
+		.dma_ctl = VID_DST_B_DMA_CTL,
+		.gpcnt_ctl = VID_DST_B_GPCNT_CTL,
+		.gpcnt = VID_DST_B_GPCNT,
+		.vip_ctl = VID_DST_B_VIP_CTL,
+		.pix_frmt = VID_DST_B_PIX_FRMT,
+	},
+
+	[SRAM_CH02] = {
+		.i = SRAM_CH02,
+		.name = "VID C",
+		.cmds_start = VID_C_DOWN_CMDS,
+		.ctrl_start = VID_C_IQ,
+		.cdt = VID_C_CDT,
+		.fifo_start = VID_C_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA3_PTR1,
+		.ptr2_reg = DMA3_PTR2,
+		.cnt1_reg = DMA3_CNT1,
+		.cnt2_reg = DMA3_CNT2,
+		.int_msk = VID_C_INT_MSK,
+		.int_stat = VID_C_INT_STAT,
+		.int_mstat = VID_C_INT_MSTAT,
+		.dma_ctl = VID_DST_C_DMA_CTL,
+		.gpcnt_ctl = VID_DST_C_GPCNT_CTL,
+		.gpcnt = VID_DST_C_GPCNT,
+		.vip_ctl = VID_DST_C_VIP_CTL,
+		.pix_frmt = VID_DST_C_PIX_FRMT,
+	},
+
+	[SRAM_CH03] = {
+		.i = SRAM_CH03,
+		.name = "VID D",
+		.cmds_start = VID_D_DOWN_CMDS,
+		.ctrl_start = VID_D_IQ,
+		.cdt = VID_D_CDT,
+		.fifo_start = VID_D_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA4_PTR1,
+		.ptr2_reg = DMA4_PTR2,
+		.cnt1_reg = DMA4_CNT1,
+		.cnt2_reg = DMA4_CNT2,
+		.int_msk = VID_D_INT_MSK,
+		.int_stat = VID_D_INT_STAT,
+		.int_mstat = VID_D_INT_MSTAT,
+		.dma_ctl = VID_DST_D_DMA_CTL,
+		.gpcnt_ctl = VID_DST_D_GPCNT_CTL,
+		.gpcnt = VID_DST_D_GPCNT,
+		.vip_ctl = VID_DST_D_VIP_CTL,
+		.pix_frmt = VID_DST_D_PIX_FRMT,
+	},
+
+	[SRAM_CH04] = {
+		.i = SRAM_CH04,
+		.name = "VID E",
+		.cmds_start = VID_E_DOWN_CMDS,
+		.ctrl_start = VID_E_IQ,
+		.cdt = VID_E_CDT,
+		.fifo_start = VID_E_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA5_PTR1,
+		.ptr2_reg = DMA5_PTR2,
+		.cnt1_reg = DMA5_CNT1,
+		.cnt2_reg = DMA5_CNT2,
+		.int_msk = VID_E_INT_MSK,
+		.int_stat = VID_E_INT_STAT,
+		.int_mstat = VID_E_INT_MSTAT,
+		.dma_ctl = VID_DST_E_DMA_CTL,
+		.gpcnt_ctl = VID_DST_E_GPCNT_CTL,
+		.gpcnt = VID_DST_E_GPCNT,
+		.vip_ctl = VID_DST_E_VIP_CTL,
+		.pix_frmt = VID_DST_E_PIX_FRMT,
+	},
+
+	[SRAM_CH05] = {
+		.i = SRAM_CH05,
+		.name = "VID F",
+		.cmds_start = VID_F_DOWN_CMDS,
+		.ctrl_start = VID_F_IQ,
+		.cdt = VID_F_CDT,
+		.fifo_start = VID_F_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA6_PTR1,
+		.ptr2_reg = DMA6_PTR2,
+		.cnt1_reg = DMA6_CNT1,
+		.cnt2_reg = DMA6_CNT2,
+		.int_msk = VID_F_INT_MSK,
+		.int_stat = VID_F_INT_STAT,
+		.int_mstat = VID_F_INT_MSTAT,
+		.dma_ctl = VID_DST_F_DMA_CTL,
+		.gpcnt_ctl = VID_DST_F_GPCNT_CTL,
+		.gpcnt = VID_DST_F_GPCNT,
+		.vip_ctl = VID_DST_F_VIP_CTL,
+		.pix_frmt = VID_DST_F_PIX_FRMT,
+	},
+
+	[SRAM_CH06] = {
+		.i = SRAM_CH06,
+		.name = "VID G",
+		.cmds_start = VID_G_DOWN_CMDS,
+		.ctrl_start = VID_G_IQ,
+		.cdt = VID_G_CDT,
+		.fifo_start = VID_G_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA7_PTR1,
+		.ptr2_reg = DMA7_PTR2,
+		.cnt1_reg = DMA7_CNT1,
+		.cnt2_reg = DMA7_CNT2,
+		.int_msk = VID_G_INT_MSK,
+		.int_stat = VID_G_INT_STAT,
+		.int_mstat = VID_G_INT_MSTAT,
+		.dma_ctl = VID_DST_G_DMA_CTL,
+		.gpcnt_ctl = VID_DST_G_GPCNT_CTL,
+		.gpcnt = VID_DST_G_GPCNT,
+		.vip_ctl = VID_DST_G_VIP_CTL,
+		.pix_frmt = VID_DST_G_PIX_FRMT,
+	},
+
+	[SRAM_CH07] = {
+		.i = SRAM_CH07,
+		.name = "VID H",
+		.cmds_start = VID_H_DOWN_CMDS,
+		.ctrl_start = VID_H_IQ,
+		.cdt = VID_H_CDT,
+		.fifo_start = VID_H_DOWN_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA8_PTR1,
+		.ptr2_reg = DMA8_PTR2,
+		.cnt1_reg = DMA8_CNT1,
+		.cnt2_reg = DMA8_CNT2,
+		.int_msk = VID_H_INT_MSK,
+		.int_stat = VID_H_INT_STAT,
+		.int_mstat = VID_H_INT_MSTAT,
+		.dma_ctl = VID_DST_H_DMA_CTL,
+		.gpcnt_ctl = VID_DST_H_GPCNT_CTL,
+		.gpcnt = VID_DST_H_GPCNT,
+		.vip_ctl = VID_DST_H_VIP_CTL,
+		.pix_frmt = VID_DST_H_PIX_FRMT,
+	},
+
+	[SRAM_CH08] = {
+		.name = "audio from",
+		.cmds_start = AUD_A_DOWN_CMDS,
+		.ctrl_start = AUD_A_IQ,
+		.cdt = AUD_A_CDT,
+		.fifo_start = AUD_A_DOWN_CLUSTER_1,
+		.fifo_size = AUDIO_CLUSTER_SIZE * 3,
+		.ptr1_reg = DMA17_PTR1,
+		.ptr2_reg = DMA17_PTR2,
+		.cnt1_reg = DMA17_CNT1,
+		.cnt2_reg = DMA17_CNT2,
+	},
+
+	[SRAM_CH09] = {
+		.i = SRAM_CH09,
+		.name = "VID Upstream I",
+		.cmds_start = VID_I_UP_CMDS,
+		.ctrl_start = VID_I_IQ,
+		.cdt = VID_I_CDT,
+		.fifo_start = VID_I_UP_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA15_PTR1,
+		.ptr2_reg = DMA15_PTR2,
+		.cnt1_reg = DMA15_CNT1,
+		.cnt2_reg = DMA15_CNT2,
+		.int_msk = VID_I_INT_MSK,
+		.int_stat = VID_I_INT_STAT,
+		.int_mstat = VID_I_INT_MSTAT,
+		.dma_ctl = VID_SRC_I_DMA_CTL,
+		.gpcnt_ctl = VID_SRC_I_GPCNT_CTL,
+		.gpcnt = VID_SRC_I_GPCNT,
+
+		.vid_fmt_ctl = VID_SRC_I_FMT_CTL,
+		.vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1,
+		.vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2,
+		.vid_cdt_size = VID_SRC_I_CDT_SZ,
+		.irq_bit = 8,
+	},
+
+	[SRAM_CH10] = {
+		.i = SRAM_CH10,
+		.name = "VID Upstream J",
+		.cmds_start = VID_J_UP_CMDS,
+		.ctrl_start = VID_J_IQ,
+		.cdt = VID_J_CDT,
+		.fifo_start = VID_J_UP_CLUSTER_1,
+		.fifo_size = (VID_CLUSTER_SIZE << 2),
+		.ptr1_reg = DMA16_PTR1,
+		.ptr2_reg = DMA16_PTR2,
+		.cnt1_reg = DMA16_CNT1,
+		.cnt2_reg = DMA16_CNT2,
+		.int_msk = VID_J_INT_MSK,
+		.int_stat = VID_J_INT_STAT,
+		.int_mstat = VID_J_INT_MSTAT,
+		.dma_ctl = VID_SRC_J_DMA_CTL,
+		.gpcnt_ctl = VID_SRC_J_GPCNT_CTL,
+		.gpcnt = VID_SRC_J_GPCNT,
+
+		.vid_fmt_ctl = VID_SRC_J_FMT_CTL,
+		.vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1,
+		.vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2,
+		.vid_cdt_size = VID_SRC_J_CDT_SZ,
+		.irq_bit = 9,
+	},
+
+	[SRAM_CH11] = {
+		.i = SRAM_CH11,
+		.name = "Audio Upstream Channel B",
+		.cmds_start = AUD_B_UP_CMDS,
+		.ctrl_start = AUD_B_IQ,
+		.cdt = AUD_B_CDT,
+		.fifo_start = AUD_B_UP_CLUSTER_1,
+		.fifo_size = (AUDIO_CLUSTER_SIZE * 3),
+		.ptr1_reg = DMA22_PTR1,
+		.ptr2_reg = DMA22_PTR2,
+		.cnt1_reg = DMA22_CNT1,
+		.cnt2_reg = DMA22_CNT2,
+		.int_msk = AUD_B_INT_MSK,
+		.int_stat = AUD_B_INT_STAT,
+		.int_mstat = AUD_B_INT_MSTAT,
+		.dma_ctl = AUD_INT_DMA_CTL,
+		.gpcnt_ctl = AUD_B_GPCNT_CTL,
+		.gpcnt = AUD_B_GPCNT,
+		.aud_length = AUD_B_LNGTH,
+		.aud_cfg = AUD_B_CFG,
+		.fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN,
+		.fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN,
+		.irq_bit = 11,
+	},
+};
+EXPORT_SYMBOL(cx25821_sram_channels);
+
+struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00];
+struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01];
+struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02];
+struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03];
+struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04];
+struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05];
+struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06];
+struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07];
+struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09];
+struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10];
+struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11];
+
+struct cx25821_dmaqueue mpegq;
+
+static int cx25821_risc_decode(u32 risc)
+{
+	static const char * const instr[16] = {
+		[RISC_SYNC >> 28] = "sync",
+		[RISC_WRITE >> 28] = "write",
+		[RISC_WRITEC >> 28] = "writec",
+		[RISC_READ >> 28] = "read",
+		[RISC_READC >> 28] = "readc",
+		[RISC_JUMP >> 28] = "jump",
+		[RISC_SKIP >> 28] = "skip",
+		[RISC_WRITERM >> 28] = "writerm",
+		[RISC_WRITECM >> 28] = "writecm",
+		[RISC_WRITECR >> 28] = "writecr",
+	};
+	static const int incr[16] = {
+		[RISC_WRITE >> 28] = 3,
+		[RISC_JUMP >> 28] = 3,
+		[RISC_SKIP >> 28] = 1,
+		[RISC_SYNC >> 28] = 1,
+		[RISC_WRITERM >> 28] = 3,
+		[RISC_WRITECM >> 28] = 3,
+		[RISC_WRITECR >> 28] = 4,
+	};
+	static const char * const bits[] = {
+		"12", "13", "14", "resync",
+		"cnt0", "cnt1", "18", "19",
+		"20", "21", "22", "23",
+		"irq1", "irq2", "eol", "sol",
+	};
+	int i;
+
+	pr_cont("0x%08x [ %s",
+		risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+	for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) {
+		if (risc & (1 << (i + 12)))
+			pr_cont(" %s", bits[i]);
+	}
+	pr_cont(" count=%d ]\n", risc & 0xfff);
+	return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	return cx_read(bus->reg_stat) & 0x01;
+}
+
+static void cx25821_registers_init(struct cx25821_dev *dev)
+{
+	u32 tmp;
+
+	/* enable RUN_RISC in Pecos */
+	cx_write(DEV_CNTRL2, 0x20);
+
+	/* Set the master PCI interrupt masks to enable video, audio, MBIF,
+	 * and GPIO interrupts
+	 * I2C interrupt masking is handled by the I2C objects themselves. */
+	cx_write(PCI_INT_MSK, 0x2001FFFF);
+
+	tmp = cx_read(RDR_TLCTL0);
+	tmp &= ~FLD_CFG_RCB_CK_EN;	/* Clear the RCB_CK_EN bit */
+	cx_write(RDR_TLCTL0, tmp);
+
+	/* PLL-A setting for the Audio Master Clock */
+	cx_write(PLL_A_INT_FRAC, 0x9807A58B);
+
+	/* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */
+	cx_write(PLL_A_POST_STAT_BIST, 0x8000019C);
+
+	/* clear reset bit [31] */
+	tmp = cx_read(PLL_A_INT_FRAC);
+	cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF);
+
+	/* PLL-B setting for Mobilygen Host Bus Interface */
+	cx_write(PLL_B_INT_FRAC, 0x9883A86F);
+
+	/* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */
+	cx_write(PLL_B_POST_STAT_BIST, 0x8000018D);
+
+	/* clear reset bit [31] */
+	tmp = cx_read(PLL_B_INT_FRAC);
+	cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF);
+
+	/* PLL-C setting for video upstream channel */
+	cx_write(PLL_C_INT_FRAC, 0x96A0EA3F);
+
+	/* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */
+	cx_write(PLL_C_POST_STAT_BIST, 0x80000103);
+
+	/* clear reset bit [31] */
+	tmp = cx_read(PLL_C_INT_FRAC);
+	cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF);
+
+	/* PLL-D setting for audio upstream channel */
+	cx_write(PLL_D_INT_FRAC, 0x98757F5B);
+
+	/* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */
+	cx_write(PLL_D_POST_STAT_BIST, 0x80000113);
+
+	/* clear reset bit [31] */
+	tmp = cx_read(PLL_D_INT_FRAC);
+	cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF);
+
+	/* This selects the PLL C clock source for the video upstream channel
+	 * I and J */
+	tmp = cx_read(VID_CH_CLK_SEL);
+	cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000);
+
+	/* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+	 * channel A-C
+	 * select 656/VIP DST for downstream Channel A - C */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	/* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */
+	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+
+	/* enables 656 port I and J as output */
+	tmp = cx_read(CLK_RST);
+	/* use external ALT_PLL_REF pin as its reference clock instead */
+	tmp |= FLD_USE_ALT_PLL_REF;
+	cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE));
+
+	mdelay(100);
+}
+
+int cx25821_sram_channel_setup(struct cx25821_dev *dev,
+			       struct sram_channel *ch,
+			       unsigned int bpl, u32 risc)
+{
+	unsigned int i, lines;
+	u32 cdt;
+
+	if (ch->cmds_start == 0) {
+		cx_write(ch->ptr1_reg, 0);
+		cx_write(ch->ptr2_reg, 0);
+		cx_write(ch->cnt2_reg, 0);
+		cx_write(ch->cnt1_reg, 0);
+		return 0;
+	}
+
+	bpl = (bpl + 7) & ~7;	/* alignment */
+	cdt = ch->cdt;
+	lines = ch->fifo_size / bpl;
+
+	if (lines > 4)
+		lines = 4;
+
+	BUG_ON(lines < 2);
+
+	cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+	cx_write(8 + 4, 8);
+	cx_write(8 + 8, 0);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++) {
+		cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+		cx_write(cdt + 16 * i + 4, 0);
+		cx_write(cdt + 16 * i + 8, 0);
+		cx_write(cdt + 16 * i + 12, 0);
+	}
+
+	/* init the first cdt buffer */
+	for (i = 0; i < 128; i++)
+		cx_write(ch->fifo_start + 4 * i, i);
+
+	/* write CMDS */
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 0, 8);
+	else
+		cx_write(ch->cmds_start + 0, risc);
+
+	cx_write(ch->cmds_start + 4, 0);	/* 64 bits 63-32 */
+	cx_write(ch->cmds_start + 8, cdt);
+	cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+	cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+	else
+		cx_write(ch->cmds_start + 20, 64 >> 2);
+
+	for (i = 24; i < 80; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cx25821_sram_channel_setup);
+
+int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
+				     struct sram_channel *ch,
+				     unsigned int bpl, u32 risc)
+{
+	unsigned int i, lines;
+	u32 cdt;
+
+	if (ch->cmds_start == 0) {
+		cx_write(ch->ptr1_reg, 0);
+		cx_write(ch->ptr2_reg, 0);
+		cx_write(ch->cnt2_reg, 0);
+		cx_write(ch->cnt1_reg, 0);
+		return 0;
+	}
+
+	bpl = (bpl + 7) & ~7;	/* alignment */
+	cdt = ch->cdt;
+	lines = ch->fifo_size / bpl;
+
+	if (lines > 3)
+		lines = 3;	/* for AUDIO */
+
+	BUG_ON(lines < 2);
+
+	cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+	cx_write(8 + 4, 8);
+	cx_write(8 + 8, 0);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++) {
+		cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+		cx_write(cdt + 16 * i + 4, 0);
+		cx_write(cdt + 16 * i + 8, 0);
+		cx_write(cdt + 16 * i + 12, 0);
+	}
+
+	/* write CMDS */
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 0, 8);
+	else
+		cx_write(ch->cmds_start + 0, risc);
+
+	cx_write(ch->cmds_start + 4, 0);	/* 64 bits 63-32 */
+	cx_write(ch->cmds_start + 8, cdt);
+	cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+	cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+	/* IQ size */
+	if (ch->jumponly)
+		cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+	else
+		cx_write(ch->cmds_start + 20, 64 >> 2);
+
+	/* zero out */
+	for (i = 24; i < 80; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cx25821_sram_channel_setup_audio);
+
+void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch)
+{
+	static char *name[] = {
+		"init risc lo",
+		"init risc hi",
+		"cdt base",
+		"cdt size",
+		"iq base",
+		"iq size",
+		"risc pc lo",
+		"risc pc hi",
+		"iq wr ptr",
+		"iq rd ptr",
+		"cdt current",
+		"pci target lo",
+		"pci target hi",
+		"line / byte",
+	};
+	u32 risc;
+	unsigned int i, j, n;
+
+	pr_warn("%s: %s - dma channel status dump\n", dev->name, ch->name);
+	for (i = 0; i < ARRAY_SIZE(name); i++)
+		pr_warn("cmds + 0x%2x:   %-15s: 0x%08x\n",
+			i * 4, name[i], cx_read(ch->cmds_start + 4 * i));
+
+	j = i * 4;
+	for (i = 0; i < 4;) {
+		risc = cx_read(ch->cmds_start + 4 * (i + 14));
+		pr_warn("cmds + 0x%2x:   risc%d: ", j + i * 4, i);
+		i += cx25821_risc_decode(risc);
+	}
+
+	for (i = 0; i < (64 >> 2); i += n) {
+		risc = cx_read(ch->ctrl_start + 4 * i);
+		/* No consideration for bits 63-32 */
+
+		pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
+			i * 4, ch->ctrl_start + 4 * i, i);
+		n = cx25821_risc_decode(risc);
+		for (j = 1; j < n; j++) {
+			risc = cx_read(ch->ctrl_start + 4 * (i + j));
+			pr_warn("ctrl + 0x%2x :   iq %x: 0x%08x [ arg #%d ]\n",
+				4 * (i + j), i + j, risc, j);
+		}
+	}
+
+	pr_warn("        :   fifo: 0x%08x -> 0x%x\n",
+		ch->fifo_start, ch->fifo_start + ch->fifo_size);
+	pr_warn("        :   ctrl: 0x%08x -> 0x%x\n",
+		ch->ctrl_start, ch->ctrl_start + 6 * 16);
+	pr_warn("        :   ptr1_reg: 0x%08x\n",
+		cx_read(ch->ptr1_reg));
+	pr_warn("        :   ptr2_reg: 0x%08x\n",
+		cx_read(ch->ptr2_reg));
+	pr_warn("        :   cnt1_reg: 0x%08x\n",
+		cx_read(ch->cnt1_reg));
+	pr_warn("        :   cnt2_reg: 0x%08x\n",
+		cx_read(ch->cnt2_reg));
+}
+EXPORT_SYMBOL(cx25821_sram_channel_dump);
+
+void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
+				     struct sram_channel *ch)
+{
+	static const char * const name[] = {
+		"init risc lo",
+		"init risc hi",
+		"cdt base",
+		"cdt size",
+		"iq base",
+		"iq size",
+		"risc pc lo",
+		"risc pc hi",
+		"iq wr ptr",
+		"iq rd ptr",
+		"cdt current",
+		"pci target lo",
+		"pci target hi",
+		"line / byte",
+	};
+
+	u32 risc, value, tmp;
+	unsigned int i, j, n;
+
+	pr_info("\n%s: %s - dma Audio channel status dump\n",
+		dev->name, ch->name);
+
+	for (i = 0; i < ARRAY_SIZE(name); i++)
+		pr_info("%s: cmds + 0x%2x:   %-15s: 0x%08x\n",
+			dev->name, i * 4, name[i],
+			cx_read(ch->cmds_start + 4 * i));
+
+	j = i * 4;
+	for (i = 0; i < 4;) {
+		risc = cx_read(ch->cmds_start + 4 * (i + 14));
+		pr_warn("cmds + 0x%2x:   risc%d: ", j + i * 4, i);
+		i += cx25821_risc_decode(risc);
+	}
+
+	for (i = 0; i < (64 >> 2); i += n) {
+		risc = cx_read(ch->ctrl_start + 4 * i);
+		/* No consideration for bits 63-32 */
+
+		pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
+			i * 4, ch->ctrl_start + 4 * i, i);
+		n = cx25821_risc_decode(risc);
+
+		for (j = 1; j < n; j++) {
+			risc = cx_read(ch->ctrl_start + 4 * (i + j));
+			pr_warn("ctrl + 0x%2x :   iq %x: 0x%08x [ arg #%d ]\n",
+				4 * (i + j), i + j, risc, j);
+		}
+	}
+
+	pr_warn("        :   fifo: 0x%08x -> 0x%x\n",
+		ch->fifo_start, ch->fifo_start + ch->fifo_size);
+	pr_warn("        :   ctrl: 0x%08x -> 0x%x\n",
+		ch->ctrl_start, ch->ctrl_start + 6 * 16);
+	pr_warn("        :   ptr1_reg: 0x%08x\n",
+		cx_read(ch->ptr1_reg));
+	pr_warn("        :   ptr2_reg: 0x%08x\n",
+		cx_read(ch->ptr2_reg));
+	pr_warn("        :   cnt1_reg: 0x%08x\n",
+		cx_read(ch->cnt1_reg));
+	pr_warn("        :   cnt2_reg: 0x%08x\n",
+		cx_read(ch->cnt2_reg));
+
+	for (i = 0; i < 4; i++) {
+		risc = cx_read(ch->cmds_start + 56 + (i * 4));
+		pr_warn("instruction %d = 0x%x\n", i, risc);
+	}
+
+	/* read data from the first cdt buffer */
+	risc = cx_read(AUD_A_CDT);
+	pr_warn("\nread cdt loc=0x%x\n", risc);
+	for (i = 0; i < 8; i++) {
+		n = cx_read(risc + i * 4);
+		pr_cont("0x%x ", n);
+	}
+	pr_cont("\n\n");
+
+	value = cx_read(CLK_RST);
+	CX25821_INFO(" CLK_RST = 0x%x\n\n", value);
+
+	value = cx_read(PLL_A_POST_STAT_BIST);
+	CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value);
+	value = cx_read(PLL_A_INT_FRAC);
+	CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value);
+
+	value = cx_read(PLL_B_POST_STAT_BIST);
+	CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value);
+	value = cx_read(PLL_B_INT_FRAC);
+	CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value);
+
+	value = cx_read(PLL_C_POST_STAT_BIST);
+	CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value);
+	value = cx_read(PLL_C_INT_FRAC);
+	CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value);
+
+	value = cx_read(PLL_D_POST_STAT_BIST);
+	CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value);
+	value = cx_read(PLL_D_INT_FRAC);
+	CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value);
+
+	value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
+	CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value);
+}
+EXPORT_SYMBOL(cx25821_sram_channel_dump_audio);
+
+static void cx25821_shutdown(struct cx25821_dev *dev)
+{
+	int i;
+
+	/* disable RISC controller */
+	cx_write(DEV_CNTRL2, 0);
+
+	/* Disable Video A/B activity */
+	for (i = 0; i < VID_CHANNEL_NUM; i++) {
+		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+		cx_write(dev->channels[i].sram_channels->int_msk, 0);
+	}
+
+	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+		cx_write(dev->channels[i].sram_channels->int_msk, 0);
+	}
+
+	/* Disable Audio activity */
+	cx_write(AUD_INT_DMA_CTL, 0);
+
+	/* Disable Serial port */
+	cx_write(UART_CTL, 0);
+
+	/* Disable Interrupts */
+	cx_write(PCI_INT_MSK, 0);
+	cx_write(AUD_A_INT_MSK, 0);
+}
+
+void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select,
+			      u32 format)
+{
+	if (channel_select <= 7 && channel_select >= 0) {
+		cx_write(dev->channels[channel_select].sram_channels->pix_frmt,
+				format);
+		dev->channels[channel_select].pixel_formats = format;
+	}
+}
+
+static void cx25821_set_vip_mode(struct cx25821_dev *dev,
+				 struct sram_channel *ch)
+{
+	cx_write(ch->pix_frmt, PIXEL_FRMT_422);
+	cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1);
+}
+
+static void cx25821_initialize(struct cx25821_dev *dev)
+{
+	int i;
+
+	dprintk(1, "%s()\n", __func__);
+
+	cx25821_shutdown(dev);
+	cx_write(PCI_INT_STAT, 0xffffffff);
+
+	for (i = 0; i < VID_CHANNEL_NUM; i++)
+		cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+
+	cx_write(AUD_A_INT_STAT, 0xffffffff);
+	cx_write(AUD_B_INT_STAT, 0xffffffff);
+	cx_write(AUD_C_INT_STAT, 0xffffffff);
+	cx_write(AUD_D_INT_STAT, 0xffffffff);
+	cx_write(AUD_E_INT_STAT, 0xffffffff);
+
+	cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
+	cx_write(PAD_CTRL, 0x12);	/* for I2C */
+	cx25821_registers_init(dev);	/* init Pecos registers */
+	mdelay(100);
+
+	for (i = 0; i < VID_CHANNEL_NUM; i++) {
+		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+		cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels,
+						1440, 0);
+		dev->channels[i].pixel_formats = PIXEL_FRMT_422;
+		dev->channels[i].use_cif_resolution = FALSE;
+	}
+
+	/* Probably only affect Downstream */
+	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+	}
+
+	cx25821_sram_channel_setup_audio(dev,
+			dev->channels[SRAM_CH08].sram_channels, 128, 0);
+
+	cx25821_gpio_init(dev);
+}
+
+static int cx25821_get_resources(struct cx25821_dev *dev)
+{
+	if (request_mem_region(pci_resource_start(dev->pci, 0),
+				pci_resource_len(dev->pci, 0), dev->name))
+		return 0;
+
+	pr_err("%s: can't get MMIO memory @ 0x%llx\n",
+		dev->name, (unsigned long long)pci_resource_start(dev->pci, 0));
+
+	return -EBUSY;
+}
+
+static void cx25821_dev_checkrevision(struct cx25821_dev *dev)
+{
+	dev->hwrevision = cx_read(RDR_CFG2) & 0xff;
+
+	pr_info("%s(): Hardware revision = 0x%02x\n",
+		__func__, dev->hwrevision);
+}
+
+static void cx25821_iounmap(struct cx25821_dev *dev)
+{
+	if (dev == NULL)
+		return;
+
+	/* Releasing IO memory */
+	if (dev->lmmio != NULL) {
+		CX25821_INFO("Releasing lmmio.\n");
+		iounmap(dev->lmmio);
+		dev->lmmio = NULL;
+	}
+}
+
+static int cx25821_dev_setup(struct cx25821_dev *dev)
+{
+	int i;
+
+	pr_info("\n***********************************\n");
+	pr_info("cx25821 set up\n");
+	pr_info("***********************************\n\n");
+
+	mutex_init(&dev->lock);
+
+	atomic_inc(&dev->refcount);
+
+	dev->nr = ++cx25821_devcount;
+	sprintf(dev->name, "cx25821[%d]", dev->nr);
+
+	mutex_lock(&cx25821_devlist_mutex);
+	list_add_tail(&dev->devlist, &cx25821_devlist);
+	mutex_unlock(&cx25821_devlist_mutex);
+
+	if (dev->pci->device != 0x8210) {
+		pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
+			__func__, dev->pci->device);
+		return -1;
+	} else {
+		pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device);
+	}
+
+	/* Apply a sensible clock frequency for the PCIe bridge */
+	dev->clk_freq = 28000000;
+	for (i = 0; i < MAX_VID_CHANNEL_NUM; i++)
+		dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+
+	if (dev->nr > 1)
+		CX25821_INFO("dev->nr > 1!");
+
+	/* board config */
+	dev->board = 1;		/* card[dev->nr]; */
+	dev->_max_num_decoders = MAX_DECODERS;
+
+	dev->pci_bus = dev->pci->bus->number;
+	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+	dev->pci_irqmask = 0x001f00;
+
+	/* External Master 1 Bus */
+	dev->i2c_bus[0].nr = 0;
+	dev->i2c_bus[0].dev = dev;
+	dev->i2c_bus[0].reg_stat = I2C1_STAT;
+	dev->i2c_bus[0].reg_ctrl = I2C1_CTRL;
+	dev->i2c_bus[0].reg_addr = I2C1_ADDR;
+	dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
+	dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
+	dev->i2c_bus[0].i2c_period = (0x07 << 24);	/* 1.95MHz */
+
+	if (cx25821_get_resources(dev) < 0) {
+		pr_err("%s: No more PCIe resources for subsystem: %04x:%04x\n",
+		       dev->name, dev->pci->subsystem_vendor,
+		       dev->pci->subsystem_device);
+
+		cx25821_devcount--;
+		return -EBUSY;
+	}
+
+	/* PCIe stuff */
+	dev->base_io_addr = pci_resource_start(dev->pci, 0);
+
+	if (!dev->base_io_addr) {
+		CX25821_ERR("No PCI Memory resources, exiting!\n");
+		return -ENODEV;
+	}
+
+	dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0));
+
+	if (!dev->lmmio) {
+		CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n");
+		cx25821_iounmap(dev);
+		return -ENOMEM;
+	}
+
+	dev->bmmio = (u8 __iomem *) dev->lmmio;
+
+	pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+		dev->name, dev->pci->subsystem_vendor,
+		dev->pci->subsystem_device, cx25821_boards[dev->board].name,
+		dev->board, card[dev->nr] == dev->board ?
+		"insmod option" : "autodetected");
+
+	/* init hardware */
+	cx25821_initialize(dev);
+
+	cx25821_i2c_register(&dev->i2c_bus[0]);
+/*  cx25821_i2c_register(&dev->i2c_bus[1]);
+ *  cx25821_i2c_register(&dev->i2c_bus[2]); */
+
+	CX25821_INFO("i2c register! bus->i2c_rc = %d\n",
+			dev->i2c_bus[0].i2c_rc);
+
+	cx25821_card_setup(dev);
+
+	if (medusa_video_init(dev) < 0)
+		CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__);
+
+	cx25821_video_register(dev);
+
+	/* register IOCTL device */
+	dev->ioctl_dev = cx25821_vdev_init(dev, dev->pci,
+			&cx25821_videoioctl_template, "video");
+
+	if (video_register_device
+	    (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) {
+		cx25821_videoioctl_unregister(dev);
+		pr_err("%s(): Failed to register video adapter for IOCTL, so unregistering videoioctl device\n",
+		       __func__);
+	}
+
+	cx25821_dev_checkrevision(dev);
+	CX25821_INFO("setup done!\n");
+
+	return 0;
+}
+
+void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev,
+				      struct upstream_user_struct *up_data)
+{
+	dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0;
+
+	dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
+	medusa_set_videostandard(dev);
+
+	cx25821_vidupstream_init_ch1(dev, dev->channel_select,
+				     dev->pixel_format);
+}
+
+void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev,
+				      struct upstream_user_struct *up_data)
+{
+	dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0;
+
+	dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
+	medusa_set_videostandard(dev);
+
+	cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2,
+				     dev->pixel_format_ch2);
+}
+
+void cx25821_start_upstream_audio(struct cx25821_dev *dev,
+				  struct upstream_user_struct *up_data)
+{
+	cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B);
+}
+
+void cx25821_dev_unregister(struct cx25821_dev *dev)
+{
+	int i;
+
+	if (!dev->base_io_addr)
+		return;
+
+	cx25821_free_mem_upstream_ch1(dev);
+	cx25821_free_mem_upstream_ch2(dev);
+	cx25821_free_mem_upstream_audio(dev);
+
+	release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0));
+
+	if (!atomic_dec_and_test(&dev->refcount))
+		return;
+
+	for (i = 0; i < VID_CHANNEL_NUM; i++)
+		cx25821_video_unregister(dev, i);
+
+	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+	     i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) {
+		cx25821_video_unregister(dev, i);
+	}
+
+	cx25821_videoioctl_unregister(dev);
+
+	cx25821_i2c_unregister(&dev->i2c_bus[0]);
+	cx25821_iounmap(dev);
+}
+EXPORT_SYMBOL(cx25821_dev_unregister);
+
+static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist,
+				  unsigned int offset, u32 sync_line,
+				  unsigned int bpl, unsigned int padding,
+				  unsigned int lines)
+{
+	struct scatterlist *sg;
+	unsigned int line, todo;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < lines; line++) {
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+		if (bpl <= sg_dma_len(sg) - offset) {
+			/* fits into current chunk */
+			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL |
+					bpl);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			offset += bpl;
+		} else {
+			/* scanline needs to be split */
+			todo = bpl;
+			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL |
+					(sg_dma_len(sg) - offset));
+			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			todo -= (sg_dma_len(sg) - offset);
+			offset = 0;
+			sg++;
+			while (todo > sg_dma_len(sg)) {
+				*(rp++) = cpu_to_le32(RISC_WRITE |
+						sg_dma_len(sg));
+				*(rp++) = cpu_to_le32(sg_dma_address(sg));
+				*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg));
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			offset += todo;
+		}
+
+		offset += padding;
+	}
+
+	return rp;
+}
+
+int cx25821_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)
+{
+	u32 instructions;
+	u32 fields;
+	__le32 *rp;
+	int rc;
+
+	fields = 0;
+	if (UNSET != top_offset)
+		fields++;
+	if (UNSET != bottom_offset)
+		fields++;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Padding
+	   can cause next bpl to start close to a page border.  First DMA
+	   region may be smaller than PAGE_SIZE */
+	/* write and jump need and extra dword */
+	instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE +
+			lines);
+	instructions += 2;
+	rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+
+	if (UNSET != top_offset) {
+		rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding,
+					lines);
+	}
+
+	if (UNSET != bottom_offset) {
+		rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl,
+					padding, lines);
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+
+	return 0;
+}
+
+static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist,
+					unsigned int offset, u32 sync_line,
+					unsigned int bpl, unsigned int padding,
+					unsigned int lines, unsigned int lpi)
+{
+	struct scatterlist *sg;
+	unsigned int line, todo, sol;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < lines; line++) {
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+
+		if (lpi && line > 0 && !(line % lpi))
+			sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+		else
+			sol = RISC_SOL;
+
+		if (bpl <= sg_dma_len(sg) - offset) {
+			/* fits into current chunk */
+			*(rp++) = cpu_to_le32(RISC_WRITE | sol | RISC_EOL |
+					bpl);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			offset += bpl;
+		} else {
+			/* scanline needs to be split */
+			todo = bpl;
+			*(rp++) = cpu_to_le32(RISC_WRITE | sol |
+					(sg_dma_len(sg) - offset));
+			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			todo -= (sg_dma_len(sg) - offset);
+			offset = 0;
+			sg++;
+			while (todo > sg_dma_len(sg)) {
+				*(rp++) = cpu_to_le32(RISC_WRITE |
+						sg_dma_len(sg));
+				*(rp++) = cpu_to_le32(sg_dma_address(sg));
+				*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
+			*(rp++) = cpu_to_le32(sg_dma_address(sg));
+			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+			offset += todo;
+		}
+		offset += padding;
+	}
+
+	return rp;
+}
+
+int cx25821_risc_databuffer_audio(struct pci_dev *pci,
+				  struct btcx_riscmem *risc,
+				  struct scatterlist *sglist,
+				  unsigned int bpl,
+				  unsigned int lines, unsigned int lpi)
+{
+	u32 instructions;
+	__le32 *rp;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Here
+	   there is no padding and no sync.  First DMA region may be smaller
+	   than PAGE_SIZE */
+	/* Jump and write need an extra dword */
+	instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
+	instructions += 1;
+
+	rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	rp = cx25821_risc_field_audio(rp, sglist, 0, NO_SYNC_LINE, bpl, 0,
+				      lines, lpi);
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	return 0;
+}
+EXPORT_SYMBOL(cx25821_risc_databuffer_audio);
+
+int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+			 u32 reg, u32 mask, u32 value)
+{
+	__le32 *rp;
+	int rc;
+
+	rc = btcx_riscmem_alloc(pci, risc, 4 * 16);
+
+	if (rc < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+
+	*(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1);
+	*(rp++) = cpu_to_le32(reg);
+	*(rp++) = cpu_to_le32(value);
+	*(rp++) = cpu_to_le32(mask);
+	*(rp++) = cpu_to_le32(RISC_JUMP);
+	*(rp++) = cpu_to_le32(risc->dma);
+	*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+	return 0;
+}
+
+void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf)
+{
+	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+	BUG_ON(in_interrupt());
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_dma_unmap(q->dev, dma);
+	videobuf_dma_free(dma);
+	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static irqreturn_t cx25821_irq(int irq, void *dev_id)
+{
+	struct cx25821_dev *dev = dev_id;
+	u32 pci_status;
+	u32 vid_status;
+	int i, handled = 0;
+	u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+	pci_status = cx_read(PCI_INT_STAT);
+
+	if (pci_status == 0)
+		goto out;
+
+	for (i = 0; i < VID_CHANNEL_NUM; i++) {
+		if (pci_status & mask[i]) {
+			vid_status = cx_read(dev->channels[i].
+				sram_channels->int_stat);
+
+			if (vid_status)
+				handled += cx25821_video_irq(dev, i,
+						vid_status);
+
+			cx_write(PCI_INT_STAT, mask[i]);
+		}
+	}
+
+out:
+	return IRQ_RETVAL(handled);
+}
+
+void cx25821_print_irqbits(char *name, char *tag, char **strings,
+			   int len, u32 bits, u32 mask)
+{
+	unsigned int i;
+
+	printk(KERN_DEBUG pr_fmt("%s: %s [0x%x]"), name, tag, bits);
+
+	for (i = 0; i < len; i++) {
+		if (!(bits & (1 << i)))
+			continue;
+		if (strings[i])
+			pr_cont(" %s", strings[i]);
+		else
+			pr_cont(" %d", i);
+		if (!(mask & (1 << i)))
+			continue;
+		pr_cont("*");
+	}
+	pr_cont("\n");
+}
+EXPORT_SYMBOL(cx25821_print_irqbits);
+
+struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci)
+{
+	struct cx25821_dev *dev = pci_get_drvdata(pci);
+	return dev;
+}
+EXPORT_SYMBOL(cx25821_dev_get);
+
+static int __devinit cx25821_initdev(struct pci_dev *pci_dev,
+				     const struct pci_device_id *pci_id)
+{
+	struct cx25821_dev *dev;
+	int err = 0;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+
+	err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+	if (err < 0)
+		goto fail_free;
+
+	/* pci init */
+	dev->pci = pci_dev;
+	if (pci_enable_device(pci_dev)) {
+		err = -EIO;
+
+		pr_info("pci enable failed!\n");
+
+		goto fail_unregister_device;
+	}
+
+	pr_info("Athena pci enable !\n");
+
+	err = cx25821_dev_setup(dev);
+	if (err) {
+		if (err == -EBUSY)
+			goto fail_unregister_device;
+		else
+			goto fail_unregister_pci;
+	}
+
+	/* print pci info */
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+	pr_info("%s/0: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
+		dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+		dev->pci_lat, (unsigned long long)dev->base_io_addr);
+
+	pci_set_master(pci_dev);
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		pr_err("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+		err = -EIO;
+		goto fail_irq;
+	}
+
+	err = request_irq(pci_dev->irq, cx25821_irq,
+			IRQF_SHARED, dev->name, dev);
+
+	if (err < 0) {
+		pr_err("%s: can't get IRQ %d\n", dev->name, pci_dev->irq);
+		goto fail_irq;
+	}
+
+	return 0;
+
+fail_irq:
+	pr_info("cx25821_initdev() can't get IRQ !\n");
+	cx25821_dev_unregister(dev);
+
+fail_unregister_pci:
+	pci_disable_device(pci_dev);
+fail_unregister_device:
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+fail_free:
+	kfree(dev);
+	return err;
+}
+
+static void __devexit cx25821_finidev(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct cx25821_dev *dev = get_cx25821(v4l2_dev);
+
+	cx25821_shutdown(dev);
+	pci_disable_device(pci_dev);
+
+	/* unregister stuff */
+	if (pci_dev->irq)
+		free_irq(pci_dev->irq, dev);
+
+	mutex_lock(&cx25821_devlist_mutex);
+	list_del(&dev->devlist);
+	mutex_unlock(&cx25821_devlist_mutex);
+
+	cx25821_dev_unregister(dev);
+	v4l2_device_unregister(v4l2_dev);
+	kfree(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = {
+	{
+		/* CX25821 Athena */
+		.vendor = 0x14f1,
+		.device = 0x8210,
+		.subvendor = 0x14f1,
+		.subdevice = 0x0920,
+	}, {
+		/* CX25821 No Brand */
+		.vendor = 0x14f1,
+		.device = 0x8210,
+		.subvendor = 0x0000,
+		.subdevice = 0x0000,
+	}, {
+		/* --- end of list --- */
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl);
+
+static struct pci_driver cx25821_pci_driver = {
+	.name = "cx25821",
+	.id_table = cx25821_pci_tbl,
+	.probe = cx25821_initdev,
+	.remove = __devexit_p(cx25821_finidev),
+	/* TODO */
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+static int __init cx25821_init(void)
+{
+	pr_info("driver version %d.%d.%d loaded\n",
+		(CX25821_VERSION_CODE >> 16) & 0xff,
+		(CX25821_VERSION_CODE >> 8) & 0xff,
+		CX25821_VERSION_CODE & 0xff);
+	return pci_register_driver(&cx25821_pci_driver);
+}
+
+static void __exit cx25821_fini(void)
+{
+	pci_unregister_driver(&cx25821_pci_driver);
+}
+
+module_init(cx25821_init);
+module_exit(cx25821_fini);
diff --git a/drivers/media/pci/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c
new file mode 100644
index 000000000000..29e43b03c85e
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-gpio.c
@@ -0,0 +1,98 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 "cx25821.h"
+
+/********************* GPIO stuffs *********************/
+void cx25821_set_gpiopin_direction(struct cx25821_dev *dev,
+				   int pin_number, int pin_logic_value)
+{
+	int bit = pin_number;
+	u32 gpio_oe_reg = GPIO_LO_OE;
+	u32 gpio_register = 0;
+	u32 value = 0;
+
+	/* Check for valid pinNumber */
+	if (pin_number >= 47)
+		return;
+
+	if (pin_number > 31) {
+		bit = pin_number - 31;
+		gpio_oe_reg = GPIO_HI_OE;
+	}
+	/* Here we will make sure that the GPIOs 0 and 1 are output. keep the
+	 * rest as is */
+	gpio_register = cx_read(gpio_oe_reg);
+
+	if (pin_logic_value == 1)
+		value = gpio_register | Set_GPIO_Bit(bit);
+	else
+		value = gpio_register & Clear_GPIO_Bit(bit);
+
+	cx_write(gpio_oe_reg, value);
+}
+EXPORT_SYMBOL(cx25821_set_gpiopin_direction);
+
+static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev,
+					   int pin_number, int pin_logic_value)
+{
+	int bit = pin_number;
+	u32 gpio_reg = GPIO_LO;
+	u32 value = 0;
+
+	/* Check for valid pinNumber */
+	if (pin_number >= 47)
+		return;
+
+	/* change to output direction */
+	cx25821_set_gpiopin_direction(dev, pin_number, 0);
+
+	if (pin_number > 31) {
+		bit = pin_number - 31;
+		gpio_reg = GPIO_HI;
+	}
+
+	value = cx_read(gpio_reg);
+
+	if (pin_logic_value == 0)
+		value &= Clear_GPIO_Bit(bit);
+	else
+		value |= Set_GPIO_Bit(bit);
+
+	cx_write(gpio_reg, value);
+}
+
+void cx25821_gpio_init(struct cx25821_dev *dev)
+{
+	if (dev == NULL)
+		return;
+
+	switch (dev->board) {
+	case CX25821_BOARD_CONEXANT_ATHENA10:
+	default:
+		/* set GPIO 5 to select the path for Medusa/Athena */
+		cx25821_set_gpiopin_logicvalue(dev, 5, 1);
+		mdelay(20);
+		break;
+	}
+
+}
diff --git a/drivers/media/pci/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c
new file mode 100644
index 000000000000..9844549764c9
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-i2c.c
@@ -0,0 +1,416 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *	Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821.h"
+#include <linux/i2c.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#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
+
+#define I2C_EXTEND  (1 << 3)
+#define I2C_NOSTOP  (1 << 4)
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	return cx_read(bus->reg_stat) & 0x01;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+	int count;
+
+	for (count = 0; count < I2C_WAIT_RETRY; count++) {
+		if (!i2c_is_busy(i2c_adap))
+			break;
+		udelay(I2C_WAIT_DELAY);
+	}
+
+	if (I2C_WAIT_RETRY == count)
+		return 0;
+
+	return 1;
+}
+
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+			 const struct i2c_msg *msg, int joined_rlen)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	u32 wdata, addr, ctrl;
+	int retval, cnt;
+
+	if (joined_rlen)
+		dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__,
+			msg->len, joined_rlen);
+	else
+		dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+	/* Deal with i2c probe functions with zero payload */
+	if (msg->len == 0) {
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
+
+		if (!i2c_wait_done(i2c_adap))
+			return -EIO;
+
+		if (!i2c_slave_did_ack(i2c_adap))
+			return -EIO;
+
+		dprintk(1, "%s(): returns 0\n", __func__);
+		return 0;
+	}
+
+	/* dev, reg + first byte */
+	addr = (msg->addr << 25) | msg->buf[0];
+	wdata = msg->buf[0];
+
+	ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+	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);
+	cx_write(bus->reg_ctrl, ctrl);
+
+	retval = i2c_wait_done(i2c_adap);
+	if (retval < 0)
+		goto err;
+
+	if (retval == 0)
+		goto eio;
+
+	if (i2c_debug) {
+		if (!(ctrl & I2C_NOSTOP))
+			printk(" >\n");
+	}
+
+	for (cnt = 1; cnt < msg->len; cnt++) {
+		/* following bytes */
+		wdata = msg->buf[cnt];
+		ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+		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);
+		cx_write(bus->reg_ctrl, ctrl);
+
+		retval = i2c_wait_done(i2c_adap);
+		if (retval < 0)
+			goto err;
+
+		if (retval == 0)
+			goto eio;
+
+		if (i2c_debug) {
+			dprintk(1, " %02x", msg->buf[cnt]);
+			if (!(ctrl & I2C_NOSTOP))
+				dprintk(1, " >\n");
+		}
+	}
+
+	return msg->len;
+
+eio:
+	retval = -EIO;
+err:
+	if (i2c_debug)
+		pr_err(" ERR: %d\n", retval);
+	return retval;
+}
+
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+			 const struct i2c_msg *msg, int joined)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	u32 ctrl, cnt;
+	int retval;
+
+	if (i2c_debug && !joined)
+		dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len);
+
+	/* Deal with i2c probe functions with zero payload */
+	if (msg->len == 0) {
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
+		if (!i2c_wait_done(i2c_adap))
+			return -EIO;
+		if (!i2c_slave_did_ack(i2c_adap))
+			return -EIO;
+
+		dprintk(1, "%s(): returns 0\n", __func__);
+		return 0;
+	}
+
+	if (i2c_debug) {
+		if (joined)
+			dprintk(1, " R");
+		else
+			dprintk(1, " <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)
+			ctrl |= I2C_NOSTOP | I2C_EXTEND;
+
+		cx_write(bus->reg_addr, msg->addr << 25);
+		cx_write(bus->reg_ctrl, ctrl);
+
+		retval = i2c_wait_done(i2c_adap);
+		if (retval < 0)
+			goto err;
+		if (retval == 0)
+			goto eio;
+		msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
+
+		if (i2c_debug) {
+			dprintk(1, " %02x", msg->buf[cnt]);
+			if (!(ctrl & I2C_NOSTOP))
+				dprintk(1, " >\n");
+		}
+	}
+
+	return msg->len;
+eio:
+	retval = -EIO;
+err:
+	if (i2c_debug)
+		pr_err(" ERR: %d\n", retval);
+	return retval;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct cx25821_i2c *bus = i2c_adap->algo_data;
+	struct cx25821_dev *dev = bus->dev;
+	int i, retval = 0;
+
+	dprintk(1, "%s(num = %d)\n", __func__, num);
+
+	for (i = 0; i < num; i++) {
+		dprintk(1, "%s(num = %d) addr = 0x%02x  len = 0x%x\n",
+			__func__, num, msgs[i].addr, msgs[i].len);
+
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read */
+			retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+		} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+			   msgs[i].addr == msgs[i + 1].addr) {
+			/* write then read from same address */
+			retval = i2c_sendbytes(i2c_adap, &msgs[i],
+					msgs[i + 1].len);
+
+			if (retval < 0)
+				goto err;
+			i++;
+			retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+		} else {
+			/* write */
+			retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+		}
+
+		if (retval < 0)
+			goto err;
+	}
+	return num;
+
+err:
+	return retval;
+}
+
+
+static u32 cx25821_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_WORD_DATA |
+		I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+}
+
+static struct i2c_algorithm cx25821_i2c_algo_template = {
+	.master_xfer = i2c_xfer,
+	.functionality = cx25821_functionality,
+#ifdef NEED_ALGO_CONTROL
+	.algo_control = dummy_algo_control,
+#endif
+};
+
+static struct i2c_adapter cx25821_i2c_adap_template = {
+	.name = "cx25821",
+	.owner = THIS_MODULE,
+	.algo = &cx25821_i2c_algo_template,
+};
+
+static struct i2c_client cx25821_i2c_client_template = {
+	.name = "cx25821 internal",
+};
+
+/* init + register i2c adapter */
+int cx25821_i2c_register(struct cx25821_i2c *bus)
+{
+	struct cx25821_dev *dev = bus->dev;
+
+	dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
+
+	bus->i2c_adap = cx25821_i2c_adap_template;
+	bus->i2c_client = cx25821_i2c_client_template;
+	bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+	strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
+
+	bus->i2c_adap.algo_data = bus;
+	i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+	i2c_add_adapter(&bus->i2c_adap);
+
+	bus->i2c_client.adapter = &bus->i2c_adap;
+
+	/* set up the I2c */
+	bus->i2c_client.addr = (0x88 >> 1);
+
+	return bus->i2c_rc;
+}
+
+int cx25821_i2c_unregister(struct cx25821_i2c *bus)
+{
+	i2c_del_adapter(&bus->i2c_adap);
+	return 0;
+}
+
+void cx25821_av_clk(struct cx25821_dev *dev, int enable)
+{
+	/* write 0 to bus 2 addr 0x144 via i2x_xfer() */
+	char buffer[3];
+	struct i2c_msg msg;
+	dprintk(1, "%s(enabled = %d)\n", __func__, enable);
+
+	/* Register 0x144 */
+	buffer[0] = 0x01;
+	buffer[1] = 0x44;
+	if (enable == 1)
+		buffer[2] = 0x05;
+	else
+		buffer[2] = 0x00;
+
+	msg.addr = 0x44;
+	msg.flags = I2C_M_TEN;
+	msg.len = 3;
+	msg.buf = buffer;
+
+	i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1);
+}
+
+int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
+{
+	struct i2c_client *client = &bus->i2c_client;
+	int v = 0;
+	u8 addr[2] = { 0, 0 };
+	u8 buf[4] = { 0, 0, 0, 0 };
+
+	struct i2c_msg msgs[2] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 2,
+			.buf = addr,
+		}, {
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = 4,
+			.buf = buf,
+		}
+	};
+
+	addr[0] = (reg_addr >> 8);
+	addr[1] = (reg_addr & 0xff);
+	msgs[0].addr = 0x44;
+	msgs[1].addr = 0x44;
+
+	i2c_xfer(client->adapter, msgs, 2);
+
+	v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+	*value = v;
+
+	return v;
+}
+
+int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value)
+{
+	struct i2c_client *client = &bus->i2c_client;
+	int retval = 0;
+	u8 buf[6] = { 0, 0, 0, 0, 0, 0 };
+
+	struct i2c_msg msgs[1] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 6,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg_addr >> 8;
+	buf[1] = reg_addr & 0xff;
+	buf[5] = (value >> 24) & 0xff;
+	buf[4] = (value >> 16) & 0xff;
+	buf[3] = (value >> 8) & 0xff;
+	buf[2] = value & 0xff;
+	client->flags = 0;
+	msgs[0].addr = 0x44;
+
+	retval = i2c_xfer(client->adapter, msgs, 1);
+
+	return retval;
+}
diff --git a/drivers/media/pci/cx25821/cx25821-medusa-defines.h b/drivers/media/pci/cx25821/cx25821-medusa-defines.h
new file mode 100644
index 000000000000..7a9e6470ba22
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-medusa-defines.h
@@ -0,0 +1,42 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 _MEDUSA_DEF_H_
+#define _MEDUSA_DEF_H_
+
+/* Video decoder that we supported */
+#define VDEC_A		0
+#define VDEC_B		1
+#define VDEC_C		2
+#define VDEC_D		3
+#define VDEC_E		4
+#define VDEC_F		5
+#define VDEC_G		6
+#define VDEC_H		7
+
+/* end of display sequence */
+#define END_OF_SEQ	0xF;
+
+/* registry string size */
+#define MAX_REGISTRY_SZ	40;
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-medusa-reg.h b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
new file mode 100644
index 000000000000..c98ac946b277
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
@@ -0,0 +1,455 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 __MEDUSA_REGISTERS__
+#define __MEDUSA_REGISTERS__
+
+/* Serial Slave Registers */
+#define	HOST_REGISTER1				0x0000
+#define	HOST_REGISTER2				0x0001
+
+/* Chip Configuration Registers */
+#define	CHIP_CTRL				0x0100
+#define	AFE_AB_CTRL				0x0104
+#define	AFE_CD_CTRL				0x0108
+#define	AFE_EF_CTRL				0x010C
+#define	AFE_GH_CTRL				0x0110
+#define	DENC_AB_CTRL				0x0114
+#define	BYP_AB_CTRL				0x0118
+#define	MON_A_CTRL				0x011C
+#define	DISP_SEQ_A				0x0120
+#define	DISP_SEQ_B				0x0124
+#define	DISP_AB_CNT				0x0128
+#define	DISP_CD_CNT				0x012C
+#define	DISP_EF_CNT				0x0130
+#define	DISP_GH_CNT				0x0134
+#define	DISP_IJ_CNT				0x0138
+#define	PIN_OE_CTRL				0x013C
+#define	PIN_SPD_CTRL				0x0140
+#define	PIN_SPD_CTRL2				0x0144
+#define	IRQ_STAT_CTRL				0x0148
+#define	POWER_CTRL_AB				0x014C
+#define	POWER_CTRL_CD				0x0150
+#define	POWER_CTRL_EF				0x0154
+#define	POWER_CTRL_GH				0x0158
+#define	TUNE_CTRL				0x015C
+#define	BIAS_CTRL				0x0160
+#define	AFE_AB_DIAG_CTRL			0x0164
+#define	AFE_CD_DIAG_CTRL			0x0168
+#define	AFE_EF_DIAG_CTRL			0x016C
+#define	AFE_GH_DIAG_CTRL			0x0170
+#define	PLL_AB_DIAG_CTRL			0x0174
+#define	PLL_CD_DIAG_CTRL			0x0178
+#define	PLL_EF_DIAG_CTRL			0x017C
+#define	PLL_GH_DIAG_CTRL			0x0180
+#define	TEST_CTRL				0x0184
+#define	BIST_STAT				0x0188
+#define	BIST_STAT2				0x018C
+#define	BIST_VID_PLL_AB_STAT			0x0190
+#define	BIST_VID_PLL_CD_STAT			0x0194
+#define	BIST_VID_PLL_EF_STAT			0x0198
+#define	BIST_VID_PLL_GH_STAT			0x019C
+#define	DLL_DIAG_CTRL				0x01A0
+#define	DEV_CH_ID_CTRL				0x01A4
+#define	ABIST_CTRL_STATUS			0x01A8
+#define	ABIST_FREQ				0x01AC
+#define	ABIST_GOERT_SHIFT			0x01B0
+#define	ABIST_COEF12				0x01B4
+#define	ABIST_COEF34				0x01B8
+#define	ABIST_COEF56				0x01BC
+#define	ABIST_COEF7_SNR				0x01C0
+#define	ABIST_ADC_CAL				0x01C4
+#define	ABIST_BIN1_VGA0				0x01C8
+#define	ABIST_BIN2_VGA1				0x01CC
+#define	ABIST_BIN3_VGA2				0x01D0
+#define	ABIST_BIN4_VGA3				0x01D4
+#define	ABIST_BIN5_VGA4				0x01D8
+#define	ABIST_BIN6_VGA5				0x01DC
+#define	ABIST_BIN7_VGA6				0x0x1E0
+#define	ABIST_CLAMP_A				0x0x1E4
+#define	ABIST_CLAMP_B				0x0x1E8
+#define	ABIST_CLAMP_C				0x01EC
+#define	ABIST_CLAMP_D				0x01F0
+#define	ABIST_CLAMP_E				0x01F4
+#define	ABIST_CLAMP_F				0x01F8
+
+/* Digital Video Encoder A Registers */
+#define	DENC_A_REG_1				0x0200
+#define	DENC_A_REG_2				0x0204
+#define	DENC_A_REG_3				0x0208
+#define	DENC_A_REG_4				0x020C
+#define	DENC_A_REG_5				0x0210
+#define	DENC_A_REG_6				0x0214
+#define	DENC_A_REG_7				0x0218
+#define	DENC_A_REG_8				0x021C
+
+/* Digital Video Encoder B Registers */
+#define	DENC_B_REG_1				0x0300
+#define	DENC_B_REG_2				0x0304
+#define	DENC_B_REG_3				0x0308
+#define	DENC_B_REG_4				0x030C
+#define	DENC_B_REG_5				0x0310
+#define	DENC_B_REG_6				0x0314
+#define	DENC_B_REG_7				0x0318
+#define	DENC_B_REG_8				0x031C
+
+/* Video Decoder A Registers */
+#define	MODE_CTRL				0x1000
+#define	OUT_CTRL1				0x1004
+#define	OUT_CTRL_NS				0x1008
+#define	GEN_STAT				0x100C
+#define	INT_STAT_MASK				0x1010
+#define	LUMA_CTRL				0x1014
+#define	CHROMA_CTRL				0x1018
+#define	CRUSH_CTRL				0x101C
+#define	HORIZ_TIM_CTRL				0x1020
+#define	VERT_TIM_CTRL				0x1024
+#define	MISC_TIM_CTRL				0x1028
+#define	FIELD_COUNT				0x102C
+#define	HSCALE_CTRL				0x1030
+#define	VSCALE_CTRL				0x1034
+#define	MAN_VGA_CTRL				0x1038
+#define	MAN_AGC_CTRL				0x103C
+#define	DFE_CTRL1				0x1040
+#define	DFE_CTRL2				0x1044
+#define	DFE_CTRL3				0x1048
+#define	PLL_CTRL				0x104C
+#define	PLL_CTRL_FAST				0x1050
+#define	HTL_CTRL				0x1054
+#define	SRC_CFG					0x1058
+#define	SC_STEP_SIZE				0x105C
+#define	SC_CONVERGE_CTRL			0x1060
+#define	SC_LOOP_CTRL				0x1064
+#define	COMB_2D_HFS_CFG				0x1068
+#define	COMB_2D_HFD_CFG				0x106C
+#define	COMB_2D_LF_CFG				0x1070
+#define	COMB_2D_BLEND				0x1074
+#define	COMB_MISC_CTRL				0x1078
+#define	COMB_FLAT_THRESH_CTRL			0x107C
+#define	COMB_TEST				0x1080
+#define	BP_MISC_CTRL				0x1084
+#define	VCR_DET_CTRL				0x1088
+#define	NOISE_DET_CTRL				0x108C
+#define	COMB_FLAT_NOISE_CTRL			0x1090
+#define	VERSION					0x11F8
+#define	SOFT_RST_CTRL				0x11FC
+
+/* Video Decoder B Registers */
+#define	VDEC_B_MODE_CTRL			0x1200
+#define	VDEC_B_OUT_CTRL1			0x1204
+#define	VDEC_B_OUT_CTRL_NS			0x1208
+#define	VDEC_B_GEN_STAT				0x120C
+#define	VDEC_B_INT_STAT_MASK			0x1210
+#define	VDEC_B_LUMA_CTRL			0x1214
+#define	VDEC_B_CHROMA_CTRL			0x1218
+#define	VDEC_B_CRUSH_CTRL			0x121C
+#define	VDEC_B_HORIZ_TIM_CTRL			0x1220
+#define	VDEC_B_VERT_TIM_CTRL			0x1224
+#define	VDEC_B_MISC_TIM_CTRL			0x1228
+#define	VDEC_B_FIELD_COUNT			0x122C
+#define	VDEC_B_HSCALE_CTRL			0x1230
+#define	VDEC_B_VSCALE_CTRL			0x1234
+#define	VDEC_B_MAN_VGA_CTRL			0x1238
+#define	VDEC_B_MAN_AGC_CTRL			0x123C
+#define	VDEC_B_DFE_CTRL1			0x1240
+#define	VDEC_B_DFE_CTRL2			0x1244
+#define	VDEC_B_DFE_CTRL3			0x1248
+#define	VDEC_B_PLL_CTRL				0x124C
+#define	VDEC_B_PLL_CTRL_FAST			0x1250
+#define	VDEC_B_HTL_CTRL				0x1254
+#define	VDEC_B_SRC_CFG				0x1258
+#define	VDEC_B_SC_STEP_SIZE			0x125C
+#define	VDEC_B_SC_CONVERGE_CTRL			0x1260
+#define	VDEC_B_SC_LOOP_CTRL			0x1264
+#define	VDEC_B_COMB_2D_HFS_CFG			0x1268
+#define	VDEC_B_COMB_2D_HFD_CFG			0x126C
+#define	VDEC_B_COMB_2D_LF_CFG			0x1270
+#define	VDEC_B_COMB_2D_BLEND			0x1274
+#define	VDEC_B_COMB_MISC_CTRL			0x1278
+#define	VDEC_B_COMB_FLAT_THRESH_CTRL		0x127C
+#define	VDEC_B_COMB_TEST			0x1280
+#define	VDEC_B_BP_MISC_CTRL			0x1284
+#define	VDEC_B_VCR_DET_CTRL			0x1288
+#define	VDEC_B_NOISE_DET_CTRL			0x128C
+#define	VDEC_B_COMB_FLAT_NOISE_CTRL		0x1290
+#define	VDEC_B_VERSION				0x13F8
+#define	VDEC_B_SOFT_RST_CTRL			0x13FC
+
+/* Video Decoder C Registers */
+#define	VDEC_C_MODE_CTRL			0x1400
+#define	VDEC_C_OUT_CTRL1			0x1404
+#define	VDEC_C_OUT_CTRL_NS			0x1408
+#define	VDEC_C_GEN_STAT				0x140C
+#define	VDEC_C_INT_STAT_MASK			0x1410
+#define VDEC_C_LUMA_CTRL			0x1414
+#define VDEC_C_CHROMA_CTRL			0x1418
+#define	VDEC_C_CRUSH_CTRL			0x141C
+#define	VDEC_C_HORIZ_TIM_CTRL			0x1420
+#define	VDEC_C_VERT_TIM_CTRL			0x1424
+#define	VDEC_C_MISC_TIM_CTRL			0x1428
+#define	VDEC_C_FIELD_COUNT			0x142C
+#define	VDEC_C_HSCALE_CTRL			0x1430
+#define	VDEC_C_VSCALE_CTRL			0x1434
+#define	VDEC_C_MAN_VGA_CTRL			0x1438
+#define	VDEC_C_MAN_AGC_CTRL			0x143C
+#define	VDEC_C_DFE_CTRL1			0x1440
+#define	VDEC_C_DFE_CTRL2			0x1444
+#define	VDEC_C_DFE_CTRL3			0x1448
+#define	VDEC_C_PLL_CTRL				0x144C
+#define	VDEC_C_PLL_CTRL_FAST			0x1450
+#define	VDEC_C_HTL_CTRL				0x1454
+#define	VDEC_C_SRC_CFG				0x1458
+#define	VDEC_C_SC_STEP_SIZE			0x145C
+#define	VDEC_C_SC_CONVERGE_CTRL			0x1460
+#define	VDEC_C_SC_LOOP_CTRL			0x1464
+#define	VDEC_C_COMB_2D_HFS_CFG			0x1468
+#define	VDEC_C_COMB_2D_HFD_CFG			0x146C
+#define	VDEC_C_COMB_2D_LF_CFG			0x1470
+#define	VDEC_C_COMB_2D_BLEND			0x1474
+#define	VDEC_C_COMB_MISC_CTRL			0x1478
+#define	VDEC_C_COMB_FLAT_THRESH_CTRL		0x147C
+#define	VDEC_C_COMB_TEST			0x1480
+#define	VDEC_C_BP_MISC_CTRL			0x1484
+#define	VDEC_C_VCR_DET_CTRL			0x1488
+#define	VDEC_C_NOISE_DET_CTRL			0x148C
+#define	VDEC_C_COMB_FLAT_NOISE_CTRL		0x1490
+#define	VDEC_C_VERSION				0x15F8
+#define	VDEC_C_SOFT_RST_CTRL			0x15FC
+
+/* Video Decoder D Registers */
+#define VDEC_D_MODE_CTRL			0x1600
+#define VDEC_D_OUT_CTRL1			0x1604
+#define VDEC_D_OUT_CTRL_NS			0x1608
+#define VDEC_D_GEN_STAT				0x160C
+#define VDEC_D_INT_STAT_MASK			0x1610
+#define VDEC_D_LUMA_CTRL			0x1614
+#define VDEC_D_CHROMA_CTRL			0x1618
+#define VDEC_D_CRUSH_CTRL			0x161C
+#define VDEC_D_HORIZ_TIM_CTRL			0x1620
+#define VDEC_D_VERT_TIM_CTRL			0x1624
+#define VDEC_D_MISC_TIM_CTRL			0x1628
+#define VDEC_D_FIELD_COUNT			0x162C
+#define VDEC_D_HSCALE_CTRL			0x1630
+#define VDEC_D_VSCALE_CTRL			0x1634
+#define VDEC_D_MAN_VGA_CTRL			0x1638
+#define VDEC_D_MAN_AGC_CTRL			0x163C
+#define VDEC_D_DFE_CTRL1			0x1640
+#define VDEC_D_DFE_CTRL2			0x1644
+#define VDEC_D_DFE_CTRL3			0x1648
+#define VDEC_D_PLL_CTRL				0x164C
+#define VDEC_D_PLL_CTRL_FAST			0x1650
+#define VDEC_D_HTL_CTRL				0x1654
+#define VDEC_D_SRC_CFG				0x1658
+#define VDEC_D_SC_STEP_SIZE			0x165C
+#define VDEC_D_SC_CONVERGE_CTRL			0x1660
+#define VDEC_D_SC_LOOP_CTRL			0x1664
+#define VDEC_D_COMB_2D_HFS_CFG			0x1668
+#define VDEC_D_COMB_2D_HFD_CFG			0x166C
+#define VDEC_D_COMB_2D_LF_CFG			0x1670
+#define VDEC_D_COMB_2D_BLEND			0x1674
+#define VDEC_D_COMB_MISC_CTRL			0x1678
+#define VDEC_D_COMB_FLAT_THRESH_CTRL		0x167C
+#define VDEC_D_COMB_TEST			0x1680
+#define VDEC_D_BP_MISC_CTRL			0x1684
+#define VDEC_D_VCR_DET_CTRL			0x1688
+#define VDEC_D_NOISE_DET_CTRL			0x168C
+#define VDEC_D_COMB_FLAT_NOISE_CTRL		0x1690
+#define VDEC_D_VERSION				0x17F8
+#define VDEC_D_SOFT_RST_CTRL			0x17FC
+
+/* Video Decoder E Registers */
+#define	VDEC_E_MODE_CTRL			0x1800
+#define	VDEC_E_OUT_CTRL1			0x1804
+#define	VDEC_E_OUT_CTRL_NS			0x1808
+#define	VDEC_E_GEN_STAT				0x180C
+#define	VDEC_E_INT_STAT_MASK			0x1810
+#define	VDEC_E_LUMA_CTRL			0x1814
+#define	VDEC_E_CHROMA_CTRL			0x1818
+#define	VDEC_E_CRUSH_CTRL			0x181C
+#define	VDEC_E_HORIZ_TIM_CTRL			0x1820
+#define	VDEC_E_VERT_TIM_CTRL			0x1824
+#define	VDEC_E_MISC_TIM_CTRL			0x1828
+#define	VDEC_E_FIELD_COUNT			0x182C
+#define	VDEC_E_HSCALE_CTRL			0x1830
+#define	VDEC_E_VSCALE_CTRL			0x1834
+#define	VDEC_E_MAN_VGA_CTRL			0x1838
+#define	VDEC_E_MAN_AGC_CTRL			0x183C
+#define	VDEC_E_DFE_CTRL1			0x1840
+#define	VDEC_E_DFE_CTRL2			0x1844
+#define	VDEC_E_DFE_CTRL3			0x1848
+#define	VDEC_E_PLL_CTRL				0x184C
+#define	VDEC_E_PLL_CTRL_FAST			0x1850
+#define	VDEC_E_HTL_CTRL				0x1854
+#define	VDEC_E_SRC_CFG				0x1858
+#define	VDEC_E_SC_STEP_SIZE			0x185C
+#define	VDEC_E_SC_CONVERGE_CTRL			0x1860
+#define	VDEC_E_SC_LOOP_CTRL			0x1864
+#define	VDEC_E_COMB_2D_HFS_CFG			0x1868
+#define	VDEC_E_COMB_2D_HFD_CFG			0x186C
+#define	VDEC_E_COMB_2D_LF_CFG			0x1870
+#define	VDEC_E_COMB_2D_BLEND			0x1874
+#define	VDEC_E_COMB_MISC_CTRL			0x1878
+#define	VDEC_E_COMB_FLAT_THRESH_CTRL		0x187C
+#define	VDEC_E_COMB_TEST			0x1880
+#define	VDEC_E_BP_MISC_CTRL			0x1884
+#define	VDEC_E_VCR_DET_CTRL			0x1888
+#define	VDEC_E_NOISE_DET_CTRL			0x188C
+#define	VDEC_E_COMB_FLAT_NOISE_CTRL		0x1890
+#define	VDEC_E_VERSION				0x19F8
+#define	VDEC_E_SOFT_RST_CTRL			0x19FC
+
+/* Video Decoder F Registers */
+#define	VDEC_F_MODE_CTRL			0x1A00
+#define	VDEC_F_OUT_CTRL1			0x1A04
+#define	VDEC_F_OUT_CTRL_NS			0x1A08
+#define	VDEC_F_GEN_STAT				0x1A0C
+#define	VDEC_F_INT_STAT_MASK			0x1A10
+#define	VDEC_F_LUMA_CTRL			0x1A14
+#define	VDEC_F_CHROMA_CTRL			0x1A18
+#define	VDEC_F_CRUSH_CTRL			0x1A1C
+#define	VDEC_F_HORIZ_TIM_CTRL			0x1A20
+#define	VDEC_F_VERT_TIM_CTRL			0x1A24
+#define	VDEC_F_MISC_TIM_CTRL			0x1A28
+#define	VDEC_F_FIELD_COUNT			0x1A2C
+#define	VDEC_F_HSCALE_CTRL			0x1A30
+#define	VDEC_F_VSCALE_CTRL			0x1A34
+#define	VDEC_F_MAN_VGA_CTRL			0x1A38
+#define	VDEC_F_MAN_AGC_CTRL			0x1A3C
+#define	VDEC_F_DFE_CTRL1			0x1A40
+#define	VDEC_F_DFE_CTRL2			0x1A44
+#define	VDEC_F_DFE_CTRL3			0x1A48
+#define	VDEC_F_PLL_CTRL				0x1A4C
+#define	VDEC_F_PLL_CTRL_FAST			0x1A50
+#define	VDEC_F_HTL_CTRL				0x1A54
+#define	VDEC_F_SRC_CFG				0x1A58
+#define	VDEC_F_SC_STEP_SIZE			0x1A5C
+#define	VDEC_F_SC_CONVERGE_CTRL			0x1A60
+#define	VDEC_F_SC_LOOP_CTRL			0x1A64
+#define	VDEC_F_COMB_2D_HFS_CFG			0x1A68
+#define	VDEC_F_COMB_2D_HFD_CFG			0x1A6C
+#define	VDEC_F_COMB_2D_LF_CFG			0x1A70
+#define	VDEC_F_COMB_2D_BLEND			0x1A74
+#define	VDEC_F_COMB_MISC_CTRL			0x1A78
+#define	VDEC_F_COMB_FLAT_THRESH_CTRL		0x1A7C
+#define	VDEC_F_COMB_TEST			0x1A80
+#define	VDEC_F_BP_MISC_CTRL			0x1A84
+#define	VDEC_F_VCR_DET_CTRL			0x1A88
+#define	VDEC_F_NOISE_DET_CTRL			0x1A8C
+#define	VDEC_F_COMB_FLAT_NOISE_CTRL		0x1A90
+#define	VDEC_F_VERSION				0x1BF8
+#define	VDEC_F_SOFT_RST_CTRL			0x1BFC
+
+/* Video Decoder G Registers */
+#define	VDEC_G_MODE_CTRL			0x1C00
+#define	VDEC_G_OUT_CTRL1			0x1C04
+#define	VDEC_G_OUT_CTRL_NS			0x1C08
+#define	VDEC_G_GEN_STAT				0x1C0C
+#define	VDEC_G_INT_STAT_MASK			0x1C10
+#define	VDEC_G_LUMA_CTRL			0x1C14
+#define	VDEC_G_CHROMA_CTRL			0x1C18
+#define	VDEC_G_CRUSH_CTRL			0x1C1C
+#define	VDEC_G_HORIZ_TIM_CTRL			0x1C20
+#define	VDEC_G_VERT_TIM_CTRL			0x1C24
+#define	VDEC_G_MISC_TIM_CTRL			0x1C28
+#define	VDEC_G_FIELD_COUNT			0x1C2C
+#define	VDEC_G_HSCALE_CTRL			0x1C30
+#define	VDEC_G_VSCALE_CTRL			0x1C34
+#define	VDEC_G_MAN_VGA_CTRL			0x1C38
+#define	VDEC_G_MAN_AGC_CTRL			0x1C3C
+#define	VDEC_G_DFE_CTRL1			0x1C40
+#define	VDEC_G_DFE_CTRL2			0x1C44
+#define	VDEC_G_DFE_CTRL3			0x1C48
+#define	VDEC_G_PLL_CTRL				0x1C4C
+#define	VDEC_G_PLL_CTRL_FAST			0x1C50
+#define	VDEC_G_HTL_CTRL				0x1C54
+#define	VDEC_G_SRC_CFG				0x1C58
+#define	VDEC_G_SC_STEP_SIZE			0x1C5C
+#define	VDEC_G_SC_CONVERGE_CTRL			0x1C60
+#define	VDEC_G_SC_LOOP_CTRL			0x1C64
+#define	VDEC_G_COMB_2D_HFS_CFG			0x1C68
+#define	VDEC_G_COMB_2D_HFD_CFG			0x1C6C
+#define	VDEC_G_COMB_2D_LF_CFG			0x1C70
+#define	VDEC_G_COMB_2D_BLEND			0x1C74
+#define	VDEC_G_COMB_MISC_CTRL			0x1C78
+#define	VDEC_G_COMB_FLAT_THRESH_CTRL		0x1C7C
+#define	VDEC_G_COMB_TEST			0x1C80
+#define	VDEC_G_BP_MISC_CTRL			0x1C84
+#define	VDEC_G_VCR_DET_CTRL			0x1C88
+#define	VDEC_G_NOISE_DET_CTRL			0x1C8C
+#define	VDEC_G_COMB_FLAT_NOISE_CTRL		0x1C90
+#define	VDEC_G_VERSION				0x1DF8
+#define	VDEC_G_SOFT_RST_CTRL			0x1DFC
+
+/* Video Decoder H Registers  */
+#define	VDEC_H_MODE_CTRL			0x1E00
+#define	VDEC_H_OUT_CTRL1			0x1E04
+#define	VDEC_H_OUT_CTRL_NS			0x1E08
+#define	VDEC_H_GEN_STAT				0x1E0C
+#define	VDEC_H_INT_STAT_MASK			0x1E1E
+#define	VDEC_H_LUMA_CTRL			0x1E14
+#define	VDEC_H_CHROMA_CTRL			0x1E18
+#define	VDEC_H_CRUSH_CTRL			0x1E1C
+#define	VDEC_H_HORIZ_TIM_CTRL			0x1E20
+#define	VDEC_H_VERT_TIM_CTRL			0x1E24
+#define	VDEC_H_MISC_TIM_CTRL			0x1E28
+#define	VDEC_H_FIELD_COUNT			0x1E2C
+#define	VDEC_H_HSCALE_CTRL			0x1E30
+#define	VDEC_H_VSCALE_CTRL			0x1E34
+#define	VDEC_H_MAN_VGA_CTRL			0x1E38
+#define	VDEC_H_MAN_AGC_CTRL			0x1E3C
+#define	VDEC_H_DFE_CTRL1			0x1E40
+#define	VDEC_H_DFE_CTRL2			0x1E44
+#define	VDEC_H_DFE_CTRL3			0x1E48
+#define	VDEC_H_PLL_CTRL				0x1E4C
+#define	VDEC_H_PLL_CTRL_FAST			0x1E50
+#define	VDEC_H_HTL_CTRL				0x1E54
+#define	VDEC_H_SRC_CFG				0x1E58
+#define	VDEC_H_SC_STEP_SIZE			0x1E5C
+#define	VDEC_H_SC_CONVERGE_CTRL			0x1E60
+#define	VDEC_H_SC_LOOP_CTRL			0x1E64
+#define	VDEC_H_COMB_2D_HFS_CFG			0x1E68
+#define	VDEC_H_COMB_2D_HFD_CFG			0x1E6C
+#define	VDEC_H_COMB_2D_LF_CFG			0x1E70
+#define	VDEC_H_COMB_2D_BLEND			0x1E74
+#define	VDEC_H_COMB_MISC_CTRL			0x1E78
+#define	VDEC_H_COMB_FLAT_THRESH_CTRL		0x1E7C
+#define	VDEC_H_COMB_TEST			0x1E80
+#define	VDEC_H_BP_MISC_CTRL			0x1E84
+#define	VDEC_H_VCR_DET_CTRL			0x1E88
+#define	VDEC_H_NOISE_DET_CTRL			0x1E8C
+#define	VDEC_H_COMB_FLAT_NOISE_CTRL		0x1E90
+#define	VDEC_H_VERSION				0x1FF8
+#define	VDEC_H_SOFT_RST_CTRL			0x1FFC
+
+/*****************************************************************************/
+/* LUMA_CTRL register fields */
+#define VDEC_A_BRITE_CTRL			0x1014
+#define VDEC_A_CNTRST_CTRL			0x1015
+#define VDEC_A_PEAK_SEL				0x1016
+
+/*****************************************************************************/
+/* CHROMA_CTRL register fields */
+#define VDEC_A_USAT_CTRL			0x1018
+#define VDEC_A_VSAT_CTRL			0x1019
+#define VDEC_A_HUE_CTRL				0x101A
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-medusa-video.c b/drivers/media/pci/cx25821/cx25821-medusa-video.c
new file mode 100644
index 000000000000..6a92e5c70c2a
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-medusa-video.c
@@ -0,0 +1,787 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821.h"
+#include "cx25821-medusa-video.h"
+#include "cx25821-biffuncs.h"
+
+/*
+ * medusa_enable_bluefield_output()
+ *
+ * Enable the generation of blue filed output if no video
+ *
+ */
+static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel,
+					   int enable)
+{
+	u32 value = 0;
+	u32 tmp = 0;
+	int out_ctrl = OUT_CTRL1;
+	int out_ctrl_ns = OUT_CTRL_NS;
+
+	switch (channel) {
+	default:
+	case VDEC_A:
+		break;
+	case VDEC_B:
+		out_ctrl = VDEC_B_OUT_CTRL1;
+		out_ctrl_ns = VDEC_B_OUT_CTRL_NS;
+		break;
+	case VDEC_C:
+		out_ctrl = VDEC_C_OUT_CTRL1;
+		out_ctrl_ns = VDEC_C_OUT_CTRL_NS;
+		break;
+	case VDEC_D:
+		out_ctrl = VDEC_D_OUT_CTRL1;
+		out_ctrl_ns = VDEC_D_OUT_CTRL_NS;
+		break;
+	case VDEC_E:
+		out_ctrl = VDEC_E_OUT_CTRL1;
+		out_ctrl_ns = VDEC_E_OUT_CTRL_NS;
+		return;
+	case VDEC_F:
+		out_ctrl = VDEC_F_OUT_CTRL1;
+		out_ctrl_ns = VDEC_F_OUT_CTRL_NS;
+		return;
+	case VDEC_G:
+		out_ctrl = VDEC_G_OUT_CTRL1;
+		out_ctrl_ns = VDEC_G_OUT_CTRL_NS;
+		return;
+	case VDEC_H:
+		out_ctrl = VDEC_H_OUT_CTRL1;
+		out_ctrl_ns = VDEC_H_OUT_CTRL_NS;
+		return;
+	}
+
+	value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp);
+	value &= 0xFFFFFF7F;	/* clear BLUE_FIELD_EN */
+	if (enable)
+		value |= 0x00000080;	/* set BLUE_FIELD_EN */
+	cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value);
+
+	value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp);
+	value &= 0xFFFFFF7F;
+	if (enable)
+		value |= 0x00000080;	/* set BLUE_FIELD_EN */
+	cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value);
+}
+
+static int medusa_initialize_ntsc(struct cx25821_dev *dev)
+{
+	int ret_val = 0;
+	int i = 0;
+	u32 value = 0;
+	u32 tmp = 0;
+
+	mutex_lock(&dev->lock);
+
+	for (i = 0; i < MAX_DECODERS; i++) {
+		/* set video format NTSC-M */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				MODE_CTRL + (0x200 * i), &tmp);
+		value &= 0xFFFFFFF0;
+		/* enable the fast locking mode bit[16] */
+		value |= 0x10001;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				MODE_CTRL + (0x200 * i), value);
+
+		/* resolution NTSC 720x480 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				HORIZ_TIM_CTRL + (0x200 * i), &tmp);
+		value &= 0x00C00C00;
+		value |= 0x612D0074;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				HORIZ_TIM_CTRL + (0x200 * i), value);
+
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				VERT_TIM_CTRL + (0x200 * i), &tmp);
+		value &= 0x00C00C00;
+		value |= 0x1C1E001A;	/* vblank_cnt + 2 to get camera ID */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				VERT_TIM_CTRL + (0x200 * i), value);
+
+		/* chroma subcarrier step size */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				SC_STEP_SIZE + (0x200 * i), 0x43E00000);
+
+		/* enable VIP optional active */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				OUT_CTRL_NS + (0x200 * i), &tmp);
+		value &= 0xFFFBFFFF;
+		value |= 0x00040000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				OUT_CTRL_NS + (0x200 * i), value);
+
+		/* enable VIP optional active (VIP_OPT_AL) for direct output. */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				OUT_CTRL1 + (0x200 * i), &tmp);
+		value &= 0xFFFBFFFF;
+		value |= 0x00040000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				OUT_CTRL1 + (0x200 * i), value);
+
+		/*
+		 * clear VPRES_VERT_EN bit, fixes the chroma run away problem
+		 * when the input switching rate < 16 fields
+		*/
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				MISC_TIM_CTRL + (0x200 * i), &tmp);
+		/* disable special play detection */
+		value = setBitAtPos(value, 14);
+		value = clearBitAtPos(value, 15);
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				MISC_TIM_CTRL + (0x200 * i), value);
+
+		/* set vbi_gate_en to 0 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DFE_CTRL1 + (0x200 * i), &tmp);
+		value = clearBitAtPos(value, 29);
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DFE_CTRL1 + (0x200 * i), value);
+
+		/* Enable the generation of blue field output if no video */
+		medusa_enable_bluefield_output(dev, i, 1);
+	}
+
+	for (i = 0; i < MAX_ENCODERS; i++) {
+		/* NTSC hclock */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_1 + (0x100 * i), &tmp);
+		value &= 0xF000FC00;
+		value |= 0x06B402D0;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_1 + (0x100 * i), value);
+
+		/* burst begin and burst end */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_2 + (0x100 * i), &tmp);
+		value &= 0xFF000000;
+		value |= 0x007E9054;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_2 + (0x100 * i), value);
+
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_3 + (0x100 * i), &tmp);
+		value &= 0xFC00FE00;
+		value |= 0x00EC00F0;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_3 + (0x100 * i), value);
+
+		/* set NTSC vblank, no phase alternation, 7.5 IRE pedestal */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_4 + (0x100 * i), &tmp);
+		value &= 0x00FCFFFF;
+		value |= 0x13020000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_4 + (0x100 * i), value);
+
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_5 + (0x100 * i), &tmp);
+		value &= 0xFFFF0000;
+		value |= 0x0000E575;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_5 + (0x100 * i), value);
+
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_6 + (0x100 * i), 0x009A89C1);
+
+		/* Subcarrier Increment */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_7 + (0x100 * i), 0x21F07C1F);
+	}
+
+	/* set picture resolutions */
+	/* 0 - 720 */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0);
+	/* 0 - 480 */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0);
+
+	/* set Bypass input format to NTSC 525 lines */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+	value |= 0x00080200;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+	mutex_unlock(&dev->lock);
+
+	return ret_val;
+}
+
+static int medusa_PALCombInit(struct cx25821_dev *dev, int dec)
+{
+	int ret_val = -1;
+	u32 value = 0, tmp = 0;
+
+	/* Setup for 2D threshold */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_2D_HFS_CFG + (0x200 * dec), 0x20002861);
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_2D_HFD_CFG + (0x200 * dec), 0x20002861);
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_2D_LF_CFG + (0x200 * dec), 0x200A1023);
+
+	/* Setup flat chroma and luma thresholds */
+	value = cx25821_i2c_read(&dev->i2c_bus[0],
+			COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp);
+	value &= 0x06230000;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_FLAT_THRESH_CTRL + (0x200 * dec), value);
+
+	/* set comb 2D blend */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_2D_BLEND + (0x200 * dec), 0x210F0F0F);
+
+	/* COMB MISC CONTROL */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+			COMB_MISC_CTRL + (0x200 * dec), 0x41120A7F);
+
+	return ret_val;
+}
+
+static int medusa_initialize_pal(struct cx25821_dev *dev)
+{
+	int ret_val = 0;
+	int i = 0;
+	u32 value = 0;
+	u32 tmp = 0;
+
+	mutex_lock(&dev->lock);
+
+	for (i = 0; i < MAX_DECODERS; i++) {
+		/* set video format PAL-BDGHI */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				MODE_CTRL + (0x200 * i), &tmp);
+		value &= 0xFFFFFFF0;
+		/* enable the fast locking mode bit[16] */
+		value |= 0x10004;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				MODE_CTRL + (0x200 * i), value);
+
+		/* resolution PAL 720x576 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				HORIZ_TIM_CTRL + (0x200 * i), &tmp);
+		value &= 0x00C00C00;
+		value |= 0x632D007D;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				HORIZ_TIM_CTRL + (0x200 * i), value);
+
+		/* vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				VERT_TIM_CTRL + (0x200 * i), &tmp);
+		value &= 0x00C00C00;
+		value |= 0x28240026;	/* vblank_cnt + 2 to get camera ID */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				VERT_TIM_CTRL + (0x200 * i), value);
+
+		/* chroma subcarrier step size */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				SC_STEP_SIZE + (0x200 * i), 0x5411E2D0);
+
+		/* enable VIP optional active */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				OUT_CTRL_NS + (0x200 * i), &tmp);
+		value &= 0xFFFBFFFF;
+		value |= 0x00040000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				OUT_CTRL_NS + (0x200 * i), value);
+
+		/* enable VIP optional active (VIP_OPT_AL) for direct output. */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				OUT_CTRL1 + (0x200 * i), &tmp);
+		value &= 0xFFFBFFFF;
+		value |= 0x00040000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				OUT_CTRL1 + (0x200 * i), value);
+
+		/*
+		 * clear VPRES_VERT_EN bit, fixes the chroma run away problem
+		 * when the input switching rate < 16 fields
+		 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				MISC_TIM_CTRL + (0x200 * i), &tmp);
+		/* disable special play detection */
+		value = setBitAtPos(value, 14);
+		value = clearBitAtPos(value, 15);
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				MISC_TIM_CTRL + (0x200 * i), value);
+
+		/* set vbi_gate_en to 0 */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DFE_CTRL1 + (0x200 * i), &tmp);
+		value = clearBitAtPos(value, 29);
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DFE_CTRL1 + (0x200 * i), value);
+
+		medusa_PALCombInit(dev, i);
+
+		/* Enable the generation of blue field output if no video */
+		medusa_enable_bluefield_output(dev, i, 1);
+	}
+
+	for (i = 0; i < MAX_ENCODERS; i++) {
+		/* PAL hclock */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_1 + (0x100 * i), &tmp);
+		value &= 0xF000FC00;
+		value |= 0x06C002D0;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_1 + (0x100 * i), value);
+
+		/* burst begin and burst end */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_2 + (0x100 * i), &tmp);
+		value &= 0xFF000000;
+		value |= 0x007E9754;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_2 + (0x100 * i), value);
+
+		/* hblank and vactive */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_3 + (0x100 * i), &tmp);
+		value &= 0xFC00FE00;
+		value |= 0x00FC0120;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_3 + (0x100 * i), value);
+
+		/* set PAL vblank, phase alternation, 0 IRE pedestal */
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_4 + (0x100 * i), &tmp);
+		value &= 0x00FCFFFF;
+		value |= 0x14010000;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_4 + (0x100 * i), value);
+
+		value = cx25821_i2c_read(&dev->i2c_bus[0],
+				DENC_A_REG_5 + (0x100 * i), &tmp);
+		value &= 0xFFFF0000;
+		value |= 0x0000F078;
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_5 + (0x100 * i), value);
+
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_6 + (0x100 * i), 0x00A493CF);
+
+		/* Subcarrier Increment */
+		ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+				DENC_A_REG_7 + (0x100 * i), 0x2A098ACB);
+	}
+
+	/* set picture resolutions */
+	/* 0 - 720 */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0);
+	/* 0 - 576 */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0);
+
+	/* set Bypass input format to PAL 625 lines */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+	value &= 0xFFF7FDFF;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+	mutex_unlock(&dev->lock);
+
+	return ret_val;
+}
+
+int medusa_set_videostandard(struct cx25821_dev *dev)
+{
+	int status = STATUS_SUCCESS;
+	u32 value = 0, tmp = 0;
+
+	if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+		status = medusa_initialize_pal(dev);
+	else
+		status = medusa_initialize_ntsc(dev);
+
+	/* Enable DENC_A output */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp);
+	value = setBitAtPos(value, 4);
+	status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value);
+
+	/* Enable DENC_B output */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp);
+	value = setBitAtPos(value, 4);
+	status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value);
+
+	return status;
+}
+
+void medusa_set_resolution(struct cx25821_dev *dev, int width,
+			   int decoder_select)
+{
+	int decoder = 0;
+	int decoder_count = 0;
+	u32 hscale = 0x0;
+	u32 vscale = 0x0;
+	const int MAX_WIDTH = 720;
+
+	mutex_lock(&dev->lock);
+
+	/* validate the width */
+	if (width > MAX_WIDTH) {
+		pr_info("%s(): width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n",
+			__func__, width, MAX_WIDTH);
+		width = MAX_WIDTH;
+	}
+
+	if (decoder_select <= 7 && decoder_select >= 0) {
+		decoder = decoder_select;
+		decoder_count = decoder_select + 1;
+	} else {
+		decoder = 0;
+		decoder_count = _num_decoders;
+	}
+
+	switch (width) {
+	case 320:
+		hscale = 0x13E34B;
+		vscale = 0x0;
+		break;
+
+	case 352:
+		hscale = 0x10A273;
+		vscale = 0x0;
+		break;
+
+	case 176:
+		hscale = 0x3115B2;
+		vscale = 0x1E00;
+		break;
+
+	case 160:
+		hscale = 0x378D84;
+		vscale = 0x1E00;
+		break;
+
+	default:		/* 720 */
+		hscale = 0x0;
+		vscale = 0x0;
+		break;
+	}
+
+	for (; decoder < decoder_count; decoder++) {
+		/* write scaling values for each decoder */
+		cx25821_i2c_write(&dev->i2c_bus[0],
+				HSCALE_CTRL + (0x200 * decoder), hscale);
+		cx25821_i2c_write(&dev->i2c_bus[0],
+				VSCALE_CTRL + (0x200 * decoder), vscale);
+	}
+
+	mutex_unlock(&dev->lock);
+}
+
+static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
+				       int duration)
+{
+	u32 fld_cnt = 0;
+	u32 tmp = 0;
+	u32 disp_cnt_reg = DISP_AB_CNT;
+
+	mutex_lock(&dev->lock);
+
+	/* no support */
+	if (decoder < VDEC_A || decoder > VDEC_H) {
+		mutex_unlock(&dev->lock);
+		return;
+	}
+
+	switch (decoder) {
+	default:
+		break;
+	case VDEC_C:
+	case VDEC_D:
+		disp_cnt_reg = DISP_CD_CNT;
+		break;
+	case VDEC_E:
+	case VDEC_F:
+		disp_cnt_reg = DISP_EF_CNT;
+		break;
+	case VDEC_G:
+	case VDEC_H:
+		disp_cnt_reg = DISP_GH_CNT;
+		break;
+	}
+
+	_display_field_cnt[decoder] = duration;
+
+	/* update hardware */
+	fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp);
+
+	if (!(decoder % 2)) {	/* EVEN decoder */
+		fld_cnt &= 0xFFFF0000;
+		fld_cnt |= duration;
+	} else {
+		fld_cnt &= 0x0000FFFF;
+		fld_cnt |= ((u32) duration) << 16;
+	}
+
+	cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt);
+
+	mutex_unlock(&dev->lock);
+}
+
+/* Map to Medusa register setting */
+static int mapM(int srcMin, int srcMax, int srcVal, int dstMin, int dstMax,
+		int *dstVal)
+{
+	int numerator;
+	int denominator;
+	int quotient;
+
+	if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax))
+		return -1;
+	/*
+	 * This is the overall expression used:
+	 * *dstVal =
+	 *   (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin;
+	 * but we need to account for rounding so below we use the modulus
+	 * operator to find the remainder and increment if necessary.
+	 */
+	numerator = (srcVal - srcMin) * (dstMax - dstMin);
+	denominator = srcMax - srcMin;
+	quotient = numerator / denominator;
+
+	if (2 * (numerator % denominator) >= denominator)
+		quotient++;
+
+	*dstVal = quotient + dstMin;
+
+	return 0;
+}
+
+static unsigned long convert_to_twos(long numeric, unsigned long bits_len)
+{
+	unsigned char temp;
+
+	if (numeric >= 0)
+		return numeric;
+	else {
+		temp = ~(abs(numeric) & 0xFF);
+		temp += 1;
+		return temp;
+	}
+}
+
+int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder)
+{
+	int ret_val = 0;
+	int value = 0;
+	u32 val = 0, tmp = 0;
+
+	mutex_lock(&dev->lock);
+	if ((brightness > VIDEO_PROCAMP_MAX) ||
+	    (brightness < VIDEO_PROCAMP_MIN)) {
+		mutex_unlock(&dev->lock);
+		return -1;
+	}
+	ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness,
+			SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
+	value = convert_to_twos(value, 8);
+	val = cx25821_i2c_read(&dev->i2c_bus[0],
+			VDEC_A_BRITE_CTRL + (0x200 * decoder), &tmp);
+	val &= 0xFFFFFF00;
+	ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+			VDEC_A_BRITE_CTRL + (0x200 * decoder), val | value);
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder)
+{
+	int ret_val = 0;
+	int value = 0;
+	u32 val = 0, tmp = 0;
+
+	mutex_lock(&dev->lock);
+
+	if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) {
+		mutex_unlock(&dev->lock);
+		return -1;
+	}
+
+	ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast,
+			UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
+	val = cx25821_i2c_read(&dev->i2c_bus[0],
+			VDEC_A_CNTRST_CTRL + (0x200 * decoder), &tmp);
+	val &= 0xFFFFFF00;
+	ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+			VDEC_A_CNTRST_CTRL + (0x200 * decoder), val | value);
+
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder)
+{
+	int ret_val = 0;
+	int value = 0;
+	u32 val = 0, tmp = 0;
+
+	mutex_lock(&dev->lock);
+
+	if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) {
+		mutex_unlock(&dev->lock);
+		return -1;
+	}
+
+	ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue,
+			SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
+
+	value = convert_to_twos(value, 8);
+	val = cx25821_i2c_read(&dev->i2c_bus[0],
+			VDEC_A_HUE_CTRL + (0x200 * decoder), &tmp);
+	val &= 0xFFFFFF00;
+
+	ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+			VDEC_A_HUE_CTRL + (0x200 * decoder), val | value);
+
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder)
+{
+	int ret_val = 0;
+	int value = 0;
+	u32 val = 0, tmp = 0;
+
+	mutex_lock(&dev->lock);
+
+	if ((saturation > VIDEO_PROCAMP_MAX) ||
+	    (saturation < VIDEO_PROCAMP_MIN)) {
+		mutex_unlock(&dev->lock);
+		return -1;
+	}
+
+	ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation,
+			UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
+
+	val = cx25821_i2c_read(&dev->i2c_bus[0],
+			VDEC_A_USAT_CTRL + (0x200 * decoder), &tmp);
+	val &= 0xFFFFFF00;
+	ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+			VDEC_A_USAT_CTRL + (0x200 * decoder), val | value);
+
+	val = cx25821_i2c_read(&dev->i2c_bus[0],
+			VDEC_A_VSAT_CTRL + (0x200 * decoder), &tmp);
+	val &= 0xFFFFFF00;
+	ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+			VDEC_A_VSAT_CTRL + (0x200 * decoder), val | value);
+
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
+
+/* Program the display sequence and monitor output. */
+
+int medusa_video_init(struct cx25821_dev *dev)
+{
+	u32 value = 0, tmp = 0;
+	int ret_val = 0;
+	int i = 0;
+
+	mutex_lock(&dev->lock);
+
+	_num_decoders = dev->_max_num_decoders;
+
+	/* disable Auto source selection on all video decoders */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
+	value &= 0xFFFFF0FF;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
+
+	if (ret_val < 0)
+		goto error;
+
+	/* Turn off Master source switch enable */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
+	value &= 0xFFFFFFDF;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
+
+	if (ret_val < 0)
+		goto error;
+
+	mutex_unlock(&dev->lock);
+
+	for (i = 0; i < _num_decoders; i++)
+		medusa_set_decoderduration(dev, i, _display_field_cnt[i]);
+
+	mutex_lock(&dev->lock);
+
+	/* Select monitor as DENC A input, power up the DAC */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp);
+	value &= 0xFF70FF70;
+	value |= 0x00090008;	/* set en_active */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value);
+
+	if (ret_val < 0)
+		goto error;
+
+	/* enable input is VIP/656 */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+	value |= 0x00040100;	/* enable VIP */
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+	if (ret_val < 0)
+		goto error;
+
+	/* select AFE clock to output mode */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
+	value &= 0x83FFFFFF;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL,
+			value | 0x10000000);
+
+	if (ret_val < 0)
+		goto error;
+
+	/* Turn on all of the data out and control output pins. */
+	value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp);
+	value &= 0xFEF0FE00;
+	if (_num_decoders == MAX_DECODERS) {
+		/*
+		 * Note: The octal board does not support control pins(bit16-19)
+		 * These bits are ignored in the octal board.
+		 *
+		 * disable VDEC A-C port, default to Mobilygen Interface
+		 */
+		value |= 0x010001F8;
+	} else {
+		/* disable VDEC A-C port, default to Mobilygen Interface */
+		value |= 0x010F0108;
+	}
+
+	value |= 7;
+	ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value);
+
+	if (ret_val < 0)
+		goto error;
+
+
+	mutex_unlock(&dev->lock);
+
+	ret_val = medusa_set_videostandard(dev);
+
+	return ret_val;
+
+error:
+	mutex_unlock(&dev->lock);
+	return ret_val;
+}
diff --git a/drivers/media/pci/cx25821/cx25821-medusa-video.h b/drivers/media/pci/cx25821/cx25821-medusa-video.h
new file mode 100644
index 000000000000..6175e0961855
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-medusa-video.h
@@ -0,0 +1,49 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 _MEDUSA_VIDEO_H
+#define _MEDUSA_VIDEO_H
+
+#include "cx25821-medusa-defines.h"
+
+/* Color control constants */
+#define VIDEO_PROCAMP_MIN                 0
+#define VIDEO_PROCAMP_MAX                 10000
+#define UNSIGNED_BYTE_MIN                 0
+#define UNSIGNED_BYTE_MAX                 0xFF
+#define SIGNED_BYTE_MIN                   -128
+#define SIGNED_BYTE_MAX                   127
+
+/* Default video color settings */
+#define SHARPNESS_DEFAULT                 50
+#define SATURATION_DEFAULT              5000
+#define BRIGHTNESS_DEFAULT              6200
+#define CONTRAST_DEFAULT                5000
+#define HUE_DEFAULT                     5000
+
+unsigned short _num_decoders;
+unsigned short _num_cameras;
+
+unsigned int _video_standard;
+int _display_field_cnt[MAX_DECODERS];
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-reg.h b/drivers/media/pci/cx25821/cx25821-reg.h
new file mode 100644
index 000000000000..a3fc25a4dc0b
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-reg.h
@@ -0,0 +1,1592 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 __CX25821_REGISTERS__
+#define __CX25821_REGISTERS__
+
+/* Risc Instructions */
+#define RISC_CNT_INC	 0x00010000
+#define RISC_CNT_RESET	 0x00030000
+#define RISC_IRQ1		 0x01000000
+#define RISC_IRQ2		 0x02000000
+#define RISC_EOL		 0x04000000
+#define RISC_SOL		 0x08000000
+#define RISC_WRITE		 0x10000000
+#define RISC_SKIP		 0x20000000
+#define RISC_JUMP		 0x70000000
+#define RISC_SYNC		 0x80000000
+#define RISC_RESYNC		 0x80008000
+#define RISC_READ		 0x90000000
+#define RISC_WRITERM	 0xB0000000
+#define RISC_WRITECM	 0xC0000000
+#define RISC_WRITECR	 0xD0000000
+#define RISC_WRITEC		 0x50000000
+#define RISC_READC		 0xA0000000
+
+#define RISC_SYNC_ODD		 0x00000000
+#define RISC_SYNC_EVEN		 0x00000200
+#define RISC_SYNC_ODD_VBI	 0x00000006
+#define RISC_SYNC_EVEN_VBI	 0x00000207
+#define RISC_NOOP			 0xF0000000
+
+/*****************************************************************************
+* ASB SRAM
+ *****************************************************************************/
+#define  TX_SRAM                   0x000000	/* Transmit SRAM */
+
+/*****************************************************************************/
+#define  RX_RAM                    0x010000	/* Receive SRAM */
+
+/*****************************************************************************
+* Application Layer (AL)
+ *****************************************************************************/
+#define  DEV_CNTRL2                0x040000	/* Device control */
+#define  FLD_RUN_RISC              0x00000020
+
+/* ***************************************************************************** */
+#define  PCI_INT_MSK               0x040010	/* PCI interrupt mask */
+#define  PCI_INT_STAT              0x040014	/* PCI interrupt status */
+#define  PCI_INT_MSTAT             0x040018	/* PCI interrupt masked status */
+#define  FLD_HAMMERHEAD_INT        (1 << 27)
+#define  FLD_UART_INT              (1 << 26)
+#define  FLD_IRQN_INT              (1 << 25)
+#define  FLD_TM_INT                (1 << 28)
+#define  FLD_I2C_3_RACK            (1 << 27)
+#define  FLD_I2C_3_INT             (1 << 26)
+#define  FLD_I2C_2_RACK            (1 << 25)
+#define  FLD_I2C_2_INT             (1 << 24)
+#define  FLD_I2C_1_RACK            (1 << 23)
+#define  FLD_I2C_1_INT             (1 << 22)
+
+#define  FLD_APB_DMA_BERR_INT      (1 << 21)
+#define  FLD_AL_WR_BERR_INT        (1 << 20)
+#define  FLD_AL_RD_BERR_INT        (1 << 19)
+#define  FLD_RISC_WR_BERR_INT      (1 << 18)
+#define  FLD_RISC_RD_BERR_INT      (1 << 17)
+
+#define  FLD_VID_I_INT             (1 << 8)
+#define  FLD_VID_H_INT             (1 << 7)
+#define  FLD_VID_G_INT             (1 << 6)
+#define  FLD_VID_F_INT             (1 << 5)
+#define  FLD_VID_E_INT             (1 << 4)
+#define  FLD_VID_D_INT             (1 << 3)
+#define  FLD_VID_C_INT             (1 << 2)
+#define  FLD_VID_B_INT             (1 << 1)
+#define  FLD_VID_A_INT             (1 << 0)
+
+/* ***************************************************************************** */
+#define  VID_A_INT_MSK             0x040020	/* Video A interrupt mask */
+#define  VID_A_INT_STAT            0x040024	/* Video A interrupt status */
+#define  VID_A_INT_MSTAT           0x040028	/* Video A interrupt masked status */
+#define  VID_A_INT_SSTAT           0x04002C	/* Video A interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_B_INT_MSK             0x040030	/* Video B interrupt mask */
+#define  VID_B_INT_STAT            0x040034	/* Video B interrupt status */
+#define  VID_B_INT_MSTAT           0x040038	/* Video B interrupt masked status */
+#define  VID_B_INT_SSTAT           0x04003C	/* Video B interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_C_INT_MSK             0x040040	/* Video C interrupt mask */
+#define  VID_C_INT_STAT            0x040044	/* Video C interrupt status */
+#define  VID_C_INT_MSTAT           0x040048	/* Video C interrupt masked status */
+#define  VID_C_INT_SSTAT           0x04004C	/* Video C interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_D_INT_MSK             0x040050	/* Video D interrupt mask */
+#define  VID_D_INT_STAT            0x040054	/* Video D interrupt status */
+#define  VID_D_INT_MSTAT           0x040058	/* Video D interrupt masked status */
+#define  VID_D_INT_SSTAT           0x04005C	/* Video D interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_E_INT_MSK             0x040060	/* Video E interrupt mask */
+#define  VID_E_INT_STAT            0x040064	/* Video E interrupt status */
+#define  VID_E_INT_MSTAT           0x040068	/* Video E interrupt masked status */
+#define  VID_E_INT_SSTAT           0x04006C	/* Video E interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_F_INT_MSK             0x040070	/* Video F interrupt mask */
+#define  VID_F_INT_STAT            0x040074	/* Video F interrupt status */
+#define  VID_F_INT_MSTAT           0x040078	/* Video F interrupt masked status */
+#define  VID_F_INT_SSTAT           0x04007C	/* Video F interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_G_INT_MSK             0x040080	/* Video G interrupt mask */
+#define  VID_G_INT_STAT            0x040084	/* Video G interrupt status */
+#define  VID_G_INT_MSTAT           0x040088	/* Video G interrupt masked status */
+#define  VID_G_INT_SSTAT           0x04008C	/* Video G interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_H_INT_MSK             0x040090	/* Video H interrupt mask */
+#define  VID_H_INT_STAT            0x040094	/* Video H interrupt status */
+#define  VID_H_INT_MSTAT           0x040098	/* Video H interrupt masked status */
+#define  VID_H_INT_SSTAT           0x04009C	/* Video H interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_I_INT_MSK             0x0400A0	/* Video I interrupt mask */
+#define  VID_I_INT_STAT            0x0400A4	/* Video I interrupt status */
+#define  VID_I_INT_MSTAT           0x0400A8	/* Video I interrupt masked status */
+#define  VID_I_INT_SSTAT           0x0400AC	/* Video I interrupt set status */
+
+/* ***************************************************************************** */
+#define  VID_J_INT_MSK             0x0400B0	/* Video J interrupt mask */
+#define  VID_J_INT_STAT            0x0400B4	/* Video J interrupt status */
+#define  VID_J_INT_MSTAT           0x0400B8	/* Video J interrupt masked status */
+#define  VID_J_INT_SSTAT           0x0400BC	/* Video J interrupt set status */
+
+#define  FLD_VID_SRC_OPC_ERR       0x00020000
+#define  FLD_VID_DST_OPC_ERR       0x00010000
+#define  FLD_VID_SRC_SYNC          0x00002000
+#define  FLD_VID_DST_SYNC          0x00001000
+#define  FLD_VID_SRC_UF            0x00000200
+#define  FLD_VID_DST_OF            0x00000100
+#define  FLD_VID_SRC_RISC2         0x00000020
+#define  FLD_VID_DST_RISC2         0x00000010
+#define  FLD_VID_SRC_RISC1         0x00000002
+#define  FLD_VID_DST_RISC1         0x00000001
+#define  FLD_VID_SRC_ERRORS		(FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF)
+#define  FLD_VID_DST_ERRORS		(FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF)
+
+/* ***************************************************************************** */
+#define  AUD_A_INT_MSK             0x0400C0	/* Audio Int interrupt mask */
+#define  AUD_A_INT_STAT            0x0400C4	/* Audio Int interrupt status */
+#define  AUD_A_INT_MSTAT           0x0400C8	/* Audio Int interrupt masked status */
+#define  AUD_A_INT_SSTAT           0x0400CC	/* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define  AUD_B_INT_MSK             0x0400D0	/* Audio Int interrupt mask */
+#define  AUD_B_INT_STAT            0x0400D4	/* Audio Int interrupt status */
+#define  AUD_B_INT_MSTAT           0x0400D8	/* Audio Int interrupt masked status */
+#define  AUD_B_INT_SSTAT           0x0400DC	/* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define  AUD_C_INT_MSK             0x0400E0	/* Audio Int interrupt mask */
+#define  AUD_C_INT_STAT            0x0400E4	/* Audio Int interrupt status */
+#define  AUD_C_INT_MSTAT           0x0400E8	/* Audio Int interrupt masked status */
+#define  AUD_C_INT_SSTAT           0x0400EC	/* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define  AUD_D_INT_MSK             0x0400F0	/* Audio Int interrupt mask */
+#define  AUD_D_INT_STAT            0x0400F4	/* Audio Int interrupt status */
+#define  AUD_D_INT_MSTAT           0x0400F8	/* Audio Int interrupt masked status */
+#define  AUD_D_INT_SSTAT           0x0400FC	/* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define  AUD_E_INT_MSK             0x040100	/* Audio Int interrupt mask */
+#define  AUD_E_INT_STAT            0x040104	/* Audio Int interrupt status */
+#define  AUD_E_INT_MSTAT           0x040108	/* Audio Int interrupt masked status */
+#define  AUD_E_INT_SSTAT           0x04010C	/* Audio Int interrupt set status */
+
+#define  FLD_AUD_SRC_OPC_ERR       0x00020000
+#define  FLD_AUD_DST_OPC_ERR       0x00010000
+#define  FLD_AUD_SRC_SYNC          0x00002000
+#define  FLD_AUD_DST_SYNC          0x00001000
+#define  FLD_AUD_SRC_OF            0x00000200
+#define  FLD_AUD_DST_OF            0x00000100
+#define  FLD_AUD_SRC_RISCI2        0x00000020
+#define  FLD_AUD_DST_RISCI2        0x00000010
+#define  FLD_AUD_SRC_RISCI1        0x00000002
+#define  FLD_AUD_DST_RISCI1        0x00000001
+
+/* ***************************************************************************** */
+#define  MBIF_A_INT_MSK             0x040110	/* MBIF Int interrupt mask */
+#define  MBIF_A_INT_STAT            0x040114	/* MBIF Int interrupt status */
+#define  MBIF_A_INT_MSTAT           0x040118	/* MBIF Int interrupt masked status */
+#define  MBIF_A_INT_SSTAT           0x04011C	/* MBIF Int interrupt set status */
+
+/* ***************************************************************************** */
+#define  MBIF_B_INT_MSK             0x040120	/* MBIF Int interrupt mask */
+#define  MBIF_B_INT_STAT            0x040124	/* MBIF Int interrupt status */
+#define  MBIF_B_INT_MSTAT           0x040128	/* MBIF Int interrupt masked status */
+#define  MBIF_B_INT_SSTAT           0x04012C	/* MBIF Int interrupt set status */
+
+#define  FLD_MBIF_DST_OPC_ERR       0x00010000
+#define  FLD_MBIF_DST_SYNC          0x00001000
+#define  FLD_MBIF_DST_OF            0x00000100
+#define  FLD_MBIF_DST_RISCI2        0x00000010
+#define  FLD_MBIF_DST_RISCI1        0x00000001
+
+/* ***************************************************************************** */
+#define  AUD_EXT_INT_MSK           0x040060	/* Audio Ext interrupt mask */
+#define  AUD_EXT_INT_STAT          0x040064	/* Audio Ext interrupt status */
+#define  AUD_EXT_INT_MSTAT         0x040068	/* Audio Ext interrupt masked status */
+#define  AUD_EXT_INT_SSTAT         0x04006C	/* Audio Ext interrupt set status */
+#define  FLD_AUD_EXT_OPC_ERR       0x00010000
+#define  FLD_AUD_EXT_SYNC          0x00001000
+#define  FLD_AUD_EXT_OF            0x00000100
+#define  FLD_AUD_EXT_RISCI2        0x00000010
+#define  FLD_AUD_EXT_RISCI1        0x00000001
+
+/* ***************************************************************************** */
+#define  GPIO_LO                   0x110010	/* Lower  of GPIO pins [31:0] */
+#define  GPIO_HI                   0x110014	/* Upper WORD  of GPIO pins [47:31] */
+
+#define  GPIO_LO_OE                0x110018	/* Lower  of GPIO output enable [31:0] */
+#define  GPIO_HI_OE                0x11001C	/* Upper word  of GPIO output enable [47:32] */
+
+#define  GPIO_LO_INT_MSK           0x11003C	/* GPIO interrupt mask */
+#define  GPIO_LO_INT_STAT          0x110044	/* GPIO interrupt status */
+#define  GPIO_LO_INT_MSTAT         0x11004C	/* GPIO interrupt masked status */
+#define  GPIO_LO_ISM_SNS           0x110054	/* GPIO interrupt sensitivity */
+#define  GPIO_LO_ISM_POL           0x11005C	/* GPIO interrupt polarity */
+
+#define  GPIO_HI_INT_MSK           0x110040	/* GPIO interrupt mask */
+#define  GPIO_HI_INT_STAT          0x110048	/* GPIO interrupt status */
+#define  GPIO_HI_INT_MSTAT         0x110050	/* GPIO interrupt masked status */
+#define  GPIO_HI_ISM_SNS           0x110058	/* GPIO interrupt sensitivity */
+#define  GPIO_HI_ISM_POL           0x110060	/* GPIO interrupt polarity */
+
+#define  FLD_GPIO43_INT            (1 << 11)
+#define  FLD_GPIO42_INT            (1 << 10)
+#define  FLD_GPIO41_INT            (1 << 9)
+#define  FLD_GPIO40_INT            (1 << 8)
+
+#define  FLD_GPIO9_INT             (1 << 9)
+#define  FLD_GPIO8_INT             (1 << 8)
+#define  FLD_GPIO7_INT             (1 << 7)
+#define  FLD_GPIO6_INT             (1 << 6)
+#define  FLD_GPIO5_INT             (1 << 5)
+#define  FLD_GPIO4_INT             (1 << 4)
+#define  FLD_GPIO3_INT             (1 << 3)
+#define  FLD_GPIO2_INT             (1 << 2)
+#define  FLD_GPIO1_INT             (1 << 1)
+#define  FLD_GPIO0_INT             (1 << 0)
+
+/* ***************************************************************************** */
+#define  TC_REQ                    0x040090	/* Rider PCI Express traFFic class request */
+
+/* ***************************************************************************** */
+#define  TC_REQ_SET                0x040094	/* Rider PCI Express traFFic class request set */
+
+/* ***************************************************************************** */
+/* Rider */
+/* ***************************************************************************** */
+
+/* PCI Compatible Header */
+/* ***************************************************************************** */
+#define  RDR_CFG0                  0x050000
+#define  RDR_VENDOR_DEVICE_ID_CFG  0x050000
+
+/* ***************************************************************************** */
+#define  RDR_CFG1                  0x050004
+
+/* ***************************************************************************** */
+#define  RDR_CFG2                  0x050008
+
+/* ***************************************************************************** */
+#define  RDR_CFG3                  0x05000C
+
+/* ***************************************************************************** */
+#define  RDR_CFG4                  0x050010
+
+/* ***************************************************************************** */
+#define  RDR_CFG5                  0x050014
+
+/* ***************************************************************************** */
+#define  RDR_CFG6                  0x050018
+
+/* ***************************************************************************** */
+#define  RDR_CFG7                  0x05001C
+
+/* ***************************************************************************** */
+#define  RDR_CFG8                  0x050020
+
+/* ***************************************************************************** */
+#define  RDR_CFG9                  0x050024
+
+/* ***************************************************************************** */
+#define  RDR_CFGA                  0x050028
+
+/* ***************************************************************************** */
+#define  RDR_CFGB                  0x05002C
+#define  RDR_SUSSYSTEM_ID_CFG      0x05002C
+
+/* ***************************************************************************** */
+#define  RDR_CFGC                  0x050030
+
+/* ***************************************************************************** */
+#define  RDR_CFGD                  0x050034
+
+/* ***************************************************************************** */
+#define  RDR_CFGE                  0x050038
+
+/* ***************************************************************************** */
+#define  RDR_CFGF                  0x05003C
+
+/* ***************************************************************************** */
+/* PCI-Express Capabilities */
+/* ***************************************************************************** */
+#define  RDR_PECAP                 0x050040
+
+/* ***************************************************************************** */
+#define  RDR_PEDEVCAP              0x050044
+
+/* ***************************************************************************** */
+#define  RDR_PEDEVSC               0x050048
+
+/* ***************************************************************************** */
+#define  RDR_PELINKCAP             0x05004C
+
+/* ***************************************************************************** */
+#define  RDR_PELINKSC              0x050050
+
+/* ***************************************************************************** */
+#define  RDR_PMICAP                0x050080
+
+/* ***************************************************************************** */
+#define  RDR_PMCSR                 0x050084
+
+/* ***************************************************************************** */
+#define  RDR_VPDCAP                0x050090
+
+/* ***************************************************************************** */
+#define  RDR_VPDDATA               0x050094
+
+/* ***************************************************************************** */
+#define  RDR_MSICAP                0x0500A0
+
+/* ***************************************************************************** */
+#define  RDR_MSIARL                0x0500A4
+
+/* ***************************************************************************** */
+#define  RDR_MSIARU                0x0500A8
+
+/* ***************************************************************************** */
+#define  RDR_MSIDATA               0x0500AC
+
+/* ***************************************************************************** */
+/* PCI Express Extended Capabilities */
+/* ***************************************************************************** */
+#define  RDR_AERXCAP               0x050100
+
+/* ***************************************************************************** */
+#define  RDR_AERUESTA              0x050104
+
+/* ***************************************************************************** */
+#define  RDR_AERUEMSK              0x050108
+
+/* ***************************************************************************** */
+#define  RDR_AERUESEV              0x05010C
+
+/* ***************************************************************************** */
+#define  RDR_AERCESTA              0x050110
+
+/* ***************************************************************************** */
+#define  RDR_AERCEMSK              0x050114
+
+/* ***************************************************************************** */
+#define  RDR_AERCC                 0x050118
+
+/* ***************************************************************************** */
+#define  RDR_AERHL0                0x05011C
+
+/* ***************************************************************************** */
+#define  RDR_AERHL1                0x050120
+
+/* ***************************************************************************** */
+#define  RDR_AERHL2                0x050124
+
+/* ***************************************************************************** */
+#define  RDR_AERHL3                0x050128
+
+/* ***************************************************************************** */
+#define  RDR_VCXCAP                0x050200
+
+/* ***************************************************************************** */
+#define  RDR_VCCAP1                0x050204
+
+/* ***************************************************************************** */
+#define  RDR_VCCAP2                0x050208
+
+/* ***************************************************************************** */
+#define  RDR_VCSC                  0x05020C
+
+/* ***************************************************************************** */
+#define  RDR_VCR0_CAP              0x050210
+
+/* ***************************************************************************** */
+#define  RDR_VCR0_CTRL             0x050214
+
+/* ***************************************************************************** */
+#define  RDR_VCR0_STAT             0x050218
+
+/* ***************************************************************************** */
+#define  RDR_VCR1_CAP              0x05021C
+
+/* ***************************************************************************** */
+#define  RDR_VCR1_CTRL             0x050220
+
+/* ***************************************************************************** */
+#define  RDR_VCR1_STAT             0x050224
+
+/* ***************************************************************************** */
+#define  RDR_VCR2_CAP              0x050228
+
+/* ***************************************************************************** */
+#define  RDR_VCR2_CTRL             0x05022C
+
+/* ***************************************************************************** */
+#define  RDR_VCR2_STAT             0x050230
+
+/* ***************************************************************************** */
+#define  RDR_VCR3_CAP              0x050234
+
+/* ***************************************************************************** */
+#define  RDR_VCR3_CTRL             0x050238
+
+/* ***************************************************************************** */
+#define  RDR_VCR3_STAT             0x05023C
+
+/* ***************************************************************************** */
+#define  RDR_VCARB0                0x050240
+
+/* ***************************************************************************** */
+#define  RDR_VCARB1                0x050244
+
+/* ***************************************************************************** */
+#define  RDR_VCARB2                0x050248
+
+/* ***************************************************************************** */
+#define  RDR_VCARB3                0x05024C
+
+/* ***************************************************************************** */
+#define  RDR_VCARB4                0x050250
+
+/* ***************************************************************************** */
+#define  RDR_VCARB5                0x050254
+
+/* ***************************************************************************** */
+#define  RDR_VCARB6                0x050258
+
+/* ***************************************************************************** */
+#define  RDR_VCARB7                0x05025C
+
+/* ***************************************************************************** */
+#define  RDR_RDRSTAT0              0x050300
+
+/* ***************************************************************************** */
+#define  RDR_RDRSTAT1              0x050304
+
+/* ***************************************************************************** */
+#define  RDR_RDRCTL0               0x050308
+
+/* ***************************************************************************** */
+#define  RDR_RDRCTL1               0x05030C
+
+/* ***************************************************************************** */
+/* Transaction Layer Registers */
+/* ***************************************************************************** */
+#define  RDR_TLSTAT0               0x050310
+
+/* ***************************************************************************** */
+#define  RDR_TLSTAT1               0x050314
+
+/* ***************************************************************************** */
+#define  RDR_TLCTL0                0x050318
+#define  FLD_CFG_UR_CPL_MODE       0x00000040
+#define  FLD_CFG_CORR_ERR_QUITE    0x00000020
+#define  FLD_CFG_RCB_CK_EN         0x00000010
+#define  FLD_CFG_BNDRY_CK_EN       0x00000008
+#define  FLD_CFG_BYTE_EN_CK_EN     0x00000004
+#define  FLD_CFG_RELAX_ORDER_MSK   0x00000002
+#define  FLD_CFG_TAG_ORDER_EN      0x00000001
+
+/* ***************************************************************************** */
+#define  RDR_TLCTL1                0x05031C
+
+/* ***************************************************************************** */
+#define  RDR_REQRCAL               0x050320
+
+/* ***************************************************************************** */
+#define  RDR_REQRCAU               0x050324
+
+/* ***************************************************************************** */
+#define  RDR_REQEPA                0x050328
+
+/* ***************************************************************************** */
+#define  RDR_REQCTRL               0x05032C
+
+/* ***************************************************************************** */
+#define  RDR_REQSTAT               0x050330
+
+/* ***************************************************************************** */
+#define  RDR_TL_TEST               0x050334
+
+/* ***************************************************************************** */
+#define  RDR_VCR01_CTL             0x050348
+
+/* ***************************************************************************** */
+#define  RDR_VCR23_CTL             0x05034C
+
+/* ***************************************************************************** */
+#define  RDR_RX_VCR0_FC            0x050350
+
+/* ***************************************************************************** */
+#define  RDR_RX_VCR1_FC            0x050354
+
+/* ***************************************************************************** */
+#define  RDR_RX_VCR2_FC            0x050358
+
+/* ***************************************************************************** */
+#define  RDR_RX_VCR3_FC            0x05035C
+
+/* ***************************************************************************** */
+/* Data Link Layer Registers */
+/* ***************************************************************************** */
+#define  RDR_DLLSTAT               0x050360
+
+/* ***************************************************************************** */
+#define  RDR_DLLCTRL               0x050364
+
+/* ***************************************************************************** */
+#define  RDR_REPLAYTO              0x050368
+
+/* ***************************************************************************** */
+#define  RDR_ACKLATTO              0x05036C
+
+/* ***************************************************************************** */
+/* MAC Layer Registers */
+/* ***************************************************************************** */
+#define  RDR_MACSTAT0              0x050380
+
+/* ***************************************************************************** */
+#define  RDR_MACSTAT1              0x050384
+
+/* ***************************************************************************** */
+#define  RDR_MACCTRL0              0x050388
+
+/* ***************************************************************************** */
+#define  RDR_MACCTRL1              0x05038C
+
+/* ***************************************************************************** */
+#define  RDR_MACCTRL2              0x050390
+
+/* ***************************************************************************** */
+#define  RDR_MAC_LB_DATA           0x050394
+
+/* ***************************************************************************** */
+#define  RDR_L0S_EXIT_LAT          0x050398
+
+/* ***************************************************************************** */
+/* DMAC */
+/* ***************************************************************************** */
+#define  DMA1_PTR1                 0x100000	/* DMA Current Ptr : Ch#1 */
+
+/* ***************************************************************************** */
+#define  DMA2_PTR1                 0x100004	/* DMA Current Ptr : Ch#2 */
+
+/* ***************************************************************************** */
+#define  DMA3_PTR1                 0x100008	/* DMA Current Ptr : Ch#3 */
+
+/* ***************************************************************************** */
+#define  DMA4_PTR1                 0x10000C	/* DMA Current Ptr : Ch#4 */
+
+/* ***************************************************************************** */
+#define  DMA5_PTR1                 0x100010	/* DMA Current Ptr : Ch#5 */
+
+/* ***************************************************************************** */
+#define  DMA6_PTR1                 0x100014	/* DMA Current Ptr : Ch#6 */
+
+/* ***************************************************************************** */
+#define  DMA7_PTR1                 0x100018	/* DMA Current Ptr : Ch#7 */
+
+/* ***************************************************************************** */
+#define  DMA8_PTR1                 0x10001C	/* DMA Current Ptr : Ch#8 */
+
+/* ***************************************************************************** */
+#define  DMA9_PTR1                 0x100020	/* DMA Current Ptr : Ch#9 */
+
+/* ***************************************************************************** */
+#define  DMA10_PTR1                0x100024	/* DMA Current Ptr : Ch#10 */
+
+/* ***************************************************************************** */
+#define  DMA11_PTR1                0x100028	/* DMA Current Ptr : Ch#11 */
+
+/* ***************************************************************************** */
+#define  DMA12_PTR1                0x10002C	/* DMA Current Ptr : Ch#12 */
+
+/* ***************************************************************************** */
+#define  DMA13_PTR1                0x100030	/* DMA Current Ptr : Ch#13 */
+
+/* ***************************************************************************** */
+#define  DMA14_PTR1                0x100034	/* DMA Current Ptr : Ch#14 */
+
+/* ***************************************************************************** */
+#define  DMA15_PTR1                0x100038	/* DMA Current Ptr : Ch#15 */
+
+/* ***************************************************************************** */
+#define  DMA16_PTR1                0x10003C	/* DMA Current Ptr : Ch#16 */
+
+/* ***************************************************************************** */
+#define  DMA17_PTR1                0x100040	/* DMA Current Ptr : Ch#17 */
+
+/* ***************************************************************************** */
+#define  DMA18_PTR1                0x100044	/* DMA Current Ptr : Ch#18 */
+
+/* ***************************************************************************** */
+#define  DMA19_PTR1                0x100048	/* DMA Current Ptr : Ch#19 */
+
+/* ***************************************************************************** */
+#define  DMA20_PTR1                0x10004C	/* DMA Current Ptr : Ch#20 */
+
+/* ***************************************************************************** */
+#define  DMA21_PTR1                0x100050	/* DMA Current Ptr : Ch#21 */
+
+/* ***************************************************************************** */
+#define  DMA22_PTR1                0x100054	/* DMA Current Ptr : Ch#22 */
+
+/* ***************************************************************************** */
+#define  DMA23_PTR1                0x100058	/* DMA Current Ptr : Ch#23 */
+
+/* ***************************************************************************** */
+#define  DMA24_PTR1                0x10005C	/* DMA Current Ptr : Ch#24 */
+
+/* ***************************************************************************** */
+#define  DMA25_PTR1                0x100060	/* DMA Current Ptr : Ch#25 */
+
+/* ***************************************************************************** */
+#define  DMA26_PTR1                0x100064	/* DMA Current Ptr : Ch#26 */
+
+/* ***************************************************************************** */
+#define  DMA1_PTR2                 0x100080	/* DMA Tab Ptr : Ch#1 */
+
+/* ***************************************************************************** */
+#define  DMA2_PTR2                 0x100084	/* DMA Tab Ptr : Ch#2 */
+
+/* ***************************************************************************** */
+#define  DMA3_PTR2                 0x100088	/* DMA Tab Ptr : Ch#3 */
+
+/* ***************************************************************************** */
+#define  DMA4_PTR2                 0x10008C	/* DMA Tab Ptr : Ch#4 */
+
+/* ***************************************************************************** */
+#define  DMA5_PTR2                 0x100090	/* DMA Tab Ptr : Ch#5 */
+
+/* ***************************************************************************** */
+#define  DMA6_PTR2                 0x100094	/* DMA Tab Ptr : Ch#6 */
+
+/* ***************************************************************************** */
+#define  DMA7_PTR2                 0x100098	/* DMA Tab Ptr : Ch#7 */
+
+/* ***************************************************************************** */
+#define  DMA8_PTR2                 0x10009C	/* DMA Tab Ptr : Ch#8 */
+
+/* ***************************************************************************** */
+#define  DMA9_PTR2                 0x1000A0	/* DMA Tab Ptr : Ch#9 */
+
+/* ***************************************************************************** */
+#define  DMA10_PTR2                0x1000A4	/* DMA Tab Ptr : Ch#10 */
+
+/* ***************************************************************************** */
+#define  DMA11_PTR2                0x1000A8	/* DMA Tab Ptr : Ch#11 */
+
+/* ***************************************************************************** */
+#define  DMA12_PTR2                0x1000AC	/* DMA Tab Ptr : Ch#12 */
+
+/* ***************************************************************************** */
+#define  DMA13_PTR2                0x1000B0	/* DMA Tab Ptr : Ch#13 */
+
+/* ***************************************************************************** */
+#define  DMA14_PTR2                0x1000B4	/* DMA Tab Ptr : Ch#14 */
+
+/* ***************************************************************************** */
+#define  DMA15_PTR2                0x1000B8	/* DMA Tab Ptr : Ch#15 */
+
+/* ***************************************************************************** */
+#define  DMA16_PTR2                0x1000BC	/* DMA Tab Ptr : Ch#16 */
+
+/* ***************************************************************************** */
+#define  DMA17_PTR2                0x1000C0	/* DMA Tab Ptr : Ch#17 */
+
+/* ***************************************************************************** */
+#define  DMA18_PTR2                0x1000C4	/* DMA Tab Ptr : Ch#18 */
+
+/* ***************************************************************************** */
+#define  DMA19_PTR2                0x1000C8	/* DMA Tab Ptr : Ch#19 */
+
+/* ***************************************************************************** */
+#define  DMA20_PTR2                0x1000CC	/* DMA Tab Ptr : Ch#20 */
+
+/* ***************************************************************************** */
+#define  DMA21_PTR2                0x1000D0	/* DMA Tab Ptr : Ch#21 */
+
+/* ***************************************************************************** */
+#define  DMA22_PTR2                0x1000D4	/* DMA Tab Ptr : Ch#22 */
+
+/* ***************************************************************************** */
+#define  DMA23_PTR2                0x1000D8	/* DMA Tab Ptr : Ch#23 */
+
+/* ***************************************************************************** */
+#define  DMA24_PTR2                0x1000DC	/* DMA Tab Ptr : Ch#24 */
+
+/* ***************************************************************************** */
+#define  DMA25_PTR2                0x1000E0	/* DMA Tab Ptr : Ch#25 */
+
+/* ***************************************************************************** */
+#define  DMA26_PTR2                0x1000E4	/* DMA Tab Ptr : Ch#26 */
+
+/* ***************************************************************************** */
+#define  DMA1_CNT1                 0x100100	/* DMA BuFFer Size : Ch#1 */
+
+/* ***************************************************************************** */
+#define  DMA2_CNT1                 0x100104	/* DMA BuFFer Size : Ch#2 */
+
+/* ***************************************************************************** */
+#define  DMA3_CNT1                 0x100108	/* DMA BuFFer Size : Ch#3 */
+
+/* ***************************************************************************** */
+#define  DMA4_CNT1                 0x10010C	/* DMA BuFFer Size : Ch#4 */
+
+/* ***************************************************************************** */
+#define  DMA5_CNT1                 0x100110	/* DMA BuFFer Size : Ch#5 */
+
+/* ***************************************************************************** */
+#define  DMA6_CNT1                 0x100114	/* DMA BuFFer Size : Ch#6 */
+
+/* ***************************************************************************** */
+#define  DMA7_CNT1                 0x100118	/* DMA BuFFer Size : Ch#7 */
+
+/* ***************************************************************************** */
+#define  DMA8_CNT1                 0x10011C	/* DMA BuFFer Size : Ch#8 */
+
+/* ***************************************************************************** */
+#define  DMA9_CNT1                 0x100120	/* DMA BuFFer Size : Ch#9 */
+
+/* ***************************************************************************** */
+#define  DMA10_CNT1                0x100124	/* DMA BuFFer Size : Ch#10 */
+
+/* ***************************************************************************** */
+#define  DMA11_CNT1                0x100128	/* DMA BuFFer Size : Ch#11 */
+
+/* ***************************************************************************** */
+#define  DMA12_CNT1                0x10012C	/* DMA BuFFer Size : Ch#12 */
+
+/* ***************************************************************************** */
+#define  DMA13_CNT1                0x100130	/* DMA BuFFer Size : Ch#13 */
+
+/* ***************************************************************************** */
+#define  DMA14_CNT1                0x100134	/* DMA BuFFer Size : Ch#14 */
+
+/* ***************************************************************************** */
+#define  DMA15_CNT1                0x100138	/* DMA BuFFer Size : Ch#15 */
+
+/* ***************************************************************************** */
+#define  DMA16_CNT1                0x10013C	/* DMA BuFFer Size : Ch#16 */
+
+/* ***************************************************************************** */
+#define  DMA17_CNT1                0x100140	/* DMA BuFFer Size : Ch#17 */
+
+/* ***************************************************************************** */
+#define  DMA18_CNT1                0x100144	/* DMA BuFFer Size : Ch#18 */
+
+/* ***************************************************************************** */
+#define  DMA19_CNT1                0x100148	/* DMA BuFFer Size : Ch#19 */
+
+/* ***************************************************************************** */
+#define  DMA20_CNT1                0x10014C	/* DMA BuFFer Size : Ch#20 */
+
+/* ***************************************************************************** */
+#define  DMA21_CNT1                0x100150	/* DMA BuFFer Size : Ch#21 */
+
+/* ***************************************************************************** */
+#define  DMA22_CNT1                0x100154	/* DMA BuFFer Size : Ch#22 */
+
+/* ***************************************************************************** */
+#define  DMA23_CNT1                0x100158	/* DMA BuFFer Size : Ch#23 */
+
+/* ***************************************************************************** */
+#define  DMA24_CNT1                0x10015C	/* DMA BuFFer Size : Ch#24 */
+
+/* ***************************************************************************** */
+#define  DMA25_CNT1                0x100160	/* DMA BuFFer Size : Ch#25 */
+
+/* ***************************************************************************** */
+#define  DMA26_CNT1                0x100164	/* DMA BuFFer Size : Ch#26 */
+
+/* ***************************************************************************** */
+#define  DMA1_CNT2                 0x100180	/* DMA Table Size : Ch#1 */
+
+/* ***************************************************************************** */
+#define  DMA2_CNT2                 0x100184	/* DMA Table Size : Ch#2 */
+
+/* ***************************************************************************** */
+#define  DMA3_CNT2                 0x100188	/* DMA Table Size : Ch#3 */
+
+/* ***************************************************************************** */
+#define  DMA4_CNT2                 0x10018C	/* DMA Table Size : Ch#4 */
+
+/* ***************************************************************************** */
+#define  DMA5_CNT2                 0x100190	/* DMA Table Size : Ch#5 */
+
+/* ***************************************************************************** */
+#define  DMA6_CNT2                 0x100194	/* DMA Table Size : Ch#6 */
+
+/* ***************************************************************************** */
+#define  DMA7_CNT2                 0x100198	/* DMA Table Size : Ch#7 */
+
+/* ***************************************************************************** */
+#define  DMA8_CNT2                 0x10019C	/* DMA Table Size : Ch#8 */
+
+/* ***************************************************************************** */
+#define  DMA9_CNT2                 0x1001A0	/* DMA Table Size : Ch#9 */
+
+/* ***************************************************************************** */
+#define  DMA10_CNT2                0x1001A4	/* DMA Table Size : Ch#10 */
+
+/* ***************************************************************************** */
+#define  DMA11_CNT2                0x1001A8	/* DMA Table Size : Ch#11 */
+
+/* ***************************************************************************** */
+#define  DMA12_CNT2                0x1001AC	/* DMA Table Size : Ch#12 */
+
+/* ***************************************************************************** */
+#define  DMA13_CNT2                0x1001B0	/* DMA Table Size : Ch#13 */
+
+/* ***************************************************************************** */
+#define  DMA14_CNT2                0x1001B4	/* DMA Table Size : Ch#14 */
+
+/* ***************************************************************************** */
+#define  DMA15_CNT2                0x1001B8	/* DMA Table Size : Ch#15 */
+
+/* ***************************************************************************** */
+#define  DMA16_CNT2                0x1001BC	/* DMA Table Size : Ch#16 */
+
+/* ***************************************************************************** */
+#define  DMA17_CNT2                0x1001C0	/* DMA Table Size : Ch#17 */
+
+/* ***************************************************************************** */
+#define  DMA18_CNT2                0x1001C4	/* DMA Table Size : Ch#18 */
+
+/* ***************************************************************************** */
+#define  DMA19_CNT2                0x1001C8	/* DMA Table Size : Ch#19 */
+
+/* ***************************************************************************** */
+#define  DMA20_CNT2                0x1001CC	/* DMA Table Size : Ch#20 */
+
+/* ***************************************************************************** */
+#define  DMA21_CNT2                0x1001D0	/* DMA Table Size : Ch#21 */
+
+/* ***************************************************************************** */
+#define  DMA22_CNT2                0x1001D4	/* DMA Table Size : Ch#22 */
+
+/* ***************************************************************************** */
+#define  DMA23_CNT2                0x1001D8	/* DMA Table Size : Ch#23 */
+
+/* ***************************************************************************** */
+#define  DMA24_CNT2                0x1001DC	/* DMA Table Size : Ch#24 */
+
+/* ***************************************************************************** */
+#define  DMA25_CNT2                0x1001E0	/* DMA Table Size : Ch#25 */
+
+/* ***************************************************************************** */
+#define  DMA26_CNT2                0x1001E4	/* DMA Table Size : Ch#26 */
+
+/* ***************************************************************************** */
+ /* ITG */
+/* ***************************************************************************** */
+#define  TM_CNT_LDW                0x110000	/* Timer : Counter low */
+
+/* ***************************************************************************** */
+#define  TM_CNT_UW                 0x110004	/* Timer : Counter high word */
+
+/* ***************************************************************************** */
+#define  TM_LMT_LDW                0x110008	/* Timer : Limit low */
+
+/* ***************************************************************************** */
+#define  TM_LMT_UW                 0x11000C	/* Timer : Limit high word */
+
+/* ***************************************************************************** */
+#define  GP0_IO                    0x110010	/* GPIO output enables data I/O */
+#define  FLD_GP_OE                 0x00FF0000	/* GPIO: GP_OE output enable */
+#define  FLD_GP_IN                 0x0000FF00	/* GPIO: GP_IN status */
+#define  FLD_GP_OUT                0x000000FF	/* GPIO: GP_OUT control */
+
+/* ***************************************************************************** */
+#define  GPIO_ISM                  0x110014	/* GPIO interrupt sensitivity mode */
+#define  FLD_GP_ISM_SNS            0x00000070
+#define  FLD_GP_ISM_POL            0x00000007
+
+/* ***************************************************************************** */
+#define  SOFT_RESET                0x11001C	/* Output system reset reg */
+#define  FLD_PECOS_SOFT_RESET      0x00000001
+
+/* ***************************************************************************** */
+#define  MC416_RWD                 0x110020	/* MC416 GPIO[18:3] pin */
+#define  MC416_OEN                 0x110024	/* Output enable of GPIO[18:3] */
+#define  MC416_CTL                 0x110028
+
+/* ***************************************************************************** */
+#define  ALT_PIN_OUT_SEL           0x11002C	/* Alternate GPIO output select */
+
+#define  FLD_ALT_GPIO_OUT_SEL      0xF0000000
+/* 0          Disabled <-- default */
+/* 1          GPIO[0] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+/* 8          ATT_IF */
+
+#define  FLD_AUX_PLL_CLK_ALT_SEL   0x0F000000
+/* 0          AUX_PLL_CLK<-- default */
+/* 1          GPIO[2] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_IR_TX_ALT_SEL         0x00F00000
+/* 0          IR_TX <-- default */
+/* 1          GPIO[1] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_IR_RX_ALT_SEL         0x000F0000
+/* 0          IR_RX <-- default */
+/* 1          GPIO[0] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_GPIO10_ALT_SEL        0x0000F000
+/* 0          GPIO[10] <-- default */
+/* 1          GPIO[0] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_GPIO2_ALT_SEL         0x00000F00
+/* 0          GPIO[2] <-- default */
+/* 1          GPIO[1] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_GPIO1_ALT_SEL         0x000000F0
+/* 0          GPIO[1] <-- default */
+/* 1          GPIO[0] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  FLD_GPIO0_ALT_SEL         0x0000000F
+/* 0          GPIO[0] <-- default */
+/* 1          GPIO[1] */
+/* 2          GPIO[10] */
+/* 3          VIP_656_DATA_VAL */
+/* 4          VIP_656_DATA[0] */
+/* 5          VIP_656_CLK */
+/* 6          VIP_656_DATA_EXT[1] */
+/* 7          VIP_656_DATA_EXT[0] */
+
+#define  ALT_PIN_IN_SEL            0x110030	/* Alternate GPIO input select */
+
+#define  FLD_GPIO10_ALT_IN_SEL     0x0000F000
+/* 0          GPIO[10] <-- default */
+/* 1          IR_RX */
+/* 2          IR_TX */
+/* 3          AUX_PLL_CLK */
+/* 4          IF_ATT_SEL */
+/* 5          GPIO[0] */
+/* 6          GPIO[1] */
+/* 7          GPIO[2] */
+
+#define  FLD_GPIO2_ALT_IN_SEL      0x00000F00
+/* 0          GPIO[2] <-- default */
+/* 1          IR_RX */
+/* 2          IR_TX */
+/* 3          AUX_PLL_CLK */
+/* 4          IF_ATT_SEL */
+
+#define  FLD_GPIO1_ALT_IN_SEL      0x000000F0
+/* 0          GPIO[1] <-- default */
+/* 1          IR_RX */
+/* 2          IR_TX */
+/* 3          AUX_PLL_CLK */
+/* 4          IF_ATT_SEL */
+
+#define  FLD_GPIO0_ALT_IN_SEL      0x0000000F
+/* 0          GPIO[0] <-- default */
+/* 1          IR_RX */
+/* 2          IR_TX */
+/* 3          AUX_PLL_CLK */
+/* 4          IF_ATT_SEL */
+
+/* ***************************************************************************** */
+#define  TEST_BUS_CTL1             0x110040	/* Test bus control register #1 */
+
+/* ***************************************************************************** */
+#define  TEST_BUS_CTL2             0x110044	/* Test bus control register #2 */
+
+/* ***************************************************************************** */
+#define  CLK_DELAY                 0x110048	/* Clock delay */
+#define  FLD_MOE_CLK_DIS           0x80000000	/* Disable MoE clock */
+
+/* ***************************************************************************** */
+#define  PAD_CTRL                  0x110068	/* Pad drive strength control */
+
+/* ***************************************************************************** */
+#define  MBIST_CTRL                0x110050	/* SRAM memory built-in self test control */
+
+/* ***************************************************************************** */
+#define  MBIST_STAT                0x110054	/* SRAM memory built-in self test status */
+
+/* ***************************************************************************** */
+/* PLL registers */
+/* ***************************************************************************** */
+#define  PLL_A_INT_FRAC            0x110088
+#define  PLL_A_POST_STAT_BIST      0x11008C
+#define  PLL_B_INT_FRAC            0x110090
+#define  PLL_B_POST_STAT_BIST      0x110094
+#define  PLL_C_INT_FRAC            0x110098
+#define  PLL_C_POST_STAT_BIST      0x11009C
+#define  PLL_D_INT_FRAC            0x1100A0
+#define  PLL_D_POST_STAT_BIST      0x1100A4
+
+#define  CLK_RST                   0x11002C
+#define  FLD_VID_I_CLK_NOE         0x00001000
+#define  FLD_VID_J_CLK_NOE         0x00002000
+#define  FLD_USE_ALT_PLL_REF       0x00004000
+
+#define  VID_CH_MODE_SEL           0x110078
+#define  VID_CH_CLK_SEL            0x11007C
+
+/* ***************************************************************************** */
+#define  VBI_A_DMA                 0x130008	/* VBI A DMA data port */
+
+/* ***************************************************************************** */
+#define  VID_A_VIP_CTL             0x130080	/* Video A VIP format control */
+#define  FLD_VIP_MODE              0x00000001
+
+/* ***************************************************************************** */
+#define  VID_A_PIXEL_FRMT          0x130084	/* Video A pixel format */
+#define  FLD_VID_A_GAMMA_DIS       0x00000008
+#define  FLD_VID_A_FORMAT          0x00000007
+#define  FLD_VID_A_GAMMA_FACTOR    0x00000010
+
+/* ***************************************************************************** */
+#define  VID_A_VBI_CTL             0x130088	/* Video A VBI miscellaneous control */
+#define  FLD_VID_A_VIP_EXT         0x00000003
+
+/* ***************************************************************************** */
+#define  VID_B_DMA                 0x130100	/* Video B DMA data port */
+
+/* ***************************************************************************** */
+#define  VBI_B_DMA                 0x130108	/* VBI B DMA data port */
+
+/* ***************************************************************************** */
+#define  VID_B_SRC_SEL             0x130144	/* Video B source select */
+#define  FLD_VID_B_SRC_SEL         0x00000000
+
+/* ***************************************************************************** */
+#define  VID_B_LNGTH               0x130150	/* Video B line length */
+#define  FLD_VID_B_LN_LNGTH        0x00000FFF
+
+/* ***************************************************************************** */
+#define  VID_B_VIP_CTL             0x130180	/* Video B VIP format control */
+
+/* ***************************************************************************** */
+#define  VID_B_PIXEL_FRMT          0x130184	/* Video B pixel format */
+#define  FLD_VID_B_GAMMA_DIS       0x00000008
+#define  FLD_VID_B_FORMAT          0x00000007
+#define  FLD_VID_B_GAMMA_FACTOR    0x00000010
+
+/* ***************************************************************************** */
+#define  VID_C_DMA                 0x130200	/* Video C DMA data port */
+
+/* ***************************************************************************** */
+#define  VID_C_LNGTH               0x130250	/* Video C line length */
+#define  FLD_VID_C_LN_LNGTH        0x00000FFF
+
+/* ***************************************************************************** */
+/* Video Destination Channels */
+/* ***************************************************************************** */
+
+#define  VID_DST_A_GPCNT           0x130020	/* Video A general purpose counter */
+#define  VID_DST_B_GPCNT           0x130120	/* Video B general purpose counter */
+#define  VID_DST_C_GPCNT           0x130220	/* Video C general purpose counter */
+#define  VID_DST_D_GPCNT           0x130320	/* Video D general purpose counter */
+#define  VID_DST_E_GPCNT           0x130420	/* Video E general purpose counter */
+#define  VID_DST_F_GPCNT           0x130520	/* Video F general purpose counter */
+#define  VID_DST_G_GPCNT           0x130620	/* Video G general purpose counter */
+#define  VID_DST_H_GPCNT           0x130720	/* Video H general purpose counter */
+
+/* ***************************************************************************** */
+
+#define  VID_DST_A_GPCNT_CTL       0x130030	/* Video A general purpose control */
+#define  VID_DST_B_GPCNT_CTL       0x130130	/* Video B general purpose control */
+#define  VID_DST_C_GPCNT_CTL       0x130230	/* Video C general purpose control */
+#define  VID_DST_D_GPCNT_CTL       0x130330	/* Video D general purpose control */
+#define  VID_DST_E_GPCNT_CTL       0x130430	/* Video E general purpose control */
+#define  VID_DST_F_GPCNT_CTL       0x130530	/* Video F general purpose control */
+#define  VID_DST_G_GPCNT_CTL       0x130630	/* Video G general purpose control */
+#define  VID_DST_H_GPCNT_CTL       0x130730	/* Video H general purpose control */
+
+/* ***************************************************************************** */
+
+#define  VID_DST_A_DMA_CTL         0x130040	/* Video A DMA control */
+#define  VID_DST_B_DMA_CTL         0x130140	/* Video B DMA control */
+#define  VID_DST_C_DMA_CTL         0x130240	/* Video C DMA control */
+#define  VID_DST_D_DMA_CTL         0x130340	/* Video D DMA control */
+#define  VID_DST_E_DMA_CTL         0x130440	/* Video E DMA control */
+#define  VID_DST_F_DMA_CTL         0x130540	/* Video F DMA control */
+#define  VID_DST_G_DMA_CTL         0x130640	/* Video G DMA control */
+#define  VID_DST_H_DMA_CTL         0x130740	/* Video H DMA control */
+
+#define  FLD_VID_RISC_EN           0x00000010
+#define  FLD_VID_FIFO_EN           0x00000001
+
+/* ***************************************************************************** */
+
+#define  VID_DST_A_VIP_CTL         0x130080	/* Video A VIP control */
+#define  VID_DST_B_VIP_CTL         0x130180	/* Video B VIP control */
+#define  VID_DST_C_VIP_CTL         0x130280	/* Video C VIP control */
+#define  VID_DST_D_VIP_CTL         0x130380	/* Video D VIP control */
+#define  VID_DST_E_VIP_CTL         0x130480	/* Video E VIP control */
+#define  VID_DST_F_VIP_CTL         0x130580	/* Video F VIP control */
+#define  VID_DST_G_VIP_CTL         0x130680	/* Video G VIP control */
+#define  VID_DST_H_VIP_CTL         0x130780	/* Video H VIP control */
+
+/* ***************************************************************************** */
+
+#define  VID_DST_A_PIX_FRMT        0x130084	/* Video A Pixel format */
+#define  VID_DST_B_PIX_FRMT        0x130184	/* Video B Pixel format */
+#define  VID_DST_C_PIX_FRMT        0x130284	/* Video C Pixel format */
+#define  VID_DST_D_PIX_FRMT        0x130384	/* Video D Pixel format */
+#define  VID_DST_E_PIX_FRMT        0x130484	/* Video E Pixel format */
+#define  VID_DST_F_PIX_FRMT        0x130584	/* Video F Pixel format */
+#define  VID_DST_G_PIX_FRMT        0x130684	/* Video G Pixel format */
+#define  VID_DST_H_PIX_FRMT        0x130784	/* Video H Pixel format */
+
+/* ***************************************************************************** */
+/* Video Source Channels */
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_GPCNT_CTL       0x130804	/* Video A general purpose control */
+#define  VID_SRC_B_GPCNT_CTL       0x130904	/* Video B general purpose control */
+#define  VID_SRC_C_GPCNT_CTL       0x130A04	/* Video C general purpose control */
+#define  VID_SRC_D_GPCNT_CTL       0x130B04	/* Video D general purpose control */
+#define  VID_SRC_E_GPCNT_CTL       0x130C04	/* Video E general purpose control */
+#define  VID_SRC_F_GPCNT_CTL       0x130D04	/* Video F general purpose control */
+#define  VID_SRC_I_GPCNT_CTL       0x130E04	/* Video I general purpose control */
+#define  VID_SRC_J_GPCNT_CTL       0x130F04	/* Video J general purpose control */
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_GPCNT           0x130808	/* Video A general purpose counter */
+#define  VID_SRC_B_GPCNT           0x130908	/* Video B general purpose counter */
+#define  VID_SRC_C_GPCNT           0x130A08	/* Video C general purpose counter */
+#define  VID_SRC_D_GPCNT           0x130B08	/* Video D general purpose counter */
+#define  VID_SRC_E_GPCNT           0x130C08	/* Video E general purpose counter */
+#define  VID_SRC_F_GPCNT           0x130D08	/* Video F general purpose counter */
+#define  VID_SRC_I_GPCNT           0x130E08	/* Video I general purpose counter */
+#define  VID_SRC_J_GPCNT           0x130F08	/* Video J general purpose counter */
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_DMA_CTL         0x13080C	/* Video A DMA control */
+#define  VID_SRC_B_DMA_CTL         0x13090C	/* Video B DMA control */
+#define  VID_SRC_C_DMA_CTL         0x130A0C	/* Video C DMA control */
+#define  VID_SRC_D_DMA_CTL         0x130B0C	/* Video D DMA control */
+#define  VID_SRC_E_DMA_CTL         0x130C0C	/* Video E DMA control */
+#define  VID_SRC_F_DMA_CTL         0x130D0C	/* Video F DMA control */
+#define  VID_SRC_I_DMA_CTL         0x130E0C	/* Video I DMA control */
+#define  VID_SRC_J_DMA_CTL         0x130F0C	/* Video J DMA control */
+
+#define  FLD_APB_RISC_EN           0x00000010
+#define  FLD_APB_FIFO_EN           0x00000001
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_FMT_CTL         0x130810	/* Video A format control */
+#define  VID_SRC_B_FMT_CTL         0x130910	/* Video B format control */
+#define  VID_SRC_C_FMT_CTL         0x130A10	/* Video C format control */
+#define  VID_SRC_D_FMT_CTL         0x130B10	/* Video D format control */
+#define  VID_SRC_E_FMT_CTL         0x130C10	/* Video E format control */
+#define  VID_SRC_F_FMT_CTL         0x130D10	/* Video F format control */
+#define  VID_SRC_I_FMT_CTL         0x130E10	/* Video I format control */
+#define  VID_SRC_J_FMT_CTL         0x130F10	/* Video J format control */
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_ACTIVE_CTL1     0x130814	/* Video A active control      1 */
+#define  VID_SRC_B_ACTIVE_CTL1     0x130914	/* Video B active control      1 */
+#define  VID_SRC_C_ACTIVE_CTL1     0x130A14	/* Video C active control      1 */
+#define  VID_SRC_D_ACTIVE_CTL1     0x130B14	/* Video D active control      1 */
+#define  VID_SRC_E_ACTIVE_CTL1     0x130C14	/* Video E active control      1 */
+#define  VID_SRC_F_ACTIVE_CTL1     0x130D14	/* Video F active control      1 */
+#define  VID_SRC_I_ACTIVE_CTL1     0x130E14	/* Video I active control      1 */
+#define  VID_SRC_J_ACTIVE_CTL1     0x130F14	/* Video J active control      1 */
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_ACTIVE_CTL2     0x130818	/* Video A active control      2 */
+#define  VID_SRC_B_ACTIVE_CTL2     0x130918	/* Video B active control      2 */
+#define  VID_SRC_C_ACTIVE_CTL2     0x130A18	/* Video C active control      2 */
+#define  VID_SRC_D_ACTIVE_CTL2     0x130B18	/* Video D active control      2 */
+#define  VID_SRC_E_ACTIVE_CTL2     0x130C18	/* Video E active control      2 */
+#define  VID_SRC_F_ACTIVE_CTL2     0x130D18	/* Video F active control      2 */
+#define  VID_SRC_I_ACTIVE_CTL2     0x130E18	/* Video I active control      2 */
+#define  VID_SRC_J_ACTIVE_CTL2     0x130F18	/* Video J active control      2 */
+
+/* ***************************************************************************** */
+
+#define  VID_SRC_A_CDT_SZ          0x13081C	/* Video A CDT size */
+#define  VID_SRC_B_CDT_SZ          0x13091C	/* Video B CDT size */
+#define  VID_SRC_C_CDT_SZ          0x130A1C	/* Video C CDT size */
+#define  VID_SRC_D_CDT_SZ          0x130B1C	/* Video D CDT size */
+#define  VID_SRC_E_CDT_SZ          0x130C1C	/* Video E CDT size */
+#define  VID_SRC_F_CDT_SZ          0x130D1C	/* Video F CDT size */
+#define  VID_SRC_I_CDT_SZ          0x130E1C	/* Video I CDT size */
+#define  VID_SRC_J_CDT_SZ          0x130F1C	/* Video J CDT size */
+
+/* ***************************************************************************** */
+/* Audio I/F */
+/* ***************************************************************************** */
+#define  AUD_DST_A_DMA             0x140000	/* Audio Int A DMA data port */
+#define  AUD_SRC_A_DMA             0x140008	/* Audio Int A DMA data port */
+
+#define  AUD_A_GPCNT               0x140010	/* Audio Int A gp counter */
+#define  FLD_AUD_A_GP_CNT          0x0000FFFF
+
+#define  AUD_A_GPCNT_CTL           0x140014	/* Audio Int A gp control */
+
+#define  AUD_A_LNGTH               0x140018	/* Audio Int A line length */
+
+#define  AUD_A_CFG                 0x14001C	/* Audio Int A configuration */
+
+/* ***************************************************************************** */
+#define  AUD_DST_B_DMA             0x140100	/* Audio Int B DMA data port */
+#define  AUD_SRC_B_DMA             0x140108	/* Audio Int B DMA data port */
+
+#define  AUD_B_GPCNT               0x140110	/* Audio Int B gp counter */
+#define  FLD_AUD_B_GP_CNT          0x0000FFFF
+
+#define  AUD_B_GPCNT_CTL           0x140114	/* Audio Int B gp control */
+
+#define  AUD_B_LNGTH               0x140118	/* Audio Int B line length */
+
+#define  AUD_B_CFG                 0x14011C	/* Audio Int B configuration */
+
+/* ***************************************************************************** */
+#define  AUD_DST_C_DMA             0x140200	/* Audio Int C DMA data port */
+#define  AUD_SRC_C_DMA             0x140208	/* Audio Int C DMA data port */
+
+#define  AUD_C_GPCNT               0x140210	/* Audio Int C gp counter */
+#define  FLD_AUD_C_GP_CNT          0x0000FFFF
+
+#define  AUD_C_GPCNT_CTL           0x140214	/* Audio Int C gp control */
+
+#define  AUD_C_LNGTH               0x140218	/* Audio Int C line length */
+
+#define  AUD_C_CFG                 0x14021C	/* Audio Int C configuration */
+
+/* ***************************************************************************** */
+#define  AUD_DST_D_DMA             0x140300	/* Audio Int D DMA data port */
+#define  AUD_SRC_D_DMA             0x140308	/* Audio Int D DMA data port */
+
+#define  AUD_D_GPCNT               0x140310	/* Audio Int D gp counter */
+#define  FLD_AUD_D_GP_CNT          0x0000FFFF
+
+#define  AUD_D_GPCNT_CTL           0x140314	/* Audio Int D gp control */
+
+#define  AUD_D_LNGTH               0x140318	/* Audio Int D line length */
+
+#define  AUD_D_CFG                 0x14031C	/* Audio Int D configuration */
+
+/* ***************************************************************************** */
+#define  AUD_SRC_E_DMA             0x140400	/* Audio Int E DMA data port */
+
+#define  AUD_E_GPCNT               0x140410	/* Audio Int E gp counter */
+#define  FLD_AUD_E_GP_CNT          0x0000FFFF
+
+#define  AUD_E_GPCNT_CTL           0x140414	/* Audio Int E gp control */
+
+#define  AUD_E_CFG                 0x14041C	/* Audio Int E configuration */
+
+/* ***************************************************************************** */
+
+#define  FLD_AUD_DST_LN_LNGTH      0x00000FFF
+
+#define  FLD_AUD_DST_PK_MODE       0x00004000
+
+#define  FLD_AUD_CLK_ENABLE        0x00000200
+
+#define  FLD_AUD_MASTER_MODE       0x00000002
+
+#define  FLD_AUD_SONY_MODE         0x00000001
+
+#define  FLD_AUD_CLK_SELECT_PLL_D  0x00001800
+
+#define  FLD_AUD_DST_ENABLE        0x00020000
+
+#define  FLD_AUD_SRC_ENABLE        0x00010000
+
+/* ***************************************************************************** */
+#define  AUD_INT_DMA_CTL           0x140500	/* Audio Int DMA control */
+
+#define  FLD_AUD_SRC_E_RISC_EN     0x00008000
+#define  FLD_AUD_SRC_C_RISC_EN     0x00004000
+#define  FLD_AUD_SRC_B_RISC_EN     0x00002000
+#define  FLD_AUD_SRC_A_RISC_EN     0x00001000
+
+#define  FLD_AUD_DST_D_RISC_EN     0x00000800
+#define  FLD_AUD_DST_C_RISC_EN     0x00000400
+#define  FLD_AUD_DST_B_RISC_EN     0x00000200
+#define  FLD_AUD_DST_A_RISC_EN     0x00000100
+
+#define  FLD_AUD_SRC_E_FIFO_EN     0x00000080
+#define  FLD_AUD_SRC_C_FIFO_EN     0x00000040
+#define  FLD_AUD_SRC_B_FIFO_EN     0x00000020
+#define  FLD_AUD_SRC_A_FIFO_EN     0x00000010
+
+#define  FLD_AUD_DST_D_FIFO_EN     0x00000008
+#define  FLD_AUD_DST_C_FIFO_EN     0x00000004
+#define  FLD_AUD_DST_B_FIFO_EN     0x00000002
+#define  FLD_AUD_DST_A_FIFO_EN     0x00000001
+
+/* ***************************************************************************** */
+/*  */
+/* Mobilygen Interface Registers */
+/*  */
+/* ***************************************************************************** */
+/* Mobilygen Interface A */
+/* ***************************************************************************** */
+#define  MB_IF_A_DMA               0x150000	/* MBIF A DMA data port */
+#define  MB_IF_A_GPCN              0x150008	/* MBIF A GP counter */
+#define  MB_IF_A_GPCN_CTRL         0x15000C
+#define  MB_IF_A_DMA_CTRL          0x150010
+#define  MB_IF_A_LENGTH            0x150014
+#define  MB_IF_A_HDMA_XFER_SZ      0x150018
+#define  MB_IF_A_HCMD              0x15001C
+#define  MB_IF_A_HCONFIG           0x150020
+#define  MB_IF_A_DATA_STRUCT_0     0x150024
+#define  MB_IF_A_DATA_STRUCT_1     0x150028
+#define  MB_IF_A_DATA_STRUCT_2     0x15002C
+#define  MB_IF_A_DATA_STRUCT_3     0x150030
+#define  MB_IF_A_DATA_STRUCT_4     0x150034
+#define  MB_IF_A_DATA_STRUCT_5     0x150038
+#define  MB_IF_A_DATA_STRUCT_6     0x15003C
+#define  MB_IF_A_DATA_STRUCT_7     0x150040
+#define  MB_IF_A_DATA_STRUCT_8     0x150044
+#define  MB_IF_A_DATA_STRUCT_9     0x150048
+#define  MB_IF_A_DATA_STRUCT_A     0x15004C
+#define  MB_IF_A_DATA_STRUCT_B     0x150050
+#define  MB_IF_A_DATA_STRUCT_C     0x150054
+#define  MB_IF_A_DATA_STRUCT_D     0x150058
+#define  MB_IF_A_DATA_STRUCT_E     0x15005C
+#define  MB_IF_A_DATA_STRUCT_F     0x150060
+/* ***************************************************************************** */
+/* Mobilygen Interface B */
+/* ***************************************************************************** */
+#define  MB_IF_B_DMA               0x160000	/* MBIF A DMA data port */
+#define  MB_IF_B_GPCN              0x160008	/* MBIF A GP counter */
+#define  MB_IF_B_GPCN_CTRL         0x16000C
+#define  MB_IF_B_DMA_CTRL          0x160010
+#define  MB_IF_B_LENGTH            0x160014
+#define  MB_IF_B_HDMA_XFER_SZ      0x160018
+#define  MB_IF_B_HCMD              0x16001C
+#define  MB_IF_B_HCONFIG           0x160020
+#define  MB_IF_B_DATA_STRUCT_0     0x160024
+#define  MB_IF_B_DATA_STRUCT_1     0x160028
+#define  MB_IF_B_DATA_STRUCT_2     0x16002C
+#define  MB_IF_B_DATA_STRUCT_3     0x160030
+#define  MB_IF_B_DATA_STRUCT_4     0x160034
+#define  MB_IF_B_DATA_STRUCT_5     0x160038
+#define  MB_IF_B_DATA_STRUCT_6     0x16003C
+#define  MB_IF_B_DATA_STRUCT_7     0x160040
+#define  MB_IF_B_DATA_STRUCT_8     0x160044
+#define  MB_IF_B_DATA_STRUCT_9     0x160048
+#define  MB_IF_B_DATA_STRUCT_A     0x16004C
+#define  MB_IF_B_DATA_STRUCT_B     0x160050
+#define  MB_IF_B_DATA_STRUCT_C     0x160054
+#define  MB_IF_B_DATA_STRUCT_D     0x160058
+#define  MB_IF_B_DATA_STRUCT_E     0x16005C
+#define  MB_IF_B_DATA_STRUCT_F     0x160060
+
+/* MB_DMA_CTRL */
+#define  FLD_MB_IF_RISC_EN         0x00000010
+#define  FLD_MB_IF_FIFO_EN         0x00000001
+
+/* MB_LENGTH */
+#define  FLD_MB_IF_LN_LNGTH        0x00000FFF
+
+/* MB_HCMD register */
+#define  FLD_MB_HCMD_H_GO          0x80000000
+#define  FLD_MB_HCMD_H_BUSY        0x40000000
+#define  FLD_MB_HCMD_H_DMA_HOLD    0x10000000
+#define  FLD_MB_HCMD_H_DMA_BUSY    0x08000000
+#define  FLD_MB_HCMD_H_DMA_TYPE    0x04000000
+#define  FLD_MB_HCMD_H_DMA_XACT    0x02000000
+#define  FLD_MB_HCMD_H_RW_N        0x01000000
+#define  FLD_MB_HCMD_H_ADDR        0x00FF0000
+#define  FLD_MB_HCMD_H_DATA        0x0000FFFF
+
+/* ***************************************************************************** */
+/* I2C #1 */
+/* ***************************************************************************** */
+#define  I2C1_ADDR                 0x180000	/* I2C #1 address */
+#define  FLD_I2C_DADDR             0xfe000000	/* RW [31:25] I2C Device Address */
+						 /* RO [24] reserved */
+/* ***************************************************************************** */
+#define  FLD_I2C_SADDR             0x00FFFFFF	/* RW [23:0]  I2C Sub-address */
+
+/* ***************************************************************************** */
+#define  I2C1_WDATA                0x180004	/* I2C #1 write data */
+#define  FLD_I2C_WDATA             0xFFFFFFFF	/* RW [31:0] */
+
+/* ***************************************************************************** */
+#define  I2C1_CTRL                 0x180008	/* I2C #1 control */
+#define  FLD_I2C_PERIOD            0xFF000000	/* RW [31:24] */
+#define  FLD_I2C_SCL_IN            0x00200000	/* RW [21] */
+#define  FLD_I2C_SDA_IN            0x00100000	/* RW [20] */
+						 /* RO [19:18] reserved */
+#define  FLD_I2C_SCL_OUT           0x00020000	/* RW [17] */
+#define  FLD_I2C_SDA_OUT           0x00010000	/* RW [16] */
+						 /* RO [15] reserved */
+#define  FLD_I2C_DATA_LEN          0x00007000	/* RW [14:12] */
+#define  FLD_I2C_SADDR_INC         0x00000800	/* RW [11] */
+						 /* RO [10:9] reserved */
+#define  FLD_I2C_SADDR_LEN         0x00000300	/* RW [9:8] */
+						 /* RO [7:6] reserved */
+#define  FLD_I2C_SOFT              0x00000020	/* RW [5] */
+#define  FLD_I2C_NOSTOP            0x00000010	/* RW [4] */
+#define  FLD_I2C_EXTEND            0x00000008	/* RW [3] */
+#define  FLD_I2C_SYNC              0x00000004	/* RW [2] */
+#define  FLD_I2C_READ_SA           0x00000002	/* RW [1] */
+#define  FLD_I2C_READ_WRN          0x00000001	/* RW [0] */
+
+/* ***************************************************************************** */
+#define  I2C1_RDATA                0x18000C	/* I2C #1 read data */
+#define  FLD_I2C_RDATA             0xFFFFFFFF	/* RO [31:0] */
+
+/* ***************************************************************************** */
+#define  I2C1_STAT                 0x180010	/* I2C #1 status */
+#define  FLD_I2C_XFER_IN_PROG      0x00000002	/* RO [1] */
+#define  FLD_I2C_RACK              0x00000001	/* RO [0] */
+
+/* ***************************************************************************** */
+/* I2C #2 */
+/* ***************************************************************************** */
+#define  I2C2_ADDR                 0x190000	/* I2C #2 address */
+
+/* ***************************************************************************** */
+#define  I2C2_WDATA                0x190004	/* I2C #2 write data */
+
+/* ***************************************************************************** */
+#define  I2C2_CTRL                 0x190008	/* I2C #2 control */
+
+/* ***************************************************************************** */
+#define  I2C2_RDATA                0x19000C	/* I2C #2 read data */
+
+/* ***************************************************************************** */
+#define  I2C2_STAT                 0x190010	/* I2C #2 status */
+
+/* ***************************************************************************** */
+/* I2C #3 */
+/* ***************************************************************************** */
+#define  I2C3_ADDR                 0x1A0000	/* I2C #3 address */
+
+/* ***************************************************************************** */
+#define  I2C3_WDATA                0x1A0004	/* I2C #3 write data */
+
+/* ***************************************************************************** */
+#define  I2C3_CTRL                 0x1A0008	/* I2C #3 control */
+
+/* ***************************************************************************** */
+#define  I2C3_RDATA                0x1A000C	/* I2C #3 read data */
+
+/* ***************************************************************************** */
+#define  I2C3_STAT                 0x1A0010	/* I2C #3 status */
+
+/* ***************************************************************************** */
+/* UART */
+/* ***************************************************************************** */
+#define  UART_CTL                  0x1B0000	/* UART Control Register */
+#define  FLD_LOOP_BACK_EN          (1 << 7)	/* RW field - default 0 */
+#define  FLD_RX_TRG_SZ             (3 << 2)	/* RW field - default 0 */
+#define  FLD_RX_EN                 (1 << 1)	/* RW field - default 0 */
+#define  FLD_TX_EN                 (1 << 0)	/* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define  UART_BRD                  0x1B0004	/* UART Baud Rate Divisor */
+#define  FLD_BRD                   0x0000FFFF	/* RW field - default 0x197 */
+
+/* ***************************************************************************** */
+#define  UART_DBUF                 0x1B0008	/* UART Tx/Rx Data BuFFer */
+#define  FLD_DB                    0xFFFFFFFF	/* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define  UART_ISR                  0x1B000C	/* UART Interrupt Status */
+#define  FLD_RXD_TIMEOUT_EN        (1 << 7)	/* RW field - default 0 */
+#define  FLD_FRM_ERR_EN            (1 << 6)	/* RW field - default 0 */
+#define  FLD_RXD_RDY_EN            (1 << 5)	/* RW field - default 0 */
+#define  FLD_TXD_EMPTY_EN          (1 << 4)	/* RW field - default 0 */
+#define  FLD_RXD_OVERFLOW          (1 << 3)	/* RW field - default 0 */
+#define  FLD_FRM_ERR               (1 << 2)	/* RW field - default 0 */
+#define  FLD_RXD_RDY               (1 << 1)	/* RW field - default 0 */
+#define  FLD_TXD_EMPTY             (1 << 0)	/* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define  UART_CNT                  0x1B0010	/* UART Tx/Rx FIFO Byte Count */
+#define  FLD_TXD_CNT               (0x1F << 8)	/* RW field - default 0 */
+#define  FLD_RXD_CNT               (0x1F << 0)	/* RW field - default 0 */
+
+/* ***************************************************************************** */
+/* Motion Detection */
+#define  MD_CH0_GRID_BLOCK_YCNT    0x170014
+#define  MD_CH1_GRID_BLOCK_YCNT    0x170094
+#define  MD_CH2_GRID_BLOCK_YCNT    0x170114
+#define  MD_CH3_GRID_BLOCK_YCNT    0x170194
+#define  MD_CH4_GRID_BLOCK_YCNT    0x170214
+#define  MD_CH5_GRID_BLOCK_YCNT    0x170294
+#define  MD_CH6_GRID_BLOCK_YCNT    0x170314
+#define  MD_CH7_GRID_BLOCK_YCNT    0x170394
+
+#define PIXEL_FRMT_422    4
+#define PIXEL_FRMT_411    5
+#define PIXEL_FRMT_Y8     6
+
+#define PIXEL_ENGINE_VIP1 0
+#define PIXEL_ENGINE_VIP2 1
+
+#endif /* Athena_REGISTERS */
diff --git a/drivers/media/pci/cx25821/cx25821-sram.h b/drivers/media/pci/cx25821/cx25821-sram.h
new file mode 100644
index 000000000000..5f05d153bc4d
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-sram.h
@@ -0,0 +1,261 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.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 __ATHENA_SRAM_H__
+#define __ATHENA_SRAM_H__
+
+/* #define RX_SRAM_START_SIZE        = 0;  //  Start of reserved SRAM */
+#define VID_CMDS_SIZE             80	/* Video CMDS size in bytes */
+#define AUDIO_CMDS_SIZE           80	/* AUDIO CMDS size in bytes */
+#define MBIF_CMDS_SIZE            80	/* MBIF  CMDS size in bytes */
+
+/* #define RX_SRAM_POOL_START_SIZE   = 0;  //  Start of useable RX SRAM for buffers */
+#define VID_IQ_SIZE               64	/* VID instruction queue size in bytes */
+#define MBIF_IQ_SIZE              64
+#define AUDIO_IQ_SIZE             64	/* AUD instruction queue size in bytes */
+
+#define VID_CDT_SIZE              64	/* VID cluster descriptor table size in bytes */
+#define MBIF_CDT_SIZE             64	/* MBIF/HBI cluster descriptor table size in bytes */
+#define AUDIO_CDT_SIZE            48	/* AUD cluster descriptor table size in bytes */
+
+/* #define RX_SRAM_POOL_FREE_SIZE    = 16; //  Start of available RX SRAM */
+/* #define RX_SRAM_END_SIZE          = 0;  //  End of RX SRAM */
+
+/* #define TX_SRAM_POOL_START_SIZE   = 0;  //  Start of transmit pool SRAM */
+/* #define MSI_DATA_SIZE             = 64; //  Reserved (MSI Data, RISC working stora */
+
+#define VID_CLUSTER_SIZE          1440	/* VID cluster data line */
+#define AUDIO_CLUSTER_SIZE        128	/* AUDIO cluster data line */
+#define MBIF_CLUSTER_SIZE         1440	/* MBIF/HBI cluster data line */
+
+/* #define TX_SRAM_POOL_FREE_SIZE    = 704;    //  Start of available TX SRAM */
+/* #define TX_SRAM_END_SIZE          = 0;      //  End of TX SRAM */
+
+/* Receive SRAM */
+#define RX_SRAM_START             0x10000
+#define VID_A_DOWN_CMDS           0x10000
+#define VID_B_DOWN_CMDS           0x10050
+#define VID_C_DOWN_CMDS           0x100A0
+#define VID_D_DOWN_CMDS           0x100F0
+#define VID_E_DOWN_CMDS           0x10140
+#define VID_F_DOWN_CMDS           0x10190
+#define VID_G_DOWN_CMDS           0x101E0
+#define VID_H_DOWN_CMDS           0x10230
+#define VID_A_UP_CMDS             0x10280
+#define VID_B_UP_CMDS             0x102D0
+#define VID_C_UP_CMDS             0x10320
+#define VID_D_UP_CMDS             0x10370
+#define VID_E_UP_CMDS             0x103C0
+#define VID_F_UP_CMDS             0x10410
+#define VID_I_UP_CMDS             0x10460
+#define VID_J_UP_CMDS             0x104B0
+#define AUD_A_DOWN_CMDS           0x10500
+#define AUD_B_DOWN_CMDS           0x10550
+#define AUD_C_DOWN_CMDS           0x105A0
+#define AUD_D_DOWN_CMDS           0x105F0
+#define AUD_A_UP_CMDS             0x10640
+#define AUD_B_UP_CMDS             0x10690
+#define AUD_C_UP_CMDS             0x106E0
+#define AUD_E_UP_CMDS             0x10730
+#define MBIF_A_DOWN_CMDS          0x10780
+#define MBIF_B_DOWN_CMDS          0x107D0
+#define DMA_SCRATCH_PAD           0x10820	/* Scratch pad area from 0x10820 to 0x10B40 */
+
+/* #define RX_SRAM_POOL_START        = 0x105B0; */
+
+#define VID_A_IQ                  0x11000
+#define VID_B_IQ                  0x11040
+#define VID_C_IQ                  0x11080
+#define VID_D_IQ                  0x110C0
+#define VID_E_IQ                  0x11100
+#define VID_F_IQ                  0x11140
+#define VID_G_IQ                  0x11180
+#define VID_H_IQ                  0x111C0
+#define VID_I_IQ                  0x11200
+#define VID_J_IQ                  0x11240
+#define AUD_A_IQ                  0x11280
+#define AUD_B_IQ                  0x112C0
+#define AUD_C_IQ                  0x11300
+#define AUD_D_IQ                  0x11340
+#define AUD_E_IQ                  0x11380
+#define MBIF_A_IQ                 0x11000
+#define MBIF_B_IQ                 0x110C0
+
+#define VID_A_CDT                 0x10C00
+#define VID_B_CDT                 0x10C40
+#define VID_C_CDT                 0x10C80
+#define VID_D_CDT                 0x10CC0
+#define VID_E_CDT                 0x10D00
+#define VID_F_CDT                 0x10D40
+#define VID_G_CDT                 0x10D80
+#define VID_H_CDT                 0x10DC0
+#define VID_I_CDT                 0x10E00
+#define VID_J_CDT                 0x10E40
+#define AUD_A_CDT                 0x10E80
+#define AUD_B_CDT                 0x10EB0
+#define AUD_C_CDT                 0x10EE0
+#define AUD_D_CDT                 0x10F10
+#define AUD_E_CDT                 0x10F40
+#define MBIF_A_CDT                0x10C00
+#define MBIF_B_CDT                0x10CC0
+
+/* Cluster Buffer for RX */
+#define VID_A_UP_CLUSTER_1        0x11400
+#define VID_A_UP_CLUSTER_2        0x119A0
+#define VID_A_UP_CLUSTER_3        0x11F40
+#define VID_A_UP_CLUSTER_4        0x124E0
+
+#define VID_B_UP_CLUSTER_1        0x12A80
+#define VID_B_UP_CLUSTER_2        0x13020
+#define VID_B_UP_CLUSTER_3        0x135C0
+#define VID_B_UP_CLUSTER_4        0x13B60
+
+#define VID_C_UP_CLUSTER_1        0x14100
+#define VID_C_UP_CLUSTER_2        0x146A0
+#define VID_C_UP_CLUSTER_3        0x14C40
+#define VID_C_UP_CLUSTER_4        0x151E0
+
+#define VID_D_UP_CLUSTER_1        0x15780
+#define VID_D_UP_CLUSTER_2        0x15D20
+#define VID_D_UP_CLUSTER_3        0x162C0
+#define VID_D_UP_CLUSTER_4        0x16860
+
+#define VID_E_UP_CLUSTER_1        0x16E00
+#define VID_E_UP_CLUSTER_2        0x173A0
+#define VID_E_UP_CLUSTER_3        0x17940
+#define VID_E_UP_CLUSTER_4        0x17EE0
+
+#define VID_F_UP_CLUSTER_1        0x18480
+#define VID_F_UP_CLUSTER_2        0x18A20
+#define VID_F_UP_CLUSTER_3        0x18FC0
+#define VID_F_UP_CLUSTER_4        0x19560
+
+#define VID_I_UP_CLUSTER_1        0x19B00
+#define VID_I_UP_CLUSTER_2        0x1A0A0
+#define VID_I_UP_CLUSTER_3        0x1A640
+#define VID_I_UP_CLUSTER_4        0x1ABE0
+
+#define VID_J_UP_CLUSTER_1        0x1B180
+#define VID_J_UP_CLUSTER_2        0x1B720
+#define VID_J_UP_CLUSTER_3        0x1BCC0
+#define VID_J_UP_CLUSTER_4        0x1C260
+
+#define AUD_A_UP_CLUSTER_1        0x1C800
+#define AUD_A_UP_CLUSTER_2        0x1C880
+#define AUD_A_UP_CLUSTER_3        0x1C900
+
+#define AUD_B_UP_CLUSTER_1        0x1C980
+#define AUD_B_UP_CLUSTER_2        0x1CA00
+#define AUD_B_UP_CLUSTER_3        0x1CA80
+
+#define AUD_C_UP_CLUSTER_1        0x1CB00
+#define AUD_C_UP_CLUSTER_2        0x1CB80
+#define AUD_C_UP_CLUSTER_3        0x1CC00
+
+#define AUD_E_UP_CLUSTER_1        0x1CC80
+#define AUD_E_UP_CLUSTER_2        0x1CD00
+#define AUD_E_UP_CLUSTER_3        0x1CD80
+
+#define RX_SRAM_POOL_FREE         0x1CE00
+#define RX_SRAM_END               0x1D000
+
+/* Free Receive SRAM    144 Bytes */
+
+/* Transmit SRAM */
+#define TX_SRAM_POOL_START        0x00000
+
+#define VID_A_DOWN_CLUSTER_1      0x00040
+#define VID_A_DOWN_CLUSTER_2      0x005E0
+#define VID_A_DOWN_CLUSTER_3      0x00B80
+#define VID_A_DOWN_CLUSTER_4      0x01120
+
+#define VID_B_DOWN_CLUSTER_1      0x016C0
+#define VID_B_DOWN_CLUSTER_2      0x01C60
+#define VID_B_DOWN_CLUSTER_3      0x02200
+#define VID_B_DOWN_CLUSTER_4      0x027A0
+
+#define VID_C_DOWN_CLUSTER_1      0x02D40
+#define VID_C_DOWN_CLUSTER_2      0x032E0
+#define VID_C_DOWN_CLUSTER_3      0x03880
+#define VID_C_DOWN_CLUSTER_4      0x03E20
+
+#define VID_D_DOWN_CLUSTER_1      0x043C0
+#define VID_D_DOWN_CLUSTER_2      0x04960
+#define VID_D_DOWN_CLUSTER_3      0x04F00
+#define VID_D_DOWN_CLUSTER_4      0x054A0
+
+#define VID_E_DOWN_CLUSTER_1      0x05a40
+#define VID_E_DOWN_CLUSTER_2      0x05FE0
+#define VID_E_DOWN_CLUSTER_3      0x06580
+#define VID_E_DOWN_CLUSTER_4      0x06B20
+
+#define VID_F_DOWN_CLUSTER_1      0x070C0
+#define VID_F_DOWN_CLUSTER_2      0x07660
+#define VID_F_DOWN_CLUSTER_3      0x07C00
+#define VID_F_DOWN_CLUSTER_4      0x081A0
+
+#define VID_G_DOWN_CLUSTER_1      0x08740
+#define VID_G_DOWN_CLUSTER_2      0x08CE0
+#define VID_G_DOWN_CLUSTER_3      0x09280
+#define VID_G_DOWN_CLUSTER_4      0x09820
+
+#define VID_H_DOWN_CLUSTER_1      0x09DC0
+#define VID_H_DOWN_CLUSTER_2      0x0A360
+#define VID_H_DOWN_CLUSTER_3      0x0A900
+#define VID_H_DOWN_CLUSTER_4      0x0AEA0
+
+#define AUD_A_DOWN_CLUSTER_1      0x0B500
+#define AUD_A_DOWN_CLUSTER_2      0x0B580
+#define AUD_A_DOWN_CLUSTER_3      0x0B600
+
+#define AUD_B_DOWN_CLUSTER_1      0x0B680
+#define AUD_B_DOWN_CLUSTER_2      0x0B700
+#define AUD_B_DOWN_CLUSTER_3      0x0B780
+
+#define AUD_C_DOWN_CLUSTER_1      0x0B800
+#define AUD_C_DOWN_CLUSTER_2      0x0B880
+#define AUD_C_DOWN_CLUSTER_3      0x0B900
+
+#define AUD_D_DOWN_CLUSTER_1      0x0B980
+#define AUD_D_DOWN_CLUSTER_2      0x0BA00
+#define AUD_D_DOWN_CLUSTER_3      0x0BA80
+
+#define TX_SRAM_POOL_FREE         0x0BB00
+#define TX_SRAM_END               0x0C000
+
+#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2)
+#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3)
+#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4)
+
+#define VID_IQ_SIZE_DW             BYTES_TO_DWORDS(VID_IQ_SIZE)
+#define VID_CDT_SIZE_QW            BYTES_TO_QWORDS(VID_CDT_SIZE)
+#define VID_CLUSTER_SIZE_OW        BYTES_TO_OWORDS(VID_CLUSTER_SIZE)
+
+#define AUDIO_IQ_SIZE_DW           BYTES_TO_DWORDS(AUDIO_IQ_SIZE)
+#define AUDIO_CDT_SIZE_QW          BYTES_TO_QWORDS(AUDIO_CDT_SIZE)
+#define AUDIO_CLUSTER_SIZE_QW      BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE)
+
+#define MBIF_IQ_SIZE_DW            BYTES_TO_DWORDS(MBIF_IQ_SIZE)
+#define MBIF_CDT_SIZE_QW           BYTES_TO_QWORDS(MBIF_CDT_SIZE)
+#define MBIF_CLUSTER_SIZE_OW       BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE)
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
new file mode 100644
index 000000000000..c8c94fbf5d8d
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
@@ -0,0 +1,802 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-video-upstream-ch2.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+			FLD_VID_SRC_OPC_ERR;
+
+static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev,
+					      __le32 *rp, unsigned int offset,
+					      unsigned int bpl, u32 sync_line,
+					      unsigned int lines,
+					      int fifo_enable, int field_type)
+{
+	unsigned int line, i;
+	int dist_betwn_starts = bpl * 2;
+
+	*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	if (USE_RISC_NOOP_VIDEO) {
+		for (i = 0; i < NUM_NO_OPS; i++)
+			*(rp++) = cpu_to_le32(RISC_NOOP);
+	}
+
+	/* scan lines */
+	for (line = 0; line < lines; line++) {
+		*(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+		*(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset);
+		*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+
+		if ((lines <= NTSC_FIELD_HEIGHT) ||
+		    (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) {
+			offset += dist_betwn_starts;
+		}
+	}
+
+	return rp;
+}
+
+static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev,
+					       __le32 *rp,
+					       dma_addr_t databuf_phys_addr,
+					       unsigned int offset,
+					       u32 sync_line, unsigned int bpl,
+					       unsigned int lines,
+					       int fifo_enable, int field_type)
+{
+	unsigned int line, i;
+	struct sram_channel *sram_ch =
+		dev->channels[dev->_channel2_upstream_select].sram_channels;
+	int dist_betwn_starts = bpl * 2;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	if (USE_RISC_NOOP_VIDEO) {
+		for (i = 0; i < NUM_NO_OPS; i++)
+			*(rp++) = cpu_to_le32(RISC_NOOP);
+	}
+
+	/* scan lines */
+	for (line = 0; line < lines; line++) {
+		*(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+		*(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+		*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+
+		if ((lines <= NTSC_FIELD_HEIGHT) ||
+		    (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) {
+			offset += dist_betwn_starts;
+		}
+
+	       /*
+		 check if we need to enable the FIFO after the first 4 lines
+		  For the upstream video channel, the risc engine will enable
+		  the FIFO.
+	       */
+		if (fifo_enable && line == 3) {
+			*(rp++) = RISC_WRITECR;
+			*(rp++) = sram_ch->dma_ctl;
+			*(rp++) = FLD_VID_FIFO_EN;
+			*(rp++) = 0x00000001;
+		}
+	}
+
+	return rp;
+}
+
+int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
+				     struct pci_dev *pci,
+				     unsigned int top_offset, unsigned int bpl,
+				     unsigned int lines)
+{
+	__le32 *rp;
+	int fifo_enable = 0;
+	int singlefield_lines = lines >> 1; /*get line count for single field */
+	int odd_num_lines = singlefield_lines;
+	int frame = 0;
+	int frame_size = 0;
+	int databuf_offset = 0;
+	int risc_program_size = 0;
+	int risc_flag = RISC_CNT_RESET;
+	unsigned int bottom_offset = bpl;
+	dma_addr_t risc_phys_jump_addr;
+
+	if (dev->_isNTSC_ch2) {
+		odd_num_lines = singlefield_lines + 1;
+		risc_program_size = FRAME1_VID_PROG_SIZE;
+		if (bpl == Y411_LINE_SZ)
+			frame_size = FRAME_SIZE_NTSC_Y411;
+		else
+			frame_size = FRAME_SIZE_NTSC_Y422;
+	} else {
+		risc_program_size = PAL_VID_PROG_SIZE;
+		if (bpl == Y411_LINE_SZ)
+			frame_size = FRAME_SIZE_PAL_Y411;
+		else
+			frame_size = FRAME_SIZE_PAL_Y422;
+	}
+
+	/* Virtual address of Risc buffer program */
+	rp = dev->_dma_virt_addr_ch2;
+
+	for (frame = 0; frame < NUM_FRAMES; frame++) {
+		databuf_offset = frame_size * frame;
+
+		if (UNSET != top_offset) {
+			fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
+			rp = cx25821_risc_field_upstream_ch2(dev, rp,
+				dev->_data_buf_phys_addr_ch2 + databuf_offset,
+				top_offset, 0, bpl, odd_num_lines, fifo_enable,
+				ODD_FIELD);
+		}
+
+		fifo_enable = FIFO_DISABLE;
+
+		/* Even field */
+		rp = cx25821_risc_field_upstream_ch2(dev, rp,
+				dev->_data_buf_phys_addr_ch2 + databuf_offset,
+				bottom_offset, 0x200, bpl, singlefield_lines,
+				fifo_enable, EVEN_FIELD);
+
+		if (frame == 0) {
+			risc_flag = RISC_CNT_RESET;
+			risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 +
+					risc_program_size;
+		} else {
+			risc_flag = RISC_CNT_INC;
+			risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2;
+		}
+
+	       /*
+		* Loop to 2ndFrameRISC or to Start of
+		* Risc program & generate IRQ
+		*/
+		*(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+		*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+		*(rp++) = cpu_to_le32(0);
+	}
+
+	return 0;
+}
+
+void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev)
+{
+	struct sram_channel *sram_ch =
+		dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels;
+	u32 tmp = 0;
+
+	if (!dev->_is_running_ch2) {
+		pr_info("No video file is currently running so return!\n");
+		return;
+	}
+	/* Disable RISC interrupts */
+	tmp = cx_read(sram_ch->int_msk);
+	cx_write(sram_ch->int_msk, tmp & ~_intr_msk);
+
+	/* Turn OFF risc and fifo */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN));
+
+	/* Clear data buffer memory */
+	if (dev->_data_buf_virt_addr_ch2)
+		memset(dev->_data_buf_virt_addr_ch2, 0,
+		       dev->_data_buf_size_ch2);
+
+	dev->_is_running_ch2 = 0;
+	dev->_is_first_frame_ch2 = 0;
+	dev->_frame_count_ch2 = 0;
+	dev->_file_status_ch2 = END_OF_FILE;
+
+	kfree(dev->_irq_queues_ch2);
+	dev->_irq_queues_ch2 = NULL;
+
+	kfree(dev->_filename_ch2);
+
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+}
+
+void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev)
+{
+	if (dev->_is_running_ch2)
+		cx25821_stop_upstream_video_ch2(dev);
+
+	if (dev->_dma_virt_addr_ch2) {
+		pci_free_consistent(dev->pci, dev->_risc_size_ch2,
+				    dev->_dma_virt_addr_ch2,
+				    dev->_dma_phys_addr_ch2);
+		dev->_dma_virt_addr_ch2 = NULL;
+	}
+
+	if (dev->_data_buf_virt_addr_ch2) {
+		pci_free_consistent(dev->pci, dev->_data_buf_size_ch2,
+				    dev->_data_buf_virt_addr_ch2,
+				    dev->_data_buf_phys_addr_ch2);
+		dev->_data_buf_virt_addr_ch2 = NULL;
+	}
+}
+
+int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int frame_index_temp = dev->_frame_index_ch2;
+	int i = 0;
+	int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ?
+		Y411_LINE_SZ : Y422_LINE_SZ;
+	int frame_size = 0;
+	int frame_offset = 0;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t file_offset;
+	loff_t pos;
+	mm_segment_t old_fs;
+
+	if (dev->_file_status_ch2 == END_OF_FILE)
+		return 0;
+
+	if (dev->_isNTSC_ch2) {
+		frame_size = (line_size == Y411_LINE_SZ) ?
+			FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+	} else {
+		frame_size = (line_size == Y411_LINE_SZ) ?
+			FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+	}
+
+	frame_offset = (frame_index_temp > 0) ? frame_size : 0;
+	file_offset = dev->_frame_count_ch2 * frame_size;
+
+	myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0);
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+		       __func__, dev->_filename_ch2, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (i = 0; i < dev->_lines_count_ch2; i++) {
+			pos = file_offset;
+
+			vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+					&pos);
+
+			if (vfs_read_retval > 0 && vfs_read_retval == line_size
+			    && dev->_data_buf_virt_addr_ch2 != NULL) {
+				memcpy((void *)(dev->_data_buf_virt_addr_ch2 +
+						frame_offset / 4), mybuf,
+						vfs_read_retval);
+			}
+
+			file_offset += vfs_read_retval;
+			frame_offset += vfs_read_retval;
+
+			if (vfs_read_retval < line_size) {
+				pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+					__func__);
+				break;
+			}
+		}
+
+		if (i > 0)
+			dev->_frame_count_ch2++;
+
+		dev->_file_status_ch2 = (vfs_read_retval == line_size) ?
+			IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+static void cx25821_vidups_handler_ch2(struct work_struct *work)
+{
+	struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+			_irq_work_entry_ch2);
+
+	if (!dev) {
+		pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+		       __func__);
+		return;
+	}
+
+	cx25821_get_frame_ch2(dev, dev->channels[dev->
+			_channel2_upstream_select].sram_channels);
+}
+
+int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int i = 0, j = 0;
+	int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ?
+		Y411_LINE_SZ : Y422_LINE_SZ;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t pos;
+	loff_t offset = (unsigned long)0;
+	mm_segment_t old_fs;
+
+	myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0);
+
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+		       __func__, dev->_filename_ch2, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!  Returning\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (j = 0; j < NUM_FRAMES; j++) {
+			for (i = 0; i < dev->_lines_count_ch2; i++) {
+				pos = offset;
+
+				vfs_read_retval = vfs_read(myfile, mybuf,
+						line_size, &pos);
+
+				if (vfs_read_retval > 0 &&
+				    vfs_read_retval == line_size &&
+				    dev->_data_buf_virt_addr_ch2 != NULL) {
+					memcpy((void *)(dev->
+							_data_buf_virt_addr_ch2
+							+ offset / 4), mybuf,
+							vfs_read_retval);
+				}
+
+				offset += vfs_read_retval;
+
+				if (vfs_read_retval < line_size) {
+					pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+						__func__);
+					break;
+				}
+			}
+
+			if (i > 0)
+				dev->_frame_count_ch2++;
+
+			if (vfs_read_retval < line_size)
+				break;
+		}
+
+		dev->_file_status_ch2 = (vfs_read_retval == line_size) ?
+			IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		myfile->f_pos = 0;
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev,
+					       struct sram_channel *sram_ch,
+					       int bpl)
+{
+	int ret = 0;
+	dma_addr_t dma_addr;
+	dma_addr_t data_dma_addr;
+
+	if (dev->_dma_virt_addr_ch2 != NULL) {
+		pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2,
+				    dev->_dma_virt_addr_ch2,
+				    dev->_dma_phys_addr_ch2);
+	}
+
+	dev->_dma_virt_addr_ch2 = pci_alloc_consistent(dev->pci,
+			dev->upstream_riscbuf_size_ch2, &dma_addr);
+	dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2;
+	dev->_dma_phys_start_addr_ch2 = dma_addr;
+	dev->_dma_phys_addr_ch2 = dma_addr;
+	dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2;
+
+	if (!dev->_dma_virt_addr_ch2) {
+		pr_err("FAILED to allocate memory for Risc buffer! Returning\n");
+		return -ENOMEM;
+	}
+
+	/* Iniitize at this address until n bytes to 0 */
+	memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2);
+
+	if (dev->_data_buf_virt_addr_ch2 != NULL) {
+		pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2,
+				    dev->_data_buf_virt_addr_ch2,
+				    dev->_data_buf_phys_addr_ch2);
+	}
+	/* For Video Data buffer allocation */
+	dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci,
+			dev->upstream_databuf_size_ch2, &data_dma_addr);
+	dev->_data_buf_phys_addr_ch2 = data_dma_addr;
+	dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2;
+
+	if (!dev->_data_buf_virt_addr_ch2) {
+		pr_err("FAILED to allocate memory for data buffer! Returning\n");
+		return -ENOMEM;
+	}
+
+	/* Initialize at this address until n bytes to 0 */
+	memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2);
+
+	ret = cx25821_openfile_ch2(dev, sram_ch);
+	if (ret < 0)
+		return ret;
+
+	/* Creating RISC programs */
+	ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl,
+						dev->_lines_count_ch2);
+	if (ret < 0) {
+		pr_info("Failed creating Video Upstream Risc programs!\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	return ret;
+}
+
+int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num,
+				   u32 status)
+{
+	u32 int_msk_tmp;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	int singlefield_lines = NTSC_FIELD_HEIGHT;
+	int line_size_in_bytes = Y422_LINE_SZ;
+	int odd_risc_prog_size = 0;
+	dma_addr_t risc_phys_jump_addr;
+	__le32 *rp;
+
+	if (status & FLD_VID_SRC_RISC1) {
+		/* We should only process one program per call */
+		u32 prog_cnt = cx_read(channel->gpcnt);
+
+		/*
+		 *  Since we've identified our IRQ, clear our bits from the
+		 *  interrupt mask and interrupt status registers
+		 */
+		int_msk_tmp = cx_read(channel->int_msk);
+		cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
+		cx_write(channel->int_stat, _intr_msk);
+
+		spin_lock(&dev->slock);
+
+		dev->_frame_index_ch2 = prog_cnt;
+
+		queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2);
+
+		if (dev->_is_first_frame_ch2) {
+			dev->_is_first_frame_ch2 = 0;
+
+			if (dev->_isNTSC_ch2) {
+				singlefield_lines += 1;
+				odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
+			} else {
+				singlefield_lines = PAL_FIELD_HEIGHT;
+				odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
+			}
+
+			if (dev->_dma_virt_start_addr_ch2 != NULL) {
+				if (dev->_pixel_format_ch2 == PIXEL_FRMT_411)
+					line_size_in_bytes = Y411_LINE_SZ;
+				else
+					line_size_in_bytes = Y422_LINE_SZ;
+				risc_phys_jump_addr =
+					dev->_dma_phys_start_addr_ch2 +
+					odd_risc_prog_size;
+
+				rp = cx25821_update_riscprogram_ch2(dev,
+						dev->_dma_virt_start_addr_ch2,
+						TOP_OFFSET, line_size_in_bytes,
+						0x0, singlefield_lines,
+						FIFO_DISABLE, ODD_FIELD);
+
+			       /* Jump to Even Risc program of 1st Frame */
+				*(rp++) = cpu_to_le32(RISC_JUMP);
+				*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+				*(rp++) = cpu_to_le32(0);
+			}
+		}
+
+		spin_unlock(&dev->slock);
+	}
+
+	if (dev->_file_status_ch2 == END_OF_FILE) {
+		pr_info("EOF Channel 2 Framecount = %d\n",
+			dev->_frame_count_ch2);
+		return -1;
+	}
+	/* ElSE, set the interrupt mask register, re-enable irq. */
+	int_msk_tmp = cx_read(channel->int_msk);
+	cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+	return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id)
+{
+	struct cx25821_dev *dev = dev_id;
+	u32 vid_status;
+	int handled = 0;
+	int channel_num = 0;
+	struct sram_channel *sram_ch;
+
+	if (!dev)
+		return -1;
+
+	channel_num = VID_UPSTREAM_SRAM_CHANNEL_J;
+	sram_ch = dev->channels[channel_num].sram_channels;
+
+	vid_status = cx_read(sram_ch->int_stat);
+
+	/* Only deal with our interrupt */
+	if (vid_status)
+		handled = cx25821_video_upstream_irq_ch2(dev, channel_num,
+				vid_status);
+
+	if (handled < 0)
+		cx25821_stop_upstream_video_ch2(dev);
+	else
+		handled += handled;
+
+	return IRQ_RETVAL(handled);
+}
+
+static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev,
+					struct sram_channel *ch, int pix_format)
+{
+	int width = WIDTH_D1;
+	int height = dev->_lines_count_ch2;
+	int num_lines, odd_num_lines;
+	u32 value;
+	int vip_mode = PIXEL_ENGINE_VIP1;
+
+	value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7);
+	value &= 0xFFFFFFEF;
+	value |= dev->_isNTSC_ch2 ? 0 : 0x10;
+	cx_write(ch->vid_fmt_ctl, value);
+
+	/*
+	 *  set number of active pixels in each line. Default is 720
+	 * pixels in both NTSC and PAL format
+	 */
+	cx_write(ch->vid_active_ctl1, width);
+
+	num_lines = (height / 2) & 0x3FF;
+	odd_num_lines = num_lines;
+
+	if (dev->_isNTSC_ch2)
+		odd_num_lines += 1;
+
+	value = (num_lines << 16) | odd_num_lines;
+
+	/* set number of active lines in field 0 (top) and field 1 (bottom) */
+	cx_write(ch->vid_active_ctl2, value);
+
+	cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
+}
+
+int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
+					 struct sram_channel *sram_ch)
+{
+	u32 tmp = 0;
+	int err = 0;
+
+	/*
+	 *  656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface
+	 * for channel A-C
+	 */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+	/*
+	 *  Set the physical start address of the RISC program in the initial
+	 *  program counter(IPC) member of the cmds.
+	 */
+	cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2);
+	cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
+
+	/* reset counter */
+	cx_write(sram_ch->gpcnt_ctl, 3);
+
+	/* Clear our bits from the interrupt status register. */
+	cx_write(sram_ch->int_stat, _intr_msk);
+
+	/* Set the interrupt mask register, enable irq. */
+	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+	tmp = cx_read(sram_ch->int_msk);
+	cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+	err = request_irq(dev->pci->irq, cx25821_upstream_irq_ch2,
+			IRQF_SHARED, dev->name, dev);
+	if (err < 0) {
+		pr_err("%s: can't get upstream IRQ %d\n",
+		       dev->name, dev->pci->irq);
+		goto fail_irq;
+	}
+	/* Start the DMA  engine */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN);
+
+	dev->_is_running_ch2 = 1;
+	dev->_is_first_frame_ch2 = 1;
+
+	return 0;
+
+fail_irq:
+	cx25821_dev_unregister(dev);
+	return err;
+}
+
+int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
+				 int pixel_format)
+{
+	struct sram_channel *sram_ch;
+	u32 tmp;
+	int retval = 0;
+	int err = 0;
+	int data_frame_size = 0;
+	int risc_buffer_size = 0;
+	int str_length = 0;
+
+	if (dev->_is_running_ch2) {
+		pr_info("Video Channel is still running so return!\n");
+		return 0;
+	}
+
+	dev->_channel2_upstream_select = channel_select;
+	sram_ch = dev->channels[channel_select].sram_channels;
+
+	INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2);
+	dev->_irq_queues_ch2 =
+	    create_singlethread_workqueue("cx25821_workqueue2");
+
+	if (!dev->_irq_queues_ch2) {
+		pr_err("create_singlethread_workqueue() for Video FAILED!\n");
+		return -ENOMEM;
+	}
+	/*
+	 * 656/VIP SRC Upstream Channel I & J and 7 -
+	 * Host Bus Interface for channel A-C
+	 */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+	dev->_is_running_ch2 = 0;
+	dev->_frame_count_ch2 = 0;
+	dev->_file_status_ch2 = RESET_STATUS;
+	dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576;
+	dev->_pixel_format_ch2 = pixel_format;
+	dev->_line_size_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_422) ?
+		(WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+	data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
+	risc_buffer_size = dev->_isNTSC_ch2 ?
+		NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
+
+	if (dev->input_filename_ch2) {
+		str_length = strlen(dev->input_filename_ch2);
+		dev->_filename_ch2 = kmemdup(dev->input_filename_ch2,
+					     str_length + 1, GFP_KERNEL);
+
+		if (!dev->_filename_ch2)
+			goto error;
+	} else {
+		str_length = strlen(dev->_defaultname_ch2);
+		dev->_filename_ch2 = kmemdup(dev->_defaultname_ch2,
+					     str_length + 1, GFP_KERNEL);
+
+		if (!dev->_filename_ch2)
+			goto error;
+	}
+
+	/* Default if filename is empty string */
+	if (strcmp(dev->input_filename_ch2, "") == 0) {
+		if (dev->_isNTSC_ch2) {
+			dev->_filename_ch2 = (dev->_pixel_format_ch2 ==
+				PIXEL_FRMT_411) ? "/root/vid411.yuv" :
+				"/root/vidtest.yuv";
+		} else {
+			dev->_filename_ch2 = (dev->_pixel_format_ch2 ==
+				PIXEL_FRMT_411) ? "/root/pal411.yuv" :
+				"/root/pal422.yuv";
+		}
+	}
+
+	retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+						dev->_line_size_ch2, 0);
+
+	/* setup fifo + format */
+	cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2);
+
+	dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2;
+	dev->upstream_databuf_size_ch2 = data_frame_size * 2;
+
+	/* Allocating buffers and prepare RISC program */
+	retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
+						dev->_line_size_ch2);
+	if (retval < 0) {
+		pr_err("%s: Failed to set up Video upstream buffers!\n",
+		       dev->name);
+		goto error;
+	}
+
+	cx25821_start_video_dma_upstream_ch2(dev, sram_ch);
+
+	return 0;
+
+error:
+	cx25821_dev_unregister(dev);
+
+	return err;
+}
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h
new file mode 100644
index 000000000000..d42dab59b663
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h
@@ -0,0 +1,138 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.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/mutex.h>
+#include <linux/workqueue.h>
+
+#define OPEN_FILE_1           0
+#define NUM_PROGS             8
+#define NUM_FRAMES            2
+#define ODD_FIELD             0
+#define EVEN_FIELD            1
+#define TOP_OFFSET            0
+#define FIFO_DISABLE          0
+#define FIFO_ENABLE           1
+#define TEST_FRAMES           5
+#define END_OF_FILE           0
+#define IN_PROGRESS           1
+#define RESET_STATUS          -1
+#define NUM_NO_OPS            5
+
+/* PAL and NTSC line sizes and number of lines. */
+#define WIDTH_D1              720
+#define NTSC_LINES_PER_FRAME  480
+#define PAL_LINES_PER_FRAME   576
+#define PAL_LINE_SZ           1440
+#define Y422_LINE_SZ          1440
+#define Y411_LINE_SZ          1080
+#define NTSC_FIELD_HEIGHT     240
+#define NTSC_ODD_FLD_LINES    241
+#define PAL_FIELD_HEIGHT      288
+
+#define FRAME_SIZE_NTSC_Y422    (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_NTSC_Y411    (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
+#define FRAME_SIZE_PAL_Y422     (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_PAL_Y411     (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
+
+#define NTSC_DATA_BUF_SZ        (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
+#define PAL_DATA_BUF_SZ         (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
+
+#define RISC_WRITECR_INSTRUCTION_SIZE   16
+#define RISC_SYNC_INSTRUCTION_SIZE      4
+#define JUMP_INSTRUCTION_SIZE           12
+#define MAXSIZE_NO_OPS                  36
+#define DWORD_SIZE                      4
+
+#define USE_RISC_NOOP_VIDEO   1
+
+#ifdef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define PAL_RISC_BUF_SIZE         (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE						\
+	((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE +			\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE +			\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_RISC_BUF_SIZE						\
+	(2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) *			\
+	 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE +		\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE +	\
+	 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE						\
+	(NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+#endif
+
+#ifndef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE						\
+	((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE +			\
+	 RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define PAL_RISC_BUF_SIZE						\
+	(2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE))
+
+#define PAL_VID_PROG_SIZE						\
+	((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE +			\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE						\
+	(NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE +			\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#define NTSC_RISC_BUF_SIZE						\
+	(2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) *			\
+	 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE +		\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c
new file mode 100644
index 000000000000..52c13e0b6492
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c
@@ -0,0 +1,856 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-video-upstream.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+			FLD_VID_SRC_OPC_ERR;
+
+int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
+					struct sram_channel *ch,
+					unsigned int bpl, u32 risc)
+{
+	unsigned int i, lines;
+	u32 cdt;
+
+	if (ch->cmds_start == 0) {
+		cx_write(ch->ptr1_reg, 0);
+		cx_write(ch->ptr2_reg, 0);
+		cx_write(ch->cnt2_reg, 0);
+		cx_write(ch->cnt1_reg, 0);
+		return 0;
+	}
+
+	bpl = (bpl + 7) & ~7;	/* alignment */
+	cdt = ch->cdt;
+	lines = ch->fifo_size / bpl;
+
+	if (lines > 4)
+		lines = 4;
+
+	BUG_ON(lines < 2);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++) {
+		cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+		cx_write(cdt + 16 * i + 4, 0);
+		cx_write(cdt + 16 * i + 8, 0);
+		cx_write(cdt + 16 * i + 12, 0);
+	}
+
+	/* write CMDS */
+	cx_write(ch->cmds_start + 0, risc);
+
+	cx_write(ch->cmds_start + 4, 0);
+	cx_write(ch->cmds_start + 8, cdt);
+	cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+	cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+	cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW);
+
+	for (i = 24; i < 80; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+	return 0;
+}
+
+static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev,
+					  __le32 *rp, unsigned int offset,
+					  unsigned int bpl, u32 sync_line,
+					  unsigned int lines, int fifo_enable,
+					  int field_type)
+{
+	unsigned int line, i;
+	int dist_betwn_starts = bpl * 2;
+
+	*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	if (USE_RISC_NOOP_VIDEO) {
+		for (i = 0; i < NUM_NO_OPS; i++)
+			*(rp++) = cpu_to_le32(RISC_NOOP);
+	}
+
+	/* scan lines */
+	for (line = 0; line < lines; line++) {
+		*(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+		*(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset);
+		*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+
+		if ((lines <= NTSC_FIELD_HEIGHT)
+		    || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) {
+			offset += dist_betwn_starts;
+		}
+	}
+
+	return rp;
+}
+
+static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp,
+					   dma_addr_t databuf_phys_addr,
+					   unsigned int offset, u32 sync_line,
+					   unsigned int bpl, unsigned int lines,
+					   int fifo_enable, int field_type)
+{
+	unsigned int line, i;
+	struct sram_channel *sram_ch =
+		dev->channels[dev->_channel_upstream_select].sram_channels;
+	int dist_betwn_starts = bpl * 2;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	if (USE_RISC_NOOP_VIDEO) {
+		for (i = 0; i < NUM_NO_OPS; i++)
+			*(rp++) = cpu_to_le32(RISC_NOOP);
+	}
+
+	/* scan lines */
+	for (line = 0; line < lines; line++) {
+		*(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+		*(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+		*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
+
+		if ((lines <= NTSC_FIELD_HEIGHT)
+		    || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC))
+			/* to skip the other field line */
+			offset += dist_betwn_starts;
+
+		/* check if we need to enable the FIFO after the first 4 lines
+		 * For the upstream video channel, the risc engine will enable
+		 * the FIFO. */
+		if (fifo_enable && line == 3) {
+			*(rp++) = RISC_WRITECR;
+			*(rp++) = sram_ch->dma_ctl;
+			*(rp++) = FLD_VID_FIFO_EN;
+			*(rp++) = 0x00000001;
+		}
+	}
+
+	return rp;
+}
+
+int cx25821_risc_buffer_upstream(struct cx25821_dev *dev,
+				 struct pci_dev *pci,
+				 unsigned int top_offset,
+				 unsigned int bpl, unsigned int lines)
+{
+	__le32 *rp;
+	int fifo_enable = 0;
+	/* get line count for single field */
+	int singlefield_lines = lines >> 1;
+	int odd_num_lines = singlefield_lines;
+	int frame = 0;
+	int frame_size = 0;
+	int databuf_offset = 0;
+	int risc_program_size = 0;
+	int risc_flag = RISC_CNT_RESET;
+	unsigned int bottom_offset = bpl;
+	dma_addr_t risc_phys_jump_addr;
+
+	if (dev->_isNTSC) {
+		odd_num_lines = singlefield_lines + 1;
+		risc_program_size = FRAME1_VID_PROG_SIZE;
+		frame_size = (bpl == Y411_LINE_SZ) ?
+			FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+	} else {
+		risc_program_size = PAL_VID_PROG_SIZE;
+		frame_size = (bpl == Y411_LINE_SZ) ?
+			FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+	}
+
+	/* Virtual address of Risc buffer program */
+	rp = dev->_dma_virt_addr;
+
+	for (frame = 0; frame < NUM_FRAMES; frame++) {
+		databuf_offset = frame_size * frame;
+
+		if (UNSET != top_offset) {
+			fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
+			rp = cx25821_risc_field_upstream(dev, rp,
+					dev->_data_buf_phys_addr +
+					databuf_offset, top_offset, 0, bpl,
+					odd_num_lines, fifo_enable, ODD_FIELD);
+		}
+
+		fifo_enable = FIFO_DISABLE;
+
+		/* Even Field */
+		rp = cx25821_risc_field_upstream(dev, rp,
+						 dev->_data_buf_phys_addr +
+						 databuf_offset, bottom_offset,
+						 0x200, bpl, singlefield_lines,
+						 fifo_enable, EVEN_FIELD);
+
+		if (frame == 0) {
+			risc_flag = RISC_CNT_RESET;
+			risc_phys_jump_addr = dev->_dma_phys_start_addr +
+				risc_program_size;
+		} else {
+			risc_phys_jump_addr = dev->_dma_phys_start_addr;
+			risc_flag = RISC_CNT_INC;
+		}
+
+		/* Loop to 2ndFrameRISC or to Start of Risc
+		 * program & generate IRQ
+		 */
+		*(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+		*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+		*(rp++) = cpu_to_le32(0);
+	}
+
+	return 0;
+}
+
+void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev)
+{
+	struct sram_channel *sram_ch =
+		dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels;
+	u32 tmp = 0;
+
+	if (!dev->_is_running) {
+		pr_info("No video file is currently running so return!\n");
+		return;
+	}
+	/* Disable RISC interrupts */
+	tmp = cx_read(sram_ch->int_msk);
+	cx_write(sram_ch->int_msk, tmp & ~_intr_msk);
+
+	/* Turn OFF risc and fifo enable */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN));
+
+	/* Clear data buffer memory */
+	if (dev->_data_buf_virt_addr)
+		memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size);
+
+	dev->_is_running = 0;
+	dev->_is_first_frame = 0;
+	dev->_frame_count = 0;
+	dev->_file_status = END_OF_FILE;
+
+	kfree(dev->_irq_queues);
+	dev->_irq_queues = NULL;
+
+	kfree(dev->_filename);
+
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+}
+
+void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev)
+{
+	if (dev->_is_running)
+		cx25821_stop_upstream_video_ch1(dev);
+
+	if (dev->_dma_virt_addr) {
+		pci_free_consistent(dev->pci, dev->_risc_size,
+				    dev->_dma_virt_addr, dev->_dma_phys_addr);
+		dev->_dma_virt_addr = NULL;
+	}
+
+	if (dev->_data_buf_virt_addr) {
+		pci_free_consistent(dev->pci, dev->_data_buf_size,
+				    dev->_data_buf_virt_addr,
+				    dev->_data_buf_phys_addr);
+		dev->_data_buf_virt_addr = NULL;
+	}
+}
+
+int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int frame_index_temp = dev->_frame_index;
+	int i = 0;
+	int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ?
+		Y411_LINE_SZ : Y422_LINE_SZ;
+	int frame_size = 0;
+	int frame_offset = 0;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t file_offset;
+	loff_t pos;
+	mm_segment_t old_fs;
+
+	if (dev->_file_status == END_OF_FILE)
+		return 0;
+
+	if (dev->_isNTSC)
+		frame_size = (line_size == Y411_LINE_SZ) ?
+			FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+	else
+		frame_size = (line_size == Y411_LINE_SZ) ?
+			FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+
+	frame_offset = (frame_index_temp > 0) ? frame_size : 0;
+	file_offset = dev->_frame_count * frame_size;
+
+	myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0);
+
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+		       __func__, dev->_filename, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (i = 0; i < dev->_lines_count; i++) {
+			pos = file_offset;
+
+			vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+					&pos);
+
+			if (vfs_read_retval > 0 && vfs_read_retval == line_size
+			    && dev->_data_buf_virt_addr != NULL) {
+				memcpy((void *)(dev->_data_buf_virt_addr +
+						frame_offset / 4), mybuf,
+				       vfs_read_retval);
+			}
+
+			file_offset += vfs_read_retval;
+			frame_offset += vfs_read_retval;
+
+			if (vfs_read_retval < line_size) {
+				pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+					__func__);
+				break;
+			}
+		}
+
+		if (i > 0)
+			dev->_frame_count++;
+
+		dev->_file_status = (vfs_read_retval == line_size) ?
+			IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+static void cx25821_vidups_handler(struct work_struct *work)
+{
+	struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+			_irq_work_entry);
+
+	if (!dev) {
+		pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+		       __func__);
+		return;
+	}
+
+	cx25821_get_frame(dev, dev->channels[dev->_channel_upstream_select].
+			sram_channels);
+}
+
+int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+	struct file *myfile;
+	int i = 0, j = 0;
+	int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ?
+		Y411_LINE_SZ : Y422_LINE_SZ;
+	ssize_t vfs_read_retval = 0;
+	char mybuf[line_size];
+	loff_t pos;
+	loff_t offset = (unsigned long)0;
+	mm_segment_t old_fs;
+
+	myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0);
+
+	if (IS_ERR(myfile)) {
+		const int open_errno = -PTR_ERR(myfile);
+		pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+		       __func__, dev->_filename, open_errno);
+		return PTR_ERR(myfile);
+	} else {
+		if (!(myfile->f_op)) {
+			pr_err("%s(): File has no file operations registered!\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		if (!myfile->f_op->read) {
+			pr_err("%s(): File has no READ operations registered!  Returning\n",
+			       __func__);
+			filp_close(myfile, NULL);
+			return -EIO;
+		}
+
+		pos = myfile->f_pos;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		for (j = 0; j < NUM_FRAMES; j++) {
+			for (i = 0; i < dev->_lines_count; i++) {
+				pos = offset;
+
+				vfs_read_retval = vfs_read(myfile, mybuf,
+						line_size, &pos);
+
+				if (vfs_read_retval > 0
+				    && vfs_read_retval == line_size
+				    && dev->_data_buf_virt_addr != NULL) {
+					memcpy((void *)(dev->
+							_data_buf_virt_addr +
+							offset / 4), mybuf,
+					       vfs_read_retval);
+				}
+
+				offset += vfs_read_retval;
+
+				if (vfs_read_retval < line_size) {
+					pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+						__func__);
+					break;
+				}
+			}
+
+			if (i > 0)
+				dev->_frame_count++;
+
+			if (vfs_read_retval < line_size)
+				break;
+		}
+
+		dev->_file_status = (vfs_read_retval == line_size) ?
+			IN_PROGRESS : END_OF_FILE;
+
+		set_fs(old_fs);
+		myfile->f_pos = 0;
+		filp_close(myfile, NULL);
+	}
+
+	return 0;
+}
+
+int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev,
+				    struct sram_channel *sram_ch, int bpl)
+{
+	int ret = 0;
+	dma_addr_t dma_addr;
+	dma_addr_t data_dma_addr;
+
+	if (dev->_dma_virt_addr != NULL)
+		pci_free_consistent(dev->pci, dev->upstream_riscbuf_size,
+				dev->_dma_virt_addr, dev->_dma_phys_addr);
+
+	dev->_dma_virt_addr = pci_alloc_consistent(dev->pci,
+			dev->upstream_riscbuf_size, &dma_addr);
+	dev->_dma_virt_start_addr = dev->_dma_virt_addr;
+	dev->_dma_phys_start_addr = dma_addr;
+	dev->_dma_phys_addr = dma_addr;
+	dev->_risc_size = dev->upstream_riscbuf_size;
+
+	if (!dev->_dma_virt_addr) {
+		pr_err("FAILED to allocate memory for Risc buffer! Returning\n");
+		return -ENOMEM;
+	}
+
+	/* Clear memory at address */
+	memset(dev->_dma_virt_addr, 0, dev->_risc_size);
+
+	if (dev->_data_buf_virt_addr != NULL)
+		pci_free_consistent(dev->pci, dev->upstream_databuf_size,
+				dev->_data_buf_virt_addr,
+				dev->_data_buf_phys_addr);
+	/* For Video Data buffer allocation */
+	dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci,
+			dev->upstream_databuf_size, &data_dma_addr);
+	dev->_data_buf_phys_addr = data_dma_addr;
+	dev->_data_buf_size = dev->upstream_databuf_size;
+
+	if (!dev->_data_buf_virt_addr) {
+		pr_err("FAILED to allocate memory for data buffer! Returning\n");
+		return -ENOMEM;
+	}
+
+	/* Clear memory at address */
+	memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size);
+
+	ret = cx25821_openfile(dev, sram_ch);
+	if (ret < 0)
+		return ret;
+
+	/* Create RISC programs */
+	ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl,
+			dev->_lines_count);
+	if (ret < 0) {
+		pr_info("Failed creating Video Upstream Risc programs!\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	return ret;
+}
+
+int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
+			       u32 status)
+{
+	u32 int_msk_tmp;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	int singlefield_lines = NTSC_FIELD_HEIGHT;
+	int line_size_in_bytes = Y422_LINE_SZ;
+	int odd_risc_prog_size = 0;
+	dma_addr_t risc_phys_jump_addr;
+	__le32 *rp;
+
+	if (status & FLD_VID_SRC_RISC1) {
+		/* We should only process one program per call */
+		u32 prog_cnt = cx_read(channel->gpcnt);
+
+		/* Since we've identified our IRQ, clear our bits from the
+		 * interrupt mask and interrupt status registers */
+		int_msk_tmp = cx_read(channel->int_msk);
+		cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
+		cx_write(channel->int_stat, _intr_msk);
+
+		spin_lock(&dev->slock);
+
+		dev->_frame_index = prog_cnt;
+
+		queue_work(dev->_irq_queues, &dev->_irq_work_entry);
+
+		if (dev->_is_first_frame) {
+			dev->_is_first_frame = 0;
+
+			if (dev->_isNTSC) {
+				singlefield_lines += 1;
+				odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
+			} else {
+				singlefield_lines = PAL_FIELD_HEIGHT;
+				odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
+			}
+
+			if (dev->_dma_virt_start_addr != NULL) {
+				line_size_in_bytes =
+				    (dev->_pixel_format ==
+				     PIXEL_FRMT_411) ? Y411_LINE_SZ :
+				    Y422_LINE_SZ;
+				risc_phys_jump_addr =
+				    dev->_dma_phys_start_addr +
+				    odd_risc_prog_size;
+
+				rp = cx25821_update_riscprogram(dev,
+					dev->_dma_virt_start_addr, TOP_OFFSET,
+					line_size_in_bytes, 0x0,
+					singlefield_lines, FIFO_DISABLE,
+					ODD_FIELD);
+
+				/* Jump to Even Risc program of 1st Frame */
+				*(rp++) = cpu_to_le32(RISC_JUMP);
+				*(rp++) = cpu_to_le32(risc_phys_jump_addr);
+				*(rp++) = cpu_to_le32(0);
+			}
+		}
+
+		spin_unlock(&dev->slock);
+	} else {
+		if (status & FLD_VID_SRC_UF)
+			pr_err("%s(): Video Received Underflow Error Interrupt!\n",
+			       __func__);
+
+		if (status & FLD_VID_SRC_SYNC)
+			pr_err("%s(): Video Received Sync Error Interrupt!\n",
+			       __func__);
+
+		if (status & FLD_VID_SRC_OPC_ERR)
+			pr_err("%s(): Video Received OpCode Error Interrupt!\n",
+			       __func__);
+	}
+
+	if (dev->_file_status == END_OF_FILE) {
+		pr_err("EOF Channel 1 Framecount = %d\n", dev->_frame_count);
+		return -1;
+	}
+	/* ElSE, set the interrupt mask register, re-enable irq. */
+	int_msk_tmp = cx_read(channel->int_msk);
+	cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+	return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
+{
+	struct cx25821_dev *dev = dev_id;
+	u32 vid_status;
+	int handled = 0;
+	int channel_num = 0;
+	struct sram_channel *sram_ch;
+
+	if (!dev)
+		return -1;
+
+	channel_num = VID_UPSTREAM_SRAM_CHANNEL_I;
+
+	sram_ch = dev->channels[channel_num].sram_channels;
+
+	vid_status = cx_read(sram_ch->int_stat);
+
+	/* Only deal with our interrupt */
+	if (vid_status)
+		handled = cx25821_video_upstream_irq(dev, channel_num,
+				vid_status);
+
+	if (handled < 0)
+		cx25821_stop_upstream_video_ch1(dev);
+	else
+		handled += handled;
+
+	return IRQ_RETVAL(handled);
+}
+
+void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch,
+			     int pix_format)
+{
+	int width = WIDTH_D1;
+	int height = dev->_lines_count;
+	int num_lines, odd_num_lines;
+	u32 value;
+	int vip_mode = OUTPUT_FRMT_656;
+
+	value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7);
+	value &= 0xFFFFFFEF;
+	value |= dev->_isNTSC ? 0 : 0x10;
+	cx_write(ch->vid_fmt_ctl, value);
+
+	/* set number of active pixels in each line.
+	 * Default is 720 pixels in both NTSC and PAL format */
+	cx_write(ch->vid_active_ctl1, width);
+
+	num_lines = (height / 2) & 0x3FF;
+	odd_num_lines = num_lines;
+
+	if (dev->_isNTSC)
+		odd_num_lines += 1;
+
+	value = (num_lines << 16) | odd_num_lines;
+
+	/* set number of active lines in field 0 (top) and field 1 (bottom) */
+	cx_write(ch->vid_active_ctl2, value);
+
+	cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
+}
+
+int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
+				     struct sram_channel *sram_ch)
+{
+	u32 tmp = 0;
+	int err = 0;
+
+	/* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+	 * channel A-C
+	 */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+	/* Set the physical start address of the RISC program in the initial
+	 * program counter(IPC) member of the cmds.
+	 */
+	cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr);
+	/* Risc IPC High 64 bits 63-32 */
+	cx_write(sram_ch->cmds_start + 4, 0);
+
+	/* reset counter */
+	cx_write(sram_ch->gpcnt_ctl, 3);
+
+	/* Clear our bits from the interrupt status register. */
+	cx_write(sram_ch->int_stat, _intr_msk);
+
+	/* Set the interrupt mask register, enable irq. */
+	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+	tmp = cx_read(sram_ch->int_msk);
+	cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+	err = request_irq(dev->pci->irq, cx25821_upstream_irq,
+			IRQF_SHARED, dev->name, dev);
+	if (err < 0) {
+		pr_err("%s: can't get upstream IRQ %d\n",
+		       dev->name, dev->pci->irq);
+		goto fail_irq;
+	}
+
+	/* Start the DMA  engine */
+	tmp = cx_read(sram_ch->dma_ctl);
+	cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN);
+
+	dev->_is_running = 1;
+	dev->_is_first_frame = 1;
+
+	return 0;
+
+fail_irq:
+	cx25821_dev_unregister(dev);
+	return err;
+}
+
+int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
+				 int pixel_format)
+{
+	struct sram_channel *sram_ch;
+	u32 tmp;
+	int retval = 0;
+	int err = 0;
+	int data_frame_size = 0;
+	int risc_buffer_size = 0;
+	int str_length = 0;
+
+	if (dev->_is_running) {
+		pr_info("Video Channel is still running so return!\n");
+		return 0;
+	}
+
+	dev->_channel_upstream_select = channel_select;
+	sram_ch = dev->channels[channel_select].sram_channels;
+
+	INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler);
+	dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue");
+
+	if (!dev->_irq_queues) {
+		pr_err("create_singlethread_workqueue() for Video FAILED!\n");
+		return -ENOMEM;
+	}
+	/* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+	 * channel A-C
+	 */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+	dev->_is_running = 0;
+	dev->_frame_count = 0;
+	dev->_file_status = RESET_STATUS;
+	dev->_lines_count = dev->_isNTSC ? 480 : 576;
+	dev->_pixel_format = pixel_format;
+	dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ?
+		(WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+	data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
+	risc_buffer_size = dev->_isNTSC ?
+		NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
+
+	if (dev->input_filename) {
+		str_length = strlen(dev->input_filename);
+		dev->_filename = kmemdup(dev->input_filename, str_length + 1,
+					 GFP_KERNEL);
+
+		if (!dev->_filename)
+			goto error;
+	} else {
+		str_length = strlen(dev->_defaultname);
+		dev->_filename = kmemdup(dev->_defaultname, str_length + 1,
+					 GFP_KERNEL);
+
+		if (!dev->_filename)
+			goto error;
+	}
+
+	/* Default if filename is empty string */
+	if (strcmp(dev->input_filename, "") == 0) {
+		if (dev->_isNTSC) {
+			dev->_filename =
+				(dev->_pixel_format == PIXEL_FRMT_411) ?
+				"/root/vid411.yuv" : "/root/vidtest.yuv";
+		} else {
+			dev->_filename =
+				(dev->_pixel_format == PIXEL_FRMT_411) ?
+				"/root/pal411.yuv" : "/root/pal422.yuv";
+		}
+	}
+
+	dev->_is_running = 0;
+	dev->_frame_count = 0;
+	dev->_file_status = RESET_STATUS;
+	dev->_lines_count = dev->_isNTSC ? 480 : 576;
+	dev->_pixel_format = pixel_format;
+	dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ?
+		(WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+
+	retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+			dev->_line_size, 0);
+
+	/* setup fifo + format */
+	cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format);
+
+	dev->upstream_riscbuf_size = risc_buffer_size * 2;
+	dev->upstream_databuf_size = data_frame_size * 2;
+
+	/* Allocating buffers and prepare RISC program */
+	retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size);
+	if (retval < 0) {
+		pr_err("%s: Failed to set up Video upstream buffers!\n",
+		       dev->name);
+		goto error;
+	}
+
+	cx25821_start_video_dma_upstream(dev, sram_ch);
+
+	return 0;
+
+error:
+	cx25821_dev_unregister(dev);
+
+	return err;
+}
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.h b/drivers/media/pci/cx25821/cx25821-video-upstream.h
new file mode 100644
index 000000000000..268ec8aa6a61
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream.h
@@ -0,0 +1,139 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.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/mutex.h>
+#include <linux/workqueue.h>
+
+#define OUTPUT_FRMT_656       0
+#define OPEN_FILE_1           0
+#define NUM_PROGS             8
+#define NUM_FRAMES            2
+#define ODD_FIELD             0
+#define EVEN_FIELD            1
+#define TOP_OFFSET            0
+#define FIFO_DISABLE          0
+#define FIFO_ENABLE           1
+#define TEST_FRAMES           5
+#define END_OF_FILE           0
+#define IN_PROGRESS           1
+#define RESET_STATUS          -1
+#define NUM_NO_OPS            5
+
+/* PAL and NTSC line sizes and number of lines. */
+#define WIDTH_D1              720
+#define NTSC_LINES_PER_FRAME  480
+#define PAL_LINES_PER_FRAME   576
+#define PAL_LINE_SZ           1440
+#define Y422_LINE_SZ          1440
+#define Y411_LINE_SZ          1080
+#define NTSC_FIELD_HEIGHT     240
+#define NTSC_ODD_FLD_LINES    241
+#define PAL_FIELD_HEIGHT      288
+
+#define FRAME_SIZE_NTSC_Y422    (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_NTSC_Y411    (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
+#define FRAME_SIZE_PAL_Y422     (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_PAL_Y411     (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
+
+#define NTSC_DATA_BUF_SZ        (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
+#define PAL_DATA_BUF_SZ         (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
+
+#define RISC_WRITECR_INSTRUCTION_SIZE   16
+#define RISC_SYNC_INSTRUCTION_SIZE      4
+#define JUMP_INSTRUCTION_SIZE           12
+#define MAXSIZE_NO_OPS                  36
+#define DWORD_SIZE                      4
+
+#define USE_RISC_NOOP_VIDEO   1
+
+#ifdef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define PAL_RISC_BUF_SIZE           (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE						\
+	((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE +			\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE						\
+	(NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE +			\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE +	\
+	 NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_RISC_BUF_SIZE						\
+	(2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE +	\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#endif
+
+#ifndef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE +	\
+	 JUMP_INSTRUCTION_SIZE)
+
+#define PAL_RISC_BUF_SIZE		(2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE						\
+	((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE +			\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE						\
+	(PAL_FIELD_HEIGHT * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE						\
+	(NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE +				\
+	 RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE +			\
+	 RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#define NTSC_RISC_BUF_SIZE						\
+	(2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE						\
+	((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE +	\
+	 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+	 JUMP_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c
new file mode 100644
index 000000000000..0a80245165d0
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video.c
@@ -0,0 +1,1990 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *  Parts adapted/taken from Eduardo Moscoso Rubino
+ *  Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug = 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]");
+
+unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num);
+
+static const struct v4l2_file_operations video_fops;
+static const struct v4l2_ioctl_ops video_ioctl_ops;
+
+#define FORMAT_FLAGS_PACKED       0x01
+
+struct cx25821_fmt formats[] = {
+	{
+		.name = "8 bpp, gray",
+		.fourcc = V4L2_PIX_FMT_GREY,
+		.depth = 8,
+		.flags = FORMAT_FLAGS_PACKED,
+	 }, {
+		.name = "4:1:1, packed, Y41P",
+		.fourcc = V4L2_PIX_FMT_Y41P,
+		.depth = 12,
+		.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,
+	}, {
+		.name = "4:2:0, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV420,
+		.depth = 12,
+		.flags = FORMAT_FLAGS_PACKED,
+	},
+};
+
+int cx25821_get_format_size(void)
+{
+	return ARRAY_SIZE(formats);
+}
+
+struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
+{
+	unsigned int i;
+
+	if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P)
+		return formats + 1;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++)
+		if (formats[i].fourcc == fourcc)
+			return formats + i;
+
+	pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+	return NULL;
+}
+
+void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
+			  u32 count)
+{
+	struct cx25821_buffer *buf;
+	int bc;
+
+	for (bc = 0;; bc++) {
+		if (list_empty(&q->active)) {
+			dprintk(1, "bc=%d (=0: active empty)\n", bc);
+			break;
+		}
+
+		buf = list_entry(q->active.next, struct cx25821_buffer,
+				vb.queue);
+
+		/* count comes from the hw and it 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);
+		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)
+		pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm)
+{
+	dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+		__func__, (unsigned int)norm, v4l2_norm_to_name(norm));
+
+	dev->tvnorm = norm;
+
+	/* Tell the internal A/V decoder */
+	cx25821_call_all(dev, core, s_std, norm);
+
+	return 0;
+}
+#endif
+
+struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
+				       struct pci_dev *pci,
+				       struct video_device *template,
+				       char *type)
+{
+	struct video_device *vfd;
+	dprintk(1, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->release = video_device_release;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type,
+		 cx25821_boards[dev->board].name);
+	video_set_drvdata(vfd, dev);
+	return vfd;
+}
+
+/*
+static int cx25821_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 < CX25821_CTLS; i++)
+		if (cx25821_ctls[i].v.id == qctrl->id)
+			break;
+	if (i == CX25821_CTLS) {
+		*qctrl = no_ctl;
+		return 0;
+	}
+	*qctrl = cx25821_ctls[i].v;
+	return 0;
+}
+*/
+
+/* resource management */
+int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+		    unsigned int bit)
+{
+	dprintk(1, "%s()\n", __func__);
+	if (fh->resources & bit)
+		/* have it already allocated */
+		return 1;
+
+	/* is it free? */
+	mutex_lock(&dev->lock);
+	if (dev->channels[fh->channel_id].resources & bit) {
+		/* no, someone else uses it */
+		mutex_unlock(&dev->lock);
+		return 0;
+	}
+	/* it's free, grab it */
+	fh->resources |= bit;
+	dev->channels[fh->channel_id].resources |= bit;
+	dprintk(1, "res: get %d\n", bit);
+	mutex_unlock(&dev->lock);
+	return 1;
+}
+
+int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit)
+{
+	return fh->resources & bit;
+}
+
+int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit)
+{
+	return fh->dev->channels[fh->channel_id].resources & bit;
+}
+
+void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+		      unsigned int bits)
+{
+	BUG_ON((fh->resources & bits) != bits);
+	dprintk(1, "%s()\n", __func__);
+
+	mutex_lock(&dev->lock);
+	fh->resources &= ~bits;
+	dev->channels[fh->channel_id].resources &= ~bits;
+	dprintk(1, "res: put %d\n", bits);
+	mutex_unlock(&dev->lock);
+}
+
+int cx25821_video_mux(struct cx25821_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",
+		__func__, 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 */
+	cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0);
+
+	return 0;
+}
+
+int cx25821_start_video_dma(struct cx25821_dev *dev,
+			    struct cx25821_dmaqueue *q,
+			    struct cx25821_buffer *buf,
+			    struct sram_channel *channel)
+{
+	int tmp = 0;
+
+	/* setup fifo + format */
+	cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma);
+
+	/* reset counter */
+	cx_write(channel->gpcnt_ctl, 3);
+	q->count = 1;
+
+	/* enable irq */
+	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
+	cx_set(channel->int_msk, 0x11);
+
+	/* start dma */
+	cx_write(channel->dma_ctl, 0x11);	/* FIFO and RISC enable */
+
+	/* make sure upstream setting if any is reversed */
+	tmp = cx_read(VID_CH_MODE_SEL);
+	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+
+	return 0;
+}
+
+int cx25821_restart_video_queue(struct cx25821_dev *dev,
+				struct cx25821_dmaqueue *q,
+				struct sram_channel *channel)
+{
+	struct cx25821_buffer *buf, *prev;
+	struct list_head *item;
+
+	if (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx25821_buffer,
+				vb.queue);
+
+		cx25821_start_video_dma(dev, q, buf, channel);
+
+		list_for_each(item, &q->active) {
+			buf = list_entry(item, struct cx25821_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 cx25821_buffer,
+				vb.queue);
+
+		if (NULL == prev) {
+			list_move_tail(&buf->vb.queue, &q->active);
+			cx25821_start_video_dma(dev, q, buf, channel);
+			buf->vb.state = VIDEOBUF_ACTIVE;
+			buf->count = q->count++;
+			mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+		} 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 */
+		} else {
+			return 0;
+		}
+		prev = buf;
+	}
+}
+
+void cx25821_vid_timeout(unsigned long data)
+{
+	struct cx25821_data *timeout_data = (struct cx25821_data *)data;
+	struct cx25821_dev *dev = timeout_data->dev;
+	struct sram_channel *channel = timeout_data->channel;
+	struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
+	struct cx25821_buffer *buf;
+	unsigned long flags;
+
+	/* cx25821_sram_channel_dump(dev, channel); */
+	cx_clear(channel->dma_ctl, 0x11);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	while (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx25821_buffer,
+				vb.queue);
+		list_del(&buf->vb.queue);
+
+		buf->vb.state = VIDEOBUF_ERROR;
+		wake_up(&buf->vb.done);
+	}
+
+	cx25821_restart_video_queue(dev, q, channel);
+	spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
+{
+	u32 count = 0;
+	int handled = 0;
+	u32 mask;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+
+	mask = cx_read(channel->int_msk);
+	if (0 == (status & mask))
+		return handled;
+
+	cx_write(channel->int_stat, status);
+
+	/* risc op code error */
+	if (status & (1 << 16)) {
+		pr_warn("%s, %s: video risc op code error\n",
+			dev->name, channel->name);
+		cx_clear(channel->dma_ctl, 0x11);
+		cx25821_sram_channel_dump(dev, channel);
+	}
+
+	/* risc1 y */
+	if (status & FLD_VID_DST_RISC1) {
+		spin_lock(&dev->slock);
+		count = cx_read(channel->gpcnt);
+		cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq,
+				count);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+
+	/* risc2 y */
+	if (status & 0x10) {
+		dprintk(2, "stopper video\n");
+		spin_lock(&dev->slock);
+		cx25821_restart_video_queue(dev,
+				&dev->channels[channel->i].vidq, channel);
+		spin_unlock(&dev->slock);
+		handled++;
+	}
+	return handled;
+}
+
+void cx25821_videoioctl_unregister(struct cx25821_dev *dev)
+{
+	if (dev->ioctl_dev) {
+		if (video_is_registered(dev->ioctl_dev))
+			video_unregister_device(dev->ioctl_dev);
+		else
+			video_device_release(dev->ioctl_dev);
+
+		dev->ioctl_dev = NULL;
+	}
+}
+
+void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
+{
+	cx_clear(PCI_INT_MSK, 1);
+
+	if (dev->channels[chan_num].video_dev) {
+		if (video_is_registered(dev->channels[chan_num].video_dev))
+			video_unregister_device(
+					dev->channels[chan_num].video_dev);
+		else
+			video_device_release(
+					dev->channels[chan_num].video_dev);
+
+		dev->channels[chan_num].video_dev = NULL;
+
+		btcx_riscmem_free(dev->pci,
+				&dev->channels[chan_num].vidq.stopper);
+
+		pr_warn("device %d released!\n", chan_num);
+	}
+
+}
+
+int cx25821_video_register(struct cx25821_dev *dev)
+{
+	int err;
+	int i;
+
+	struct video_device cx25821_video_device = {
+		.name = "cx25821-video",
+		.fops = &video_fops,
+		.minor = -1,
+		.ioctl_ops = &video_ioctl_ops,
+		.tvnorms = CX25821_NORMS,
+		.current_norm = V4L2_STD_NTSC_M,
+	};
+
+	spin_lock_init(&dev->slock);
+
+	for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
+		cx25821_init_controls(dev, i);
+
+		cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper,
+			dev->channels[i].sram_channels->dma_ctl, 0x11, 0);
+
+		dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+		dev->channels[i].video_dev = NULL;
+		dev->channels[i].resources = 0;
+
+		cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+
+		INIT_LIST_HEAD(&dev->channels[i].vidq.active);
+		INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
+
+		dev->channels[i].timeout_data.dev = dev;
+		dev->channels[i].timeout_data.channel =
+			&cx25821_sram_channels[i];
+		dev->channels[i].vidq.timeout.function = cx25821_vid_timeout;
+		dev->channels[i].vidq.timeout.data =
+			(unsigned long)&dev->channels[i].timeout_data;
+		init_timer(&dev->channels[i].vidq.timeout);
+
+		/* register v4l devices */
+		dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci,
+				&cx25821_video_device, "video");
+
+		err = video_register_device(dev->channels[i].video_dev,
+				VFL_TYPE_GRABBER, video_nr[dev->nr]);
+
+		if (err < 0)
+			goto fail_unreg;
+
+	}
+
+	/* set PCI interrupt */
+	cx_set(PCI_INT_MSK, 0xff);
+
+	/* initial device configuration */
+	mutex_lock(&dev->lock);
+#ifdef TUNER_FLAG
+	dev->tvnorm = cx25821_video_device.current_norm;
+	cx25821_set_tvnorm(dev, dev->tvnorm);
+#endif
+	mutex_unlock(&dev->lock);
+
+	return 0;
+
+fail_unreg:
+	cx25821_video_unregister(dev, i);
+	return err;
+}
+
+int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+		 unsigned int *size)
+{
+	struct cx25821_fh *fh = q->priv_data;
+
+	*size = fh->fmt->depth * fh->width * fh->height >> 3;
+
+	if (0 == *count)
+		*count = 32;
+
+	if (*size * *count > vid_limit * 1024 * 1024)
+		*count = (vid_limit * 1024 * 1024) / *size;
+
+	return 0;
+}
+
+int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+		   enum v4l2_field field)
+{
+	struct cx25821_fh *fh = q->priv_data;
+	struct cx25821_dev *dev = fh->dev;
+	struct cx25821_buffer *buf =
+		container_of(vb, struct cx25821_buffer, vb);
+	int rc, init_buffer = 0;
+	u32 line0_offset;
+	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+	int bpl_local = LINE_SIZE_D1;
+	int channel_opened = fh->channel_id;
+
+	BUG_ON(NULL == fh->fmt);
+	if (fh->width < 48 || fh->width > 720 ||
+	    fh->height < 32 || fh->height > 576)
+		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) {
+			printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n"));
+			goto fail;
+		}
+	}
+
+	dprintk(1, "init_buffer=%d\n", init_buffer);
+
+	if (init_buffer) {
+
+		channel_opened = dev->channel_opened;
+		if (channel_opened < 0 || channel_opened > 7)
+			channel_opened = 7;
+
+		if (dev->channels[channel_opened].pixel_formats ==
+				PIXEL_FRMT_411)
+			buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
+		else
+			buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
+
+		if (dev->channels[channel_opened].pixel_formats ==
+				PIXEL_FRMT_411) {
+			bpl_local = buf->bpl;
+		} else {
+			bpl_local = buf->bpl;   /* Default */
+
+			if (channel_opened >= 0 && channel_opened <= 7) {
+				if (dev->channels[channel_opened]
+						.use_cif_resolution) {
+					if (dev->tvnorm & V4L2_STD_PAL_BG ||
+					    dev->tvnorm & V4L2_STD_PAL_DK)
+						bpl_local = 352 << 1;
+					else
+						bpl_local = dev->channels[
+							channel_opened].
+							cif_width << 1;
+				}
+			}
+		}
+
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			cx25821_risc_buffer(dev->pci, &buf->risc,
+					    dma->sglist, 0, UNSET,
+					    buf->bpl, 0, buf->vb.height);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			cx25821_risc_buffer(dev->pci, &buf->risc,
+					    dma->sglist, UNSET, 0,
+					    buf->bpl, 0, buf->vb.height);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			/* All other formats are top field first */
+			line0_offset = 0;
+			dprintk(1, "top field first\n");
+
+			cx25821_risc_buffer(dev->pci, &buf->risc,
+					    dma->sglist, line0_offset,
+					    bpl_local, bpl_local, bpl_local,
+					    buf->vb.height >> 1);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			cx25821_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:
+			cx25821_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:
+	cx25821_free_buffer(q, buf);
+	return rc;
+}
+
+void cx25821_buffer_release(struct videobuf_queue *q,
+			    struct videobuf_buffer *vb)
+{
+	struct cx25821_buffer *buf =
+		container_of(vb, struct cx25821_buffer, vb);
+
+	cx25821_free_buffer(q, buf);
+}
+
+struct videobuf_queue *get_queue(struct cx25821_fh *fh)
+{
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &fh->vidq;
+	default:
+		BUG();
+		return NULL;
+	}
+}
+
+int cx25821_get_resource(struct cx25821_fh *fh, int resource)
+{
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return resource;
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cx25821_fh *fh = file->private_data;
+
+	return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct cx25821_buffer *buf =
+		container_of(vb, struct cx25821_buffer, vb);
+	struct cx25821_buffer *prev;
+	struct cx25821_fh *fh = vq->priv_data;
+	struct cx25821_dev *dev = fh->dev;
+	struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].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 */
+
+	dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
+
+	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);
+		cx25821_start_video_dma(dev, q, buf,
+				dev->channels[fh->channel_id].sram_channels);
+		buf->vb.state = VIDEOBUF_ACTIVE;
+		buf->count = q->count++;
+		mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+		dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
+				buf, buf->vb.i, buf->count, q->count);
+	} else {
+		prev = list_entry(q->active.prev, struct cx25821_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, buf->count=%d\n",
+					buf, buf->vb.i, buf->count);
+
+		} 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);
+		}
+	}
+
+	if (list_empty(&q->active))
+		dprintk(2, "active queue empty!\n");
+}
+
+static struct videobuf_queue_ops cx25821_video_qops = {
+	.buf_setup = cx25821_buffer_setup,
+	.buf_prepare = cx25821_buffer_prepare,
+	.buf_queue = buffer_queue,
+	.buf_release = cx25821_buffer_release,
+};
+
+static int video_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx25821_dev *h, *dev = video_drvdata(file);
+	struct cx25821_fh *fh;
+	struct list_head *list;
+	int minor = video_devdata(file)->minor;
+	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	u32 pix_format;
+	int ch_id = 0;
+	int i;
+
+	dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev),
+			v4l2_type_names[type]);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	mutex_lock(&cx25821_devlist_mutex);
+
+	list_for_each(list, &cx25821_devlist)
+	{
+		h = list_entry(list, struct cx25821_dev, devlist);
+
+		for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
+			if (h->channels[i].video_dev &&
+			    h->channels[i].video_dev->minor == minor) {
+				dev = h;
+				ch_id = i;
+				type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			}
+		}
+	}
+
+	if (NULL == dev) {
+		mutex_unlock(&cx25821_devlist_mutex);
+		kfree(fh);
+		return -ENODEV;
+	}
+
+	file->private_data = fh;
+	fh->dev = dev;
+	fh->type = type;
+	fh->width = 720;
+	fh->channel_id = ch_id;
+
+	if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+		fh->height = 576;
+	else
+		fh->height = 480;
+
+	dev->channel_opened = fh->channel_id;
+	if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411)
+		pix_format = V4L2_PIX_FMT_Y41P;
+	else
+		pix_format = V4L2_PIX_FMT_YUYV;
+	fh->fmt = cx25821_format_by_fourcc(pix_format);
+
+	v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
+
+	videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev,
+			&dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer),
+			fh, NULL);
+
+	dprintk(1, "post videobuf_queue_init()\n");
+	mutex_unlock(&cx25821_devlist_mutex);
+
+	return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user * data, size_t count,
+			 loff_t *ppos)
+{
+	struct cx25821_fh *fh = file->private_data;
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
+			return -EBUSY;
+
+		return videobuf_read_one(&fh->vidq, data, count, ppos,
+					file->f_flags & O_NONBLOCK);
+
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static unsigned int video_poll(struct file *file,
+			      struct poll_table_struct *wait)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_buffer *buf;
+
+	if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+		/* streaming capture */
+		if (list_empty(&fh->vidq.stream))
+			return POLLERR;
+		buf = list_entry(fh->vidq.stream.next,
+				struct cx25821_buffer, vb.stream);
+	} else {
+		/* read() capture */
+		buf = (struct cx25821_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) {
+		if (buf->vb.state == VIDEOBUF_DONE) {
+			struct cx25821_dev *dev = fh->dev;
+
+			if (dev && dev->channels[fh->channel_id]
+					.use_cif_resolution) {
+				u8 cam_id = *((char *)buf->vb.baddr + 3);
+				memcpy((char *)buf->vb.baddr,
+				      (char *)buf->vb.baddr + (fh->width * 2),
+				      (fh->width * 2));
+				*((char *)buf->vb.baddr + 3) = cam_id;
+			}
+		}
+
+		return POLLIN | POLLRDNORM;
+	}
+
+	return 0;
+}
+
+static int video_release(struct file *file)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_dev *dev = fh->dev;
+
+	/* stop the risc engine and fifo */
+	cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
+
+	/* stop video capture */
+	if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+		videobuf_queue_cancel(&fh->vidq);
+		cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
+	}
+
+	if (fh->vidq.read_buf) {
+		cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
+		kfree(fh->vidq.read_buf);
+	}
+
+	videobuf_mmap_free(&fh->vidq);
+
+	v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = fh->dev;
+
+	if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+		return -EINVAL;
+
+	if (unlikely(i != fh->type))
+		return -EINVAL;
+
+	if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh,
+						RESOURCE_VIDEO0))))
+		return -EBUSY;
+
+	return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = fh->dev;
+	int err, res;
+
+	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (i != fh->type)
+		return -EINVAL;
+
+	res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
+	err = videobuf_streamoff(get_queue(fh));
+	if (err < 0)
+		return err;
+	cx25821_res_free(dev, fh, res);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int err;
+	int pix_format = PIXEL_FRMT_422;
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+
+	dprintk(2, "%s()\n", __func__);
+	err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
+
+	if (0 != err)
+		return err;
+
+	fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+	fh->vidq.field = f->fmt.pix.field;
+
+	/* check if width and height is valid based on set standard */
+	if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
+		fh->width = f->fmt.pix.width;
+
+	if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
+		fh->height = f->fmt.pix.height;
+
+	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
+		pix_format = PIXEL_FRMT_411;
+	else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+		pix_format = PIXEL_FRMT_422;
+	else
+		return -EINVAL;
+
+	cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
+
+	/* check if cif resolution */
+	if (fh->width == 320 || fh->width == 352)
+		dev->channels[fh->channel_id].use_cif_resolution = 1;
+	else
+		dev->channels[fh->channel_id].use_cif_resolution = 0;
+
+	dev->channels[fh->channel_id].cif_width = fh->width;
+	medusa_set_resolution(dev, fh->width, SRAM_CH00);
+
+	dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width,
+		fh->height, fh->vidq.field);
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+	cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+
+	return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	int ret_val = 0;
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+	ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
+
+	p->sequence = dev->channels[fh->channel_id].vidq.count;
+
+	return ret_val;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct cx25821_fh *fh = priv;
+	char name[32 + 2];
+
+	struct sram_channel *sram_ch = dev->channels[fh->channel_id]
+								.sram_channels;
+	u32 tmp = 0;
+
+	snprintf(name, sizeof(name), "%s/2", dev->name);
+	pr_info("%s/2: ============  START LOG STATUS  ============\n",
+		dev->name);
+	cx25821_call_all(dev, core, log_status);
+	tmp = cx_read(sram_ch->dma_ctl);
+	pr_info("Video input 0 is %s\n",
+		(tmp & 0x11) ? "streaming" : "stopped");
+	pr_info("%s/2: =============  END LOG STATUS  =============\n",
+		dev->name);
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+			struct v4l2_control *ctl)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	int err;
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+
+	return cx25821_set_control(dev, ctl, fh->channel_id);
+}
+
+/* VIDEO IOCTLS */
+int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				 struct v4l2_format *f)
+{
+	struct cx25821_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;
+}
+
+int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_format *f)
+{
+	struct cx25821_fmt *fmt;
+	enum v4l2_field field;
+	unsigned int maxw, maxh;
+
+	fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+	if (NULL == fmt)
+		return -EINVAL;
+
+	field = f->fmt.pix.field;
+	maxw = 720;
+	maxh = 576;
+
+	if (V4L2_FIELD_ANY == field) {
+		if (f->fmt.pix.height > maxh / 2)
+			field = V4L2_FIELD_INTERLACED;
+		else
+			field = V4L2_FIELD_TOP;
+	}
+
+	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;
+}
+
+int cx25821_vidioc_querycap(struct file *file, void *priv,
+			    struct v4l2_capability *cap)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+	strcpy(cap->driver, "cx25821");
+	strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card));
+	sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+	cap->version = CX25821_VERSION_CODE;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+		V4L2_CAP_STREAMING;
+	if (UNSET != dev->tuner_type)
+		cap->capabilities |= V4L2_CAP_TUNER;
+	return 0;
+}
+
+int cx25821_vidioc_enum_fmt_vid_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;
+}
+
+int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+			   struct v4l2_requestbuffers *p)
+{
+	struct cx25821_fh *fh = priv;
+	return videobuf_reqbufs(get_queue(fh), p);
+}
+
+int cx25821_vidioc_querybuf(struct file *file, void *priv,
+			    struct v4l2_buffer *p)
+{
+	struct cx25821_fh *fh = priv;
+	return videobuf_querybuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct cx25821_fh *fh = priv;
+	return videobuf_qbuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+	struct cx25821_fh *fh = f;
+
+	*p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
+
+	return 0;
+}
+
+int cx25821_vidioc_s_priority(struct file *file, void *f,
+			      enum v4l2_priority prio)
+{
+	struct cx25821_fh *fh = f;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+
+	return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio,
+			prio);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	int err;
+
+	dprintk(1, "%s()\n", __func__);
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+
+	if (dev->tvnorm == *tvnorms)
+		return 0;
+
+	mutex_lock(&dev->lock);
+	cx25821_set_tvnorm(dev, *tvnorms);
+	mutex_unlock(&dev->lock);
+
+	medusa_set_videostandard(dev);
+
+	return 0;
+}
+#endif
+
+int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
+{
+	static const char * const iname[] = {
+		[CX25821_VMUX_COMPOSITE] = "Composite",
+		[CX25821_VMUX_SVIDEO] = "S-Video",
+		[CX25821_VMUX_DEBUG] = "for debug only",
+	};
+	unsigned int n;
+	dprintk(1, "%s()\n", __func__);
+
+	n = i->index;
+	if (n >= 2)
+		return -EINVAL;
+
+	if (0 == INPUT(n)->type)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strcpy(i->name, iname[INPUT(n)->type]);
+
+	i->std = CX25821_NORMS;
+	return 0;
+}
+
+int cx25821_vidioc_enum_input(struct file *file, void *priv,
+			      struct v4l2_input *i)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	dprintk(1, "%s()\n", __func__);
+	return cx25821_enum_input(dev, i);
+}
+
+int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+	*i = dev->input;
+	dprintk(1, "%s(): returns %d\n", __func__, *i);
+	return 0;
+}
+
+int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	int err;
+
+	dprintk(1, "%s(%d)\n", __func__, i);
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+
+	if (i >= CX25821_NR_INPUT) {
+		dprintk(1, "%s(): -EINVAL\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->lock);
+	cx25821_video_mux(dev, i);
+	mutex_unlock(&dev->lock);
+	return 0;
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+			       struct v4l2_frequency *f)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev = fh->dev;
+
+	f->frequency = dev->freq;
+
+	cx25821_call_all(dev, tuner, g_frequency, f);
+
+	return 0;
+}
+
+int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f)
+{
+	mutex_lock(&dev->lock);
+	dev->freq = f->frequency;
+
+	cx25821_call_all(dev, tuner, s_frequency, f);
+
+	/* When changing channels it is required to reset TVAUDIO */
+	msleep(10);
+
+	mutex_unlock(&dev->lock);
+
+	return 0;
+}
+
+int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+			       struct v4l2_frequency *f)
+{
+	struct cx25821_fh *fh = priv;
+	struct cx25821_dev *dev;
+	int err;
+
+	if (fh) {
+		dev = fh->dev;
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	} else {
+		pr_err("Invalid fh pointer!\n");
+		return -EINVAL;
+	}
+
+	return cx25821_set_freq(dev, f);
+}
+#endif
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx25821_vidioc_g_register(struct file *file, void *fh,
+		      struct v4l2_dbg_register *reg)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+
+	cx25821_call_all(dev, core, g_register, reg);
+
+	return 0;
+}
+
+int cx25821_vidioc_s_register(struct file *file, void *fh,
+		      struct v4l2_dbg_register *reg)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+
+	cx25821_call_all(dev, core, s_register, reg);
+
+	return 0;
+}
+
+#endif
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+	struct cx25821_dev *dev = ((struct cx25821_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;
+}
+
+int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct cx25821_fh *fh = priv;
+	int err;
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+
+	dprintk(1, "%s()\n", __func__);
+	if (UNSET == dev->tuner_type)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	return 0;
+}
+
+#endif
+/*****************************************************************************/
+static const struct v4l2_queryctrl no_ctl = {
+	.name = "42",
+	.flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct v4l2_queryctrl cx25821_ctls[] = {
+	/* --- video --- */
+	{
+		.id = V4L2_CID_BRIGHTNESS,
+		.name = "Brightness",
+		.minimum = 0,
+		.maximum = 10000,
+		.step = 1,
+		.default_value = 6200,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+	}, {
+		.id = V4L2_CID_CONTRAST,
+		.name = "Contrast",
+		.minimum = 0,
+		.maximum = 10000,
+		.step = 1,
+		.default_value = 5000,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+	}, {
+		.id = V4L2_CID_SATURATION,
+		.name = "Saturation",
+		.minimum = 0,
+		.maximum = 10000,
+		.step = 1,
+		.default_value = 5000,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+	}, {
+		.id = V4L2_CID_HUE,
+		.name = "Hue",
+		.minimum = 0,
+		.maximum = 10000,
+		.step = 1,
+		.default_value = 5000,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+	}
+};
+static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls);
+
+static int cx25821_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 < CX25821_CTLS; i++)
+		if (cx25821_ctls[i].id == qctrl->id)
+			break;
+	if (i == CX25821_CTLS) {
+		*qctrl = no_ctl;
+		return 0;
+	}
+	*qctrl = cx25821_ctls[i];
+	return 0;
+}
+
+int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+		     struct v4l2_queryctrl *qctrl)
+{
+	return cx25821_ctrl_query(qctrl);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS                                                  */
+
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+	unsigned int i;
+
+	for (i = 0; i < CX25821_CTLS; i++)
+		if (cx25821_ctls[i].id == id)
+			return cx25821_ctls + i;
+	return NULL;
+}
+
+int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+			  struct v4l2_control *ctl)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct cx25821_fh *fh = priv;
+
+	const struct v4l2_queryctrl *ctrl;
+
+	ctrl = ctrl_by_id(ctl->id);
+
+	if (NULL == ctrl)
+		return -EINVAL;
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctl->value = dev->channels[fh->channel_id].ctl_bright;
+		break;
+	case V4L2_CID_HUE:
+		ctl->value = dev->channels[fh->channel_id].ctl_hue;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctl->value = dev->channels[fh->channel_id].ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		ctl->value = dev->channels[fh->channel_id].ctl_saturation;
+		break;
+	}
+	return 0;
+}
+
+int cx25821_set_control(struct cx25821_dev *dev,
+			struct v4l2_control *ctl, int chan_num)
+{
+	int err;
+	const struct v4l2_queryctrl *ctrl;
+
+	err = -EINVAL;
+
+	ctrl = ctrl_by_id(ctl->id);
+
+	if (NULL == ctrl)
+		return err;
+
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_BOOLEAN:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_INTEGER:
+		if (ctl->value < ctrl->minimum)
+			ctl->value = ctrl->minimum;
+		if (ctl->value > ctrl->maximum)
+			ctl->value = ctrl->maximum;
+		break;
+	default:
+		/* nothing */ ;
+	}
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		dev->channels[chan_num].ctl_bright = ctl->value;
+		medusa_set_brightness(dev, ctl->value, chan_num);
+		break;
+	case V4L2_CID_HUE:
+		dev->channels[chan_num].ctl_hue = ctl->value;
+		medusa_set_hue(dev, ctl->value, chan_num);
+		break;
+	case V4L2_CID_CONTRAST:
+		dev->channels[chan_num].ctl_contrast = ctl->value;
+		medusa_set_contrast(dev, ctl->value, chan_num);
+		break;
+	case V4L2_CID_SATURATION:
+		dev->channels[chan_num].ctl_saturation = ctl->value;
+		medusa_set_saturation(dev, ctl->value, chan_num);
+		break;
+	}
+
+	err = 0;
+
+	return err;
+}
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num)
+{
+	struct v4l2_control ctrl;
+	int i;
+	for (i = 0; i < CX25821_CTLS; i++) {
+		ctrl.id = cx25821_ctls[i].id;
+		ctrl.value = cx25821_ctls[i].default_value;
+
+		cx25821_set_control(dev, &ctrl, chan_num);
+	}
+}
+
+int cx25821_vidioc_cropcap(struct file *file, void *priv,
+			   struct v4l2_cropcap *cropcap)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	cropcap->bounds.top = 0;
+	cropcap->bounds.left = 0;
+	cropcap->bounds.width = 720;
+	cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480;
+	cropcap->pixelaspect.numerator =
+		dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
+	cropcap->pixelaspect.denominator =
+		dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
+	cropcap->defrect = cropcap->bounds;
+	return 0;
+}
+
+int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop)
+{
+	struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct cx25821_fh *fh = priv;
+	int err;
+
+	if (fh) {
+		err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+				      fh->prio);
+		if (0 != err)
+			return err;
+	}
+	/* cx25821_vidioc_s_crop not supported */
+	return -EINVAL;
+}
+
+int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+	/* cx25821_vidioc_g_crop not supported */
+	return -EINVAL;
+}
+
+int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm)
+{
+	/* medusa does not support video standard sensing of current input */
+	*norm = CX25821_NORMS;
+
+	return 0;
+}
+
+int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm)
+{
+	if (tvnorm == V4L2_STD_PAL_BG) {
+		if (width == 352 || width == 720)
+			return 1;
+		else
+			return 0;
+	}
+
+	if (tvnorm == V4L2_STD_NTSC_M) {
+		if (width == 320 || width == 352 || width == 720)
+			return 1;
+		else
+			return 0;
+	}
+	return 0;
+}
+
+int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm)
+{
+	if (tvnorm == V4L2_STD_PAL_BG) {
+		if (height == 576 || height == 288)
+			return 1;
+		else
+			return 0;
+	}
+
+	if (tvnorm == V4L2_STD_NTSC_M) {
+		if (height == 480 || height == 240)
+			return 1;
+		else
+			return 0;
+	}
+
+	return 0;
+}
+
+static long video_ioctl_upstream9(struct file *file, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_dev *dev = fh->dev;
+	int command = 0;
+	struct upstream_user_struct *data_from_user;
+
+	data_from_user = (struct upstream_user_struct *)arg;
+
+	if (!data_from_user) {
+		pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+		return 0;
+	}
+
+	command = data_from_user->command;
+
+	if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+		return 0;
+
+	dev->input_filename = data_from_user->input_filename;
+	dev->input_audiofilename = data_from_user->input_filename;
+	dev->vid_stdname = data_from_user->vid_stdname;
+	dev->pixel_format = data_from_user->pixel_format;
+	dev->channel_select = data_from_user->channel_select;
+	dev->command = data_from_user->command;
+
+	switch (command) {
+	case UPSTREAM_START_VIDEO:
+		cx25821_start_upstream_video_ch1(dev, data_from_user);
+		break;
+
+	case UPSTREAM_STOP_VIDEO:
+		cx25821_stop_upstream_video_ch1(dev);
+		break;
+	}
+
+	return 0;
+}
+
+static long video_ioctl_upstream10(struct file *file, unsigned int cmd,
+				  unsigned long arg)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_dev *dev = fh->dev;
+	int command = 0;
+	struct upstream_user_struct *data_from_user;
+
+	data_from_user = (struct upstream_user_struct *)arg;
+
+	if (!data_from_user) {
+		pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+		return 0;
+	}
+
+	command = data_from_user->command;
+
+	if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+		return 0;
+
+	dev->input_filename_ch2 = data_from_user->input_filename;
+	dev->input_audiofilename = data_from_user->input_filename;
+	dev->vid_stdname_ch2 = data_from_user->vid_stdname;
+	dev->pixel_format_ch2 = data_from_user->pixel_format;
+	dev->channel_select_ch2 = data_from_user->channel_select;
+	dev->command_ch2 = data_from_user->command;
+
+	switch (command) {
+	case UPSTREAM_START_VIDEO:
+		cx25821_start_upstream_video_ch2(dev, data_from_user);
+		break;
+
+	case UPSTREAM_STOP_VIDEO:
+		cx25821_stop_upstream_video_ch2(dev);
+		break;
+	}
+
+	return 0;
+}
+
+static long video_ioctl_upstream11(struct file *file, unsigned int cmd,
+				  unsigned long arg)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_dev *dev = fh->dev;
+	int command = 0;
+	struct upstream_user_struct *data_from_user;
+
+	data_from_user = (struct upstream_user_struct *)arg;
+
+	if (!data_from_user) {
+		pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+		return 0;
+	}
+
+	command = data_from_user->command;
+
+	if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO)
+		return 0;
+
+	dev->input_filename = data_from_user->input_filename;
+	dev->input_audiofilename = data_from_user->input_filename;
+	dev->vid_stdname = data_from_user->vid_stdname;
+	dev->pixel_format = data_from_user->pixel_format;
+	dev->channel_select = data_from_user->channel_select;
+	dev->command = data_from_user->command;
+
+	switch (command) {
+	case UPSTREAM_START_AUDIO:
+		cx25821_start_upstream_audio(dev, data_from_user);
+		break;
+
+	case UPSTREAM_STOP_AUDIO:
+		cx25821_stop_upstream_audio(dev);
+		break;
+	}
+
+	return 0;
+}
+
+static long video_ioctl_set(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct cx25821_fh *fh = file->private_data;
+	struct cx25821_dev *dev = fh->dev;
+	struct downstream_user_struct *data_from_user;
+	int command;
+	int width = 720;
+	int selected_channel = 0;
+	int pix_format = 0;
+	int i = 0;
+	int cif_enable = 0;
+	int cif_width = 0;
+
+	data_from_user = (struct downstream_user_struct *)arg;
+
+	if (!data_from_user) {
+		pr_err("%s(): User data is INVALID. Returning\n", __func__);
+		return 0;
+	}
+
+	command = data_from_user->command;
+
+	if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
+	   && command != ENABLE_CIF_RESOLUTION && command != REG_READ
+	   && command != REG_WRITE && command != MEDUSA_READ
+	   && command != MEDUSA_WRITE) {
+		return 0;
+	}
+
+	switch (command) {
+	case SET_VIDEO_STD:
+		if (!strcmp(data_from_user->vid_stdname, "PAL"))
+			dev->tvnorm = V4L2_STD_PAL_BG;
+		else
+			dev->tvnorm = V4L2_STD_NTSC_M;
+		medusa_set_videostandard(dev);
+		break;
+
+	case SET_PIXEL_FORMAT:
+		selected_channel = data_from_user->decoder_select;
+		pix_format = data_from_user->pixel_format;
+
+		if (!(selected_channel <= 7 && selected_channel >= 0)) {
+			selected_channel -= 4;
+			selected_channel = selected_channel % 8;
+		}
+
+		if (selected_channel >= 0)
+			cx25821_set_pixel_format(dev, selected_channel,
+						pix_format);
+
+		break;
+
+	case ENABLE_CIF_RESOLUTION:
+		selected_channel = data_from_user->decoder_select;
+		cif_enable = data_from_user->cif_resolution_enable;
+		cif_width = data_from_user->cif_width;
+
+		if (cif_enable) {
+			if (dev->tvnorm & V4L2_STD_PAL_BG
+			    || dev->tvnorm & V4L2_STD_PAL_DK) {
+				width = 352;
+			} else {
+				width = cif_width;
+				if (cif_width != 320 && cif_width != 352)
+					width = 320;
+			}
+		}
+
+		if (!(selected_channel <= 7 && selected_channel >= 0)) {
+			selected_channel -= 4;
+			selected_channel = selected_channel % 8;
+		}
+
+		if (selected_channel <= 7 && selected_channel >= 0) {
+			dev->channels[selected_channel].use_cif_resolution =
+				cif_enable;
+			dev->channels[selected_channel].cif_width = width;
+		} else {
+			for (i = 0; i < VID_CHANNEL_NUM; i++) {
+				dev->channels[i].use_cif_resolution =
+					cif_enable;
+				dev->channels[i].cif_width = width;
+			}
+		}
+
+		medusa_set_resolution(dev, width, selected_channel);
+		break;
+	case REG_READ:
+		data_from_user->reg_data = cx_read(data_from_user->reg_address);
+		break;
+	case REG_WRITE:
+		cx_write(data_from_user->reg_address, data_from_user->reg_data);
+		break;
+	case MEDUSA_READ:
+		cx25821_i2c_read(&dev->i2c_bus[0],
+					 (u16) data_from_user->reg_address,
+					 &data_from_user->reg_data);
+		break;
+	case MEDUSA_WRITE:
+		cx25821_i2c_write(&dev->i2c_bus[0],
+				  (u16) data_from_user->reg_address,
+				  data_from_user->reg_data);
+		break;
+	}
+
+	return 0;
+}
+
+static long cx25821_video_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+
+	struct cx25821_fh *fh = file->private_data;
+
+	/* check to see if it's the video upstream */
+	if (fh->channel_id == SRAM_CH09) {
+		ret = video_ioctl_upstream9(file, cmd, arg);
+		return ret;
+	} else if (fh->channel_id == SRAM_CH10) {
+		ret = video_ioctl_upstream10(file, cmd, arg);
+		return ret;
+	} else if (fh->channel_id == SRAM_CH11) {
+		ret = video_ioctl_upstream11(file, cmd, arg);
+		ret = video_ioctl_set(file, cmd, arg);
+		return ret;
+	}
+
+	return video_ioctl2(file, cmd, arg);
+}
+
+/* exported stuff */
+static const struct v4l2_file_operations video_fops = {
+	.owner = THIS_MODULE,
+	.open = video_open,
+	.release = video_release,
+	.read = video_read,
+	.poll = video_poll,
+	.mmap = cx25821_video_mmap,
+	.ioctl = cx25821_video_ioctl,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+	.vidioc_querycap = cx25821_vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs = cx25821_vidioc_reqbufs,
+	.vidioc_querybuf = cx25821_vidioc_querybuf,
+	.vidioc_qbuf = cx25821_vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+#ifdef TUNER_FLAG
+	.vidioc_s_std = cx25821_vidioc_s_std,
+	.vidioc_querystd = cx25821_vidioc_querystd,
+#endif
+	.vidioc_cropcap = cx25821_vidioc_cropcap,
+	.vidioc_s_crop = cx25821_vidioc_s_crop,
+	.vidioc_g_crop = cx25821_vidioc_g_crop,
+	.vidioc_enum_input = cx25821_vidioc_enum_input,
+	.vidioc_g_input = cx25821_vidioc_g_input,
+	.vidioc_s_input = cx25821_vidioc_s_input,
+	.vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
+	.vidioc_s_ctrl = vidioc_s_ctrl,
+	.vidioc_queryctrl = cx25821_vidioc_queryctrl,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+	.vidioc_log_status = vidioc_log_status,
+	.vidioc_g_priority = cx25821_vidioc_g_priority,
+	.vidioc_s_priority = cx25821_vidioc_s_priority,
+#ifdef TUNER_FLAG
+	.vidioc_g_tuner = cx25821_vidioc_g_tuner,
+	.vidioc_s_tuner = cx25821_vidioc_s_tuner,
+	.vidioc_g_frequency = cx25821_vidioc_g_frequency,
+	.vidioc_s_frequency = cx25821_vidioc_s_frequency,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register = cx25821_vidioc_g_register,
+	.vidioc_s_register = cx25821_vidioc_s_register,
+#endif
+};
+
+struct video_device cx25821_videoioctl_template = {
+	.name = "cx25821-videoioctl",
+	.fops = &video_fops,
+	.ioctl_ops = &video_ioctl_ops,
+	.tvnorms = CX25821_NORMS,
+	.current_norm = V4L2_STD_NTSC_M,
+};
diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h
new file mode 100644
index 000000000000..c265e35b37c3
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video.h
@@ -0,0 +1,186 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CX25821_VIDEO_H_
+#define CX25821_VIDEO_H_
+
+#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 "cx25821.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#define TUNER_FLAG
+
+#define VIDEO_DEBUG 0
+
+#define dprintk(level, fmt, arg...)					\
+do {									\
+	if (VIDEO_DEBUG >= level)					\
+		printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg);	\
+} while (0)
+
+/* For IOCTL to identify running upstream */
+#define UPSTREAM_START_VIDEO        700
+#define UPSTREAM_STOP_VIDEO         701
+#define UPSTREAM_START_AUDIO        702
+#define UPSTREAM_STOP_AUDIO         703
+#define UPSTREAM_DUMP_REGISTERS     702
+#define SET_VIDEO_STD               800
+#define SET_PIXEL_FORMAT            1000
+#define ENABLE_CIF_RESOLUTION       1001
+
+#define REG_READ		    900
+#define REG_WRITE		    901
+#define MEDUSA_READ		    910
+#define MEDUSA_WRITE		    911
+
+extern struct sram_channel *channel0;
+extern struct sram_channel *channel1;
+extern struct sram_channel *channel2;
+extern struct sram_channel *channel3;
+extern struct sram_channel *channel4;
+extern struct sram_channel *channel5;
+extern struct sram_channel *channel6;
+extern struct sram_channel *channel7;
+extern struct sram_channel *channel9;
+extern struct sram_channel *channel10;
+extern struct sram_channel *channel11;
+extern struct video_device cx25821_videoioctl_template;
+/* extern const u32 *ctrl_classes[]; */
+
+extern unsigned int vid_limit;
+
+#define FORMAT_FLAGS_PACKED       0x01
+extern struct cx25821_fmt formats[];
+extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
+extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
+
+extern void cx25821_video_wakeup(struct cx25821_dev *dev,
+				 struct cx25821_dmaqueue *q, u32 count);
+
+#ifdef TUNER_FLAG
+extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
+#endif
+
+extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+			   unsigned int bit);
+extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit);
+extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit);
+extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+			     unsigned int bits);
+extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
+extern int cx25821_start_video_dma(struct cx25821_dev *dev,
+				   struct cx25821_dmaqueue *q,
+				   struct cx25821_buffer *buf,
+				   struct sram_channel *channel);
+
+extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width,
+			     unsigned int height, enum v4l2_field field);
+extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status);
+extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num);
+extern int cx25821_video_register(struct cx25821_dev *dev);
+extern int cx25821_get_format_size(void);
+
+extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+				unsigned int *size);
+extern int cx25821_buffer_prepare(struct videobuf_queue *q,
+				  struct videobuf_buffer *vb,
+				  enum v4l2_field field);
+extern void cx25821_buffer_release(struct videobuf_queue *q,
+				   struct videobuf_buffer *vb);
+extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
+extern int cx25821_get_resource(struct cx25821_fh *fh, int resource);
+extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma);
+extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+					  struct v4l2_format *f);
+extern int cx25821_vidioc_querycap(struct file *file, void *priv,
+				   struct v4l2_capability *cap);
+extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+					   struct v4l2_fmtdesc *f);
+extern int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+				  struct v4l2_requestbuffers *p);
+extern int cx25821_vidioc_querybuf(struct file *file, void *priv,
+				   struct v4l2_buffer *p);
+extern int cx25821_vidioc_qbuf(struct file *file, void *priv,
+			       struct v4l2_buffer *p);
+extern int cx25821_vidioc_s_std(struct file *file, void *priv,
+				v4l2_std_id *tvnorms);
+extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
+extern int cx25821_vidioc_enum_input(struct file *file, void *priv,
+				     struct v4l2_input *i);
+extern int cx25821_vidioc_g_input(struct file *file, void *priv,
+				  unsigned int *i);
+extern int cx25821_vidioc_s_input(struct file *file, void *priv,
+				  unsigned int i);
+extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+				 struct v4l2_control *ctl);
+extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f);
+extern int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+				      struct v4l2_frequency *f);
+extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
+extern int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+				      struct v4l2_frequency *f);
+extern int cx25821_vidioc_g_register(struct file *file, void *fh,
+				     struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_s_register(struct file *file, void *fh,
+				     struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_g_tuner(struct file *file, void *priv,
+				  struct v4l2_tuner *t);
+extern int cx25821_vidioc_s_tuner(struct file *file, void *priv,
+				  struct v4l2_tuner *t);
+
+extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm);
+extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm);
+
+extern int cx25821_vidioc_g_priority(struct file *file, void *f,
+				     enum v4l2_priority *p);
+extern int cx25821_vidioc_s_priority(struct file *file, void *f,
+				     enum v4l2_priority prio);
+
+extern int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+				    struct v4l2_queryctrl *qctrl);
+extern int cx25821_set_control(struct cx25821_dev *dev,
+			       struct v4l2_control *ctrl, int chan_num);
+
+extern int cx25821_vidioc_cropcap(struct file *file, void *fh,
+				  struct v4l2_cropcap *cropcap);
+extern int cx25821_vidioc_s_crop(struct file *file, void *priv,
+				 const struct v4l2_crop *crop);
+extern int cx25821_vidioc_g_crop(struct file *file, void *priv,
+				 struct v4l2_crop *crop);
+
+extern int cx25821_vidioc_querystd(struct file *file, void *priv,
+				   v4l2_std_id *norm);
+#endif
diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h
new file mode 100644
index 000000000000..8a9c0c869412
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821.h
@@ -0,0 +1,615 @@
+/*
+ *  Driver for the Conexant CX25821 PCIe bridge
+ *
+ *  Copyright (C) 2009 Conexant Systems Inc.
+ *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CX25821_H_
+#define CX25821_H_
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/kdev_t.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+
+#include "btcx-risc.h"
+#include "cx25821-reg.h"
+#include "cx25821-medusa-reg.h"
+#include "cx25821-sram.h"
+#include "cx25821-audio.h"
+#include "media/cx2341x.h"
+
+#include <linux/version.h>
+#include <linux/mutex.h>
+
+#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106)
+
+#define UNSET (-1U)
+#define NO_SYNC_LINE (-1U)
+
+#define CX25821_MAXBOARDS 2
+
+#define TRUE    1
+#define FALSE   0
+#define LINE_SIZE_D1    1440
+
+/* Number of decoders and encoders */
+#define MAX_DECODERS            8
+#define MAX_ENCODERS            2
+#define QUAD_DECODERS           4
+#define MAX_CAMERAS             16
+
+/* Max number of inputs by card */
+#define MAX_CX25821_INPUT     8
+#define INPUT(nr) (&cx25821_boards[dev->board].input[nr])
+#define RESOURCE_VIDEO0       1
+#define RESOURCE_VIDEO1       2
+#define RESOURCE_VIDEO2       4
+#define RESOURCE_VIDEO3       8
+#define RESOURCE_VIDEO4       16
+#define RESOURCE_VIDEO5       32
+#define RESOURCE_VIDEO6       64
+#define RESOURCE_VIDEO7       128
+#define RESOURCE_VIDEO8       256
+#define RESOURCE_VIDEO9       512
+#define RESOURCE_VIDEO10      1024
+#define RESOURCE_VIDEO11      2048
+#define RESOURCE_VIDEO_IOCTL  4096
+
+#define BUFFER_TIMEOUT     (HZ)	/* 0.5 seconds */
+
+#define UNKNOWN_BOARD        0
+#define CX25821_BOARD        1
+
+/* Currently supported by the driver */
+#define CX25821_NORMS (\
+	V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \
+	V4L2_STD_PAL_BG |  V4L2_STD_PAL_DK    |  V4L2_STD_PAL_I    | \
+	V4L2_STD_PAL_M  |  V4L2_STD_PAL_N     |  V4L2_STD_PAL_H    | \
+	V4L2_STD_PAL_Nc)
+
+#define CX25821_BOARD_CONEXANT_ATHENA10 1
+#define MAX_VID_CHANNEL_NUM     12
+#define VID_CHANNEL_NUM 8
+#define CX25821_NR_INPUT 2
+
+struct cx25821_fmt {
+	char *name;
+	u32 fourcc;		/* v4l2 format id */
+	int depth;
+	int flags;
+	u32 cxformat;
+};
+
+struct cx25821_ctrl {
+	struct v4l2_queryctrl v;
+	u32 off;
+	u32 reg;
+	u32 mask;
+	u32 shift;
+};
+
+struct cx25821_tvnorm {
+	char *name;
+	v4l2_std_id id;
+	u32 cxiformat;
+	u32 cxoformat;
+};
+
+struct cx25821_fh {
+	struct cx25821_dev *dev;
+	enum v4l2_buf_type type;
+	int radio;
+	u32 resources;
+
+	enum v4l2_priority prio;
+
+	/* video overlay */
+	struct v4l2_window win;
+	struct v4l2_clip *clips;
+	unsigned int nclips;
+
+	/* video capture */
+	struct cx25821_fmt *fmt;
+	unsigned int width, height;
+	int channel_id;
+
+	/* vbi capture */
+	struct videobuf_queue vidq;
+	struct videobuf_queue vbiq;
+
+	/* H264 Encoder specifics ONLY */
+	struct videobuf_queue mpegq;
+	atomic_t v4l_reading;
+};
+
+enum cx25821_itype {
+	CX25821_VMUX_COMPOSITE = 1,
+	CX25821_VMUX_SVIDEO,
+	CX25821_VMUX_DEBUG,
+	CX25821_RADIO,
+};
+
+enum cx25821_src_sel_type {
+	CX25821_SRC_SEL_EXT_656_VIDEO = 0,
+	CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO
+};
+
+/* buffer for one video frame */
+struct cx25821_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer vb;
+
+	/* cx25821 specific */
+	unsigned int bpl;
+	struct btcx_riscmem risc;
+	struct cx25821_fmt *fmt;
+	u32 count;
+};
+
+struct cx25821_input {
+	enum cx25821_itype type;
+	unsigned int vmux;
+	u32 gpio0, gpio1, gpio2, gpio3;
+};
+
+enum port {
+	CX25821_UNDEFINED = 0,
+	CX25821_RAW,
+	CX25821_264
+};
+
+struct cx25821_board {
+	const char *name;
+	enum port porta;
+	enum port portb;
+	enum port portc;
+	unsigned int tuner_type;
+	unsigned int radio_type;
+	unsigned char tuner_addr;
+	unsigned char radio_addr;
+
+	u32 clk_freq;
+	struct cx25821_input input[CX25821_NR_INPUT];
+};
+
+struct cx25821_subid {
+	u16 subvendor;
+	u16 subdevice;
+	u32 card;
+};
+
+struct cx25821_i2c {
+	struct cx25821_dev *dev;
+
+	int nr;
+
+	/* i2c i/o */
+	struct i2c_adapter i2c_adap;
+	struct i2c_client i2c_client;
+	u32 i2c_rc;
+
+	/* cx25821 registers used for raw addess */
+	u32 i2c_period;
+	u32 reg_ctrl;
+	u32 reg_stat;
+	u32 reg_addr;
+	u32 reg_rdata;
+	u32 reg_wdata;
+};
+
+struct cx25821_dmaqueue {
+	struct list_head active;
+	struct list_head queued;
+	struct timer_list timeout;
+	struct btcx_riscmem stopper;
+	u32 count;
+};
+
+struct cx25821_data {
+	struct cx25821_dev *dev;
+	struct sram_channel *channel;
+};
+
+struct cx25821_channel {
+	struct v4l2_prio_state prio;
+
+	int ctl_bright;
+	int ctl_contrast;
+	int ctl_hue;
+	int ctl_saturation;
+	struct cx25821_data timeout_data;
+
+	struct video_device *video_dev;
+	struct cx25821_dmaqueue vidq;
+
+	struct sram_channel *sram_channels;
+
+	struct mutex lock;
+	int resources;
+
+	int pixel_formats;
+	int use_cif_resolution;
+	int cif_width;
+};
+
+struct cx25821_dev {
+	struct list_head devlist;
+	atomic_t refcount;
+	struct v4l2_device v4l2_dev;
+
+	/* pci stuff */
+	struct pci_dev *pci;
+	unsigned char pci_rev, pci_lat;
+	int pci_bus, pci_slot;
+	u32 base_io_addr;
+	u32 __iomem *lmmio;
+	u8 __iomem *bmmio;
+	int pci_irqmask;
+	int hwrevision;
+
+	u32 clk_freq;
+
+	/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
+	struct cx25821_i2c i2c_bus[3];
+
+	int nr;
+	struct mutex lock;
+
+	struct cx25821_channel channels[MAX_VID_CHANNEL_NUM];
+
+	/* board details */
+	unsigned int board;
+	char name[32];
+
+	/* 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;
+	unsigned int videc_type;
+	unsigned char videc_addr;
+	unsigned short _max_num_decoders;
+
+	/* Analog Audio Upstream */
+	int _audio_is_running;
+	int _audiopixel_format;
+	int _is_first_audio_frame;
+	int _audiofile_status;
+	int _audio_lines_count;
+	int _audioframe_count;
+	int _audio_upstream_channel;
+	int _last_index_irq;    /* The last interrupt index processed. */
+
+	__le32 *_risc_audio_jmp_addr;
+	__le32 *_risc_virt_start_addr;
+	__le32 *_risc_virt_addr;
+	dma_addr_t _risc_phys_addr;
+	dma_addr_t _risc_phys_start_addr;
+
+	unsigned int _audiorisc_size;
+	unsigned int _audiodata_buf_size;
+	__le32 *_audiodata_buf_virt_addr;
+	dma_addr_t _audiodata_buf_phys_addr;
+	char *_audiofilename;
+
+	/* V4l */
+	u32 freq;
+	struct video_device *vbi_dev;
+	struct video_device *radio_dev;
+	struct video_device *ioctl_dev;
+
+	spinlock_t slock;
+
+	/* Video Upstream */
+	int _line_size;
+	int _prog_cnt;
+	int _pixel_format;
+	int _is_first_frame;
+	int _is_running;
+	int _file_status;
+	int _lines_count;
+	int _frame_count;
+	int _channel_upstream_select;
+	unsigned int _risc_size;
+
+	__le32 *_dma_virt_start_addr;
+	__le32 *_dma_virt_addr;
+	dma_addr_t _dma_phys_addr;
+	dma_addr_t _dma_phys_start_addr;
+
+	unsigned int _data_buf_size;
+	__le32 *_data_buf_virt_addr;
+	dma_addr_t _data_buf_phys_addr;
+	char *_filename;
+	char *_defaultname;
+
+	int _line_size_ch2;
+	int _prog_cnt_ch2;
+	int _pixel_format_ch2;
+	int _is_first_frame_ch2;
+	int _is_running_ch2;
+	int _file_status_ch2;
+	int _lines_count_ch2;
+	int _frame_count_ch2;
+	int _channel2_upstream_select;
+	unsigned int _risc_size_ch2;
+
+	__le32 *_dma_virt_start_addr_ch2;
+	__le32 *_dma_virt_addr_ch2;
+	dma_addr_t _dma_phys_addr_ch2;
+	dma_addr_t _dma_phys_start_addr_ch2;
+
+	unsigned int _data_buf_size_ch2;
+	__le32 *_data_buf_virt_addr_ch2;
+	dma_addr_t _data_buf_phys_addr_ch2;
+	char *_filename_ch2;
+	char *_defaultname_ch2;
+
+	/* MPEG Encoder ONLY settings */
+	u32 cx23417_mailbox;
+	struct cx2341x_mpeg_params mpeg_params;
+	struct video_device *v4l_device;
+	atomic_t v4l_reader_count;
+	struct cx25821_tvnorm encodernorm;
+
+	u32 upstream_riscbuf_size;
+	u32 upstream_databuf_size;
+	u32 upstream_riscbuf_size_ch2;
+	u32 upstream_databuf_size_ch2;
+	u32 audio_upstream_riscbuf_size;
+	u32 audio_upstream_databuf_size;
+	int _isNTSC;
+	int _frame_index;
+	int _audioframe_index;
+	struct workqueue_struct *_irq_queues;
+	struct work_struct _irq_work_entry;
+	struct workqueue_struct *_irq_queues_ch2;
+	struct work_struct _irq_work_entry_ch2;
+	struct workqueue_struct *_irq_audio_queues;
+	struct work_struct _audio_work_entry;
+	char *input_filename;
+	char *input_filename_ch2;
+	int _frame_index_ch2;
+	int _isNTSC_ch2;
+	char *vid_stdname_ch2;
+	int pixel_format_ch2;
+	int channel_select_ch2;
+	int command_ch2;
+	char *input_audiofilename;
+	char *vid_stdname;
+	int pixel_format;
+	int channel_select;
+	int command;
+	int channel_opened;
+};
+
+struct upstream_user_struct {
+	char *input_filename;
+	char *vid_stdname;
+	int pixel_format;
+	int channel_select;
+	int command;
+};
+
+struct downstream_user_struct {
+	char *vid_stdname;
+	int pixel_format;
+	int cif_resolution_enable;
+	int cif_width;
+	int decoder_select;
+	int command;
+	int reg_address;
+	int reg_data;
+};
+
+extern struct upstream_user_struct *up_data;
+
+static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev);
+}
+
+#define cx25821_call_all(dev, o, f, args...) \
+	v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+extern struct list_head cx25821_devlist;
+extern struct mutex cx25821_devlist_mutex;
+
+extern struct cx25821_board cx25821_boards[];
+extern struct cx25821_subid cx25821_subids[];
+
+#define SRAM_CH00  0		/* Video A */
+#define SRAM_CH01  1		/* Video B */
+#define SRAM_CH02  2		/* Video C */
+#define SRAM_CH03  3		/* Video D */
+#define SRAM_CH04  4		/* Video E */
+#define SRAM_CH05  5		/* Video F */
+#define SRAM_CH06  6		/* Video G */
+#define SRAM_CH07  7		/* Video H */
+
+#define SRAM_CH08  8		/* Audio A */
+#define SRAM_CH09  9		/* Video Upstream I */
+#define SRAM_CH10  10		/* Video Upstream J */
+#define SRAM_CH11  11		/* Audio Upstream AUD_CHANNEL_B */
+
+#define VID_UPSTREAM_SRAM_CHANNEL_I     SRAM_CH09
+#define VID_UPSTREAM_SRAM_CHANNEL_J     SRAM_CH10
+#define AUDIO_UPSTREAM_SRAM_CHANNEL_B   SRAM_CH11
+#define VIDEO_IOCTL_CH  11
+
+struct sram_channel {
+	char *name;
+	u32 i;
+	u32 cmds_start;
+	u32 ctrl_start;
+	u32 cdt;
+	u32 fifo_start;
+	u32 fifo_size;
+	u32 ptr1_reg;
+	u32 ptr2_reg;
+	u32 cnt1_reg;
+	u32 cnt2_reg;
+	u32 int_msk;
+	u32 int_stat;
+	u32 int_mstat;
+	u32 dma_ctl;
+	u32 gpcnt_ctl;
+	u32 gpcnt;
+	u32 aud_length;
+	u32 aud_cfg;
+	u32 fld_aud_fifo_en;
+	u32 fld_aud_risc_en;
+
+	/* For Upstream Video */
+	u32 vid_fmt_ctl;
+	u32 vid_active_ctl1;
+	u32 vid_active_ctl2;
+	u32 vid_cdt_size;
+
+	u32 vip_ctl;
+	u32 pix_frmt;
+	u32 jumponly;
+	u32 irq_bit;
+};
+extern struct sram_channel cx25821_sram_channels[];
+
+#define STATUS_SUCCESS         0
+#define STATUS_UNSUCCESSFUL    -1
+
+#define cx_read(reg)             readl(dev->lmmio + ((reg)>>2))
+#define cx_write(reg, value)     writel((value), dev->lmmio + ((reg)>>2))
+
+#define cx_andor(reg, mask, value) \
+	writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+	((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#define cx_set(reg, bit)          cx_andor((reg), (bit), (bit))
+#define cx_clear(reg, bit)        cx_andor((reg), (bit), 0)
+
+#define Set_GPIO_Bit(Bit)                       (1 << Bit)
+#define Clear_GPIO_Bit(Bit)                     (~(1 << Bit))
+
+#define CX25821_ERR(fmt, args...)			\
+	pr_err("(%d): " fmt, dev->board, ##args)
+#define CX25821_WARN(fmt, args...)			\
+	pr_warn("(%d): " fmt, dev->board, ##args)
+#define CX25821_INFO(fmt, args...)			\
+	pr_info("(%d): " fmt, dev->board, ##args)
+
+extern int cx25821_i2c_register(struct cx25821_i2c *bus);
+extern void cx25821_card_setup(struct cx25821_dev *dev);
+extern int cx25821_ir_init(struct cx25821_dev *dev);
+extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value);
+extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value);
+extern int cx25821_i2c_unregister(struct cx25821_i2c *bus);
+extern void cx25821_gpio_init(struct cx25821_dev *dev);
+extern void cx25821_set_gpiopin_direction(struct cx25821_dev *dev,
+					  int pin_number, int pin_logic_value);
+
+extern int medusa_video_init(struct cx25821_dev *dev);
+extern int medusa_set_videostandard(struct cx25821_dev *dev);
+extern void medusa_set_resolution(struct cx25821_dev *dev, int width,
+				  int decoder_select);
+extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness,
+				 int decoder);
+extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast,
+			       int decoder);
+extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder);
+extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation,
+				 int decoder);
+
+extern int cx25821_sram_channel_setup(struct cx25821_dev *dev,
+				      struct sram_channel *ch, unsigned int bpl,
+				      u32 risc);
+
+extern int cx25821_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);
+extern int cx25821_risc_databuffer_audio(struct pci_dev *pci,
+					 struct btcx_riscmem *risc,
+					 struct scatterlist *sglist,
+					 unsigned int bpl,
+					 unsigned int lines, unsigned int lpi);
+extern void cx25821_free_buffer(struct videobuf_queue *q,
+				struct cx25821_buffer *buf);
+extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+				u32 reg, u32 mask, u32 value);
+extern void cx25821_sram_channel_dump(struct cx25821_dev *dev,
+				      struct sram_channel *ch);
+extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
+					    struct sram_channel *ch);
+
+extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci);
+extern void cx25821_print_irqbits(char *name, char *tag, char **strings,
+				  int len, u32 bits, u32 mask);
+extern void cx25821_dev_unregister(struct cx25821_dev *dev);
+extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
+					    struct sram_channel *ch,
+					    unsigned int bpl, u32 risc);
+
+extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev,
+					int channel_select, int pixel_format);
+extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev,
+					int channel_select, int pixel_format);
+extern int cx25821_audio_upstream_init(struct cx25821_dev *dev,
+				       int channel_select);
+extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev);
+extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev);
+extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev);
+extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev,
+					     struct upstream_user_struct
+					     *up_data);
+extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev,
+					     struct upstream_user_struct
+					     *up_data);
+extern void cx25821_start_upstream_audio(struct cx25821_dev *dev,
+					 struct upstream_user_struct *up_data);
+extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev);
+extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev);
+extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev);
+extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
+					       struct sram_channel *ch,
+					       unsigned int bpl, u32 risc);
+extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel,
+				     u32 format);
+extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev);
+extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
+					      struct pci_dev *pci,
+					      struct video_device *template,
+					      char *type);
+#endif
diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig
new file mode 100644
index 000000000000..d27fccbf03c4
--- /dev/null
+++ b/drivers/media/pci/cx88/Kconfig
@@ -0,0 +1,86 @@
+config VIDEO_CX88
+	tristate "Conexant 2388x (bt878 successor) support"
+	depends on VIDEO_DEV && PCI && I2C && RC_CORE
+	select I2C_ALGOBIT
+	select VIDEO_BTCX
+	select VIDEOBUF_DMA_SG
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for Conexant 2388x based
+	  TV cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx8800
+
+config VIDEO_CX88_ALSA
+	tristate "Conexant 2388x DMA audio support"
+	depends on VIDEO_CX88 && SND
+	select SND_PCM
+	---help---
+	  This is a video4linux driver for direct (DMA) audio on
+	  Conexant 2388x based TV cards using ALSA.
+
+	  It only works with boards with function 01 enabled.
+	  To check if your board supports, use lspci -n.
+	  If supported, you should see 14f1:8801 or 14f1:8811
+	  PCI device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx88-alsa.
+
+config VIDEO_CX88_BLACKBIRD
+	tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
+	depends on VIDEO_CX88
+	select VIDEO_CX2341X
+	---help---
+	  This adds support for MPEG encoder cards based on the
+	  Blackbird reference design, using the Conexant 2388x
+	  and 23416 chips.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx88-blackbird.
+
+config VIDEO_CX88_DVB
+	tristate "DVB/ATSC Support for cx2388x based TV cards"
+	depends on VIDEO_CX88 && DVB_CORE
+	select VIDEOBUF_DVB
+	select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This adds support for DVB/ATSC cards based on the
+	  Conexant 2388x chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx88-dvb.
+
+config VIDEO_CX88_VP3054
+	tristate "VP-3054 Secondary I2C Bus Support"
+	default m
+	depends on VIDEO_CX88_DVB && DVB_MT352
+	---help---
+	  This adds DVB-T support for cards based on the
+	  Conexant 2388x chip and the MT352 demodulator,
+	  which also require support for the VP-3054
+	  Secondary I2C bus, such at DNTV Live! DVB-T Pro.
+
+config VIDEO_CX88_MPEG
+	tristate
+	depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
+	default y
diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile
new file mode 100644
index 000000000000..d3679c3ee248
--- /dev/null
+++ b/drivers/media/pci/cx88/Makefile
@@ -0,0 +1,16 @@
+cx88xx-objs	:= cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
+		   cx88-dsp.o cx88-input.o
+cx8800-objs	:= cx88-video.o cx88-vbi.o
+cx8802-objs	:= cx88-mpeg.o
+
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
+obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
+obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
+obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
+obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
+obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
new file mode 100644
index 000000000000..3aa6856ead3b
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -0,0 +1,975 @@
+/*
+ *
+ *  Support for audio capture
+ *  PCI function #1 of the cx2388x.
+ *
+ *    (c) 2007 Trent Piepho <xyzzy@speakeasy.org>
+ *    (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org>
+ *    (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *    Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org>
+ *    Based on dummy.c by Jaroslav Kysela <perex@perex.cz>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <asm/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <media/wm8775.h>
+
+#include "cx88.h"
+#include "cx88-reg.h"
+
+#define dprintk(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)
+
+#define dprintk_core(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)
+
+/****************************************************************************
+	Data type declarations - Can be moded to a header file later
+ ****************************************************************************/
+
+struct cx88_audio_buffer {
+	unsigned int               bpl;
+	struct btcx_riscmem        risc;
+	struct videobuf_dmabuf     dma;
+};
+
+struct cx88_audio_dev {
+	struct cx88_core           *core;
+	struct cx88_dmaqueue       q;
+
+	/* pci i/o */
+	struct pci_dev             *pci;
+
+	/* audio controls */
+	int                        irq;
+
+	struct snd_card            *card;
+
+	spinlock_t                 reg_lock;
+	atomic_t		   count;
+
+	unsigned int               dma_size;
+	unsigned int               period_size;
+	unsigned int               num_periods;
+
+	struct videobuf_dmabuf     *dma_risc;
+
+	struct cx88_audio_buffer   *buf;
+
+	struct snd_pcm_substream   *substream;
+};
+typedef struct cx88_audio_dev snd_cx88_card_t;
+
+
+
+/****************************************************************************
+			Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
+
+
+/****************************************************************************
+				Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Ricardo Cerqueira");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
+			"{{Conexant,23882},"
+			"{{Conexant,23883}");
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+/****************************************************************************
+			Module specific funtions
+ ****************************************************************************/
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
+{
+	struct cx88_audio_buffer *buf = chip->buf;
+	struct cx88_core *core=chip->core;
+	const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
+
+	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+	cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+	/* setup fifo + format - out channel */
+	cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
+
+	/* sets bpl size */
+	cx_write(MO_AUDD_LNGTH, buf->bpl);
+
+	/* reset counter */
+	cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+	atomic_set(&chip->count, 0);
+
+	dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+		"byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1,
+		chip->num_periods, buf->bpl * chip->num_periods);
+
+	/* Enables corresponding bits at AUD_INT_STAT */
+	cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+				AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+	/* Clean any pending interrupt bits already set */
+	cx_write(MO_AUD_INTSTAT, ~0);
+
+	/* enable audio irqs */
+	cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
+
+	/* start dma */
+	cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+	cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */
+
+	if (debug)
+		cx88_sram_channel_dump(chip->core, audio_ch);
+
+	return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
+{
+	struct cx88_core *core=chip->core;
+	dprintk(1, "Stopping audio DMA\n");
+
+	/* stop dma */
+	cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+	/* disable irqs */
+	cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+	cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+				AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+	if (debug)
+		cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);
+
+	return 0;
+}
+
+#define MAX_IRQ_LOOP 50
+
+/*
+ * BOARD Specific: IRQ dma bits
+ */
+static const char *cx88_aud_irqs[32] = {
+	"dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
+	NULL,					  /* reserved */
+	"dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
+	NULL,					  /* reserved */
+	"dnf_of", "upf_uf", "rds_dnf_uf",	  /* 8-10 */
+	NULL,					  /* reserved */
+	"dn_sync", "up_sync", "rds_dn_sync",	  /* 12-14 */
+	NULL,					  /* reserved */
+	"opc_err", "par_err", "rip_err",	  /* 16-18 */
+	"pci_abort", "ber_irq", "mchg_irq"	  /* 19-21 */
+};
+
+/*
+ * BOARD Specific: Threats IRQ audio specific calls
+ */
+static void cx8801_aud_irq(snd_cx88_card_t *chip)
+{
+	struct cx88_core *core = chip->core;
+	u32 status, mask;
+
+	status = cx_read(MO_AUD_INTSTAT);
+	mask   = cx_read(MO_AUD_INTMSK);
+	if (0 == (status & mask))
+		return;
+	cx_write(MO_AUD_INTSTAT, status);
+	if (debug > 1  ||  (status & mask & ~0xff))
+		cx88_print_irqbits(core->name, "irq aud",
+				   cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
+				   status, mask);
+	/* risc op code error */
+	if (status & AUD_INT_OPC_ERR) {
+		printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name);
+		cx_clear(MO_AUD_DMACNTRL, 0x11);
+		cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
+	}
+	if (status & AUD_INT_DN_SYNC) {
+		dprintk(1, "Downstream sync error\n");
+		cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+		return;
+	}
+	/* risc1 downstream */
+	if (status & AUD_INT_DN_RISCI1) {
+		atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
+		snd_pcm_period_elapsed(chip->substream);
+	}
+	/* FIXME: Any other status should deserve a special handling? */
+}
+
+/*
+ * BOARD Specific: Handles IRQ calls
+ */
+static irqreturn_t cx8801_irq(int irq, void *dev_id)
+{
+	snd_cx88_card_t *chip = dev_id;
+	struct cx88_core *core = chip->core;
+	u32 status;
+	int loop, handled = 0;
+
+	for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+		status = cx_read(MO_PCI_INTSTAT) &
+			(core->pci_irqmask | PCI_INT_AUDINT);
+		if (0 == status)
+			goto out;
+		dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
+			loop, MAX_IRQ_LOOP, status);
+		handled = 1;
+		cx_write(MO_PCI_INTSTAT, status);
+
+		if (status & core->pci_irqmask)
+			cx88_core_irq(core, status);
+		if (status & PCI_INT_AUDINT)
+			cx8801_aud_irq(chip);
+	}
+
+	if (MAX_IRQ_LOOP == loop) {
+		printk(KERN_ERR
+		       "%s/1: IRQ loop detected, disabling interrupts\n",
+		       core->name);
+		cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+	}
+
+ out:
+	return IRQ_RETVAL(handled);
+}
+
+
+static int dsp_buffer_free(snd_cx88_card_t *chip)
+{
+	BUG_ON(!chip->dma_size);
+
+	dprintk(2,"Freeing buffer\n");
+	videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+	videobuf_dma_free(chip->dma_risc);
+	btcx_riscmem_free(chip->pci,&chip->buf->risc);
+	kfree(chip->buf);
+
+	chip->dma_risc = NULL;
+	chip->dma_size = 0;
+
+	return 0;
+}
+
+/****************************************************************************
+				ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE	4096
+static const struct snd_pcm_hardware snd_cx88_digital_hw = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* Analog audio output will be full of clicks and pops if there
+	   are not exactly four lines in the SRAM FIFO buffer.  */
+	.period_bytes_min = DEFAULT_FIFO_SIZE/4,
+	.period_bytes_max = DEFAULT_FIFO_SIZE/4,
+	.periods_min = 1,
+	.periods_max = 1024,
+	.buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
+{
+	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	if (!chip) {
+		printk(KERN_ERR "BUG: cx88 can't find device struct."
+				" Can't proceed with open\n");
+		return -ENODEV;
+	}
+
+	err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
+		goto _error;
+
+	chip->substream = substream;
+
+	runtime->hw = snd_cx88_digital_hw;
+
+	if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
+		unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
+		bpl &= ~7; /* must be multiple of 8 */
+		runtime->hw.period_bytes_min = bpl;
+		runtime->hw.period_bytes_max = bpl;
+	}
+
+	return 0;
+_error:
+	dprintk(1,"Error opening PCM!\n");
+	return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx88_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
+			      struct snd_pcm_hw_params * hw_params)
+{
+	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+	struct videobuf_dmabuf *dma;
+
+	struct cx88_audio_buffer *buf;
+	int ret;
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	chip->period_size = params_period_bytes(hw_params);
+	chip->num_periods = params_periods(hw_params);
+	chip->dma_size = chip->period_size * params_periods(hw_params);
+
+	BUG_ON(!chip->dma_size);
+	BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (NULL == buf)
+		return -ENOMEM;
+
+	buf->bpl = chip->period_size;
+
+	dma = &buf->dma;
+	videobuf_dma_init(dma);
+	ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+	if (ret < 0)
+		goto error;
+
+	ret = videobuf_dma_map(&chip->pci->dev, dma);
+	if (ret < 0)
+		goto error;
+
+	ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+				   chip->period_size, chip->num_periods, 1);
+	if (ret < 0)
+		goto error;
+
+	/* Loop back to start of program */
+	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+	chip->buf = buf;
+	chip->dma_risc = dma;
+
+	substream->runtime->dma_area = chip->dma_risc->vaddr;
+	substream->runtime->dma_bytes = chip->dma_size;
+	substream->runtime->dma_addr = 0;
+	return 0;
+
+error:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx88_hw_free(struct snd_pcm_substream * substream)
+{
+
+	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+
+	if (substream->runtime->dma_area) {
+		dsp_buffer_free(chip);
+		substream->runtime->dma_area = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx88_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+	int err;
+
+	/* Local interrupts are already disabled by ALSA */
+	spin_lock(&chip->reg_lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err=_cx88_start_audio_dma(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err=_cx88_stop_audio_dma(chip);
+		break;
+	default:
+		err=-EINVAL;
+		break;
+	}
+
+	spin_unlock(&chip->reg_lock);
+
+	return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
+{
+	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	u16 count;
+
+	count = atomic_read(&chip->count);
+
+//	dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,
+//		count, new, count & (runtime->periods-1),
+//		runtime->period_size * (count & (runtime->periods-1)));
+	return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
+				unsigned long offset)
+{
+	void *pageptr = substream->runtime->dma_area + offset;
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx88_pcm_ops = {
+	.open = snd_cx88_pcm_open,
+	.close = snd_cx88_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_cx88_hw_params,
+	.hw_free = snd_cx88_hw_free,
+	.prepare = snd_cx88_prepare,
+	.trigger = snd_cx88_card_trigger,
+	.pointer = snd_cx88_pointer,
+	.page = snd_cx88_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = chip;
+	strcpy(pcm->name, name);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
+
+	return 0;
+}
+
+/****************************************************************************
+				CONTROL INTERFACE
+ ****************************************************************************/
+static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *info)
+{
+	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	info->count = 2;
+	info->value.integer.min = 0;
+	info->value.integer.max = 0x3f;
+
+	return 0;
+}
+
+static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core=chip->core;
+	int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
+	    bal = cx_read(AUD_BAL_CTL);
+
+	value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
+	vol -= (bal & 0x3f);
+	value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
+
+	return 0;
+}
+
+static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	int left = value->value.integer.value[0];
+	int right = value->value.integer.value[1];
+	int v, b;
+
+	/* Pass volume & balance onto any WM8775 */
+	if (left >= right) {
+		v = left << 10;
+		b = left ? (0x8000 * right) / left : 0x8000;
+	} else {
+		v = right << 10;
+		b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
+	}
+	wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
+	wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
+}
+
+/* OK - TODO: test it */
+static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core=chip->core;
+	int left, right, v, b;
+	int changed = 0;
+	u32 old;
+
+	if (core->board.audio_chip == V4L2_IDENT_WM8775)
+		snd_cx88_wm8775_volume_put(kcontrol, value);
+
+	left = value->value.integer.value[0] & 0x3f;
+	right = value->value.integer.value[1] & 0x3f;
+	b = right - left;
+	if (b < 0) {
+		v = 0x3f - left;
+		b = (-b) | 0x40;
+	} else {
+		v = 0x3f - right;
+	}
+	/* Do we really know this will always be called with IRQs on? */
+	spin_lock_irq(&chip->reg_lock);
+	old = cx_read(AUD_VOL_CTL);
+	if (v != (old & 0x3f)) {
+		cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
+		changed = 1;
+	}
+	if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
+		cx_write(AUD_BAL_CTL, b);
+		changed = 1;
+	}
+	spin_unlock_irq(&chip->reg_lock);
+
+	return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
+
+static const struct snd_kcontrol_new snd_cx88_volume = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	.name = "Analog-TV Volume",
+	.info = snd_cx88_volume_info,
+	.get = snd_cx88_volume_get,
+	.put = snd_cx88_volume_put,
+	.tlv.p = snd_cx88_db_scale,
+};
+
+static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	u32 bit = kcontrol->private_value;
+
+	value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
+	return 0;
+}
+
+static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	u32 bit = kcontrol->private_value;
+	int ret = 0;
+	u32 vol;
+
+	spin_lock_irq(&chip->reg_lock);
+	vol = cx_read(AUD_VOL_CTL);
+	if (value->value.integer.value[0] != !(vol & bit)) {
+		vol ^= bit;
+		cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
+		/* Pass mute onto any WM8775 */
+		if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
+		    ((1<<6) == bit))
+			wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
+		ret = 1;
+	}
+	spin_unlock_irq(&chip->reg_lock);
+	return ret;
+}
+
+static const struct snd_kcontrol_new snd_cx88_dac_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Audio-Out Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = snd_cx88_switch_get,
+	.put = snd_cx88_switch_put,
+	.private_value = (1<<8),
+};
+
+static const struct snd_kcontrol_new snd_cx88_source_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog-TV Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = snd_cx88_switch_get,
+	.put = snd_cx88_switch_put,
+	.private_value = (1<<6),
+};
+
+static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	s32 val;
+
+	val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
+	value->value.integer.value[0] = val ? 1 : 0;
+	return 0;
+}
+
+static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	struct v4l2_control client_ctl;
+
+	memset(&client_ctl, 0, sizeof(client_ctl));
+	client_ctl.value = 0 != value->value.integer.value[0];
+	client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+	call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_cx88_alc_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Line-In ALC Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = snd_cx88_alc_get,
+	.put = snd_cx88_alc_put,
+};
+
+/****************************************************************************
+			Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
+ * Only boards with eeprom and byte 1 at eeprom=1 have it
+ */
+
+static const struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = {
+	{0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+	{0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+	{0, }
+};
+MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
+
+/*
+ * Chip-specific destructor
+ */
+
+static int snd_cx88_free(snd_cx88_card_t *chip)
+{
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+
+	cx88_core_put(chip->core,chip->pci);
+
+	pci_disable_device(chip->pci);
+	return 0;
+}
+
+/*
+ * Component Destructor
+ */
+static void snd_cx88_dev_free(struct snd_card * card)
+{
+	snd_cx88_card_t *chip = card->private_data;
+
+	snd_cx88_free(chip);
+}
+
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+static int devno;
+static int __devinit snd_cx88_create(struct snd_card *card,
+				     struct pci_dev *pci,
+				     snd_cx88_card_t **rchip,
+				     struct cx88_core **core_ptr)
+{
+	snd_cx88_card_t   *chip;
+	struct cx88_core  *core;
+	int               err;
+	unsigned char     pci_lat;
+
+	*rchip = NULL;
+
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	pci_set_master(pci);
+
+	chip = card->private_data;
+
+	core = cx88_core_get(pci);
+	if (NULL == core) {
+		err = -EINVAL;
+		return err;
+	}
+
+	if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
+		dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
+		err = -EIO;
+		cx88_core_put(core, pci);
+		return err;
+	}
+
+
+	/* pci init */
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	spin_lock_init(&chip->reg_lock);
+
+	chip->core = core;
+
+	/* get irq */
+	err = request_irq(chip->pci->irq, cx8801_irq,
+			  IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip);
+	if (err < 0) {
+		dprintk(0, "%s: can't get IRQ %d\n",
+		       chip->core->name, chip->pci->irq);
+		return err;
+	}
+
+	/* print pci info */
+	pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
+
+	dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", core->name, devno,
+	       pci_name(pci), pci->revision, pci->irq,
+	       pci_lat, (unsigned long long)pci_resource_start(pci,0));
+
+	chip->irq = pci->irq;
+	synchronize_irq(chip->irq);
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rchip = chip;
+	*core_ptr = core;
+
+	return 0;
+}
+
+static int __devinit cx88_audio_initdev(struct pci_dev *pci,
+				    const struct pci_device_id *pci_id)
+{
+	struct snd_card  *card;
+	snd_cx88_card_t  *chip;
+	struct cx88_core *core = NULL;
+	int              err;
+
+	if (devno >= SNDRV_CARDS)
+		return (-ENODEV);
+
+	if (!enable[devno]) {
+		++devno;
+		return (-ENOENT);
+	}
+
+	err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+			      sizeof(snd_cx88_card_t), &card);
+	if (err < 0)
+		return err;
+
+	card->private_free = snd_cx88_dev_free;
+
+	err = snd_cx88_create(card, pci, &chip, &core);
+	if (err < 0)
+		goto error;
+
+	err = snd_cx88_pcm(chip, 0, "CX88 Digital");
+	if (err < 0)
+		goto error;
+
+	err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
+	if (err < 0)
+		goto error;
+	err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
+	if (err < 0)
+		goto error;
+	err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
+	if (err < 0)
+		goto error;
+
+	/* If there's a wm8775 then add a Line-In ALC switch */
+	if (core->board.audio_chip == V4L2_IDENT_WM8775)
+		snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
+
+	strcpy (card->driver, "CX88x");
+	sprintf(card->shortname, "Conexant CX%x", pci->device);
+	sprintf(card->longname, "%s at %#llx",
+		card->shortname,(unsigned long long)pci_resource_start(pci, 0));
+	strcpy (card->mixername, "CX88");
+
+	dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
+	       card->driver,devno);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+	pci_set_drvdata(pci,card);
+
+	devno++;
+	return 0;
+
+error:
+	snd_card_free(card);
+	return err;
+}
+/*
+ * ALSA destructor
+ */
+static void __devexit cx88_audio_finidev(struct pci_dev *pci)
+{
+	struct cx88_audio_dev *card = pci_get_drvdata(pci);
+
+	snd_card_free((void *)card);
+
+	pci_set_drvdata(pci, NULL);
+
+	devno--;
+}
+
+/*
+ * PCI driver definition
+ */
+
+static struct pci_driver cx88_audio_pci_driver = {
+	.name     = "cx88_audio",
+	.id_table = cx88_audio_pci_tbl,
+	.probe    = cx88_audio_initdev,
+	.remove   = __devexit_p(cx88_audio_finidev),
+};
+
+/****************************************************************************
+				LINUX MODULE INIT
+ ****************************************************************************/
+
+/*
+ * module init
+ */
+static int __init cx88_audio_init(void)
+{
+	printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
+	       CX88_VERSION);
+	return pci_register_driver(&cx88_audio_pci_driver);
+}
+
+/*
+ * module remove
+ */
+static void __exit cx88_audio_fini(void)
+{
+	pci_unregister_driver(&cx88_audio_pci_driver);
+}
+
+module_init(cx88_audio_init);
+module_exit(cx88_audio_fini);
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
new file mode 100644
index 000000000000..def363fb71c0
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  Support for a cx23416 mpeg encoder via cx2388x host port.
+ *  "blackbird" reference design.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.us>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *
+ *    (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *        - video_ioctl2 conversion
+ *
+ *  Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/cx2341x.h>
+
+#include "cx88.h"
+
+MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs,int,0644);
+MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
+
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
+
+#define dprintk(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+
+#define BLACKBIRD_FIRM_IMAGE_SIZE 376836
+
+/* defines below are from ivtv-driver.h */
+
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+enum blackbird_capture_type {
+	BLACKBIRD_MPEG_CAPTURE,
+	BLACKBIRD_RAW_CAPTURE,
+	BLACKBIRD_RAW_PASSTHRU_CAPTURE
+};
+enum blackbird_capture_bits {
+	BLACKBIRD_RAW_BITS_NONE             = 0x00,
+	BLACKBIRD_RAW_BITS_YUV_CAPTURE      = 0x01,
+	BLACKBIRD_RAW_BITS_PCM_CAPTURE      = 0x02,
+	BLACKBIRD_RAW_BITS_VBI_CAPTURE      = 0x04,
+	BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+	BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE  = 0x10
+};
+enum blackbird_capture_end {
+	BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */
+	BLACKBIRD_END_NOW, /* stop immediately, no irq */
+};
+enum blackbird_framerate {
+	BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+	BLACKBIRD_FRAMERATE_PAL_25   /* PAL: 25fps */
+};
+enum blackbird_stream_port {
+	BLACKBIRD_OUTPUT_PORT_MEMORY,
+	BLACKBIRD_OUTPUT_PORT_STREAMING,
+	BLACKBIRD_OUTPUT_PORT_SERIAL
+};
+enum blackbird_data_xfer_status {
+	BLACKBIRD_MORE_BUFFERS_FOLLOW,
+	BLACKBIRD_LAST_BUFFER,
+};
+enum blackbird_picture_mask {
+	BLACKBIRD_PICTURE_MASK_NONE,
+	BLACKBIRD_PICTURE_MASK_I_FRAMES,
+	BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3,
+	BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum blackbird_vbi_mode_bits {
+	BLACKBIRD_VBI_BITS_SLICED,
+	BLACKBIRD_VBI_BITS_RAW,
+};
+enum blackbird_vbi_insertion_bits {
+	BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+	BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+	BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+	BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+	BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum blackbird_dma_unit {
+	BLACKBIRD_DMA_BYTES,
+	BLACKBIRD_DMA_FRAMES,
+};
+enum blackbird_dma_transfer_status_bits {
+	BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01,
+	BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04,
+	BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum blackbird_pause {
+	BLACKBIRD_PAUSE_ENCODING,
+	BLACKBIRD_RESUME_ENCODING,
+};
+enum blackbird_copyright {
+	BLACKBIRD_COPYRIGHT_OFF,
+	BLACKBIRD_COPYRIGHT_ON,
+};
+enum blackbird_notification_type {
+	BLACKBIRD_NOTIFICATION_REFRESH,
+};
+enum blackbird_notification_status {
+	BLACKBIRD_NOTIFICATION_OFF,
+	BLACKBIRD_NOTIFICATION_ON,
+};
+enum blackbird_notification_mailbox {
+	BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum blackbird_field1_lines {
+	BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */
+	BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */
+	BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum blackbird_field2_lines {
+	BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */
+	BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */
+	BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum blackbird_custom_data_type {
+	BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
+	BLACKBIRD_CUSTOM_PRIVATE_PACKET,
+};
+enum blackbird_mute {
+	BLACKBIRD_UNMUTE,
+	BLACKBIRD_MUTE,
+};
+enum blackbird_mute_video_mask {
+	BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00,
+	BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000,
+	BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum blackbird_mute_video_shift {
+	BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8,
+	BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16,
+	BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
+
+/* ------------------------------------------------------------------ */
+
+static void host_setup(struct cx88_core *core)
+{
+	/* toggle reset of the host */
+	cx_write(MO_GPHST_SOFT_RST, 1);
+	udelay(100);
+	cx_write(MO_GPHST_SOFT_RST, 0);
+	udelay(100);
+
+	/* host port setup */
+	cx_write(MO_GPHST_WSC, 0x44444444U);
+	cx_write(MO_GPHST_XFR, 0);
+	cx_write(MO_GPHST_WDTH, 15);
+	cx_write(MO_GPHST_HDSHK, 0);
+	cx_write(MO_GPHST_MUX16, 0x44448888U);
+	cx_write(MO_GPHST_MODE, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+#define P1_MDATA0 0x390000
+#define P1_MDATA1 0x390001
+#define P1_MDATA2 0x390002
+#define P1_MDATA3 0x390003
+#define P1_MADDR2 0x390004
+#define P1_MADDR1 0x390005
+#define P1_MADDR0 0x390006
+#define P1_RDATA0 0x390008
+#define P1_RDATA1 0x390009
+#define P1_RDATA2 0x39000A
+#define P1_RDATA3 0x39000B
+#define P1_RADDR0 0x39000C
+#define P1_RADDR1 0x39000D
+#define P1_RRDWR  0x39000E
+
+static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(1);
+	u32 gpio0,need;
+
+	need = state ? 2 : 0;
+	for (;;) {
+		gpio0 = cx_read(MO_GP0_IO) & 2;
+		if (need == gpio0)
+			return 0;
+		if (time_after(jiffies,timeout))
+			return -1;
+		udelay(1);
+	}
+}
+
+static int memory_write(struct cx88_core *core, u32 address, u32 value)
+{
+	/* Warning: address is dword address (4 bytes) */
+	cx_writeb(P1_MDATA0, (unsigned int)value);
+	cx_writeb(P1_MDATA1, (unsigned int)(value >> 8));
+	cx_writeb(P1_MDATA2, (unsigned int)(value >> 16));
+	cx_writeb(P1_MDATA3, (unsigned int)(value >> 24));
+	cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40);
+	cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+	cx_writeb(P1_MADDR0, (unsigned int)address);
+	cx_read(P1_MDATA0);
+	cx_read(P1_MADDR0);
+
+	return wait_ready_gpio0_bit1(core,1);
+}
+
+static int memory_read(struct cx88_core *core, u32 address, u32 *value)
+{
+	int retval;
+	u32 val;
+
+	/* Warning: address is dword address (4 bytes) */
+	cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
+	cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+	cx_writeb(P1_MADDR0, (unsigned int)address);
+	cx_read(P1_MADDR0);
+
+	retval = wait_ready_gpio0_bit1(core,1);
+
+	cx_writeb(P1_MDATA3, 0);
+	val     = (unsigned char)cx_read(P1_MDATA3) << 24;
+	cx_writeb(P1_MDATA2, 0);
+	val    |= (unsigned char)cx_read(P1_MDATA2) << 16;
+	cx_writeb(P1_MDATA1, 0);
+	val    |= (unsigned char)cx_read(P1_MDATA1) << 8;
+	cx_writeb(P1_MDATA0, 0);
+	val    |= (unsigned char)cx_read(P1_MDATA0);
+
+	*value  = val;
+	return retval;
+}
+
+static int register_write(struct cx88_core *core, u32 address, u32 value)
+{
+	cx_writeb(P1_RDATA0, (unsigned int)value);
+	cx_writeb(P1_RDATA1, (unsigned int)(value >> 8));
+	cx_writeb(P1_RDATA2, (unsigned int)(value >> 16));
+	cx_writeb(P1_RDATA3, (unsigned int)(value >> 24));
+	cx_writeb(P1_RADDR0, (unsigned int)address);
+	cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+	cx_writeb(P1_RRDWR, 1);
+	cx_read(P1_RDATA0);
+	cx_read(P1_RADDR0);
+
+	return wait_ready_gpio0_bit1(core,1);
+}
+
+
+static int register_read(struct cx88_core *core, u32 address, u32 *value)
+{
+	int retval;
+	u32 val;
+
+	cx_writeb(P1_RADDR0, (unsigned int)address);
+	cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+	cx_writeb(P1_RRDWR, 0);
+	cx_read(P1_RADDR0);
+
+	retval  = wait_ready_gpio0_bit1(core,1);
+	val     = (unsigned char)cx_read(P1_RDATA0);
+	val    |= (unsigned char)cx_read(P1_RDATA1) << 8;
+	val    |= (unsigned char)cx_read(P1_RDATA2) << 16;
+	val    |= (unsigned char)cx_read(P1_RDATA3) << 24;
+
+	*value  = val;
+	return retval;
+}
+
+/* ------------------------------------------------------------------ */
+
+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;
+	u32 value, flag, retval;
+	int i;
+
+	dprintk(1,"%s: 0x%X\n", __func__, command);
+
+	/* this may not be 100% safe if we can't read any memory location
+	   without side effects */
+	memory_read(dev->core, dev->mailbox - 4, &value);
+	if (value != 0x12345678) {
+		dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
+		return -1;
+	}
+
+	memory_read(dev->core, dev->mailbox, &flag);
+	if (flag) {
+		dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
+		return -1;
+	}
+
+	flag |= 1; /* tell 'em we're working on it */
+	memory_write(dev->core, dev->mailbox, flag);
+
+	/* write command + args + fill remaining with zeros */
+	memory_write(dev->core, dev->mailbox + 1, command); /* command code */
+	memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
+	for (i = 0; i < in; i++) {
+		memory_write(dev->core, dev->mailbox + 4 + i, data[i]);
+		dprintk(1, "API Input %d = %d\n", i, data[i]);
+	}
+	for (; i < CX2341X_MBOX_MAX_DATA; i++)
+		memory_write(dev->core, dev->mailbox + 4 + i, 0);
+
+	flag |= 3; /* tell 'em we're done writing */
+	memory_write(dev->core, dev->mailbox, flag);
+
+	/* wait for firmware to handle the API command */
+	timeout = jiffies + msecs_to_jiffies(10);
+	for (;;) {
+		memory_read(dev->core, dev->mailbox, &flag);
+		if (0 != (flag & 4))
+			break;
+		if (time_after(jiffies,timeout)) {
+			dprintk(0, "ERROR: API Mailbox timeout\n");
+			return -1;
+		}
+		udelay(10);
+	}
+
+	/* read output values */
+	for (i = 0; i < out; i++) {
+		memory_read(dev->core, dev->mailbox + 4 + i, data + i);
+		dprintk(1, "API Output %d = %d\n", i, data[i]);
+	}
+
+	memory_read(dev->core, dev->mailbox + 2, &retval);
+	dprintk(1, "API result = %d\n",retval);
+
+	flag = 0;
+	memory_write(dev->core, dev->mailbox, flag);
+	return retval;
+}
+/* ------------------------------------------------------------------ */
+
+/* We don't need to call the API often, so using just one mailbox will probably suffice */
+static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
+			     u32 inputcnt, u32 outputcnt, ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list vargs;
+	int i, err;
+
+	va_start(vargs, outputcnt);
+
+	for (i = 0; i < inputcnt; i++) {
+		data[i] = va_arg(vargs, int);
+	}
+	err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data);
+	for (i = 0; i < outputcnt; i++) {
+		int *vptr = va_arg(vargs, int *);
+		*vptr = data[i];
+	}
+	va_end(vargs);
+	return err;
+}
+
+static int blackbird_find_mailbox(struct cx8802_dev *dev)
+{
+	u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
+	int signaturecnt=0;
+	u32 value;
+	int i;
+
+	for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
+		memory_read(dev->core, i, &value);
+		if (value == signature[signaturecnt])
+			signaturecnt++;
+		else
+			signaturecnt = 0;
+		if (4 == signaturecnt) {
+			dprintk(1, "Mailbox signature found\n");
+			return i+1;
+		}
+	}
+	dprintk(0, "Mailbox signature values not found!\n");
+	return -1;
+}
+
+static int blackbird_load_firmware(struct cx8802_dev *dev)
+{
+	static const unsigned char magic[8] = {
+		0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+	};
+	const struct firmware *firmware;
+	int i, retval = 0;
+	u32 value = 0;
+	u32 checksum = 0;
+	u32 *dataptr;
+
+	retval  = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
+	retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+	retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
+	retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+	msleep(1);
+	retval |= register_write(dev->core, IVTV_REG_APU, 0);
+
+	if (retval < 0)
+		dprintk(0, "Error with register_write\n");
+
+	retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME,
+				  &dev->pci->dev);
+
+
+	if (retval != 0) {
+		dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+			CX2341X_FIRM_ENC_FILENAME);
+		dprintk(0, "Please fix your hotplug setup, the board will "
+			"not work without firmware loaded!\n");
+		return -1;
+	}
+
+	if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
+		dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+			firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
+		release_firmware(firmware);
+		return -1;
+	}
+
+	if (0 != memcmp(firmware->data, magic, 8)) {
+		dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+		release_firmware(firmware);
+		return -1;
+	}
+
+	/* transfer to the chip */
+	dprintk(1,"Loading firmware ...\n");
+	dataptr = (u32*)firmware->data;
+	for (i = 0; i < (firmware->size >> 2); i++) {
+		value = le32_to_cpu(*dataptr);
+		checksum += ~value;
+		memory_write(dev->core, i, value);
+		dataptr++;
+	}
+
+	/* read back to verify with the checksum */
+	for (i--; i >= 0; i--) {
+		memory_read(dev->core, i, &value);
+		checksum -= ~value;
+	}
+	if (checksum) {
+		dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
+		release_firmware(firmware);
+		return -1;
+	}
+	release_firmware(firmware);
+	dprintk(0, "Firmware upload successful.\n");
+
+	retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+	retval |= register_read(dev->core, IVTV_REG_SPU, &value);
+	retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
+	msleep(1);
+
+	retval |= register_read(dev->core, IVTV_REG_VPU, &value);
+	retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+	if (retval < 0)
+		dprintk(0, "Error with register_write\n");
+	return 0;
+}
+
+/**
+ Settings used by the windows tv app for PVR2000:
+=================================================================================================================
+Profile | Codec | Resolution | CBR/VBR | Video Qlty   | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode
+-----------------------------------------------------------------------------------------------------------------
+MPEG-1  | MPEG1 | 352x288PAL | (CBR)   | 1000:Optimal | 2000 Kbps  | 25fps   | MPG1 Layer2 | 224kbps    | Stereo
+MPEG-2  | MPEG2 | 720x576PAL | VBR     | 600 :Good    | 4000 Kbps  | 25fps   | MPG1 Layer2 | 224kbps    | Stereo
+VCD     | MPEG1 | 352x288PAL | (CBR)   | 1000:Optimal | 1150 Kbps  | 25fps   | MPG1 Layer2 | 224kbps    | Stereo
+DVD     | MPEG2 | 720x576PAL | VBR     | 600 :Good    | 6000 Kbps  | 25fps   | MPG1 Layer2 | 224kbps    | Stereo
+DB* DVD | MPEG2 | 720x576PAL | CBR     | 600 :Good    | 6000 Kbps  | 25fps   | MPG1 Layer2 | 224kbps    | Stereo
+=================================================================================================================
+*DB: "DirectBurn"
+*/
+
+static void blackbird_codec_settings(struct cx8802_dev *dev)
+{
+	/* assign frame size */
+	blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+				dev->height, dev->width);
+
+	dev->cxhdl.width = dev->width;
+	dev->cxhdl.height = dev->height;
+	cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
+	cx2341x_handler_setup(&dev->cxhdl);
+}
+
+static int blackbird_initialize_codec(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	int version;
+	int retval;
+
+	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 */
+		cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
+		retval = blackbird_load_firmware(dev);
+		if (retval < 0)
+			return retval;
+
+		retval = blackbird_find_mailbox(dev);
+		if (retval < 0)
+			return -1;
+
+		dev->mailbox = retval;
+
+		retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+		if (retval < 0) {
+			dprintk(0, "ERROR: Firmware ping failed!\n");
+			return -1;
+		}
+
+		retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version);
+		if (retval < 0) {
+			dprintk(0, "ERROR: Firmware get encoder version failed!\n");
+			return -1;
+		}
+		dprintk(0, "Firmware version is 0x%08x\n", version);
+	}
+
+	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? */
+	cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */
+	cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
+
+	blackbird_codec_settings(dev);
+
+	blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+			BLACKBIRD_FIELD1_SAA7115,
+			BLACKBIRD_FIELD2_SAA7115
+		);
+
+	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);
+
+	return 0;
+}
+
+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;
+
+	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);
+
+	cx2341x_handler_set_busy(&dev->cxhdl, 1);
+
+	/* start capturing to the host interface */
+	blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+			BLACKBIRD_MPEG_CAPTURE,
+			BLACKBIRD_RAW_BITS_NONE
+		);
+
+	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
+		);
+
+	cx2341x_handler_set_busy(&dev->cxhdl, 0);
+
+	dev->mpeg_active = 0;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+			unsigned int *count, unsigned int *size)
+{
+	struct cx8802_fh *fh = q->priv_data;
+
+	fh->dev->ts_packet_size  = 188 * 4; /* was: 512 */
+	fh->dev->ts_packet_count = mpegbufs; /* was: 100 */
+
+	*size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
+	*count = fh->dev->ts_packet_count;
+	return 0;
+}
+
+static int
+bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+	       enum v4l2_field field)
+{
+	struct cx8802_fh *fh = q->priv_data;
+	return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field);
+}
+
+static void
+bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct cx8802_fh *fh = q->priv_data;
+	cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static struct videobuf_queue_ops blackbird_qops = {
+	.buf_setup    = bb_buf_setup,
+	.buf_prepare  = bb_buf_prepare,
+	.buf_queue    = bb_buf_queue,
+	.buf_release  = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *cap)
+{
+	struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
+	struct cx88_core  *core = dev->core;
+
+	strcpy(cap->driver, "cx88_blackbird");
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+	cx88_querycap(file, core, cap);
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
+					struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+	f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+	f->fmt.pix.width        = dev->width;
+	f->fmt.pix.height       = dev->height;
+	f->fmt.pix.field        = fh->mpegq.field;
+	dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+		dev->width, dev->height, fh->mpegq.field );
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+			struct v4l2_format *f)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+	dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+		dev->width, dev->height, fh->mpegq.field );
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+	struct cx88_core  *core = dev->core;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+	dev->width              = f->fmt.pix.width;
+	dev->height             = f->fmt.pix.height;
+	fh->mpegq.field         = f->fmt.pix.field;
+	cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+	blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+				f->fmt.pix.height, f->fmt.pix.width);
+	dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
+	return 0;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+	struct cx8802_fh  *fh   = priv;
+	return (videobuf_reqbufs(&fh->mpegq, p));
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct cx8802_fh  *fh   = priv;
+	return (videobuf_querybuf(&fh->mpegq, p));
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct cx8802_fh  *fh   = priv;
+	return (videobuf_qbuf(&fh->mpegq, p));
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct cx8802_fh  *fh   = priv;
+	return (videobuf_dqbuf(&fh->mpegq, p,
+				file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+
+	if (!dev->mpeg_active)
+		blackbird_start_codec(file, fh);
+	return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+
+	if (dev->mpeg_active)
+		blackbird_stop_codec(dev);
+	return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx8802_dev *dev  = fh->dev;
+	struct cx88_core  *core = dev->core;
+
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (unlikely(f->tuner != 0))
+		return -EINVAL;
+	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,
+			fh->mpegq.field);
+	return 0;
+}
+
+static int vidioc_log_status (struct file *file, void *priv)
+{
+	struct cx8802_dev *dev  = ((struct cx8802_fh *)priv)->dev;
+	struct cx88_core  *core = dev->core;
+	char name[32 + 2];
+
+	snprintf(name, sizeof(name), "%s/2", core->name);
+	call_all(core, core, log_status);
+	v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
+	return 0;
+}
+
+static int vidioc_enum_input (struct file *file, void *priv,
+				struct v4l2_input *i)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+	return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct cx8802_fh  *fh   = priv;
+	struct cx88_core  *core = fh->dev->core;
+
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (unlikely(f->tuner != 0))
+		return -EINVAL;
+
+	f->frequency = core->freq;
+	call_all(core, tuner, g_frequency, f);
+
+	return 0;
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+
+	*i = core->input;
+	return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+
+	if (i >= 4)
+		return -EINVAL;
+	if (0 == INPUT(i).type)
+		return -EINVAL;
+
+	mutex_lock(&core->lock);
+	cx88_newstation(core);
+	cx88_video_mux(core,i);
+	mutex_unlock(&core->lock);
+	return 0;
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+	u32 reg;
+
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "Television");
+	t->capability = V4L2_TUNER_CAP_NORM;
+	t->rangehigh  = 0xffffffffUL;
+	call_all(core, tuner, g_tuner, t);
+
+	cx88_get_stereo(core ,t);
+	reg = cx_read(MO_DEVICE_STATUS);
+	t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+	return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+
+	if (UNSET == core->board.tuner_type)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	cx88_set_stereo(core, t->audmode, 1);
+	return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+	struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+	*tvnorm = core->tvnorm;
+	return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct cx88_core  *core = ((struct cx8802_fh *)priv)->dev->core;
+
+	mutex_lock(&core->lock);
+	cx88_set_tvnorm(core,*id);
+	mutex_unlock(&core->lock);
+	return 0;
+}
+
+/* FIXME: cx88_ioctl_hook not implemented */
+
+static int mpeg_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8802_dev *dev = video_drvdata(file);
+	struct cx8802_fh *fh;
+	struct cx8802_driver *drv = NULL;
+	int err;
+
+	dprintk( 1, "%s\n", __func__);
+
+	mutex_lock(&dev->core->lock);
+
+	/* Make sure we can acquire the hardware */
+	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+	if (!drv) {
+		dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
+		mutex_unlock(&dev->core->lock);
+		return -ENODEV;
+	}
+
+	err = drv->request_acquire(drv);
+	if (err != 0) {
+		dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
+		mutex_unlock(&dev->core->lock);
+		return err;
+	}
+
+	if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) {
+		drv->request_release(drv);
+		mutex_unlock(&dev->core->lock);
+		return -EINVAL;
+	}
+	dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+	if (NULL == fh) {
+		drv->request_release(drv);
+		mutex_unlock(&dev->core->lock);
+		return -ENOMEM;
+	}
+	v4l2_fh_init(&fh->fh, vdev);
+	file->private_data = fh;
+	fh->dev      = dev;
+
+	videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx88_buffer),
+			    fh, NULL);
+
+	/* FIXME: locking against other video device */
+	cx88_set_scale(dev->core, dev->width, dev->height,
+			fh->mpegq.field);
+
+	dev->core->mpeg_users++;
+	mutex_unlock(&dev->core->lock);
+	v4l2_fh_add(&fh->fh);
+	return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+	struct cx8802_fh  *fh  = file->private_data;
+	struct cx8802_dev *dev = fh->dev;
+	struct cx8802_driver *drv = NULL;
+
+	mutex_lock(&dev->core->lock);
+
+	if (dev->mpeg_active && dev->core->mpeg_users == 1)
+		blackbird_stop_codec(dev);
+
+	cx8802_cancel_buffers(fh->dev);
+	/* stop mpeg capture */
+	videobuf_stop(&fh->mpegq);
+
+	videobuf_mmap_free(&fh->mpegq);
+
+	v4l2_fh_del(&fh->fh);
+	v4l2_fh_exit(&fh->fh);
+	file->private_data = NULL;
+	kfree(fh);
+
+	/* Make sure we release the hardware */
+	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+	WARN_ON(!drv);
+	if (drv)
+		drv->request_release(drv);
+
+	dev->core->mpeg_users--;
+
+	mutex_unlock(&dev->core->lock);
+
+	return 0;
+}
+
+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);
+}
+
+static unsigned int
+mpeg_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned long req_events = poll_requested_events(wait);
+	struct cx8802_fh *fh = file->private_data;
+	struct cx8802_dev *dev = fh->dev;
+
+	if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
+		blackbird_start_codec(file, fh);
+
+	return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int
+mpeg_mmap(struct file *file, struct vm_area_struct * vma)
+{
+	struct cx8802_fh *fh = file->private_data;
+
+	return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static const struct v4l2_file_operations mpeg_fops =
+{
+	.owner	       = THIS_MODULE,
+	.open	       = mpeg_open,
+	.release       = mpeg_release,
+	.read	       = mpeg_read,
+	.poll          = mpeg_poll,
+	.mmap	       = mpeg_mmap,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs       = vidioc_reqbufs,
+	.vidioc_querybuf      = vidioc_querybuf,
+	.vidioc_qbuf          = vidioc_qbuf,
+	.vidioc_dqbuf         = vidioc_dqbuf,
+	.vidioc_streamon      = vidioc_streamon,
+	.vidioc_streamoff     = vidioc_streamoff,
+	.vidioc_s_frequency   = vidioc_s_frequency,
+	.vidioc_log_status    = vidioc_log_status,
+	.vidioc_enum_input    = vidioc_enum_input,
+	.vidioc_g_frequency   = vidioc_g_frequency,
+	.vidioc_g_input       = vidioc_g_input,
+	.vidioc_s_input       = vidioc_s_input,
+	.vidioc_g_tuner       = vidioc_g_tuner,
+	.vidioc_s_tuner       = vidioc_s_tuner,
+	.vidioc_g_std         = vidioc_g_std,
+	.vidioc_s_std         = vidioc_s_std,
+	.vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
+};
+
+static struct video_device cx8802_mpeg_template = {
+	.name                 = "cx8802",
+	.fops                 = &mpeg_fops,
+	.ioctl_ops 	      = &mpeg_ioctl_ops,
+	.tvnorms              = CX88_NORMS,
+};
+
+/* ------------------------------------------------------------------ */
+
+/* The CX8802 MPEG API will call this when we can use the hardware */
+static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* By default, core setup will leave the cx22702 out of reset, on the bus.
+		 * We left the hardware on power up with the cx22702 active.
+		 * We're being given access to re-arrange the GPIOs.
+		 * Take the bus off the cx22702 and put the cx23416 on it.
+		 */
+		/* Toggle reset on cx22702 leaving i2c active */
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		cx_clear(MO_GP0_IO, 0x00000080);
+		udelay(50);
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		/* tri-state the cx22702 pins */
+		cx_set(MO_GP0_IO, 0x00000004);
+		udelay(1000);
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+/* The CX8802 MPEG API will call this when we need to release the hardware */
+static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* Exit leaving the cx23416 on the bus */
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+static void blackbird_unregister_video(struct cx8802_dev *dev)
+{
+	if (dev->mpeg_dev) {
+		if (video_is_registered(dev->mpeg_dev))
+			video_unregister_device(dev->mpeg_dev);
+		else
+			video_device_release(dev->mpeg_dev);
+		dev->mpeg_dev = NULL;
+	}
+}
+
+static int blackbird_register_video(struct cx8802_dev *dev)
+{
+	int err;
+
+	dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
+				       &cx8802_mpeg_template,"mpeg");
+	dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
+	video_set_drvdata(dev->mpeg_dev, dev);
+	err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
+	if (err < 0) {
+		printk(KERN_INFO "%s/2: can't register mpeg device\n",
+		       dev->core->name);
+		return err;
+	}
+	printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
+	       dev->core->name, video_device_node_name(dev->mpeg_dev));
+	return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx8802_blackbird_probe(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = core->dvbdev;
+	int err;
+
+	dprintk( 1, "%s\n", __func__);
+	dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+		core->boardnr,
+		core->name,
+		core->pci_bus,
+		core->pci_slot);
+
+	err = -ENODEV;
+	if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))
+		goto fail_core;
+
+	dev->width = 720;
+	if (core->tvnorm & V4L2_STD_525_60) {
+		dev->height = 480;
+	} else {
+		dev->height = 576;
+	}
+	dev->cxhdl.port = CX2341X_PORT_STREAMING;
+	dev->cxhdl.width = dev->width;
+	dev->cxhdl.height = dev->height;
+	dev->cxhdl.func = blackbird_mbox_func;
+	dev->cxhdl.priv = dev;
+	err = cx2341x_handler_init(&dev->cxhdl, 36);
+	if (err)
+		goto fail_core;
+	v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL);
+
+	/* blackbird stuff */
+	printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
+	       core->name);
+	host_setup(dev->core);
+
+	blackbird_initialize_codec(dev);
+
+	/* initial device configuration: needed ? */
+//	init_controls(core);
+	cx88_set_tvnorm(core,core->tvnorm);
+	cx88_video_mux(core,0);
+	cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
+	cx2341x_handler_setup(&dev->cxhdl);
+	blackbird_register_video(dev);
+
+	return 0;
+
+ fail_core:
+	return err;
+}
+
+static int cx8802_blackbird_remove(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = core->dvbdev;
+
+	/* blackbird */
+	blackbird_unregister_video(drv->core->dvbdev);
+	v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
+
+	return 0;
+}
+
+static struct cx8802_driver cx8802_blackbird_driver = {
+	.type_id	= CX88_MPEG_BLACKBIRD,
+	.hw_access	= CX8802_DRVCTL_SHARED,
+	.probe		= cx8802_blackbird_probe,
+	.remove		= cx8802_blackbird_remove,
+	.advise_acquire	= cx8802_blackbird_advise_acquire,
+	.advise_release	= cx8802_blackbird_advise_release,
+};
+
+static int __init blackbird_init(void)
+{
+	printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
+	       CX88_VERSION);
+	return cx8802_register_driver(&cx8802_blackbird_driver);
+}
+
+static void __exit blackbird_fini(void)
+{
+	cx8802_unregister_driver(&cx8802_blackbird_driver);
+}
+
+module_init(blackbird_init);
+module_exit(blackbird_fini);
+
+module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [video]");
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
new file mode 100644
index 000000000000..0c255248cbcd
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -0,0 +1,3811 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "cx88.h"
+#include "tea5767.h"
+#include "xc4000.h"
+
+static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[]  = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(radio, int, NULL, 0444);
+module_param_array(card,  int, NULL, 0444);
+
+MODULE_PARM_DESC(tuner,"tuner type");
+MODULE_PARM_DESC(radio,"radio tuner type");
+MODULE_PARM_DESC(card,"card type");
+
+static unsigned int latency = UNSET;
+module_param(latency,int,0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+static int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "Disable IR support");
+
+#define info_printk(core, fmt, arg...) \
+	printk(KERN_INFO "%s: " fmt, core->name , ## arg)
+
+#define warn_printk(core, fmt, arg...) \
+	printk(KERN_WARNING "%s: " fmt, core->name , ## arg)
+
+#define err_printk(core, fmt, arg...) \
+	printk(KERN_ERR "%s: " fmt, core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+/* board config info                                                  */
+
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
+static const struct cx88_board cx88_boards[] = {
+	[CX88_BOARD_UNKNOWN] = {
+		.name		= "UNKNOWN/GENERIC",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE2,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_COMPOSITE3,
+			.vmux   = 2,
+		},{
+			.type   = CX88_VMUX_COMPOSITE4,
+			.vmux   = 3,
+		}},
+	},
+	[CX88_BOARD_HAUPPAUGE] = {
+		.name		= "Hauppauge WinTV 34xxx models",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xff00,  // internal decoder
+		},{
+			.type   = CX88_VMUX_DEBUG,
+			.vmux   = 0,
+			.gpio0  = 0xff01,  // mono from tuner chip
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xff02,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xff02,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0xff01,
+		},
+	},
+	[CX88_BOARD_GDI] = {
+		.name		= "GDI Black Gold",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+		}},
+	},
+	[CX88_BOARD_PIXELVIEW] = {
+		.name           = "PixelView",
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xff00,  // internal decoder
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0xff10,
+		},
+	},
+	[CX88_BOARD_ATI_WONDER_PRO] = {
+		.name           = "ATI TV Wonder Pro",
+		.tuner_type     = TUNER_PHILIPS_4IN1,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_INTERCARRIER,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x03ff,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x03fe,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x03fe,
+		}},
+	},
+	[CX88_BOARD_WINFAST2000XP_EXPERT] = {
+		.name           = "Leadtek Winfast 2000XP Expert",
+		.tuner_type     = TUNER_PHILIPS_4IN1,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0	= 0x00F5e700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5e700,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0x00F5c700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5c700,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0x00F5c700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5c700,
+			.gpio3  = 0x02000000,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0	= 0x00F5d700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5d700,
+			.gpio3  = 0x02000000,
+		},
+	},
+	[CX88_BOARD_AVERTV_STUDIO_303] = {
+		.name           = "AverTV Studio 303 (M126)",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio1  = 0xe09f,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio1  = 0xe05f,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio1  = 0xe05f,
+		}},
+		.radio = {
+			.gpio1  = 0xe0df,
+			.type   = CX88_RADIO,
+		},
+	},
+	[CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
+		// added gpio values thanks to Michal
+		// values for PAL from DScaler
+		.name           = "MSI TV-@nywhere Master",
+		.tuner_type     = TUNER_MT2032,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf	= TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x000040bf,
+			.gpio1  = 0x000080c0,
+			.gpio2  = 0x0000ff40,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000040bf,
+			.gpio1  = 0x000080c0,
+			.gpio2  = 0x0000ff40,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000040bf,
+			.gpio1  = 0x000080c0,
+			.gpio2  = 0x0000ff40,
+		}},
+		.radio = {
+			 .type   = CX88_RADIO,
+			 .vmux   = 3,
+			 .gpio0  = 0x000040bf,
+			 .gpio1  = 0x000080c0,
+			 .gpio2  = 0x0000ff20,
+		},
+	},
+	[CX88_BOARD_WINFAST_DV2000] = {
+		.name           = "Leadtek Winfast DV2000",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0035e700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x0035e700,
+			.gpio3  = 0x02000000,
+		},{
+
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0035c700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x0035c700,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0035c700,
+			.gpio1  = 0x0035c700,
+			.gpio2  = 0x02000000,
+			.gpio3  = 0x02000000,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0035d700,
+			.gpio1  = 0x00007004,
+			.gpio2  = 0x0035d700,
+			.gpio3  = 0x02000000,
+		},
+	},
+	[CX88_BOARD_LEADTEK_PVR2000] = {
+		// gpio values for PAL version from regspy by DScaler
+		.name           = "Leadtek PVR 2000",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0000bde2,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0000bde6,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0000bde6,
+			.audioroute = 1,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0000bd62,
+			.audioroute = 1,
+		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_IODATA_GVVCP3PCI] = {
+		.name		= "IODATA GV-VCP3/PCI",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE2,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+		}},
+	},
+	[CX88_BOARD_PROLINK_PLAYTVPVR] = {
+		.name           = "Prolink PlayTV PVR",
+		.tuner_type     = TUNER_PHILIPS_FM1236_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf	= TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xbff0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xbff3,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xbff3,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0xbff0,
+		},
+	},
+	[CX88_BOARD_ASUS_PVR_416] = {
+		.name		= "ASUS PVR-416",
+		.tuner_type     = TUNER_PHILIPS_FM1236_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0000fde6,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+			.audioroute = 1,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0000fde2,
+		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_MSI_TVANYWHERE] = {
+		.name           = "MSI TV-@nywhere",
+		.tuner_type     = TUNER_MT2032,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00000fbf,
+			.gpio2  = 0x0000fc08,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00000fbf,
+			.gpio2  = 0x0000fc68,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00000fbf,
+			.gpio2  = 0x0000fc68,
+		}},
+	},
+	[CX88_BOARD_KWORLD_DVB_T] = {
+		.name           = "KWorld/VStream XPert DVB-T",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
+		.name           = "DViCO FusionHDTV DVB-T1",
+		.tuner_type     = TUNER_ABSENT, /* No analog tuner */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000027df,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000027df,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_KWORLD_LTV883] = {
+		.name           = "KWorld LTV883RF",
+		.tuner_type     = TUNER_TNF_8831BGFF,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x07f8,
+		},{
+			.type   = CX88_VMUX_DEBUG,
+			.vmux   = 0,
+			.gpio0  = 0x07f9,  // mono from tuner chip
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000007fa,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000007fa,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x000007f8,
+		},
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = {
+		.name		= "DViCO FusionHDTV 3 Gold-Q",
+		.tuner_type     = TUNER_MICROTUNE_4042FI5,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		/*
+		   GPIO[0] resets DT3302 DTV receiver
+		    0 - reset asserted
+		    1 - normal operation
+		   GPIO[1] mutes analog audio output connector
+		    0 - enable selected source
+		    1 - mute
+		   GPIO[2] selects source for analog audio output connector
+		    0 - analog audio input connector on tab
+		    1 - analog DAC output from CX23881 chip
+		   GPIO[3] selects RF input connector on tuner module
+		    0 - RF connector labeled CABLE
+		    1 - RF connector labeled ANT
+		   GPIO[4] selects high RF for QAM256 mode
+		    0 - normal RF
+		    1 - high RF
+		*/
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0	= 0x0f0d,
+		},{
+			.type   = CX88_VMUX_CABLE,
+			.vmux   = 0,
+			.gpio0	= 0x0f05,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0x0f00,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0x0f00,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_DVB_T1] = {
+		.name           = "Hauppauge Nova-T DVB-T",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_CONEXANT_DVB_T1] = {
+		.name           = "Conexant DVB-T reference design",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PROVIDEO_PV259] = {
+		.name		= "Provideo PV259",
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.audioroute = 1,
+		}},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
+		.name           = "DViCO FusionHDTV DVB-T Plus",
+		.tuner_type     = TUNER_ABSENT, /* No analog tuner */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000027df,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000027df,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DNTV_LIVE_DVB_T] = {
+		.name		= "digitalnow DNTV Live! DVB-T",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input		= {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00000700,
+			.gpio2  = 0x00000101,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00000700,
+			.gpio2  = 0x00000101,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PCHDTV_HD3000] = {
+		.name           = "pcHDTV HD3000 HDTV",
+		.tuner_type     = TUNER_THOMSON_DTT761X,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		/* GPIO[2] = audio source for analog audio out connector
+		 *  0 = analog audio input connector
+		 *  1 = CX88 audio DACs
+		 *
+		 * GPIO[7] = input to CX88's audio/chroma ADC
+		 *  0 = FM 10.7 MHz IF
+		 *  1 = Sound 4.5 MHz IF
+		 *
+		 * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively
+		 *
+		 * GPIO[16] = Remote control input
+		 */
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00008484,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00008400,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00008400,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x00008404,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_ROSLYN] = {
+		// entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu>
+		// GPIO values obtained from regspy, courtesy Sean Covel
+		.name           = "Hauppauge WinTV 28xxx (Roslyn) models",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xed1a,
+			.gpio2  = 0x00ff,
+		},{
+			.type   = CX88_VMUX_DEBUG,
+			.vmux   = 0,
+			.gpio0  = 0xff01,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xff02,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xed92,
+			.gpio2  = 0x00ff,
+		}},
+		.radio = {
+			 .type   = CX88_RADIO,
+			 .gpio0  = 0xed96,
+			 .gpio2  = 0x00ff,
+		 },
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_DIGITALLOGIC_MEC] = {
+		.name           = "Digital-Logic MICROSPACE Entertainment Center (MEC)",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00009d80,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00009d76,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00009d76,
+			.audioroute = 1,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x00009d00,
+			.audioroute = 1,
+		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_IODATA_GVBCTV7E] = {
+		.name           = "IODATA GV/BCTV7E",
+		.tuner_type     = TUNER_PHILIPS_FQ1286,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 1,
+			.gpio1  = 0x0000e03f,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 2,
+			.gpio1  = 0x0000e07f,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 3,
+			.gpio1  = 0x0000e07f,
+		}}
+	},
+	[CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = {
+		.name           = "PixelView PlayTV Ultra Pro (Stereo)",
+		/* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		/* Some variants use a tda9874 and so need the tvaudio module. */
+		.audio_chip     = V4L2_IDENT_TVAUDIO,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xbf61,  /* internal decoder */
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0xbf63,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0xbf63,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0xbf60,
+		 },
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = {
+		.name           = "DViCO FusionHDTV 3 Gold-T",
+		.tuner_type     = TUNER_THOMSON_DTT761X,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x97ed,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x97e9,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x97e9,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_ADSTECH_DVB_T_PCI] = {
+		.name           = "ADS Tech Instant TV DVB-T PCI",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
+		.name           = "TerraTec Cinergy 1400 DVB-T",
+		.tuner_type     = TUNER_ABSENT,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 2,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = {
+		.name           = "DViCO FusionHDTV 5 Gold",
+		.tuner_type     = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x87fd,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x87f9,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x87f9,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = {
+		.name           = "AverMedia UltraTV Media Center PCI 550",
+		.tuner_type     = TUNER_PHILIPS_FM1236_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 0,
+			.gpio0  = 0x0000cd73,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 1,
+			.gpio0  = 0x0000cd73,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 3,
+			.gpio0  = 0x0000cdb3,
+			.audioroute = 1,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.vmux   = 2,
+			.gpio0  = 0x0000cdf3,
+			.audioroute = 1,
+		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
+		 /* Alexander Wold <awold@bigfoot.com> */
+		 .name           = "Kworld V-Stream Xpert DVD",
+		 .tuner_type     = UNSET,
+		 .input          = {{
+			 .type   = CX88_VMUX_COMPOSITE1,
+			 .vmux   = 1,
+			 .gpio0  = 0x03000000,
+			 .gpio1  = 0x01000000,
+			 .gpio2  = 0x02000000,
+			 .gpio3  = 0x00100000,
+		 },{
+			 .type   = CX88_VMUX_SVIDEO,
+			 .vmux   = 2,
+			 .gpio0  = 0x03000000,
+			 .gpio1  = 0x01000000,
+			 .gpio2  = 0x02000000,
+			 .gpio3  = 0x00100000,
+		 }},
+	},
+	[CX88_BOARD_ATI_HDTVWONDER] = {
+		.name           = "ATI HDTV Wonder",
+		.tuner_type     = TUNER_PHILIPS_TUV1236D,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00000ff7,
+			.gpio1  = 0x000000ff,
+			.gpio2  = 0x00000001,
+			.gpio3  = 0x00000000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00000ffe,
+			.gpio1  = 0x000000ff,
+			.gpio2  = 0x00000001,
+			.gpio3  = 0x00000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00000ffe,
+			.gpio1  = 0x000000ff,
+			.gpio2  = 0x00000001,
+			.gpio3  = 0x00000000,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_WINFAST_DTV1000] = {
+		.name           = "WinFast DTV1000-T",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_AVERTV_303] = {
+		.name           = "AVerTV 303 (M126)",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00ff,
+			.gpio1  = 0xe09f,
+			.gpio2  = 0x0010,
+			.gpio3  = 0x0000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00ff,
+			.gpio1  = 0xe05f,
+			.gpio2  = 0x0010,
+			.gpio3  = 0x0000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00ff,
+			.gpio1  = 0xe05f,
+			.gpio2  = 0x0010,
+			.gpio3  = 0x0000,
+		}},
+	},
+	[CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
+		.name		= "Hauppauge Nova-S-Plus DVB-S",
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.audio_chip	= V4L2_IDENT_WM8775,
+		.i2sinputcntl   = 2,
+		.input		= {{
+			.type	= CX88_VMUX_DVB,
+			.vmux	= 0,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			/* 2: Line-In */
+			.audioroute = 2,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
+		.name		= "Hauppauge Nova-SE2 DVB-S",
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input		= {{
+			.type	= CX88_VMUX_DVB,
+			.vmux	= 0,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_KWORLD_DVBS_100] = {
+		.name		= "KWorld DVB-S 100",
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.audio_chip = V4L2_IDENT_WM8775,
+		.input		= {{
+			.type	= CX88_VMUX_DVB,
+			.vmux	= 0,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			/* 2: Line-In */
+			.audioroute = 2,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR1100] = {
+		.name		= "Hauppauge WinTV-HVR1100 DVB-T/Hybrid",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input		= {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+		},{
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+		},{
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+		}},
+		/* fixme: Add radio support */
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR1100LP] = {
+		.name		= "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input		= {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+		},{
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+		}},
+		/* fixme: Add radio support */
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = {
+		.name           = "digitalnow DNTV Live! DVB-T Pro",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+				  TDA9887_PORT2_ACTIVE,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xf80808,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0xf80808,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0xf80808,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0xf80808,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_KWORLD_DVB_T_CX22702] = {
+		/* Kworld V-stream Xpert DVB-T with Thomson tuner */
+		/* DTT 7579 Conexant CX22702-19 Conexant CX2388x  */
+		/* Manenti Marco <marco_manenti@colman.it> */
+		.name           = "KWorld/VStream XPert DVB-T with cx22702",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0700,
+			.gpio2  = 0x0101,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
+		.name           = "DViCO FusionHDTV DVB-T Dual Digital",
+		.tuner_type     = TUNER_ABSENT, /* No analog tuner */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000067df,
+		 },{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000067df,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
+		.name           = "KWorld HardwareMpegTV XPert",
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x3de2,
+			.gpio2  = 0x00ff,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x3de6,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x3de6,
+			.audioroute = 1,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x3de6,
+			.gpio2  = 0x00ff,
+		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = {
+		.name           = "DViCO FusionHDTV DVB-T Hybrid",
+		.tuner_type     = TUNER_THOMSON_FE6600,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0000a75f,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0000a75b,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0000a75b,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PCHDTV_HD5500] = {
+		.name           = "pcHDTV HD5500 HDTV",
+		.tuner_type     = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x87fd,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x87f9,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x87f9,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_KWORLD_MCE200_DELUXE] = {
+		/* FIXME: tested TV input only, disabled composite,
+		   svideo and radio until they can be tested also. */
+		.name           = "Kworld MCE 200 Deluxe",
+		.tuner_type     = TUNER_TENA_9533_DI,
+		.radio_type     = UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0000BDE6
+		}},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = {
+		/* FIXME: SVideo, Composite and FM inputs are untested */
+		.name           = "PixelView PlayTV P7000",
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+				  TDA9887_PORT2_ACTIVE,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x5da6,
+		}},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
+	},
+	[CX88_BOARD_NPGTECH_REALTV_TOP10FM] = {
+		.name           = "NPG Tech Real TV FM Top 10",
+		.tuner_type     = TUNER_TNF_5335MF, /* Actually a TNF9535 */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0	= 0x0788,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0x078b,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0x078b,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0x074a,
+		},
+	},
+	[CX88_BOARD_WINFAST_DTV2000H] = {
+		.name           = "WinFast DTV2000 H",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00017304,
+			.gpio1  = 0x00008203,
+			.gpio2  = 0x00017304,
+			.gpio3  = 0x02000000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0001d701,
+			.gpio1  = 0x0000b207,
+			.gpio2  = 0x0001d701,
+			.gpio3  = 0x02000000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE2,
+			.vmux   = 2,
+			.gpio0  = 0x0001d503,
+			.gpio1  = 0x0000b207,
+			.gpio2  = 0x0001d503,
+			.gpio3  = 0x02000000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 3,
+			.gpio0  = 0x0001d701,
+			.gpio1  = 0x0000b207,
+			.gpio2  = 0x0001d701,
+			.gpio3  = 0x02000000,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0x00015702,
+			 .gpio1 = 0x0000f207,
+			 .gpio2 = 0x00015702,
+			 .gpio3 = 0x02000000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_WINFAST_DTV2000H_J] = {
+		.name           = "WinFast DTV2000 H rev. J",
+		.tuner_type     = TUNER_PHILIPS_FMD1216MEX_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00017300,
+			.gpio1  = 0x00008207,
+			.gpio2	= 0x00000000,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00018300,
+			.gpio1  = 0x0000f207,
+			.gpio2	= 0x00017304,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00018301,
+			.gpio1  = 0x0000f207,
+			.gpio2	= 0x00017304,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00018301,
+			.gpio1  = 0x0000f207,
+			.gpio2	= 0x00017304,
+			.gpio3  = 0x02000000,
+		}},
+		.radio = {
+			 .type  = CX88_RADIO,
+			 .gpio0 = 0x00015702,
+			 .gpio1 = 0x0000f207,
+			 .gpio2 = 0x00015702,
+			 .gpio3 = 0x02000000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_GENIATECH_DVBS] = {
+		.name          = "Geniatech DVB-S",
+		.tuner_type    = TUNER_ABSENT,
+		.radio_type    = UNSET,
+		.tuner_addr    = ADDR_UNSET,
+		.radio_addr    = ADDR_UNSET,
+		.input  = {{
+			.type  = CX88_VMUX_DVB,
+			.vmux  = 0,
+		},{
+			.type  = CX88_VMUX_COMPOSITE1,
+			.vmux  = 1,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR3000] = {
+		.name           = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.audio_chip     = V4L2_IDENT_WM8775,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x84bf,
+			/* 1: TV Audio / FM Mono */
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x84bf,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x84bf,
+			/* 2: Line-In */
+			.audioroute = 2,
+		}},
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0	= 0x84bf,
+			/* 4: FM Stereo (untested) */
+			.audioroute = 8,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+		.num_frontends	= 2,
+	},
+	[CX88_BOARD_NORWOOD_MICRO] = {
+		.name           = "Norwood Micro TV Tuner",
+		.tuner_type     = TUNER_TNF_5335MF,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0709,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x070b,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x070b,
+		}},
+	},
+	[CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+		.name           = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
+		}},
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR1300] = {
+		.name		= "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.audio_chip     = V4L2_IDENT_WM8775,
+		/*
+		 * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
+		 */
+		.input		= {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0	= 0xef88,
+			/* 1: TV Audio / FM Mono */
+			.audioroute = 1,
+		},{
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			.gpio0	= 0xef88,
+			/* 2: Line-In */
+			.audioroute = 2,
+		},{
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			.gpio0	= 0xef88,
+			/* 2: Line-In */
+			.audioroute = 2,
+		}},
+		.mpeg           = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0	= 0xef88,
+			/* 4: FM Stereo (untested) */
+			.audioroute = 8,
+		},
+	},
+	[CX88_BOARD_SAMSUNG_SMT_7020] = {
+		.name		= "Samsung SMT 7020 DVB-S",
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input		= { {
+			.type	= CX88_VMUX_DVB,
+			.vmux	= 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_ADSTECH_PTV_390] = {
+		.name           = "ADS Tech Instant Video PCI",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DEBUG,
+			.vmux   = 3,
+			.gpio0  = 0x04ff,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x07fa,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.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,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
+		.name           = "DViCO FusionHDTV 5 PCI nano",
+		/* xc3008 tuner, digital only for now */
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x000027df, /* Unconfirmed */
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000027df, /* Unconfirmed */
+			.audioroute = 1,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000027df, /* Unconfirmed */
+			.audioroute = 1,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PINNACLE_HYBRID_PCTV] = {
+		.name           = "Pinnacle Hybrid PCTV",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x004ff,
+			.gpio1  = 0x010ff,
+			.gpio2  = 0x00001,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x004fb,
+			.gpio1  = 0x010ef,
+			.audioroute = 1,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x004fb,
+			.gpio1  = 0x010ef,
+			.audioroute = 1,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x004ff,
+			.gpio1  = 0x010ff,
+			.gpio2  = 0x0ff,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	/* Terry Wu <terrywu2009@gmail.com> */
+	/* TV Audio :      set GPIO 2, 18, 19 value to 0, 1, 0 */
+	/* FM Audio :      set GPIO 2, 18, 19 value to 0, 0, 0 */
+	/* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */
+	/* Mute Audio :    set GPIO 2 value to 1               */
+	[CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
+		.name           = "Leadtek TV2000 XP Global",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C04,       /* pin 18 = 1, pin 19 = 0 */
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C0C,       /* pin 18 = 1, pin 19 = 1 */
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C0C,       /* pin 18 = 1, pin 19 = 1 */
+			.gpio3  = 0x0000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0400,        /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C00,       /* pin 18 = 0, pin 19 = 0 */
+			.gpio3  = 0x0000,
+		},
+	},
+	[CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36] = {
+		.name           = "Leadtek TV2000 XP Global (SC4100)",
+		.tuner_type     = TUNER_XC4000,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C04,       /* pin 18 = 1, pin 19 = 0 */
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C0C,       /* pin 18 = 1, pin 19 = 1 */
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C0C,       /* pin 18 = 1, pin 19 = 1 */
+			.gpio3  = 0x0000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0400,        /* pin 2 = 0 */
+			.gpio1  = 0x0000,
+			.gpio2  = 0x0C00,       /* pin 18 = 0, pin 19 = 0 */
+			.gpio3  = 0x0000,
+		},
+	},
+	[CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43] = {
+		.name           = "Leadtek TV2000 XP Global (XC4100)",
+		.tuner_type     = TUNER_XC4000,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6040,       /* pin 14 = 1, pin 13 = 0 */
+			.gpio2  = 0x0000,
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6060,       /* pin 14 = 1, pin 13 = 1 */
+			.gpio2  = 0x0000,
+			.gpio3  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6060,       /* pin 14 = 1, pin 13 = 1 */
+			.gpio2  = 0x0000,
+			.gpio3  = 0x0000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0400,        /* pin 2 = 0 */
+			.gpio1  = 0x6000,        /* pin 14 = 1, pin 13 = 0 */
+			.gpio2  = 0x0000,
+			.gpio3  = 0x0000,
+		},
+	},
+	[CX88_BOARD_POWERCOLOR_REAL_ANGEL] = {
+		.name           = "PowerColor RA330",	/* Long names may confuse LIRC. */
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.input          = { {
+			.type   = CX88_VMUX_DEBUG,
+			.vmux   = 3,		/* Due to the way the cx88 driver is written,	*/
+			.gpio0 = 0x00ff,	/* there is no way to deactivate audio pass-	*/
+			.gpio1 = 0xf39d,	/* through without this entry. Furthermore, if	*/
+			.gpio3 = 0x0000,	/* the TV mux entry is first, you get audio	*/
+		}, {				/* from the tuner on boot for a little while.	*/
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0 = 0x00ff,
+			.gpio1 = 0xf35d,
+			.gpio3 = 0x0000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0 = 0x00ff,
+			.gpio1 = 0xf37d,
+			.gpio3 = 0x0000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000ff,
+			.gpio1  = 0x0f37d,
+			.gpio3  = 0x00000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x000ff,
+			.gpio1  = 0x0f35d,
+			.gpio3  = 0x00000,
+		},
+	},
+	[CX88_BOARD_GENIATECH_X8000_MT] = {
+		/* Also PowerColor Real Angel 330 and Geniatech X800 OEM */
+		.name           = "Geniatech X8000-MT DVBT",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x00000000,
+			.gpio1  = 0x00e3e341,
+			.gpio2  = 0x00000000,
+			.gpio3  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x00000000,
+			.gpio1  = 0x00e3e361,
+			.gpio2  = 0x00000000,
+			.gpio3  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x00000000,
+			.gpio1  = 0x00e3e361,
+			.gpio2  = 0x00000000,
+			.gpio3  = 0x00000000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x00000000,
+			.gpio1  = 0x00e3e341,
+			.gpio2  = 0x00000000,
+			.gpio3  = 0x00000000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = {
+		.name           = "DViCO FusionHDTV DVB-T PRO",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000067df,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000067df,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = {
+		.name           = "DViCO FusionHDTV 7 Gold",
+		.tuner_type     = TUNER_XC5000,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x10df,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x16d9,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x16d9,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PROLINK_PV_8000GT] = {
+		.name           = "Prolink Pixelview MPEG 8000GT",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0 = 0x0ff,
+			.gpio2 = 0x0cfb,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio2 = 0x0cfb,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio2 = 0x0cfb,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio2 = 0x0cfb,
+		},
+	},
+	[CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
+		.name           = "Prolink Pixelview Global Extreme",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0 = 0x04fb,
+			.gpio1 = 0x04080,
+			.gpio2 = 0x0cf7,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0 = 0x04fb,
+			.gpio1 = 0x04080,
+			.gpio2 = 0x0cfb,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0 = 0x04fb,
+			.gpio1 = 0x04080,
+			.gpio2 = 0x0cfb,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0 = 0x04ff,
+			.gpio1 = 0x04080,
+			.gpio2 = 0x0cf7,
+		},
+	},
+	/* Both radio, analog and ATSC work with this board.
+	   However, for analog to work, s5h1409 gate should be open,
+	   otherwise, tuner-xc3028 won't be detected.
+	   A proper fix require using the newer i2c methods to add
+	   tuner-xc3028 without doing an i2c probe.
+	 */
+	[CX88_BOARD_KWORLD_ATSC_120] = {
+		.name           = "Kworld PlusTV HD PCI 120 (ATSC 120)",
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f35d,
+			.gpio2  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f37e,
+			.gpio2  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f37e,
+			.gpio2  = 0x00000000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f35d,
+			.gpio2  = 0x00000000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR4000] = {
+		.name           = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.audio_chip     = V4L2_IDENT_WM8775,
+		/*
+		 * GPIO0 (WINTV2000)
+		 *
+		 * Analogue     SAT     DVB-T
+		 * Antenna      0xc4bf  0xc4bb
+		 * Composite    0xc4bf  0xc4bb
+		 * S-Video      0xc4bf  0xc4bb
+		 * Composite1   0xc4ff  0xc4fb
+		 * S-Video1     0xc4ff  0xc4fb
+		 *
+		 * BIT  VALUE   FUNCTION GP{x}_IO
+		 * 0    1       I:?
+		 * 1    1       I:?
+		 * 2    1       O:MPEG PORT 0=DVB-T 1=DVB-S
+		 * 3    1       I:?
+		 * 4    1       I:?
+		 * 5    1       I:?
+		 * 6    0       O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+		 * 7    1       O:DVB-T DEMOD RESET LOW
+		 *
+		 * BIT  VALUE   FUNCTION GP{x}_OE
+		 * 8    0       I
+		 * 9    0       I
+		 * a    1       O
+		 * b    0       I
+		 * c    0       I
+		 * d    0       I
+		 * e    1       O
+		 * f    1       O
+		 *
+		 * WM8775 ADC
+		 *
+		 * 1: TV Audio / FM Mono
+		 * 2: Line-In
+		 * 3: Line-In Expansion
+		 * 4: FM Stereo
+		 */
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xc4bf,
+			/* 1: TV Audio / FM Mono */
+			.audioroute = 1,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xc4bf,
+			/* 2: Line-In */
+			.audioroute = 2,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xc4bf,
+			/* 2: Line-In */
+			.audioroute = 2,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0	= 0xc4bf,
+			/* 4: FM Stereo */
+			.audioroute = 8,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+		.num_frontends	= 2,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+		.name           = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TEVII_S420] = {
+		.name           = "TeVii S420 DVB-S",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TEVII_S460] = {
+		.name           = "TeVii S460 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TEVII_S464] = {
+		.name           = "TeVii S464 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_OMICOM_SS4_PCI] = {
+		.name           = "Omicom SS4 DVB-S/S2 PCI",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TBS_8910] = {
+		.name           = "TBS 8910 DVB-S",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TBS_8920] = {
+		.name           = "TBS 8920 DVB-S/S2",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+			.gpio0  = 0x8080,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PROF_6200] = {
+		.name           = "Prof 6200 DVB-S",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PROF_7300] = {
+		.name           = "PROF 7300 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_SATTRADE_ST4200] = {
+		.name           = "SATTRADE ST4200 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = {
+		.name           = "Terratec Cinergy HT PCI MKII",
+		.tuner_type     = TUNER_XC2028,
+		.tuner_addr     = 0x61,
+		.radio_type     = UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x004ff,
+			.gpio1  = 0x010ff,
+			.gpio2  = 0x00001,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x004fb,
+			.gpio1  = 0x010ef,
+			.audioroute = 1,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x004fb,
+			.gpio1  = 0x010ef,
+			.audioroute = 1,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x004ff,
+			.gpio1  = 0x010ff,
+			.gpio2  = 0x0ff,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_HAUPPAUGE_IRONLY] = {
+		.name           = "Hauppauge WinTV-IR Only",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+	},
+	[CX88_BOARD_WINFAST_DTV1800H] = {
+		.name           = "Leadtek WinFast DTV1800 Hybrid",
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr     = 0x61,
+		.radio_addr     = ADDR_UNSET,
+		/*
+		 * GPIO setting
+		 *
+		 *  2: mute (0=off,1=on)
+		 * 12: tuner reset pin
+		 * 13: audio source (0=tuner audio,1=line in)
+		 * 14: FM (0=on,1=off ???)
+		 */
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6040,       /* pin 13 = 0, pin 14 = 1 */
+			.gpio2  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6060,       /* pin 13 = 1, pin 14 = 1 */
+			.gpio2  = 0x0000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6060,       /* pin 13 = 1, pin 14 = 1 */
+			.gpio2  = 0x0000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x0400,       /* pin 2 = 0 */
+			.gpio1  = 0x6000,       /* pin 13 = 0, pin 14 = 0 */
+			.gpio2  = 0x0000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
+		.name		= "Leadtek WinFast DTV1800 H (XC4000)",
+		.tuner_type	= TUNER_XC4000,
+		.radio_type	= UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		/*
+		 * GPIO setting
+		 *
+		 *  2: mute (0=off,1=on)
+		 * 12: tuner reset pin
+		 * 13: audio source (0=tuner audio,1=line in)
+		 * 14: FM (0=on,1=off ???)
+		 */
+		.input		= {{
+			.type	= CX88_VMUX_TELEVISION,
+			.vmux	= 0,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6040,	/* pin 13 = 0, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6060,	/* pin 13 = 1, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6060,	/* pin 13 = 1, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}},
+		.radio = {
+			.type	= CX88_RADIO,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6000,	/* pin 13 = 0, pin 14 = 0 */
+			.gpio2	= 0x0000,
+		},
+		.mpeg		= CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
+		.name		= "Leadtek WinFast DTV2000 H PLUS",
+		.tuner_type	= TUNER_XC4000,
+		.radio_type	= UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		/*
+		 * GPIO
+		 *   2: 1: mute audio
+		 *  12: 0: reset XC4000
+		 *  13: 1: audio input is line in (0: tuner)
+		 *  14: 0: FM radio
+		 *  16: 0: RF input is cable
+		 */
+		.input		= {{
+			.type	= CX88_VMUX_TELEVISION,
+			.vmux	= 0,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF0D7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_CABLE,
+			.vmux	= 0,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF0D7,
+			.gpio2	= 0x0100,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			.gpio0	= 0x0403,	/* was 0x0407 */
+			.gpio1	= 0xF0F7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			.gpio0	= 0x0403,	/* was 0x0407 */
+			.gpio1	= 0xF0F7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}},
+		.radio = {
+			.type	= CX88_RADIO,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF097,
+			.gpio2	= 0x0100,
+			.gpio3	= 0x0000,
+		},
+		.mpeg		= CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_PROF_7301] = {
+		.name           = "Prof 7301 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_TWINHAN_VP1027_DVBS] = {
+		.name		= "Twinhan VP-1027 DVB-S",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+		       .type   = CX88_VMUX_DVB,
+		       .vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
+};
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs                                                  */
+
+static const struct cx88_subid cx88_subids[] = {
+	{
+		.subvendor = 0x0070,
+		.subdevice = 0x3400,
+		.card      = CX88_BOARD_HAUPPAUGE,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x3401,
+		.card      = CX88_BOARD_HAUPPAUGE,
+	},{
+		.subvendor = 0x14c7,
+		.subdevice = 0x0106,
+		.card      = CX88_BOARD_GDI,
+	},{
+		.subvendor = 0x14c7,
+		.subdevice = 0x0107, /* with mpeg encoder */
+		.card      = CX88_BOARD_GDI,
+	},{
+		.subvendor = PCI_VENDOR_ID_ATI,
+		.subdevice = 0x00f8,
+		.card      = CX88_BOARD_ATI_WONDER_PRO,
+	}, {
+		.subvendor = PCI_VENDOR_ID_ATI,
+		.subdevice = 0x00f9,
+		.card      = CX88_BOARD_ATI_WONDER_PRO,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6611,
+		.card      = CX88_BOARD_WINFAST2000XP_EXPERT,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x6613,	/* NTSC */
+		.card      = CX88_BOARD_WINFAST2000XP_EXPERT,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x6620,
+		.card      = CX88_BOARD_WINFAST_DV2000,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x663b,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x663c,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	},{
+		.subvendor = 0x1461,
+		.subdevice = 0x000b,
+		.card      = CX88_BOARD_AVERTV_STUDIO_303,
+	},{
+		.subvendor = 0x1462,
+		.subdevice = 0x8606,
+		.card      = CX88_BOARD_MSI_TVANYWHERE_MASTER,
+	},{
+		.subvendor = 0x10fc,
+		.subdevice = 0xd003,
+		.card      = CX88_BOARD_IODATA_GVVCP3PCI,
+	},{
+		.subvendor = 0x1043,
+		.subdevice = 0x4823,  /* with mpeg encoder */
+		.card      = CX88_BOARD_ASUS_PVR_416,
+	},{
+		.subvendor = 0x17de,
+		.subdevice = 0x08a6,
+		.card      = CX88_BOARD_KWORLD_DVB_T,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xd810,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xd820,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb00,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9002,
+		.card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
+	},{
+		.subvendor = 0x14f1,
+		.subdevice = 0x0187,
+		.card      = CX88_BOARD_CONEXANT_DVB_T1,
+	},{
+		.subvendor = 0x1540,
+		.subdevice = 0x2580,
+		.card      = CX88_BOARD_PROVIDEO_PV259,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb10,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+	},{
+		.subvendor = 0x1554,
+		.subdevice = 0x4811,
+		.card      = CX88_BOARD_PIXELVIEW,
+	},{
+		.subvendor = 0x7063,
+		.subdevice = 0x3000, /* HD-3000 card */
+		.card      = CX88_BOARD_PCHDTV_HD3000,
+	},{
+		.subvendor = 0x17de,
+		.subdevice = 0xa8a6,
+		.card      = CX88_BOARD_DNTV_LIVE_DVB_T,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x2801,
+		.card      = CX88_BOARD_HAUPPAUGE_ROSLYN,
+	},{
+		.subvendor = 0x14f1,
+		.subdevice = 0x0342,
+		.card      = CX88_BOARD_DIGITALLOGIC_MEC,
+	},{
+		.subvendor = 0x10fc,
+		.subdevice = 0xd035,
+		.card      = CX88_BOARD_IODATA_GVBCTV7E,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0334,
+		.card      = CX88_BOARD_ADSTECH_DVB_T_PCI,
+	},{
+		.subvendor = 0x153b,
+		.subdevice = 0x1166,
+		.card      = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xd500,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD,
+	},{
+		.subvendor = 0x1461,
+		.subdevice = 0x8011,
+		.card      = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550,
+	},{
+		.subvendor = PCI_VENDOR_ID_ATI,
+		.subdevice = 0xa101,
+		.card      = CX88_BOARD_ATI_HDTVWONDER,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x665f,
+		.card      = CX88_BOARD_WINFAST_DTV1000,
+	},{
+		.subvendor = 0x1461,
+		.subdevice = 0x000a,
+		.card      = CX88_BOARD_AVERTV_303,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9200,
+		.card      = CX88_BOARD_HAUPPAUGE_NOVASE2_S1,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9201,
+		.card      = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9202,
+		.card      = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+	},{
+		.subvendor = 0x17de,
+		.subdevice = 0x08b2,
+		.card      = CX88_BOARD_KWORLD_DVBS_100,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9400,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1100,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9402,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1100,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9800,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9802,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9001,
+		.card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
+	},{
+		.subvendor = 0x1822,
+		.subdevice = 0x0025,
+		.card      = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+	},{
+		.subvendor = 0x17de,
+		.subdevice = 0x08a1,
+		.card      = CX88_BOARD_KWORLD_DVB_T_CX22702,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb50,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb54,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+		/* Re-branded DViCO: DigitalNow DVB-T Dual */
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb11,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+		/* Re-branded DViCO: UltraView DVB-T Plus */
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb30,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO,
+	}, {
+		.subvendor = 0x17de,
+		.subdevice = 0x0840,
+		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0305,
+		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb40,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xdb44,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+	},{
+		.subvendor = 0x7063,
+		.subdevice = 0x5500,
+		.card      = CX88_BOARD_PCHDTV_HD5500,
+	},{
+		.subvendor = 0x17de,
+		.subdevice = 0x0841,
+		.card      = CX88_BOARD_KWORLD_MCE200_DELUXE,
+	},{
+		.subvendor = 0x1822,
+		.subdevice = 0x0019,
+		.card      = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+	},{
+		.subvendor = 0x1554,
+		.subdevice = 0x4813,
+		.card      = CX88_BOARD_PIXELVIEW_PLAYTV_P7000,
+	},{
+		.subvendor = 0x14f1,
+		.subdevice = 0x0842,
+		.card      = CX88_BOARD_NPGTECH_REALTV_TOP10FM,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x665e,
+		.card      = CX88_BOARD_WINFAST_DTV2000H,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x6f2b,
+		.card      = CX88_BOARD_WINFAST_DTV2000H_J,
+	},{
+		.subvendor = 0x18ac,
+		.subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+	},{
+		.subvendor = 0x14f1,
+		.subdevice = 0x0084,
+		.card      = CX88_BOARD_GENIATECH_DVBS,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x1404,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR3000,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xdc00,
+		.card      = CX88_BOARD_SAMSUNG_SMT_7020,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xdccd,
+		.card      = CX88_BOARD_SAMSUNG_SMT_7020,
+	},{
+		.subvendor = 0x1461,
+		.subdevice = 0xc111, /* AverMedia M150-D */
+		/* This board is known to work with the ASUS PVR416 config */
+		.card      = CX88_BOARD_ASUS_PVR_416,
+	},{
+		.subvendor = 0xc180,
+		.subdevice = 0xc980,
+		.card      = CX88_BOARD_TE_DTV_250_OEM_SWANN,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9600,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1300,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9601,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1300,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9602,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR1300,
+	},{
+		.subvendor = 0x107d,
+		.subdevice = 0x6632,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	},{
+		.subvendor = 0x12ab,
+		.subdevice = 0x2300, /* Club3D Zap TV2100 */
+		.card      = CX88_BOARD_KWORLD_DVB_T_CX22702,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x9000,
+		.card      = CX88_BOARD_HAUPPAUGE_DVB_T1,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x1400,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR3000,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x1401,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR3000,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x1402,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR3000,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
+		.card      = CX88_BOARD_KWORLD_DVBS_100,
+	},{
+		.subvendor = 0x1421,
+		.subdevice = 0x0390,
+		.card      = CX88_BOARD_ADSTECH_PTV_390,
+	},{
+		.subvendor = 0x11bd,
+		.subdevice = 0x0051,
+		.card      = CX88_BOARD_PINNACLE_PCTV_HD_800i,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xd530,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO,
+	}, {
+		.subvendor = 0x12ab,
+		.subdevice = 0x1788,
+		.card      = CX88_BOARD_PINNACLE_HYBRID_PCTV,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0xea3d,
+		.card      = CX88_BOARD_POWERCOLOR_REAL_ANGEL,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f18,
+		.card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8852,
+		.card      = CX88_BOARD_GENIATECH_X8000_MT,
+	}, {
+		.subvendor = 0x18ac,
+		.subdevice = 0xd610,
+		.card      = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD,
+	}, {
+		.subvendor = 0x1554,
+		.subdevice = 0x4935,
+		.card      = CX88_BOARD_PROLINK_PV_8000GT,
+	}, {
+		.subvendor = 0x1554,
+		.subdevice = 0x4976,
+		.card      = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
+	}, {
+		.subvendor = 0x17de,
+		.subdevice = 0x08c1,
+		.card      = CX88_BOARD_KWORLD_ATSC_120,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x6900,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x6904,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x6902,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x6905,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x6906,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+	}, {
+		.subvendor = 0xd420,
+		.subdevice = 0x9022,
+		.card      = CX88_BOARD_TEVII_S420,
+	}, {
+		.subvendor = 0xd460,
+		.subdevice = 0x9022,
+		.card      = CX88_BOARD_TEVII_S460,
+	}, {
+		.subvendor = 0xd464,
+		.subdevice = 0x9022,
+		.card      = CX88_BOARD_TEVII_S464,
+	}, {
+		.subvendor = 0xA044,
+		.subdevice = 0x2011,
+		.card      = CX88_BOARD_OMICOM_SS4_PCI,
+	}, {
+		.subvendor = 0x8910,
+		.subdevice = 0x8888,
+		.card      = CX88_BOARD_TBS_8910,
+	}, {
+		.subvendor = 0x8920,
+		.subdevice = 0x8888,
+		.card      = CX88_BOARD_TBS_8920,
+	}, {
+		.subvendor = 0xb022,
+		.subdevice = 0x3022,
+		.card      = CX88_BOARD_PROF_6200,
+	}, {
+		.subvendor = 0xB033,
+		.subdevice = 0x3033,
+		.card      = CX88_BOARD_PROF_7300,
+	}, {
+		.subvendor = 0xb200,
+		.subdevice = 0x4200,
+		.card      = CX88_BOARD_SATTRADE_ST4200,
+	}, {
+		.subvendor = 0x153b,
+		.subdevice = 0x1177,
+		.card      = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x9290,
+		.card      = CX88_BOARD_HAUPPAUGE_IRONLY,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6654,
+		.card      = CX88_BOARD_WINFAST_DTV1800H,
+	}, {
+		/* WinFast DTV1800 H with XC4000 tuner */
+		.subvendor = 0x107d,
+		.subdevice = 0x6f38,
+		.card      = CX88_BOARD_WINFAST_DTV1800H_XC4000,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f42,
+		.card      = CX88_BOARD_WINFAST_DTV2000H_PLUS,
+	}, {
+		/* PVR2000 PAL Model [107d:6630] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6630,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	}, {
+		/* PVR2000 PAL Model [107d:6638] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6638,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	}, {
+		/* PVR2000 NTSC Model [107d:6631] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6631,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	}, {
+		/* PVR2000 NTSC Model [107d:6637] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6637,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	}, {
+		/* PVR2000 NTSC Model [107d:663d] */
+		.subvendor = 0x107d,
+		.subdevice = 0x663d,
+		.card      = CX88_BOARD_LEADTEK_PVR2000,
+	}, {
+		/* DV2000 NTSC Model [107d:6621] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6621,
+		.card      = CX88_BOARD_WINFAST_DV2000,
+	}, {
+		/* TV2000 XP Global [107d:6618]  */
+		.subvendor = 0x107d,
+		.subdevice = 0x6618,
+		.card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+	}, {
+		/* TV2000 XP Global [107d:6618] */
+		.subvendor = 0x107d,
+		.subdevice = 0x6619,
+		.card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+	}, {
+		/* WinFast TV2000 XP Global with XC4000 tuner */
+		.subvendor = 0x107d,
+		.subdevice = 0x6f36,
+		.card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36,
+	}, {
+		/* WinFast TV2000 XP Global with XC4000 tuner and different GPIOs */
+		.subvendor = 0x107d,
+		.subdevice = 0x6f43,
+		.card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43,
+	}, {
+		.subvendor = 0xb034,
+		.subdevice = 0x3034,
+		.card      = CX88_BOARD_PROF_7301,
+	}, {
+		.subvendor = 0x1822,
+		.subdevice = 0x0023,
+		.card      = CX88_BOARD_TWINHAN_VP1027_DVBS,
+	},
+};
+
+/* ----------------------------------------------------------------------- */
+/* some leadtek specific stuff                                             */
+
+static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+	if (eeprom_data[4] != 0x7d ||
+	    eeprom_data[5] != 0x10 ||
+	    eeprom_data[7] != 0x66) {
+		warn_printk(core, "Leadtek eeprom invalid.\n");
+		return;
+	}
+
+	/* Terry Wu <terrywu2009@gmail.com> */
+	switch (eeprom_data[6]) {
+	case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */
+	case 0x21: /* SSID 6621 for DV2000 NTSC Model */
+	case 0x31: /* SSID 6631 for PVR2000 NTSC Model */
+	case 0x37: /* SSID 6637 for PVR2000 NTSC Model */
+	case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */
+		core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3;
+		break;
+	default:
+		core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+		break;
+	}
+
+	info_printk(core, "Leadtek Winfast 2000XP Expert config: "
+		    "tuner=%d, eeprom[0]=0x%02x\n",
+		    core->board.tuner_type, eeprom_data[0]);
+}
+
+static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+	struct tveeprom tv;
+
+	tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data);
+	core->board.tuner_type = tv.tuner_type;
+	core->tuner_formats = tv.tuner_formats;
+	core->board.radio.type = tv.has_radio ? CX88_RADIO : 0;
+
+	/* Make sure we support the board model */
+	switch (tv.model)
+	{
+	case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */
+	case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */
+	case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */
+	case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */
+	case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */
+	case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */
+	case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */
+	case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */
+	case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
+	case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
+	case 34519: /* WinTV-PCI-FM */
+	case 69009:
+		/* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+	case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+	case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+	case 69559:
+		/* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+	case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
+	case 90002: /* Nova-T-PCI (9002) */
+	case 92001: /* Nova-S-Plus (Video and IR) */
+	case 92002: /* Nova-S-Plus (Video and IR) */
+	case 90003: /* Nova-T-PCI (9002 No RF out) */
+	case 90500: /* Nova-T-PCI (oem) */
+	case 90501: /* Nova-T-PCI (oem/IR) */
+	case 92000: /* Nova-SE2 (OEM, No Video or IR) */
+	case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */
+	case 94009: /* WinTV-HVR1100 (Video and IR Retail) */
+	case 94501: /* WinTV-HVR1100 (Video and IR OEM) */
+	case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */
+	case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */
+	case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */
+	case 96569: /* WinTV-HVR1300 () */
+	case 96659: /* WinTV-HVR1300 () */
+	case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
+		/* known */
+		break;
+	case CX88_BOARD_SAMSUNG_SMT_7020:
+		cx_set(MO_GP0_IO, 0x008989FF);
+		break;
+	default:
+		warn_printk(core, "warning: unknown hauppauge model #%d\n",
+			    tv.model);
+		break;
+	}
+
+	info_printk(core, "hauppauge eeprom: model=%d\n", tv.model);
+}
+
+/* ----------------------------------------------------------------------- */
+/* some GDI (was: Modular Technology) specific stuff                       */
+
+static const struct {
+	int  id;
+	int  fm;
+	const char *name;
+} gdi_tuner[] = {
+	[ 0x01 ] = { .id   = TUNER_ABSENT,
+		     .name = "NTSC_M" },
+	[ 0x02 ] = { .id   = TUNER_ABSENT,
+		     .name = "PAL_B" },
+	[ 0x03 ] = { .id   = TUNER_ABSENT,
+		     .name = "PAL_I" },
+	[ 0x04 ] = { .id   = TUNER_ABSENT,
+		     .name = "PAL_D" },
+	[ 0x05 ] = { .id   = TUNER_ABSENT,
+		     .name = "SECAM" },
+
+	[ 0x10 ] = { .id   = TUNER_ABSENT,
+		     .fm   = 1,
+		     .name = "TEMIC_4049" },
+	[ 0x11 ] = { .id   = TUNER_TEMIC_4136FY5,
+		     .name = "TEMIC_4136" },
+	[ 0x12 ] = { .id   = TUNER_ABSENT,
+		     .name = "TEMIC_4146" },
+
+	[ 0x20 ] = { .id   = TUNER_PHILIPS_FQ1216ME,
+		     .fm   = 1,
+		     .name = "PHILIPS_FQ1216_MK3" },
+	[ 0x21 ] = { .id   = TUNER_ABSENT, .fm = 1,
+		     .name = "PHILIPS_FQ1236_MK3" },
+	[ 0x22 ] = { .id   = TUNER_ABSENT,
+		     .name = "PHILIPS_FI1236_MK3" },
+	[ 0x23 ] = { .id   = TUNER_ABSENT,
+		     .name = "PHILIPS_FI1216_MK3" },
+};
+
+static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+	const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
+		? gdi_tuner[eeprom_data[0x0d]].name : NULL;
+
+	info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown");
+	if (NULL == name)
+		return;
+	core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id;
+	core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ?
+		CX88_RADIO : 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff                                           */
+static int cx88_dvico_xc2028_callback(struct cx88_core *core,
+				      int command, int arg)
+{
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		switch (core->boardnr) {
+		case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+			/* GPIO-4 xc3028 tuner */
+
+			cx_set(MO_GP0_IO, 0x00001000);
+			cx_clear(MO_GP0_IO, 0x00000010);
+			msleep(100);
+			cx_set(MO_GP0_IO, 0x00000010);
+			msleep(100);
+			break;
+		default:
+			cx_write(MO_GP0_IO, 0x101000);
+			mdelay(5);
+			cx_set(MO_GP0_IO, 0x101010);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* some Geniatech specific stuff                                           */
+
+static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core,
+						int command, int mode)
+{
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		switch (INPUT(core->input).type) {
+		case CX88_RADIO:
+			break;
+		case CX88_VMUX_DVB:
+			cx_write(MO_GP1_IO, 0x030302);
+			mdelay(50);
+			break;
+		default:
+			cx_write(MO_GP1_IO, 0x030301);
+			mdelay(50);
+		}
+		cx_write(MO_GP1_IO, 0x101010);
+		mdelay(50);
+		cx_write(MO_GP1_IO, 0x101000);
+		mdelay(50);
+		cx_write(MO_GP1_IO, 0x101010);
+		mdelay(50);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core,
+					     int command, int arg)
+{
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		/* GPIO 12 (xc3028 tuner reset) */
+		cx_set(MO_GP1_IO, 0x1010);
+		mdelay(50);
+		cx_clear(MO_GP1_IO, 0x10);
+		mdelay(75);
+		cx_set(MO_GP1_IO, 0x10);
+		mdelay(75);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
+						  int command, int arg)
+{
+	switch (command) {
+	case XC4000_TUNER_RESET:
+		/* GPIO 12 (xc4000 tuner reset) */
+		cx_set(MO_GP1_IO, 0x1010);
+		mdelay(50);
+		cx_clear(MO_GP1_IO, 0x10);
+		mdelay(75);
+		cx_set(MO_GP1_IO, 0x10);
+		mdelay(75);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff                                           */
+static int cx88_pv_8000gt_callback(struct cx88_core *core,
+				   int command, int arg)
+{
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		cx_write(MO_GP2_IO, 0xcf7);
+		mdelay(50);
+		cx_write(MO_GP2_IO, 0xef5);
+		mdelay(50);
+		cx_write(MO_GP2_IO, 0xcf7);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some DViCO specific stuff                                               */
+
+static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
+{
+	struct i2c_msg msg = { .addr = 0x45, .flags = 0 };
+	int i, err;
+	static u8 init_bufs[13][5] = {
+		{ 0x10, 0x00, 0x20, 0x01, 0x03 },
+		{ 0x10, 0x10, 0x01, 0x00, 0x21 },
+		{ 0x10, 0x10, 0x10, 0x00, 0xCA },
+		{ 0x10, 0x10, 0x12, 0x00, 0x08 },
+		{ 0x10, 0x10, 0x13, 0x00, 0x0A },
+		{ 0x10, 0x10, 0x16, 0x01, 0xC0 },
+		{ 0x10, 0x10, 0x22, 0x01, 0x3D },
+		{ 0x10, 0x10, 0x73, 0x01, 0x2E },
+		{ 0x10, 0x10, 0x72, 0x00, 0xC5 },
+		{ 0x10, 0x10, 0x71, 0x01, 0x97 },
+		{ 0x10, 0x10, 0x70, 0x00, 0x0F },
+		{ 0x10, 0x10, 0xB0, 0x00, 0x01 },
+		{ 0x03, 0x0C },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
+		msg.buf = init_bufs[i];
+		msg.len = (i != 12 ? 5 : 2);
+		err = i2c_transfer(&core->i2c_adap, &msg, 1);
+		if (err != 1) {
+			warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d "
+					  "failed (err = %d)!\n", i, err);
+			return;
+		}
+	}
+}
+
+static int cx88_xc2028_tuner_callback(struct cx88_core *core,
+				      int command, int arg)
+{
+	/* Board-specific callbacks */
+	switch (core->boardnr) {
+	case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+	case CX88_BOARD_GENIATECH_X8000_MT:
+	case CX88_BOARD_KWORLD_ATSC_120:
+		return cx88_xc3028_geniatech_tuner_callback(core,
+							command, arg);
+	case CX88_BOARD_PROLINK_PV_8000GT:
+	case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+		return cx88_pv_8000gt_callback(core, command, arg);
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+		return cx88_dvico_xc2028_callback(core, command, arg);
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+	case CX88_BOARD_WINFAST_DTV1800H:
+		return cx88_xc3028_winfast1800h_callback(core, command, arg);
+	}
+
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		switch (INPUT(core->input).type) {
+		case CX88_RADIO:
+			info_printk(core, "setting GPIO to radio!\n");
+			cx_write(MO_GP0_IO, 0x4ff);
+			mdelay(250);
+			cx_write(MO_GP2_IO, 0xff);
+			mdelay(250);
+			break;
+		case CX88_VMUX_DVB:	/* Digital TV*/
+		default:		/* Analog TV */
+			info_printk(core, "setting GPIO to TV!\n");
+			break;
+		}
+		cx_write(MO_GP1_IO, 0x101010);
+		mdelay(250);
+		cx_write(MO_GP1_IO, 0x101000);
+		mdelay(250);
+		cx_write(MO_GP1_IO, 0x101010);
+		mdelay(250);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int cx88_xc4000_tuner_callback(struct cx88_core *core,
+				      int command, int arg)
+{
+	/* Board-specific callbacks */
+	switch (core->boardnr) {
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+		return cx88_xc4000_winfast2000h_plus_callback(core,
+							      command, arg);
+	}
+	return -EINVAL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* 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)    */
+
+static int cx88_xc5000_tuner_callback(struct cx88_core *core,
+				      int command, int arg)
+{
+	switch (core->boardnr) {
+	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+		if (command == 0) { /* This is the reset command from xc5000 */
+
+			/* djh - According to the engineer at PCTV Systems,
+			   the xc5000 reset pin is supposed to be on GPIO12.
+			   However, despite three nights of effort, pulling
+			   that GPIO low didn't reset the xc5000.  While
+			   pulling MO_SRST_IO low does reset the xc5000, this
+			   also resets in the s5h1409 being reset as well.
+			   This causes tuning to always fail since the internal
+			   state of the s5h1409 does not match the driver's
+			   state.  Given that the only two conditions in which
+			   the driver performs a reset is during firmware load
+			   and powering down the chip, I am taking out the
+			   reset.  We know that the chip is being reset
+			   when the cx88 comes online, and not being able to
+			   do power management for this board is worse than
+			   not having any tuning at all. */
+			return 0;
+		} else {
+			err_printk(core, "xc5000: unknown tuner "
+				   "callback command.\n");
+			return -EINVAL;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+		if (command == 0) { /* This is the reset command from xc5000 */
+			cx_clear(MO_GP0_IO, 0x00000010);
+			msleep(10);
+			cx_set(MO_GP0_IO, 0x00000010);
+			return 0;
+		} else {
+			printk(KERN_ERR
+				"xc5000: unknown tuner callback command.\n");
+			return -EINVAL;
+		}
+		break;
+	}
+	return 0; /* Should never be here */
+}
+
+int cx88_tuner_callback(void *priv, int component, int command, int arg)
+{
+	struct i2c_algo_bit_data *i2c_algo = priv;
+	struct cx88_core *core;
+
+	if (!i2c_algo) {
+		printk(KERN_ERR "cx88: Error - i2c private data undefined.\n");
+		return -EINVAL;
+	}
+
+	core = i2c_algo->data;
+
+	if (!core) {
+		printk(KERN_ERR "cx88: Error - device struct undefined.\n");
+		return -EINVAL;
+	}
+
+	if (component != DVB_FRONTEND_COMPONENT_TUNER)
+		return -EINVAL;
+
+	switch (core->board.tuner_type) {
+		case TUNER_XC2028:
+			info_printk(core, "Calling XC2028/3028 callback\n");
+			return cx88_xc2028_tuner_callback(core, command, arg);
+		case TUNER_XC4000:
+			info_printk(core, "Calling XC4000 callback\n");
+			return cx88_xc4000_tuner_callback(core, command, arg);
+		case TUNER_XC5000:
+			info_printk(core, "Calling XC5000 callback\n");
+			return cx88_xc5000_tuner_callback(core, command, arg);
+	}
+	err_printk(core, "Error: Calling callback for tuner %d\n",
+		   core->board.tuner_type);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
+/* ----------------------------------------------------------------------- */
+
+static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
+{
+	int i;
+
+	if (0 == pci->subsystem_vendor &&
+	    0 == pci->subsystem_device) {
+		printk(KERN_ERR
+		       "%s: Your board has no valid PCI Subsystem ID 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.  Best regards,\n"
+		       "%s:         -- tux\n",
+		       core->name,core->name,core->name,core->name,core->name);
+	} else {
+		printk(KERN_ERR
+		       "%s: Your board isn't known (yet) to the driver.  You can\n"
+		       "%s: try to pick one of the existing card configs via\n"
+		       "%s: card=<n> insmod option.  Updating to the latest\n"
+		       "%s: version might help as well.\n",
+		       core->name,core->name,core->name,core->name);
+	}
+	err_printk(core, "Here is a list of valid choices for the card=<n> "
+		   "insmod option:\n");
+	for (i = 0; i < ARRAY_SIZE(cx88_boards); i++)
+		printk(KERN_ERR "%s:    card=%d -> %s\n",
+		       core->name, i, cx88_boards[i].name);
+}
+
+static void cx88_card_setup_pre_i2c(struct cx88_core *core)
+{
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/*
+		 * Bring the 702 demod up before i2c scanning/attach or devices are hidden
+		 * We leave here with the 702 on the bus
+		 *
+		 * "reset the IR receiver on GPIO[3]"
+		 * Reported by Mike Crash <mike AT mikecrash.com>
+		 */
+		cx_write(MO_GP0_IO, 0x0000ef88);
+		udelay(1000);
+		cx_clear(MO_GP0_IO, 0x00000088);
+		udelay(50);
+		cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
+		udelay(1000);
+		break;
+
+	case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+	case CX88_BOARD_PROLINK_PV_8000GT:
+		cx_write(MO_GP2_IO, 0xcf7);
+		mdelay(50);
+		cx_write(MO_GP2_IO, 0xef5);
+		mdelay(50);
+		cx_write(MO_GP2_IO, 0xcf7);
+		msleep(10);
+		break;
+
+	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+		/* Enable the xc5000 tuner */
+		cx_set(MO_GP0_IO, 0x00001010);
+		break;
+
+	case CX88_BOARD_WINFAST_DTV2000H_J:
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		/* Init GPIO */
+		cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+		udelay(1000);
+		cx_clear(MO_GP0_IO, 0x00000080);
+		udelay(50);
+		cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+		udelay(1000);
+		break;
+
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+	case CX88_BOARD_WINFAST_DTV1800H:
+		cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
+		break;
+
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+		cx88_xc4000_winfast2000h_plus_callback(core,
+						       XC4000_TUNER_RESET, 0);
+		break;
+
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		cx_write(MO_GP0_IO, 0x00003230);
+		cx_write(MO_GP0_IO, 0x00003210);
+		msleep(1);
+		cx_write(MO_GP0_IO, 0x00001230);
+		break;
+	}
+}
+
+/*
+ * Sets board-dependent xc3028 configuration
+ */
+void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
+{
+	memset(ctl, 0, sizeof(*ctl));
+
+	ctl->fname   = XC2028_DEFAULT_FIRMWARE;
+	ctl->max_len = 64;
+
+	switch (core->boardnr) {
+	case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+		/* Now works with firmware version 2.7 */
+		if (core->i2c_algo.udelay < 16)
+			core->i2c_algo.udelay = 16;
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+	case CX88_BOARD_WINFAST_DTV1800H:
+		ctl->demod = XC3028_FE_ZARLINK456;
+		break;
+	case CX88_BOARD_KWORLD_ATSC_120:
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+		ctl->demod = XC3028_FE_OREN538;
+		break;
+	case CX88_BOARD_GENIATECH_X8000_MT:
+		/* FIXME: For this board, the xc3028 never recovers after being
+		   powered down (the reset GPIO probably is not set properly).
+		   We don't have access to the hardware so we cannot determine
+		   which GPIO is used for xc3028, so just disable power xc3028
+		   power management for now */
+		ctl->disable_power_mgmt = 1;
+		break;
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+	case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+	case CX88_BOARD_PROLINK_PV_8000GT:
+		/*
+		 * Those boards uses non-MTS firmware
+		 */
+		break;
+	case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+	case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+		ctl->demod = XC3028_FE_ZARLINK456;
+		ctl->mts = 1;
+		break;
+	default:
+		ctl->demod = XC3028_FE_OREN538;
+		ctl->mts = 1;
+	}
+}
+EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
+
+static void cx88_card_setup(struct cx88_core *core)
+{
+	static u8 eeprom[256];
+	struct tuner_setup tun_setup;
+	unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
+
+	memset(&tun_setup, 0, sizeof(tun_setup));
+
+	if (0 == core->i2c_rc) {
+		core->i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
+	}
+
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE:
+	case CX88_BOARD_HAUPPAUGE_ROSLYN:
+		if (0 == core->i2c_rc)
+			hauppauge_eeprom(core, eeprom+8);
+		break;
+	case CX88_BOARD_GDI:
+		if (0 == core->i2c_rc)
+			gdi_eeprom(core, eeprom);
+		break;
+	case CX88_BOARD_LEADTEK_PVR2000:
+	case CX88_BOARD_WINFAST_DV2000:
+	case CX88_BOARD_WINFAST2000XP_EXPERT:
+		if (0 == core->i2c_rc)
+			leadtek_eeprom(core, eeprom);
+		break;
+	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+	case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+	case CX88_BOARD_HAUPPAUGE_DVB_T1:
+	case CX88_BOARD_HAUPPAUGE_HVR1100:
+	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+	case CX88_BOARD_HAUPPAUGE_IRONLY:
+		if (0 == core->i2c_rc)
+			hauppauge_eeprom(core, eeprom);
+		break;
+	case CX88_BOARD_KWORLD_DVBS_100:
+		cx_write(MO_GP0_IO, 0x000007f8);
+		cx_write(MO_GP1_IO, 0x00000001);
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+		/* GPIO0:0 is hooked to demod reset */
+		/* GPIO0:4 is hooked to xc3028 reset */
+		cx_write(MO_GP0_IO, 0x00111100);
+		msleep(1);
+		cx_write(MO_GP0_IO, 0x00111111);
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+		/* GPIO0:6 is hooked to FX2 reset pin */
+		cx_set(MO_GP0_IO, 0x00004040);
+		cx_clear(MO_GP0_IO, 0x00000040);
+		msleep(1000);
+		cx_set(MO_GP0_IO, 0x00004040);
+		/* FALLTHROUGH */
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+		/* GPIO0:0 is hooked to mt352 reset pin */
+		cx_set(MO_GP0_IO, 0x00000101);
+		cx_clear(MO_GP0_IO, 0x00000001);
+		msleep(1);
+		cx_set(MO_GP0_IO, 0x00000101);
+		if (0 == core->i2c_rc &&
+		    core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID)
+			dvico_fusionhdtv_hybrid_init(core);
+		break;
+	case CX88_BOARD_KWORLD_DVB_T:
+	case CX88_BOARD_DNTV_LIVE_DVB_T:
+		cx_set(MO_GP0_IO, 0x00000707);
+		cx_set(MO_GP2_IO, 0x00000101);
+		cx_clear(MO_GP2_IO, 0x00000001);
+		msleep(1);
+		cx_clear(MO_GP0_IO, 0x00000007);
+		cx_set(MO_GP2_IO, 0x00000101);
+		break;
+	case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+		cx_write(MO_GP0_IO, 0x00080808);
+		break;
+	case CX88_BOARD_ATI_HDTVWONDER:
+		if (0 == core->i2c_rc) {
+			/* enable tuner */
+			int i;
+			static const u8 buffer [][2] = {
+				{0x10,0x12},
+				{0x13,0x04},
+				{0x16,0x00},
+				{0x14,0x04},
+				{0x17,0x00}
+			};
+			core->i2c_client.addr = 0x0a;
+
+			for (i = 0; i < ARRAY_SIZE(buffer); i++)
+				if (2 != i2c_master_send(&core->i2c_client,
+							buffer[i],2))
+					warn_printk(core, "Unable to enable "
+						    "tuner(%i).\n", 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;
+
+		call_all(core, tuner, s_config, &tea5767_cfg);
+		break;
+	}
+	case  CX88_BOARD_TEVII_S420:
+	case  CX88_BOARD_TEVII_S460:
+	case  CX88_BOARD_TEVII_S464:
+	case  CX88_BOARD_OMICOM_SS4_PCI:
+	case  CX88_BOARD_TBS_8910:
+	case  CX88_BOARD_TBS_8920:
+	case  CX88_BOARD_PROF_6200:
+	case  CX88_BOARD_PROF_7300:
+	case  CX88_BOARD_PROF_7301:
+	case  CX88_BOARD_SATTRADE_ST4200:
+		cx_write(MO_GP0_IO, 0x8000);
+		msleep(100);
+		cx_write(MO_SRST_IO, 0);
+		msleep(10);
+		cx_write(MO_GP0_IO, 0x8080);
+		msleep(100);
+		cx_write(MO_SRST_IO, 1);
+		msleep(100);
+		break;
+	} /*end switch() */
+
+
+	/* Setup tuners */
+	if ((core->board.radio_type != UNSET)) {
+		tun_setup.mode_mask      = T_RADIO;
+		tun_setup.type           = core->board.radio_type;
+		tun_setup.addr           = core->board.radio_addr;
+		tun_setup.tuner_callback = cx88_tuner_callback;
+		call_all(core, tuner, s_type_addr, &tun_setup);
+		mode_mask &= ~T_RADIO;
+	}
+
+	if (core->board.tuner_type != TUNER_ABSENT) {
+		tun_setup.mode_mask      = mode_mask;
+		tun_setup.type           = core->board.tuner_type;
+		tun_setup.addr           = core->board.tuner_addr;
+		tun_setup.tuner_callback = cx88_tuner_callback;
+
+		call_all(core, tuner, s_type_addr, &tun_setup);
+	}
+
+	if (core->board.tda9887_conf) {
+		struct v4l2_priv_tun_config tda9887_cfg;
+
+		tda9887_cfg.tuner = TUNER_TDA9887;
+		tda9887_cfg.priv  = &core->board.tda9887_conf;
+
+		call_all(core, tuner, s_config, &tda9887_cfg);
+	}
+
+	if (core->board.tuner_type == TUNER_XC2028) {
+		struct v4l2_priv_tun_config  xc2028_cfg;
+		struct xc2028_ctrl           ctl;
+
+		/* Fills device-dependent initialization parameters */
+		cx88_setup_xc3028(core, &ctl);
+
+		/* Sends parameters to xc2028/3028 tuner */
+		memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+		xc2028_cfg.tuner = TUNER_XC2028;
+		xc2028_cfg.priv  = &ctl;
+		info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
+			    ctl.fname);
+		call_all(core, tuner, s_config, &xc2028_cfg);
+	}
+	call_all(core, core, s_power, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_pci_quirks(const char *name, struct pci_dev *pci)
+{
+	unsigned int lat = UNSET;
+	u8 ctrl = 0;
+	u8 value;
+
+	/* check pci quirks */
+	if (pci_pci_problems & PCIPCI_TRITON) {
+		printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n",
+		       name);
+		ctrl |= CX88X_EN_TBFX;
+	}
+	if (pci_pci_problems & PCIPCI_NATOMA) {
+		printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n",
+		       name);
+		ctrl |= CX88X_EN_TBFX;
+	}
+	if (pci_pci_problems & PCIPCI_VIAETBF) {
+		printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n",
+		       name);
+		ctrl |= CX88X_EN_TBFX;
+	}
+	if (pci_pci_problems & PCIPCI_VSFX) {
+		printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n",
+		       name);
+		ctrl |= CX88X_EN_VSFX;
+	}
+#ifdef PCIPCI_ALIMAGIK
+	if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+		printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+		       name);
+		lat = 0x0A;
+	}
+#endif
+
+	/* check insmod options */
+	if (UNSET != latency)
+		lat = latency;
+
+	/* apply stuff */
+	if (ctrl) {
+		pci_read_config_byte(pci, CX88X_DEVCTRL, &value);
+		value |= ctrl;
+		pci_write_config_byte(pci, CX88X_DEVCTRL, value);
+	}
+	if (UNSET != lat) {
+		printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+		       name, latency);
+		pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency);
+	}
+	return 0;
+}
+
+int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci)
+{
+	if (request_mem_region(pci_resource_start(pci,0),
+			       pci_resource_len(pci,0),
+			       core->name))
+		return 0;
+	printk(KERN_ERR
+	       "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n",
+	       core->name, PCI_FUNC(pci->devfn),
+	       (unsigned long long)pci_resource_start(pci, 0),
+	       pci->subsystem_vendor, pci->subsystem_device);
+	return -EBUSY;
+}
+
+/* Allocate and initialize the cx88 core struct.  One should hold the
+ * devlist mutex before calling this.  */
+struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+{
+	struct cx88_core *core;
+	int i;
+
+	core = kzalloc(sizeof(*core), GFP_KERNEL);
+	if (core == NULL)
+		return NULL;
+
+	atomic_inc(&core->refcount);
+	core->pci_bus  = pci->bus->number;
+	core->pci_slot = PCI_SLOT(pci->devfn);
+	core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT |
+			    PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT |
+			    PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT;
+	mutex_init(&core->lock);
+
+	core->nr = nr;
+	sprintf(core->name, "cx88[%d]", core->nr);
+
+	strcpy(core->v4l2_dev.name, core->name);
+	if (v4l2_device_register(NULL, &core->v4l2_dev)) {
+		kfree(core);
+		return NULL;
+	}
+
+	if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) {
+		v4l2_device_unregister(&core->v4l2_dev);
+		kfree(core);
+		return NULL;
+	}
+
+	if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) {
+		v4l2_ctrl_handler_free(&core->video_hdl);
+		v4l2_device_unregister(&core->v4l2_dev);
+		kfree(core);
+		return NULL;
+	}
+
+	if (0 != cx88_get_resources(core, pci)) {
+		v4l2_ctrl_handler_free(&core->video_hdl);
+		v4l2_ctrl_handler_free(&core->audio_hdl);
+		v4l2_device_unregister(&core->v4l2_dev);
+		kfree(core);
+		return NULL;
+	}
+
+	/* PCI stuff */
+	cx88_pci_quirks(core->name, pci);
+	core->lmmio = ioremap(pci_resource_start(pci, 0),
+			      pci_resource_len(pci, 0));
+	core->bmmio = (u8 __iomem *)core->lmmio;
+
+	if (core->lmmio == NULL) {
+		release_mem_region(pci_resource_start(pci, 0),
+			   pci_resource_len(pci, 0));
+		v4l2_ctrl_handler_free(&core->video_hdl);
+		v4l2_ctrl_handler_free(&core->audio_hdl);
+		v4l2_device_unregister(&core->v4l2_dev);
+		kfree(core);
+		return NULL;
+	}
+
+	/* board config */
+	core->boardnr = UNSET;
+	if (card[core->nr] < ARRAY_SIZE(cx88_boards))
+		core->boardnr = card[core->nr];
+	for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++)
+		if (pci->subsystem_vendor == cx88_subids[i].subvendor &&
+		    pci->subsystem_device == cx88_subids[i].subdevice)
+			core->boardnr = cx88_subids[i].card;
+	if (UNSET == core->boardnr) {
+		core->boardnr = CX88_BOARD_UNKNOWN;
+		cx88_card_list(core, pci);
+	}
+
+	memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board));
+
+	if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB))
+		core->board.num_frontends = 1;
+
+	info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n",
+		pci->subsystem_vendor, pci->subsystem_device, core->board.name,
+		core->boardnr, card[core->nr] == core->boardnr ?
+		"insmod option" : "autodetected",
+		core->board.num_frontends);
+
+	if (tuner[core->nr] != UNSET)
+		core->board.tuner_type = tuner[core->nr];
+	if (radio[core->nr] != UNSET)
+		core->board.radio_type = radio[core->nr];
+
+	info_printk(core, "TV tuner type %d, Radio tuner type %d\n",
+		    core->board.tuner_type, core->board.radio_type);
+
+	/* init hardware */
+	cx88_reset(core);
+	cx88_card_setup_pre_i2c(core);
+	cx88_i2c_init(core, pci);
+
+	/* load tuner module, if needed */
+	if (TUNER_ABSENT != core->board.tuner_type) {
+		/* 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. */
+		static const unsigned short tv_addrs[] = {
+			0x42, 0x43, 0x4a, 0x4b,		/* tda8290 */
+			0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+			0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e,
+			I2C_CLIENT_END
+		};
+		int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT);
+
+		/* I don't trust the radio_type as is stored in the card
+		   definitions, so we just probe for it.
+		   The radio_type is sometimes missing, or set to UNSET but
+		   later code configures a tea5767.
+		 */
+		v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+				"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+		if (has_demod)
+			v4l2_i2c_new_subdev(&core->v4l2_dev,
+				&core->i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+		if (core->board.tuner_addr == ADDR_UNSET) {
+			v4l2_i2c_new_subdev(&core->v4l2_dev,
+				&core->i2c_adap, "tuner",
+				0, has_demod ? tv_addrs + 4 : tv_addrs);
+		} else {
+			v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+				"tuner", core->board.tuner_addr, NULL);
+		}
+	}
+
+	cx88_card_setup(core);
+	if (!disable_ir) {
+		cx88_i2c_init_ir(core);
+		cx88_ir_init(core, pci);
+	}
+
+	return core;
+}
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
new file mode 100644
index 000000000000..c97b174be3ab
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -0,0 +1,1131 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * driver core
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *     - Multituner support
+ *     - video_ioctl2 conversion
+ *     - PAL/M fixes
+ *
+ *  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/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int core_debug;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int nicam;
+module_param(nicam,int,0644);
+MODULE_PARM_DESC(nicam,"tv audio is nicam");
+
+static unsigned int nocomb;
+module_param(nocomb,int,0644);
+MODULE_PARM_DESC(nocomb,"disable comb filter");
+
+#define dprintk(level,fmt, arg...)	if (core_debug >= level)	\
+	printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
+static unsigned int cx88_devcount;
+static LIST_HEAD(cx88_devlist);
+static DEFINE_MUTEX(devlist);
+
+#define NO_SYNC_LINE (-1U)
+
+/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
+	 generated _after_ lpi lines are transferred. */
+static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
+			    unsigned int offset, u32 sync_line,
+			    unsigned int bpl, unsigned int padding,
+			    unsigned int lines, unsigned int lpi)
+{
+	struct scatterlist *sg;
+	unsigned int line,todo,sol;
+
+	/* sync instruction */
+	if (sync_line != NO_SYNC_LINE)
+		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < lines; line++) {
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+		if (lpi && line>0 && !(line % lpi))
+			sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+		else
+			sol = RISC_SOL;
+		if (bpl <= sg_dma_len(sg)-offset) {
+			/* fits into current chunk */
+			*(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
+			*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+			offset+=bpl;
+		} else {
+			/* scanline needs to be split */
+			todo = bpl;
+			*(rp++)=cpu_to_le32(RISC_WRITE|sol|
+					    (sg_dma_len(sg)-offset));
+			*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+			todo -= (sg_dma_len(sg)-offset);
+			offset = 0;
+			sg++;
+			while (todo > sg_dma_len(sg)) {
+				*(rp++)=cpu_to_le32(RISC_WRITE|
+						    sg_dma_len(sg));
+				*(rp++)=cpu_to_le32(sg_dma_address(sg));
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+			*(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
+			*(rp++)=cpu_to_le32(sg_dma_address(sg));
+			offset += todo;
+		}
+		offset += padding;
+	}
+
+	return rp;
+}
+
+int cx88_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)
+{
+	u32 instructions,fields;
+	__le32 *rp;
+	int rc;
+
+	fields = 0;
+	if (UNSET != top_offset)
+		fields++;
+	if (UNSET != bottom_offset)
+		fields++;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Padding
+	   can cause next bpl to start close to a page border.  First DMA
+	   region may be smaller than PAGE_SIZE */
+	instructions  = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
+	instructions += 2;
+	if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	if (UNSET != top_offset)
+		rp = cx88_risc_field(rp, sglist, top_offset, 0,
+				     bpl, padding, lines, 0);
+	if (UNSET != bottom_offset)
+		rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
+				     bpl, padding, lines, 0);
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+	return 0;
+}
+
+int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+			 struct scatterlist *sglist, unsigned int bpl,
+			 unsigned int lines, unsigned int lpi)
+{
+	u32 instructions;
+	__le32 *rp;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + syncs + jump (all 2 dwords).  Here
+	   there is no padding and no sync.  First DMA region may be smaller
+	   than PAGE_SIZE */
+	instructions  = 1 + (bpl * lines) / PAGE_SIZE + lines;
+	instructions += 1;
+	if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+	return 0;
+}
+
+int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+		      u32 reg, u32 mask, u32 value)
+{
+	__le32 *rp;
+	int rc;
+
+	if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
+		return rc;
+
+	/* write risc instructions */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(RISC_WRITECR  | RISC_IRQ2 | RISC_IMM);
+	*(rp++) = cpu_to_le32(reg);
+	*(rp++) = cpu_to_le32(value);
+	*(rp++) = cpu_to_le32(mask);
+	*(rp++) = cpu_to_le32(RISC_JUMP);
+	*(rp++) = cpu_to_le32(risc->dma);
+	return 0;
+}
+
+void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
+{
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+	BUG_ON(in_interrupt());
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_dma_unmap(q->dev, dma);
+	videobuf_dma_free(dma);
+	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+/* our SRAM memory layout                                             */
+
+/* we are going to put all thr risc programs into host memory, so we
+ * can use the whole SDRAM for the DMA fifos.  To simplify things, we
+ * use a static memory layout.  That surely will waste memory in case
+ * we don't use all DMA channels at the same time (which will be the
+ * case most of the time).  But that still gives us enough FIFO space
+ * to be able to deal with insane long pci latencies ...
+ *
+ * FIFO space allocations:
+ *    channel  21    (y video)  - 10.0k
+ *    channel  22    (u video)  -  2.0k
+ *    channel  23    (v video)  -  2.0k
+ *    channel  24    (vbi)      -  4.0k
+ *    channels 25+26 (audio)    -  4.0k
+ *    channel  28    (mpeg)     -  4.0k
+ *    channel  27    (audio rds)-  3.0k
+ *    TOTAL                     = 29.0k
+ *
+ * Every channel has 160 bytes control data (64 bytes instruction
+ * queue and 6 CDT entries), which is close to 2k total.
+ *
+ * Address layout:
+ *    0x0000 - 0x03ff    CMDs / reserved
+ *    0x0400 - 0x0bff    instruction queues + CDs
+ *    0x0c00 -           FIFOs
+ */
+
+const struct sram_channel cx88_sram_channels[] = {
+	[SRAM_CH21] = {
+		.name       = "video y / packed",
+		.cmds_start = 0x180040,
+		.ctrl_start = 0x180400,
+		.cdt        = 0x180400 + 64,
+		.fifo_start = 0x180c00,
+		.fifo_size  = 0x002800,
+		.ptr1_reg   = MO_DMA21_PTR1,
+		.ptr2_reg   = MO_DMA21_PTR2,
+		.cnt1_reg   = MO_DMA21_CNT1,
+		.cnt2_reg   = MO_DMA21_CNT2,
+	},
+	[SRAM_CH22] = {
+		.name       = "video u",
+		.cmds_start = 0x180080,
+		.ctrl_start = 0x1804a0,
+		.cdt        = 0x1804a0 + 64,
+		.fifo_start = 0x183400,
+		.fifo_size  = 0x000800,
+		.ptr1_reg   = MO_DMA22_PTR1,
+		.ptr2_reg   = MO_DMA22_PTR2,
+		.cnt1_reg   = MO_DMA22_CNT1,
+		.cnt2_reg   = MO_DMA22_CNT2,
+	},
+	[SRAM_CH23] = {
+		.name       = "video v",
+		.cmds_start = 0x1800c0,
+		.ctrl_start = 0x180540,
+		.cdt        = 0x180540 + 64,
+		.fifo_start = 0x183c00,
+		.fifo_size  = 0x000800,
+		.ptr1_reg   = MO_DMA23_PTR1,
+		.ptr2_reg   = MO_DMA23_PTR2,
+		.cnt1_reg   = MO_DMA23_CNT1,
+		.cnt2_reg   = MO_DMA23_CNT2,
+	},
+	[SRAM_CH24] = {
+		.name       = "vbi",
+		.cmds_start = 0x180100,
+		.ctrl_start = 0x1805e0,
+		.cdt        = 0x1805e0 + 64,
+		.fifo_start = 0x184400,
+		.fifo_size  = 0x001000,
+		.ptr1_reg   = MO_DMA24_PTR1,
+		.ptr2_reg   = MO_DMA24_PTR2,
+		.cnt1_reg   = MO_DMA24_CNT1,
+		.cnt2_reg   = MO_DMA24_CNT2,
+	},
+	[SRAM_CH25] = {
+		.name       = "audio from",
+		.cmds_start = 0x180140,
+		.ctrl_start = 0x180680,
+		.cdt        = 0x180680 + 64,
+		.fifo_start = 0x185400,
+		.fifo_size  = 0x001000,
+		.ptr1_reg   = MO_DMA25_PTR1,
+		.ptr2_reg   = MO_DMA25_PTR2,
+		.cnt1_reg   = MO_DMA25_CNT1,
+		.cnt2_reg   = MO_DMA25_CNT2,
+	},
+	[SRAM_CH26] = {
+		.name       = "audio to",
+		.cmds_start = 0x180180,
+		.ctrl_start = 0x180720,
+		.cdt        = 0x180680 + 64,  /* same as audio IN */
+		.fifo_start = 0x185400,       /* same as audio IN */
+		.fifo_size  = 0x001000,       /* same as audio IN */
+		.ptr1_reg   = MO_DMA26_PTR1,
+		.ptr2_reg   = MO_DMA26_PTR2,
+		.cnt1_reg   = MO_DMA26_CNT1,
+		.cnt2_reg   = MO_DMA26_CNT2,
+	},
+	[SRAM_CH28] = {
+		.name       = "mpeg",
+		.cmds_start = 0x180200,
+		.ctrl_start = 0x1807C0,
+		.cdt        = 0x1807C0 + 64,
+		.fifo_start = 0x186400,
+		.fifo_size  = 0x001000,
+		.ptr1_reg   = MO_DMA28_PTR1,
+		.ptr2_reg   = MO_DMA28_PTR2,
+		.cnt1_reg   = MO_DMA28_CNT1,
+		.cnt2_reg   = MO_DMA28_CNT2,
+	},
+	[SRAM_CH27] = {
+		.name       = "audio rds",
+		.cmds_start = 0x1801C0,
+		.ctrl_start = 0x180860,
+		.cdt        = 0x180860 + 64,
+		.fifo_start = 0x187400,
+		.fifo_size  = 0x000C00,
+		.ptr1_reg   = MO_DMA27_PTR1,
+		.ptr2_reg   = MO_DMA27_PTR2,
+		.cnt1_reg   = MO_DMA27_CNT1,
+		.cnt2_reg   = MO_DMA27_CNT2,
+	},
+};
+
+int cx88_sram_channel_setup(struct cx88_core *core,
+			    const struct sram_channel *ch,
+			    unsigned int bpl, u32 risc)
+{
+	unsigned int i,lines;
+	u32 cdt;
+
+	bpl   = (bpl + 7) & ~7; /* alignment */
+	cdt   = ch->cdt;
+	lines = ch->fifo_size / bpl;
+	if (lines > 6)
+		lines = 6;
+	BUG_ON(lines < 2);
+
+	/* write CDT */
+	for (i = 0; i < lines; i++)
+		cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
+
+	/* write CMDS */
+	cx_write(ch->cmds_start +  0, risc);
+	cx_write(ch->cmds_start +  4, cdt);
+	cx_write(ch->cmds_start +  8, (lines*16) >> 3);
+	cx_write(ch->cmds_start + 12, ch->ctrl_start);
+	cx_write(ch->cmds_start + 16, 64 >> 2);
+	for (i = 20; i < 64; i += 4)
+		cx_write(ch->cmds_start + i, 0);
+
+	/* fill registers */
+	cx_write(ch->ptr1_reg, ch->fifo_start);
+	cx_write(ch->ptr2_reg, cdt);
+	cx_write(ch->cnt1_reg, (bpl >> 3) -1);
+	cx_write(ch->cnt2_reg, (lines*16) >> 3);
+
+	dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* debug helper code                                                  */
+
+static int cx88_risc_decode(u32 risc)
+{
+	static const char * const instr[16] = {
+		[ RISC_SYNC    >> 28 ] = "sync",
+		[ RISC_WRITE   >> 28 ] = "write",
+		[ RISC_WRITEC  >> 28 ] = "writec",
+		[ RISC_READ    >> 28 ] = "read",
+		[ RISC_READC   >> 28 ] = "readc",
+		[ RISC_JUMP    >> 28 ] = "jump",
+		[ RISC_SKIP    >> 28 ] = "skip",
+		[ RISC_WRITERM >> 28 ] = "writerm",
+		[ RISC_WRITECM >> 28 ] = "writecm",
+		[ RISC_WRITECR >> 28 ] = "writecr",
+	};
+	static int const incr[16] = {
+		[ RISC_WRITE   >> 28 ] = 2,
+		[ RISC_JUMP    >> 28 ] = 2,
+		[ RISC_WRITERM >> 28 ] = 3,
+		[ RISC_WRITECM >> 28 ] = 3,
+		[ RISC_WRITECR >> 28 ] = 4,
+	};
+	static const char * const bits[] = {
+		"12",   "13",   "14",   "resync",
+		"cnt0", "cnt1", "18",   "19",
+		"20",   "21",   "22",   "23",
+		"irq1", "irq2", "eol",  "sol",
+	};
+	int i;
+
+	printk("0x%08x [ %s", risc,
+	       instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+	for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+		if (risc & (1 << (i + 12)))
+			printk(" %s",bits[i]);
+	printk(" count=%d ]\n", risc & 0xfff);
+	return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+
+void cx88_sram_channel_dump(struct cx88_core *core,
+			    const struct sram_channel *ch)
+{
+	static const char * const name[] = {
+		"initial risc",
+		"cdt base",
+		"cdt size",
+		"iq base",
+		"iq size",
+		"risc pc",
+		"iq wr ptr",
+		"iq rd ptr",
+		"cdt current",
+		"pci target",
+		"line / byte",
+	};
+	u32 risc;
+	unsigned int i,j,n;
+
+	printk("%s: %s - dma channel status dump\n",
+	       core->name,ch->name);
+	for (i = 0; i < ARRAY_SIZE(name); i++)
+		printk("%s:   cmds: %-12s: 0x%08x\n",
+		       core->name,name[i],
+		       cx_read(ch->cmds_start + 4*i));
+	for (n = 1, i = 0; i < 4; i++) {
+		risc = cx_read(ch->cmds_start + 4 * (i+11));
+		printk("%s:   risc%d: ", core->name, i);
+		if (--n)
+			printk("0x%08x [ arg #%d ]\n", risc, n);
+		else
+			n = cx88_risc_decode(risc);
+	}
+	for (i = 0; i < 16; i += n) {
+		risc = cx_read(ch->ctrl_start + 4 * i);
+		printk("%s:   iq %x: ", core->name, i);
+		n = cx88_risc_decode(risc);
+		for (j = 1; j < n; j++) {
+			risc = cx_read(ch->ctrl_start + 4 * (i+j));
+			printk("%s:   iq %x: 0x%08x [ arg #%d ]\n",
+			       core->name, i+j, risc, j);
+		}
+	}
+
+	printk("%s: fifo: 0x%08x -> 0x%x\n",
+	       core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
+	printk("%s: ctrl: 0x%08x -> 0x%x\n",
+	       core->name, ch->ctrl_start, ch->ctrl_start+6*16);
+	printk("%s:   ptr1_reg: 0x%08x\n",
+	       core->name,cx_read(ch->ptr1_reg));
+	printk("%s:   ptr2_reg: 0x%08x\n",
+	       core->name,cx_read(ch->ptr2_reg));
+	printk("%s:   cnt1_reg: 0x%08x\n",
+	       core->name,cx_read(ch->cnt1_reg));
+	printk("%s:   cnt2_reg: 0x%08x\n",
+	       core->name,cx_read(ch->cnt2_reg));
+}
+
+static const char *cx88_pci_irqs[32] = {
+	"vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
+	"src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
+	"brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
+	"i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
+};
+
+void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+			int len, u32 bits, u32 mask)
+{
+	unsigned int i;
+
+	printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
+	for (i = 0; i < len; i++) {
+		if (!(bits & (1 << i)))
+			continue;
+		if (strings[i])
+			printk(" %s", strings[i]);
+		else
+			printk(" %d", i);
+		if (!(mask & (1 << i)))
+			continue;
+		printk("*");
+	}
+	printk("\n");
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_core_irq(struct cx88_core *core, u32 status)
+{
+	int handled = 0;
+
+	if (status & PCI_INT_IR_SMPINT) {
+		cx88_ir_irq(core);
+		handled++;
+	}
+	if (!handled)
+		cx88_print_irqbits(core->name, "irq pci",
+				   cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
+				   status, core->pci_irqmask);
+	return handled;
+}
+
+void cx88_wakeup(struct cx88_core *core,
+		 struct cx88_dmaqueue *q, u32 count)
+{
+	struct cx88_buffer *buf;
+	int bc;
+
+	for (bc = 0;; bc++) {
+		if (list_empty(&q->active))
+			break;
+		buf = list_entry(q->active.next,
+				 struct cx88_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)
+		dprintk(2, "%s: %d buffers handled (should be 1)\n",
+			__func__, bc);
+}
+
+void cx88_shutdown(struct cx88_core *core)
+{
+	/* disable RISC controller + IRQs */
+	cx_write(MO_DEV_CNTRL2, 0);
+
+	/* stop dma transfers */
+	cx_write(MO_VID_DMACNTRL, 0x0);
+	cx_write(MO_AUD_DMACNTRL, 0x0);
+	cx_write(MO_TS_DMACNTRL, 0x0);
+	cx_write(MO_VIP_DMACNTRL, 0x0);
+	cx_write(MO_GPHST_DMACNTRL, 0x0);
+
+	/* stop interrupts */
+	cx_write(MO_PCI_INTMSK, 0x0);
+	cx_write(MO_VID_INTMSK, 0x0);
+	cx_write(MO_AUD_INTMSK, 0x0);
+	cx_write(MO_TS_INTMSK, 0x0);
+	cx_write(MO_VIP_INTMSK, 0x0);
+	cx_write(MO_GPHST_INTMSK, 0x0);
+
+	/* stop capturing */
+	cx_write(VID_CAPTURE_CONTROL, 0);
+}
+
+int cx88_reset(struct cx88_core *core)
+{
+	dprintk(1,"%s\n",__func__);
+	cx88_shutdown(core);
+
+	/* clear irq status */
+	cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+	cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+	cx_write(MO_INT1_STAT,   0xFFFFFFFF); // Clear RISC int
+
+	/* wait a bit */
+	msleep(100);
+
+	/* init sram */
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
+
+	/* misc init ... */
+	cx_write(MO_INPUT_FORMAT, ((1 << 13) |   // agc enable
+				   (1 << 12) |   // agc gain
+				   (1 << 11) |   // adaptibe agc
+				   (0 << 10) |   // chroma agc
+				   (0 <<  9) |   // ckillen
+				   (7)));
+
+	/* setup image format */
+	cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
+
+	/* setup FIFO Thresholds */
+	cx_write(MO_PDMA_STHRSH,   0x0807);
+	cx_write(MO_PDMA_DTHRSH,   0x0807);
+
+	/* fixes flashing of image */
+	cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
+	cx_write(MO_AGC_BACK_VBI,  0x00E00555);
+
+	cx_write(MO_VID_INTSTAT,   0xFFFFFFFF); // Clear PIV int
+	cx_write(MO_PCI_INTSTAT,   0xFFFFFFFF); // Clear PCI int
+	cx_write(MO_INT1_STAT,     0xFFFFFFFF); // Clear RISC int
+
+	/* Reset on-board parts */
+	cx_write(MO_SRST_IO, 0);
+	msleep(10);
+	cx_write(MO_SRST_IO, 1);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int inline norm_swidth(v4l2_std_id norm)
+{
+	return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+static unsigned int inline norm_hdelay(v4l2_std_id norm)
+{
+	return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
+}
+
+static unsigned int inline norm_vdelay(v4l2_std_id norm)
+{
+	return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
+}
+
+static unsigned int inline norm_fsc8(v4l2_std_id norm)
+{
+	if (norm & V4L2_STD_PAL_M)
+		return 28604892;      // 3.575611 MHz
+
+	if (norm & (V4L2_STD_PAL_Nc))
+		return 28656448;      // 3.582056 MHz
+
+	if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
+		return 28636360;      // 3.57954545 MHz +/- 10 Hz
+
+	/* SECAM have also different sub carrier for chroma,
+	   but step_db and step_dr, at cx88_set_tvnorm already handles that.
+
+	   The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
+	 */
+
+	return 35468950;      // 4.43361875 MHz +/- 5 Hz
+}
+
+static unsigned int inline norm_htotal(v4l2_std_id norm)
+{
+
+	unsigned int fsc4=norm_fsc8(norm)/2;
+
+	/* returns 4*FSC / vtotal / frames per seconds */
+	return (norm & V4L2_STD_625_50) ?
+				((fsc4+312)/625+12)/25 :
+				((fsc4+262)/525*1001+15000)/30000;
+}
+
+static unsigned int inline norm_vbipack(v4l2_std_id norm)
+{
+	return (norm & V4L2_STD_625_50) ? 511 : 400;
+}
+
+int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
+		   enum v4l2_field field)
+{
+	unsigned int swidth  = norm_swidth(core->tvnorm);
+	unsigned int sheight = norm_maxh(core->tvnorm);
+	u32 value;
+
+	dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
+		V4L2_FIELD_HAS_TOP(field)    ? "T" : "",
+		V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
+		v4l2_norm_to_name(core->tvnorm));
+	if (!V4L2_FIELD_HAS_BOTH(field))
+		height *= 2;
+
+	// recalc H delay and scale registers
+	value = (width * norm_hdelay(core->tvnorm)) / swidth;
+	value &= 0x3fe;
+	cx_write(MO_HDELAY_EVEN,  value);
+	cx_write(MO_HDELAY_ODD,   value);
+	dprintk(1,"set_scale: hdelay  0x%04x (width %d)\n", value,swidth);
+
+	value = (swidth * 4096 / width) - 4096;
+	cx_write(MO_HSCALE_EVEN,  value);
+	cx_write(MO_HSCALE_ODD,   value);
+	dprintk(1,"set_scale: hscale  0x%04x\n", value);
+
+	cx_write(MO_HACTIVE_EVEN, width);
+	cx_write(MO_HACTIVE_ODD,  width);
+	dprintk(1,"set_scale: hactive 0x%04x\n", width);
+
+	// recalc V scale Register (delay is constant)
+	cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
+	cx_write(MO_VDELAY_ODD,  norm_vdelay(core->tvnorm));
+	dprintk(1,"set_scale: vdelay  0x%04x\n", norm_vdelay(core->tvnorm));
+
+	value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
+	cx_write(MO_VSCALE_EVEN,  value);
+	cx_write(MO_VSCALE_ODD,   value);
+	dprintk(1,"set_scale: vscale  0x%04x\n", value);
+
+	cx_write(MO_VACTIVE_EVEN, sheight);
+	cx_write(MO_VACTIVE_ODD,  sheight);
+	dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
+
+	// setup filters
+	value = 0;
+	value |= (1 << 19);        // CFILT (default)
+	if (core->tvnorm & V4L2_STD_SECAM) {
+		value |= (1 << 15);
+		value |= (1 << 16);
+	}
+	if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
+		value |= (1 << 13) | (1 << 5);
+	if (V4L2_FIELD_INTERLACED == field)
+		value |= (1 << 3); // VINT (interlaced vertical scaling)
+	if (width < 385)
+		value |= (1 << 0); // 3-tap interpolation
+	if (width < 193)
+		value |= (1 << 1); // 5-tap interpolation
+	if (nocomb)
+		value |= (3 << 5); // disable comb filter
+
+	cx_andor(MO_FILTER_EVEN,  0x7ffc7f, value); /* preserve PEAKEN, PSEL */
+	cx_andor(MO_FILTER_ODD,   0x7ffc7f, value);
+	dprintk(1,"set_scale: filter  0x%04x\n", value);
+
+	return 0;
+}
+
+static const u32 xtal = 28636363;
+
+static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
+{
+	static const u32 pre[] = { 0, 0, 0, 3, 2, 1 };
+	u64 pll;
+	u32 reg;
+	int i;
+
+	if (prescale < 2)
+		prescale = 2;
+	if (prescale > 5)
+		prescale = 5;
+
+	pll = ofreq * 8 * prescale * (u64)(1 << 20);
+	do_div(pll,xtal);
+	reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
+	if (((reg >> 20) & 0x3f) < 14) {
+		printk("%s/0: pll out of range\n",core->name);
+		return -1;
+	}
+
+	dprintk(1,"set_pll:    MO_PLL_REG       0x%08x [old=0x%08x,freq=%d]\n",
+		reg, cx_read(MO_PLL_REG), ofreq);
+	cx_write(MO_PLL_REG, reg);
+	for (i = 0; i < 100; i++) {
+		reg = cx_read(MO_DEVICE_STATUS);
+		if (reg & (1<<2)) {
+			dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
+				prescale,ofreq);
+			return 0;
+		}
+		dprintk(1,"pll not locked yet, waiting ...\n");
+		msleep(10);
+	}
+	dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
+	return -1;
+}
+
+int cx88_start_audio_dma(struct cx88_core *core)
+{
+	/* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
+	int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
+
+	int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
+
+	/* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+	if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+		return 0;
+
+	/* setup fifo + format */
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
+				rds_bpl, 0);
+
+	cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
+	cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
+
+	/* enable Up, Down and Audio RDS fifo */
+	cx_write(MO_AUD_DMACNTRL, 0x0007);
+
+	return 0;
+}
+
+int cx88_stop_audio_dma(struct cx88_core *core)
+{
+	/* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+	if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+		return 0;
+
+	/* stop dma */
+	cx_write(MO_AUD_DMACNTRL, 0x0000);
+
+	return 0;
+}
+
+static int set_tvaudio(struct cx88_core *core)
+{
+	v4l2_std_id norm = core->tvnorm;
+
+	if (CX88_VMUX_TELEVISION != INPUT(core->input).type &&
+	    CX88_VMUX_CABLE != INPUT(core->input).type)
+		return 0;
+
+	if (V4L2_STD_PAL_BG & norm) {
+		core->tvaudio = WW_BG;
+
+	} else if (V4L2_STD_PAL_DK & norm) {
+		core->tvaudio = WW_DK;
+
+	} else if (V4L2_STD_PAL_I & norm) {
+		core->tvaudio = WW_I;
+
+	} else if (V4L2_STD_SECAM_L & norm) {
+		core->tvaudio = WW_L;
+
+	} else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
+		core->tvaudio = WW_BG;
+
+	} else if (V4L2_STD_SECAM_DK & norm) {
+		core->tvaudio = WW_DK;
+
+	} else if ((V4L2_STD_NTSC_M & norm) ||
+		   (V4L2_STD_PAL_M  & norm)) {
+		core->tvaudio = WW_BTSC;
+
+	} else if (V4L2_STD_NTSC_M_JP & norm) {
+		core->tvaudio = WW_EIAJ;
+
+	} else {
+		printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
+		       core->name, v4l2_norm_to_name(core->tvnorm));
+		core->tvaudio = WW_NONE;
+		return 0;
+	}
+
+	cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
+	cx88_set_tvaudio(core);
+	/* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
+
+/*
+   This should be needed only on cx88-alsa. It seems that some cx88 chips have
+   bugs and does require DMA enabled for it to work.
+ */
+	cx88_start_audio_dma(core);
+	return 0;
+}
+
+
+
+int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
+{
+	u32 fsc8;
+	u32 adc_clock;
+	u32 vdec_clock;
+	u32 step_db,step_dr;
+	u64 tmp64;
+	u32 bdelay,agcdelay,htotal;
+	u32 cxiformat, cxoformat;
+
+	core->tvnorm = norm;
+	fsc8       = norm_fsc8(norm);
+	adc_clock  = xtal;
+	vdec_clock = fsc8;
+	step_db    = fsc8;
+	step_dr    = fsc8;
+
+	if (norm & V4L2_STD_NTSC_M_JP) {
+		cxiformat = VideoFormatNTSCJapan;
+		cxoformat = 0x181f0008;
+	} else if (norm & V4L2_STD_NTSC_443) {
+		cxiformat = VideoFormatNTSC443;
+		cxoformat = 0x181f0008;
+	} else if (norm & V4L2_STD_PAL_M) {
+		cxiformat = VideoFormatPALM;
+		cxoformat = 0x1c1f0008;
+	} else if (norm & V4L2_STD_PAL_N) {
+		cxiformat = VideoFormatPALN;
+		cxoformat = 0x1c1f0008;
+	} else if (norm & V4L2_STD_PAL_Nc) {
+		cxiformat = VideoFormatPALNC;
+		cxoformat = 0x1c1f0008;
+	} else if (norm & V4L2_STD_PAL_60) {
+		cxiformat = VideoFormatPAL60;
+		cxoformat = 0x181f0008;
+	} else if (norm & V4L2_STD_NTSC) {
+		cxiformat = VideoFormatNTSC;
+		cxoformat = 0x181f0008;
+	} else if (norm & V4L2_STD_SECAM) {
+		step_db = 4250000 * 8;
+		step_dr = 4406250 * 8;
+
+		cxiformat = VideoFormatSECAM;
+		cxoformat = 0x181f0008;
+	} else { /* PAL */
+		cxiformat = VideoFormatPAL;
+		cxoformat = 0x181f0008;
+	}
+
+	dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
+		v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
+		step_db, step_dr);
+	set_pll(core,2,vdec_clock);
+
+	dprintk(1,"set_tvnorm: MO_INPUT_FORMAT  0x%08x [old=0x%08x]\n",
+		cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
+	/* Chroma AGC must be disabled if SECAM is used, we enable it
+	   by default on PAL and NTSC */
+	cx_andor(MO_INPUT_FORMAT, 0x40f,
+		 norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
+
+	// FIXME: as-is from DScaler
+	dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
+		cxoformat, cx_read(MO_OUTPUT_FORMAT));
+	cx_write(MO_OUTPUT_FORMAT, cxoformat);
+
+	// MO_SCONV_REG = adc clock / video dec clock * 2^17
+	tmp64  = adc_clock * (u64)(1 << 17);
+	do_div(tmp64, vdec_clock);
+	dprintk(1,"set_tvnorm: MO_SCONV_REG     0x%08x [old=0x%08x]\n",
+		(u32)tmp64, cx_read(MO_SCONV_REG));
+	cx_write(MO_SCONV_REG, (u32)tmp64);
+
+	// MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
+	tmp64  = step_db * (u64)(1 << 22);
+	do_div(tmp64, vdec_clock);
+	dprintk(1,"set_tvnorm: MO_SUB_STEP      0x%08x [old=0x%08x]\n",
+		(u32)tmp64, cx_read(MO_SUB_STEP));
+	cx_write(MO_SUB_STEP, (u32)tmp64);
+
+	// MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
+	tmp64  = step_dr * (u64)(1 << 22);
+	do_div(tmp64, vdec_clock);
+	dprintk(1,"set_tvnorm: MO_SUB_STEP_DR   0x%08x [old=0x%08x]\n",
+		(u32)tmp64, cx_read(MO_SUB_STEP_DR));
+	cx_write(MO_SUB_STEP_DR, (u32)tmp64);
+
+	// bdelay + agcdelay
+	bdelay   = vdec_clock * 65 / 20000000 + 21;
+	agcdelay = vdec_clock * 68 / 20000000 + 15;
+	dprintk(1,"set_tvnorm: MO_AGC_BURST     0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
+		(bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
+	cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
+
+	// htotal
+	tmp64 = norm_htotal(norm) * (u64)vdec_clock;
+	do_div(tmp64, fsc8);
+	htotal = (u32)tmp64;
+	dprintk(1,"set_tvnorm: MO_HTOTAL        0x%08x [old=0x%08x,htotal=%d]\n",
+		htotal, cx_read(MO_HTOTAL), (u32)tmp64);
+	cx_andor(MO_HTOTAL, 0x07ff, htotal);
+
+	// vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
+	// the effective vbi offset ~244 samples, the same as the Bt8x8
+	cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
+
+	// this is needed as well to set all tvnorm parameter
+	cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
+
+	// audio
+	set_tvaudio(core);
+
+	// tell i2c chips
+	call_all(core, core, s_std, norm);
+
+	/* The chroma_agc control should be inaccessible if the video format is SECAM */
+	v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
+
+	// done
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct video_device *cx88_vdev_init(struct cx88_core *core,
+				    struct pci_dev *pci,
+				    const struct video_device *template_,
+				    const char *type)
+{
+	struct video_device *vfd;
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template_;
+	vfd->v4l2_dev = &core->v4l2_dev;
+	vfd->release = video_device_release;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+		 core->name, type, core->board.name);
+	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+	return vfd;
+}
+
+struct cx88_core* cx88_core_get(struct pci_dev *pci)
+{
+	struct cx88_core *core;
+
+	mutex_lock(&devlist);
+	list_for_each_entry(core, &cx88_devlist, devlist) {
+		if (pci->bus->number != core->pci_bus)
+			continue;
+		if (PCI_SLOT(pci->devfn) != core->pci_slot)
+			continue;
+
+		if (0 != cx88_get_resources(core, pci)) {
+			mutex_unlock(&devlist);
+			return NULL;
+		}
+		atomic_inc(&core->refcount);
+		mutex_unlock(&devlist);
+		return core;
+	}
+
+	core = cx88_core_create(pci, cx88_devcount);
+	if (NULL != core) {
+		cx88_devcount++;
+		list_add_tail(&core->devlist, &cx88_devlist);
+	}
+
+	mutex_unlock(&devlist);
+	return core;
+}
+
+void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
+{
+	release_mem_region(pci_resource_start(pci,0),
+			   pci_resource_len(pci,0));
+
+	if (!atomic_dec_and_test(&core->refcount))
+		return;
+
+	mutex_lock(&devlist);
+	cx88_ir_fini(core);
+	if (0 == core->i2c_rc) {
+		if (core->i2c_rtc)
+			i2c_unregister_device(core->i2c_rtc);
+		i2c_del_adapter(&core->i2c_adap);
+	}
+	list_del(&core->devlist);
+	iounmap(core->lmmio);
+	cx88_devcount--;
+	mutex_unlock(&devlist);
+	v4l2_ctrl_handler_free(&core->video_hdl);
+	v4l2_ctrl_handler_free(&core->audio_hdl);
+	v4l2_device_unregister(&core->v4l2_dev);
+	kfree(core);
+}
+
+/* ------------------------------------------------------------------ */
+
+EXPORT_SYMBOL(cx88_print_irqbits);
+
+EXPORT_SYMBOL(cx88_core_irq);
+EXPORT_SYMBOL(cx88_wakeup);
+EXPORT_SYMBOL(cx88_reset);
+EXPORT_SYMBOL(cx88_shutdown);
+
+EXPORT_SYMBOL(cx88_risc_buffer);
+EXPORT_SYMBOL(cx88_risc_databuffer);
+EXPORT_SYMBOL(cx88_risc_stopper);
+EXPORT_SYMBOL(cx88_free_buffer);
+
+EXPORT_SYMBOL(cx88_sram_channels);
+EXPORT_SYMBOL(cx88_sram_channel_setup);
+EXPORT_SYMBOL(cx88_sram_channel_dump);
+
+EXPORT_SYMBOL(cx88_set_tvnorm);
+EXPORT_SYMBOL(cx88_set_scale);
+
+EXPORT_SYMBOL(cx88_vdev_init);
+EXPORT_SYMBOL(cx88_core_get);
+EXPORT_SYMBOL(cx88_core_put);
+
+EXPORT_SYMBOL(cx88_ir_start);
+EXPORT_SYMBOL(cx88_ir_stop);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/pci/cx88/cx88-dsp.c b/drivers/media/pci/cx88/cx88-dsp.c
new file mode 100644
index 000000000000..a9907265ff66
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-dsp.c
@@ -0,0 +1,322 @@
+/*
+ *
+ *  Stereo and SAP detection for cx88
+ *
+ *  Copyright (c) 2009 Marton Balint <cus@fazekas.hu>
+ *
+ *  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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "cx88.h"
+#include "cx88-reg.h"
+
+#define INT_PI			((s32)(3.141592653589 * 32768.0))
+
+#define compat_remainder(a, b) \
+	 ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0)
+
+#define baseband_freq(carrier, srate, tone) ((s32)( \
+	 (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI))
+
+/* We calculate the baseband frequencies of the carrier and the pilot tones
+ * based on the the sampling rate of the audio rds fifo. */
+
+#define FREQ_A2_CARRIER         baseband_freq(54687.5, 2689.36, 0.0)
+#define FREQ_A2_DUAL            baseband_freq(54687.5, 2689.36, 274.1)
+#define FREQ_A2_STEREO          baseband_freq(54687.5, 2689.36, 117.5)
+
+/* The frequencies below are from the reference driver. They probably need
+ * further adjustments, because they are not tested at all. You may even need
+ * to play a bit with the registers of the chip to select the proper signal
+ * for the input of the audio rds fifo, and measure it's sampling rate to
+ * calculate the proper baseband frequencies... */
+
+#define FREQ_A2M_CARRIER	((s32)(2.114516 * 32768.0))
+#define FREQ_A2M_DUAL		((s32)(2.754916 * 32768.0))
+#define FREQ_A2M_STEREO		((s32)(2.462326 * 32768.0))
+
+#define FREQ_EIAJ_CARRIER	((s32)(1.963495 * 32768.0)) /* 5pi/8  */
+#define FREQ_EIAJ_DUAL		((s32)(2.562118 * 32768.0))
+#define FREQ_EIAJ_STEREO	((s32)(2.601053 * 32768.0))
+
+#define FREQ_BTSC_DUAL		((s32)(1.963495 * 32768.0)) /* 5pi/8  */
+#define FREQ_BTSC_DUAL_REF	((s32)(1.374446 * 32768.0)) /* 7pi/16 */
+
+#define FREQ_BTSC_SAP		((s32)(2.471532 * 32768.0))
+#define FREQ_BTSC_SAP_REF	((s32)(1.730072 * 32768.0))
+
+/* The spectrum of the signal should be empty between these frequencies. */
+#define FREQ_NOISE_START	((s32)(0.100000 * 32768.0))
+#define FREQ_NOISE_END		((s32)(1.200000 * 32768.0))
+
+static unsigned int dsp_debug;
+module_param(dsp_debug, int, 0644);
+MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages");
+
+#define dprintk(level, fmt, arg...)	if (dsp_debug >= level) \
+	printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+static s32 int_cos(u32 x)
+{
+	u32 t2, t4, t6, t8;
+	s32 ret;
+	u16 period = x / INT_PI;
+	if (period % 2)
+		return -int_cos(x - INT_PI);
+	x = x % INT_PI;
+	if (x > INT_PI/2)
+		return -int_cos(INT_PI/2 - (x % (INT_PI/2)));
+	/* Now x is between 0 and INT_PI/2.
+	 * To calculate cos(x) we use it's Taylor polinom. */
+	t2 = x*x/32768/2;
+	t4 = t2*x/32768*x/32768/3/4;
+	t6 = t4*x/32768*x/32768/5/6;
+	t8 = t6*x/32768*x/32768/7/8;
+	ret = 32768-t2+t4-t6+t8;
+	return ret;
+}
+
+static u32 int_goertzel(s16 x[], u32 N, u32 freq)
+{
+	/* We use the Goertzel algorithm to determine the power of the
+	 * given frequency in the signal */
+	s32 s_prev = 0;
+	s32 s_prev2 = 0;
+	s32 coeff = 2*int_cos(freq);
+	u32 i;
+
+	u64 tmp;
+	u32 divisor;
+
+	for (i = 0; i < N; i++) {
+		s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2;
+		s_prev2 = s_prev;
+		s_prev = s;
+	}
+
+	tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev -
+		      (s64)coeff * s_prev2 * s_prev / 32768;
+
+	/* XXX: N must be low enough so that N*N fits in s32.
+	 * Else we need two divisions. */
+	divisor = N * N;
+	do_div(tmp, divisor);
+
+	return (u32) tmp;
+}
+
+static u32 freq_magnitude(s16 x[], u32 N, u32 freq)
+{
+	u32 sum = int_goertzel(x, N, freq);
+	return (u32)int_sqrt(sum);
+}
+
+static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end)
+{
+	int i;
+	u32 sum = 0;
+	u32 freq_step;
+	int samples = 5;
+
+	if (N > 192) {
+		/* The last 192 samples are enough for noise detection */
+		x += (N-192);
+		N = 192;
+	}
+
+	freq_step = (freq_end - freq_start) / (samples - 1);
+
+	for (i = 0; i < samples; i++) {
+		sum += int_goertzel(x, N, freq_start);
+		freq_start += freq_step;
+	}
+
+	return (u32)int_sqrt(sum / samples);
+}
+
+static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N)
+{
+	s32 carrier, stereo, dual, noise;
+	s32 carrier_freq, stereo_freq, dual_freq;
+	s32 ret;
+
+	switch (core->tvaudio) {
+	case WW_BG:
+	case WW_DK:
+		carrier_freq = FREQ_A2_CARRIER;
+		stereo_freq = FREQ_A2_STEREO;
+		dual_freq = FREQ_A2_DUAL;
+		break;
+	case WW_M:
+		carrier_freq = FREQ_A2M_CARRIER;
+		stereo_freq = FREQ_A2M_STEREO;
+		dual_freq = FREQ_A2M_DUAL;
+		break;
+	case WW_EIAJ:
+		carrier_freq = FREQ_EIAJ_CARRIER;
+		stereo_freq = FREQ_EIAJ_STEREO;
+		dual_freq = FREQ_EIAJ_DUAL;
+		break;
+	default:
+		printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n",
+		       core->name, core->tvaudio, __func__);
+		return UNSET;
+	}
+
+	carrier = freq_magnitude(x, N, carrier_freq);
+	stereo  = freq_magnitude(x, N, stereo_freq);
+	dual    = freq_magnitude(x, N, dual_freq);
+	noise   = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END);
+
+	dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, "
+		   "noise=%d\n", carrier, stereo, dual, noise);
+
+	if (stereo > dual)
+		ret = V4L2_TUNER_SUB_STEREO;
+	else
+		ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+	if (core->tvaudio == WW_EIAJ) {
+		/* EIAJ checks may need adjustments */
+		if ((carrier > max(stereo, dual)*2) &&
+		    (carrier < max(stereo, dual)*6) &&
+		    (carrier > 20 && carrier < 200) &&
+		    (max(stereo, dual) > min(stereo, dual))) {
+			/* For EIAJ the carrier is always present,
+			   so we probably don't need noise detection */
+			return ret;
+		}
+	} else {
+		if ((carrier > max(stereo, dual)*2) &&
+		    (carrier < max(stereo, dual)*8) &&
+		    (carrier > 20 && carrier < 200) &&
+		    (noise < 10) &&
+		    (max(stereo, dual) > min(stereo, dual)*2)) {
+			return ret;
+		}
+	}
+	return V4L2_TUNER_SUB_MONO;
+}
+
+static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N)
+{
+	s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF);
+	s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP);
+	s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF);
+	s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL);
+	dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d"
+		   "\n", dual_ref, dual, sap_ref, sap);
+	/* FIXME: Currently not supported */
+	return UNSET;
+}
+
+static s16 *read_rds_samples(struct cx88_core *core, u32 *N)
+{
+	const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
+	s16 *samples;
+
+	unsigned int i;
+	unsigned int bpl = srch->fifo_size/AUD_RDS_LINES;
+	unsigned int spl = bpl/4;
+	unsigned int sample_count = spl*(AUD_RDS_LINES-1);
+
+	u32 current_address = cx_read(srch->ptr1_reg);
+	u32 offset = (current_address - srch->fifo_start + bpl);
+
+	dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), "
+		"sample_count=%d, aud_intstat=%08x\n", current_address,
+		current_address - srch->fifo_start, sample_count,
+		cx_read(MO_AUD_INTSTAT));
+
+	samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL);
+	if (!samples)
+		return NULL;
+
+	*N = sample_count;
+
+	for (i = 0; i < sample_count; i++)  {
+		offset = offset % (AUD_RDS_LINES*bpl);
+		samples[i] = cx_read(srch->fifo_start + offset);
+		offset += 4;
+	}
+
+	if (dsp_debug >= 2) {
+		dprintk(2, "RDS samples dump: ");
+		for (i = 0; i < sample_count; i++)
+			printk("%hd ", samples[i]);
+		printk(".\n");
+	}
+
+	return samples;
+}
+
+s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core)
+{
+	s16 *samples;
+	u32 N = 0;
+	s32 ret = UNSET;
+
+	/* If audio RDS fifo is disabled, we can't read the samples */
+	if (!(cx_read(MO_AUD_DMACNTRL) & 0x04))
+		return ret;
+	if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS))
+		return ret;
+
+	/* Wait at least 500 ms after an audio standard change */
+	if (time_before(jiffies, core->last_change + msecs_to_jiffies(500)))
+		return ret;
+
+	samples = read_rds_samples(core, &N);
+
+	if (!samples)
+		return ret;
+
+	switch (core->tvaudio) {
+	case WW_BG:
+	case WW_DK:
+	case WW_EIAJ:
+	case WW_M:
+		ret = detect_a2_a2m_eiaj(core, samples, N);
+		break;
+	case WW_BTSC:
+		ret = detect_btsc(core, samples, N);
+		break;
+	case WW_NONE:
+	case WW_I:
+	case WW_L:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+		break;
+	}
+
+	kfree(samples);
+
+	if (UNSET != ret)
+		dprintk(1, "stereo/sap detection result:%s%s%s\n",
+			   (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "",
+			   (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
+			   (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : "");
+
+	return ret;
+}
+EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap);
+
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
new file mode 100644
index 000000000000..d803bba09525
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -0,0 +1,1778 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * MPEG Transport Stream (DVB) routines
+ *
+ * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx88.h"
+#include "dvb-pll.h"
+#include <media/v4l2-common.h>
+
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "cx88-vp3054-i2c.h"
+#include "zl10353.h"
+#include "cx22702.h"
+#include "or51132.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "nxt200x.h"
+#include "cx24123.h"
+#include "isl6421.h"
+#include "tuner-simple.h"
+#include "tda9887.h"
+#include "s5h1411.h"
+#include "stv0299.h"
+#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "cx24116.h"
+#include "stv0900.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
+#include "mb86a16.h"
+#include "ds3000.h"
+
+MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
+
+static unsigned int dvb_buf_tscnt = 32;
+module_param(dvb_buf_tscnt, int, 0644);
+MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+			 unsigned int *count, unsigned int *size)
+{
+	struct cx8802_dev *dev = q->priv_data;
+
+	dev->ts_packet_size  = 188 * 4;
+	dev->ts_packet_count = dvb_buf_tscnt;
+
+	*size  = dev->ts_packet_size * dev->ts_packet_count;
+	*count = dvb_buf_tscnt;
+	return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+			   struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct cx8802_dev *dev = q->priv_data;
+	return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct cx8802_dev *dev = q->priv_data;
+	cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+			    struct videobuf_buffer *vb)
+{
+	cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static const struct videobuf_queue_ops dvb_qops = {
+	.buf_setup    = dvb_buf_setup,
+	.buf_prepare  = dvb_buf_prepare,
+	.buf_queue    = dvb_buf_queue,
+	.buf_release  = dvb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx8802_driver *drv = NULL;
+	int ret = 0;
+	int fe_id;
+
+	fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
+	if (!fe_id) {
+		printk(KERN_ERR "%s() No frontend found\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->core->lock);
+	drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
+	if (drv) {
+		if (acquire){
+			dev->frontends.active_fe_id = fe_id;
+			ret = drv->request_acquire(drv);
+		} else {
+			ret = drv->request_release(drv);
+			dev->frontends.active_fe_id = 0;
+		}
+	}
+	mutex_unlock(&dev->core->lock);
+
+	return ret;
+}
+
+static void cx88_dvb_gate_ctrl(struct cx88_core  *core, int open)
+{
+	struct videobuf_dvb_frontends *f;
+	struct videobuf_dvb_frontend *fe;
+
+	if (!core->dvbdev)
+		return;
+
+	f = &core->dvbdev->frontends;
+
+	if (!f)
+		return;
+
+	if (f->gate <= 1) /* undefined or fe0 */
+		fe = videobuf_dvb_get_frontend(f, 1);
+	else
+		fe = videobuf_dvb_get_frontend(f, f->gate);
+
+	if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+		fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
+{
+	static const u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
+	static const u8 reset []         = { RESET,      0x80 };
+	static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static const u8 agc_cfg []       = { AGC_TARGET, 0x24, 0x20 };
+	static const u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+	static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(200);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	mt352_write(fe, gpp_ctl_cfg,    sizeof(gpp_ctl_cfg));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+	return 0;
+}
+
+static int dvico_dual_demod_init(struct dvb_frontend *fe)
+{
+	static const u8 clock_config []  = { CLOCK_CTL,  0x38, 0x38 };
+	static const u8 reset []         = { RESET,      0x80 };
+	static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static const u8 agc_cfg []       = { AGC_TARGET, 0x28, 0x20 };
+	static const u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+	static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(200);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	mt352_write(fe, gpp_ctl_cfg,    sizeof(gpp_ctl_cfg));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+	return 0;
+}
+
+static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
+{
+	static const u8 clock_config []  = { 0x89, 0x38, 0x39 };
+	static const u8 reset []         = { 0x50, 0x80 };
+	static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static const u8 agc_cfg []       = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+				       0x00, 0xFF, 0x00, 0x40, 0x40 };
+	static const u8 dntv_extra[]     = { 0xB5, 0x7A };
+	static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(2000);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	udelay(2000);
+	mt352_write(fe, dntv_extra,     sizeof(dntv_extra));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+	return 0;
+}
+
+static const struct mt352_config dvico_fusionhdtv = {
+	.demod_address = 0x0f,
+	.demod_init    = dvico_fusionhdtv_demod_init,
+};
+
+static const struct mt352_config dntv_live_dvbt_config = {
+	.demod_address = 0x0f,
+	.demod_init    = dntv_live_dvbt_demod_init,
+};
+
+static const struct mt352_config dvico_fusionhdtv_dual = {
+	.demod_address = 0x0f,
+	.demod_init    = dvico_dual_demod_init,
+};
+
+static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
+	.demod_address = (0x1e >> 1),
+	.no_tuner      = 1,
+	.if2           = 45600,
+};
+
+static struct mb86a16_config twinhan_vp1027 = {
+	.demod_address  = 0x08,
+};
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
+{
+	static const u8 clock_config []  = { 0x89, 0x38, 0x38 };
+	static const u8 reset []         = { 0x50, 0x80 };
+	static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static const u8 agc_cfg []       = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
+				       0x00, 0xFF, 0x00, 0x40, 0x40 };
+	static const u8 dntv_extra[]     = { 0xB5, 0x7A };
+	static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(2000);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	udelay(2000);
+	mt352_write(fe, dntv_extra,     sizeof(dntv_extra));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+	return 0;
+}
+
+static const struct mt352_config dntv_live_dvbt_pro_config = {
+	.demod_address = 0x0f,
+	.no_tuner      = 1,
+	.demod_init    = dntv_live_dvbt_pro_demod_init,
+};
+#endif
+
+static const struct zl10353_config dvico_fusionhdtv_hybrid = {
+	.demod_address = 0x0f,
+	.no_tuner      = 1,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_xc3028 = {
+	.demod_address = 0x0f,
+	.if2           = 45600,
+	.no_tuner      = 1,
+};
+
+static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
+	.demod_address = 0x0f,
+	.if2 = 4560,
+	.no_tuner = 1,
+	.demod_init = dvico_fusionhdtv_demod_init,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
+	.demod_address = 0x0f,
+};
+
+static const struct cx22702_config connexant_refboard_config = {
+	.demod_address = 0x43,
+	.output_mode   = CX22702_SERIAL_OUTPUT,
+};
+
+static const struct cx22702_config hauppauge_hvr_config = {
+	.demod_address = 0x63,
+	.output_mode   = CX22702_SERIAL_OUTPUT,
+};
+
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+	return 0;
+}
+
+static const struct or51132_config pchdtv_hd3000 = {
+	.demod_address = 0x15,
+	.set_ts_params = or51132_set_ts_param,
+};
+
+static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	dprintk(1, "%s: index = %d\n", __func__, index);
+	if (index == 0)
+		cx_clear(MO_GP0_IO, 8);
+	else
+		cx_set(MO_GP0_IO, 8);
+	return 0;
+}
+
+static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	if (is_punctured)
+		dev->ts_gen_cntrl |= 0x04;
+	else
+		dev->ts_gen_cntrl &= ~0x04;
+	return 0;
+}
+
+static struct lgdt330x_config fusionhdtv_3_gold = {
+	.demod_address = 0x0e,
+	.demod_chip    = LGDT3302,
+	.serial_mpeg   = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
+	.set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config fusionhdtv_5_gold = {
+	.demod_address = 0x0e,
+	.demod_chip    = LGDT3303,
+	.serial_mpeg   = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+	.set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config pchdtv_hd5500 = {
+	.demod_address = 0x59,
+	.demod_chip    = LGDT3303,
+	.serial_mpeg   = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+	.set_ts_params = lgdt330x_set_ts_param,
+};
+
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+	return 0;
+}
+
+static const struct nxt200x_config ati_hdtvwonder = {
+	.demod_address = 0x0a,
+	.set_ts_params = nxt200x_set_ts_param,
+};
+
+static int cx24123_set_ts_param(struct dvb_frontend* fe,
+	int is_punctured)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	dev->ts_gen_cntrl = 0x02;
+	return 0;
+}
+
+static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
+				       fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	if (voltage == SEC_VOLTAGE_OFF)
+		cx_write(MO_GP0_IO, 0x000006fb);
+	else
+		cx_write(MO_GP0_IO, 0x000006f9);
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	if (voltage == SEC_VOLTAGE_OFF) {
+		dprintk(1,"LNB Voltage OFF\n");
+		cx_write(MO_GP0_IO, 0x0000efff);
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_set(MO_GP0_IO, 0x6040);
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		cx_clear(MO_GP0_IO, 0x20);
+		break;
+	case SEC_VOLTAGE_18:
+		cx_set(MO_GP0_IO, 0x20);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx_clear(MO_GP0_IO, 0x20);
+		break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static int vp1027_set_voltage(struct dvb_frontend *fe,
+				    fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk(1, "LNB SEC Voltage=13\n");
+		cx_write(MO_GP0_IO, 0x00001220);
+		break;
+	case SEC_VOLTAGE_18:
+		dprintk(1, "LNB SEC Voltage=18\n");
+		cx_write(MO_GP0_IO, 0x00001222);
+		break;
+	case SEC_VOLTAGE_OFF:
+		dprintk(1, "LNB Voltage OFF\n");
+		cx_write(MO_GP0_IO, 0x00001230);
+		break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static const struct cx24123_config geniatech_dvbs_config = {
+	.demod_address = 0x55,
+	.set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config hauppauge_novas_config = {
+	.demod_address = 0x55,
+	.set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config kworld_dvbs_100_config = {
+	.demod_address = 0x15,
+	.set_ts_params = cx24123_set_ts_param,
+	.lnb_polarity  = 1,
+};
+
+static const 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 const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
+	.demod_address = 0x32 >> 1,
+	.output_mode   = S5H1409_SERIAL_OUTPUT,
+	.gpio          = S5H1409_GPIO_OFF,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct s5h1409_config kworld_atsc_120_config = {
+	.demod_address = 0x32 >> 1,
+	.output_mode   = S5H1409_SERIAL_OUTPUT,
+	.gpio	       = S5H1409_GPIO_OFF,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+	.i2c_address	= 0x64,
+	.if_khz		= 5380,
+};
+
+static const struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+	.demod_address = (0x1e >> 1),
+	.no_tuner      = 1,
+	.if2           = 45600,
+};
+
+static const struct zl10353_config cx88_geniatech_x8000_mt = {
+	.demod_address = (0x1e >> 1),
+	.no_tuner = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static const struct s5h1411_config dvico_fusionhdtv7_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_ON,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+	.qam_if        = S5H1411_IF_44000,
+	.vsb_if        = S5H1411_IF_44000,
+	.inversion     = S5H1411_INVERSION_OFF,
+	.status_mode   = S5H1411_DEMODLOCKING
+};
+
+static const struct xc5000_config dvico_fusionhdtv7_tuner_config = {
+	.i2c_address    = 0xc2 >> 1,
+	.if_khz         = 5380,
+};
+
+static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
+{
+	struct dvb_frontend *fe;
+	struct videobuf_dvb_frontend *fe0 = NULL;
+	struct xc2028_ctrl ctl;
+	struct xc2028_config cfg = {
+		.i2c_adap  = &dev->core->i2c_adap,
+		.i2c_addr  = addr,
+		.ctrl      = &ctl,
+	};
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	if (!fe0->dvb.frontend) {
+		printk(KERN_ERR "%s/2: dvb frontend not attached. "
+				"Can't attach xc3028\n",
+		       dev->core->name);
+		return -EINVAL;
+	}
+
+	/*
+	 * Some xc3028 devices may be hidden by an I2C gate. This is known
+	 * to happen with some s5h1409-based devices.
+	 * Now that I2C gate is open, sets up xc3028 configuration
+	 */
+	cx88_setup_xc3028(dev->core, &ctl);
+
+	fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+	if (!fe) {
+		printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+		       dev->core->name);
+		dvb_frontend_detach(fe0->dvb.frontend);
+		dvb_unregister_frontend(fe0->dvb.frontend);
+		fe0->dvb.frontend = NULL;
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "%s/2: xc3028 attached\n",
+	       dev->core->name);
+
+	return 0;
+}
+
+static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
+{
+	struct dvb_frontend *fe;
+	struct videobuf_dvb_frontend *fe0 = NULL;
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	if (!fe0->dvb.frontend) {
+		printk(KERN_ERR "%s/2: dvb frontend not attached. "
+				"Can't attach xc4000\n",
+		       dev->core->name);
+		return -EINVAL;
+	}
+
+	fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
+			cfg);
+	if (!fe) {
+		printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+		       dev->core->name);
+		dvb_frontend_detach(fe0->dvb.frontend);
+		dvb_unregister_frontend(fe0->dvb.frontend);
+		fe0->dvb.frontend = NULL;
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
+
+	return 0;
+}
+
+static int cx24116_set_ts_param(struct dvb_frontend *fe,
+	int is_punctured)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	dev->ts_gen_cntrl = 0x2;
+
+	return 0;
+}
+
+static int stv0900_set_ts_param(struct dvb_frontend *fe,
+	int is_punctured)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	dev->ts_gen_cntrl = 0;
+
+	return 0;
+}
+
+static int cx24116_reset_device(struct dvb_frontend *fe)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	/* Reset the part */
+	/* Put the cx24116 into reset */
+	cx_write(MO_SRST_IO, 0);
+	msleep(10);
+	/* Take the cx24116 out of reset */
+	cx_write(MO_SRST_IO, 1);
+	msleep(10);
+
+	return 0;
+}
+
+static const struct cx24116_config hauppauge_hvr4000_config = {
+	.demod_address          = 0x05,
+	.set_ts_params          = cx24116_set_ts_param,
+	.reset_device           = cx24116_reset_device,
+};
+
+static const struct cx24116_config tevii_s460_config = {
+	.demod_address = 0x55,
+	.set_ts_params = cx24116_set_ts_param,
+	.reset_device  = cx24116_reset_device,
+};
+
+static int ds3000_set_ts_param(struct dvb_frontend *fe,
+	int is_punctured)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	dev->ts_gen_cntrl = 4;
+
+	return 0;
+}
+
+static struct ds3000_config tevii_ds3000_config = {
+	.demod_address = 0x68,
+	.set_ts_params = ds3000_set_ts_param,
+};
+
+static const struct stv0900_config prof_7301_stv0900_config = {
+	.demod_address = 0x6a,
+/*	demod_mode = 0,*/
+	.xtal = 27000000,
+	.clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+	.diseqc_mode = 2,/* 2/3 PWM */
+	.tun1_maddress = 0,/* 0x60 */
+	.tun1_adc = 0,/* 2 Vpp */
+	.path1_mode = 3,
+	.set_ts_params = stv0900_set_ts_param,
+};
+
+static const struct stb6100_config prof_7301_stb6100_config = {
+	.tuner_address = 0x60,
+	.refclock = 27000000,
+};
+
+static const struct stv0299_config tevii_tuner_sharp_config = {
+	.demod_address = 0x68,
+	.inittab = sharp_z0194a_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.skip_reinit = 0,
+	.lock_output = 1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = sharp_z0194a_set_symbol_rate,
+	.set_ts_params = cx24116_set_ts_param,
+};
+
+static const struct stv0288_config tevii_tuner_earda_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+	.set_ts_params = cx24116_set_ts_param,
+};
+
+static int cx8802_alloc_frontends(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	struct videobuf_dvb_frontend *fe = NULL;
+	int i;
+
+	mutex_init(&dev->frontends.lock);
+	INIT_LIST_HEAD(&dev->frontends.felist);
+
+	if (!core->board.num_frontends)
+		return -ENODEV;
+
+	printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+			 core->board.num_frontends);
+	for (i = 1; i <= core->board.num_frontends; i++) {
+		fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
+		if (!fe) {
+			printk(KERN_ERR "%s() failed to alloc\n", __func__);
+			videobuf_dvb_dealloc_frontends(&dev->frontends);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+
+
+static const u8 samsung_smt_7020_inittab[] = {
+	     0x01, 0x15,
+	     0x02, 0x00,
+	     0x03, 0x00,
+	     0x04, 0x7D,
+	     0x05, 0x0F,
+	     0x06, 0x02,
+	     0x07, 0x00,
+	     0x08, 0x60,
+
+	     0x0A, 0xC2,
+	     0x0B, 0x00,
+	     0x0C, 0x01,
+	     0x0D, 0x81,
+	     0x0E, 0x44,
+	     0x0F, 0x09,
+	     0x10, 0x3C,
+	     0x11, 0x84,
+	     0x12, 0xDA,
+	     0x13, 0x99,
+	     0x14, 0x8D,
+	     0x15, 0xCE,
+	     0x16, 0xE8,
+	     0x17, 0x43,
+	     0x18, 0x1C,
+	     0x19, 0x1B,
+	     0x1A, 0x1D,
+
+	     0x1C, 0x12,
+	     0x1D, 0x00,
+	     0x1E, 0x00,
+	     0x1F, 0x00,
+	     0x20, 0x00,
+	     0x21, 0x00,
+	     0x22, 0x00,
+	     0x23, 0x00,
+
+	     0x28, 0x02,
+	     0x29, 0x28,
+	     0x2A, 0x14,
+	     0x2B, 0x0F,
+	     0x2C, 0x09,
+	     0x2D, 0x05,
+
+	     0x31, 0x1F,
+	     0x32, 0x19,
+	     0x33, 0xFC,
+	     0x34, 0x13,
+	     0xff, 0xff,
+};
+
+
+static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct cx8802_dev *dev = fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = {
+		.addr = 0x61,
+		.flags = 0,
+		.buf = buf,
+		.len = sizeof(buf) };
+
+	div = c->frequency / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x84;  /* 0xC4 */
+	buf[3] = 0x00;
+
+	if (c->frequency < 1500000)
+		buf[3] |= 0x10;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static int samsung_smt_7020_set_tone(struct dvb_frontend *fe,
+	fe_sec_tone_mode_t tone)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_set(MO_GP0_IO, 0x0800);
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		cx_set(MO_GP0_IO, 0x08);
+		break;
+	case SEC_TONE_OFF:
+		cx_clear(MO_GP0_IO, 0x08);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe,
+	fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	u8 data;
+	struct i2c_msg msg = {
+		.addr = 8,
+		.flags = 0,
+		.buf = &data,
+		.len = sizeof(data) };
+
+	cx_set(MO_GP0_IO, 0x8000);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_OFF:
+		break;
+	case SEC_VOLTAGE_13:
+		data = ISL6421_EN1 | ISL6421_LLC1;
+		cx_clear(MO_GP0_IO, 0x80);
+		break;
+	case SEC_VOLTAGE_18:
+		data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1;
+		cx_clear(MO_GP0_IO, 0x80);
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe,
+	u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) {
+		aclk = 0xb7;
+		bclk = 0x47;
+	} else if (srate < 3000000) {
+		aclk = 0xb7;
+		bclk = 0x4b;
+	} else if (srate < 7000000) {
+		aclk = 0xb7;
+		bclk = 0x4f;
+	} else if (srate < 14000000) {
+		aclk = 0xb7;
+		bclk = 0x53;
+	} else if (srate < 30000000) {
+		aclk = 0xb6;
+		bclk = 0x53;
+	} else if (srate < 45000000) {
+		aclk = 0xb4;
+		bclk = 0x51;
+	}
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg(fe, 0x21, ratio & 0xf0);
+
+	return 0;
+}
+
+
+static const struct stv0299_config samsung_stv0299_config = {
+	.demod_address = 0x68,
+	.inittab = samsung_smt_7020_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_LK,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate,
+};
+
+static int dvb_register(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+	int mfe_shared = 0; /* bus not shared by default */
+	int res = -EINVAL;
+
+	if (0 != core->i2c_rc) {
+		printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name);
+		goto frontend_detach;
+	}
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		goto frontend_detach;
+
+	/* multi-frontend gate control is undefined or defaults to fe0 */
+	dev->frontends.gate = 0;
+
+	/* Sets the gate control callback to be used by i2c command calls */
+	core->gate_ctrl = cx88_dvb_gate_ctrl;
+
+	/* init frontend(s) */
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_DVB_T1:
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
+					       &connexant_refboard_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x61, &core->i2c_adap,
+					DVB_PLL_THOMSON_DTT759X))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
+	case CX88_BOARD_CONEXANT_DVB_T1:
+	case CX88_BOARD_KWORLD_DVB_T_CX22702:
+	case CX88_BOARD_WINFAST_DTV1000:
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
+					       &connexant_refboard_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x60, &core->i2c_adap,
+					DVB_PLL_THOMSON_DTT7579))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_WINFAST_DTV2000H:
+	case CX88_BOARD_HAUPPAUGE_HVR1100:
+	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
+					       &hauppauge_hvr_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &core->i2c_adap, 0x61,
+				   TUNER_PHILIPS_FMD1216ME_MK3))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_WINFAST_DTV2000H_J:
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
+					       &hauppauge_hvr_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &core->i2c_adap, 0x61,
+				   TUNER_PHILIPS_FMD1216MEX_MK3))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+		/* MFE frontend 1 */
+		mfe_shared = 1;
+		dev->frontends.gate = 2;
+		/* DVB-S init */
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
+					&hauppauge_novas_config,
+					&dev->core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (!dvb_attach(isl6421_attach,
+					fe0->dvb.frontend,
+					&dev->core->i2c_adap,
+					0x08, ISL6421_DCL, 0x00))
+				goto frontend_detach;
+		}
+		/* MFE frontend 2 */
+		fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+		if (!fe1)
+			goto frontend_detach;
+		/* DVB-T init */
+		fe1->dvb.frontend = dvb_attach(cx22702_attach,
+					&hauppauge_hvr_config,
+					&dev->core->i2c_adap);
+		if (fe1->dvb.frontend) {
+			fe1->dvb.frontend->id = 1;
+			if (!dvb_attach(simple_tuner_attach,
+					fe1->dvb.frontend,
+					&dev->core->i2c_adap,
+					0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+					       &dvico_fusionhdtv,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+				goto frontend_detach;
+			break;
+		}
+		/* ZL10353 replaces MT352 on later cards */
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_plus_v1_1,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+		/* The tin box says DEE1601, but it seems to be DTT7579
+		 * compatible, with a slightly different MT352 AGC gain. */
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+					       &dvico_fusionhdtv_dual,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+				goto frontend_detach;
+			break;
+		}
+		/* ZL10353 replaces MT352 on later cards */
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_plus_v1_1,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+					       &dvico_fusionhdtv,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x61, NULL, DVB_PLL_LG_Z201))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_KWORLD_DVB_T:
+	case CX88_BOARD_DNTV_LIVE_DVB_T:
+	case CX88_BOARD_ADSTECH_DVB_T_PCI:
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+					       &dntv_live_dvbt_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+					0x61, NULL, DVB_PLL_UNKNOWN_1))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+		/* MT352 is on a secondary I2C bus made from some GPIO lines */
+		fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
+					       &dev->vp3054->adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_PHILIPS_FMD1216ME_MK3))
+				goto frontend_detach;
+		}
+#else
+		printk(KERN_ERR "%s/2: built without vp3054 support\n",
+				core->name);
+#endif
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_hybrid,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &core->i2c_adap, 0x61,
+				   TUNER_THOMSON_FE6600))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_xc3028,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend == NULL)
+			fe0->dvb.frontend = dvb_attach(mt352_attach,
+						&dvico_fusionhdtv_mt352_xc3028,
+						&core->i2c_adap);
+		/*
+		 * On this board, the demod provides the I2C bus pullup.
+		 * We must not permit gate_ctrl to be performed, or
+		 * the xc3028 cannot communicate on the bus.
+		 */
+		if (fe0->dvb.frontend)
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+		if (attach_xc3028(0x61, dev) < 0)
+			goto frontend_detach;
+		break;
+	case CX88_BOARD_PCHDTV_HD3000:
+		fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_THOMSON_DTT761X))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+		dev->ts_gen_cntrl = 0x08;
+
+		/* Do a hardware reset of chip before using it. */
+		cx_clear(MO_GP0_IO, 1);
+		mdelay(100);
+		cx_set(MO_GP0_IO, 1);
+		mdelay(200);
+
+		/* Select RF connector callback */
+		fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+					       &fusionhdtv_3_gold,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_MICROTUNE_4042FI5))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+		dev->ts_gen_cntrl = 0x08;
+
+		/* Do a hardware reset of chip before using it. */
+		cx_clear(MO_GP0_IO, 1);
+		mdelay(100);
+		cx_set(MO_GP0_IO, 9);
+		mdelay(200);
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+					       &fusionhdtv_3_gold,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_THOMSON_DTT761X))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+		dev->ts_gen_cntrl = 0x08;
+
+		/* Do a hardware reset of chip before using it. */
+		cx_clear(MO_GP0_IO, 1);
+		mdelay(100);
+		cx_set(MO_GP0_IO, 1);
+		mdelay(200);
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+					       &fusionhdtv_5_gold,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_LG_TDVS_H06XF))
+				goto frontend_detach;
+			if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+				   &core->i2c_adap, 0x43))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_PCHDTV_HD5500:
+		dev->ts_gen_cntrl = 0x08;
+
+		/* Do a hardware reset of chip before using it. */
+		cx_clear(MO_GP0_IO, 1);
+		mdelay(100);
+		cx_set(MO_GP0_IO, 1);
+		mdelay(200);
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+					       &pchdtv_hd5500,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_LG_TDVS_H06XF))
+				goto frontend_detach;
+			if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+				   &core->i2c_adap, 0x43))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_ATI_HDTVWONDER:
+		fe0->dvb.frontend = dvb_attach(nxt200x_attach,
+					       &ati_hdtvwonder,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x61,
+					TUNER_PHILIPS_TUV1236D))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+	case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
+					       &hauppauge_novas_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
+					&core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_KWORLD_DVBS_100:
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
+					       &kworld_dvbs_100_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
+		}
+		break;
+	case CX88_BOARD_GENIATECH_DVBS:
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
+					       &geniatech_dvbs_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
+		}
+		break;
+	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+					       &pinnacle_pctv_hd_800i_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+					&core->i2c_adap,
+					&pinnacle_pctv_hd_800i_tuner_config))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+						&dvico_hdtv5_pci_nano_config,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend *fe;
+			struct xc2028_config cfg = {
+				.i2c_adap  = &core->i2c_adap,
+				.i2c_addr  = 0x61,
+			};
+			static struct xc2028_ctrl ctl = {
+				.fname       = XC2028_DEFAULT_FIRMWARE,
+				.max_len     = 64,
+				.scode_table = XC3028_FE_OREN538,
+			};
+
+			fe = dvb_attach(xc2028_attach,
+					fe0->dvb.frontend, &cfg);
+			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+				fe->ops.tuner_ops.set_config(fe, &ctl);
+		}
+		break;
+	case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+	case CX88_BOARD_WINFAST_DTV1800H:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &cx88_pinnacle_hybrid_pctv,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+			if (attach_xc3028(0x61, dev) < 0)
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &cx88_pinnacle_hybrid_pctv,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			struct xc4000_config cfg = {
+				.i2c_address	  = 0x61,
+				.default_pm	  = 0,
+				.dvb_amplitude	  = 134,
+				.set_smoothedcvbs = 1,
+				.if_khz		  = 4560
+			};
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+			if (attach_xc4000(dev, &cfg) < 0)
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_GENIATECH_X8000_MT:
+		dev->ts_gen_cntrl = 0x00;
+
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &cx88_geniatech_x8000_mt,
+					       &core->i2c_adap);
+		if (attach_xc3028(0x61, dev) < 0)
+			goto frontend_detach;
+		break;
+	 case CX88_BOARD_KWORLD_ATSC_120:
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+					       &kworld_atsc_120_config,
+					       &core->i2c_adap);
+		if (attach_xc3028(0x61, dev) < 0)
+			goto frontend_detach;
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+					       &dvico_fusionhdtv7_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+					&core->i2c_adap,
+					&dvico_fusionhdtv7_tuner_config))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		/* MFE frontend 1 */
+		mfe_shared = 1;
+		dev->frontends.gate = 2;
+		/* DVB-S/S2 Init */
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+					&hauppauge_hvr4000_config,
+					&dev->core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (!dvb_attach(isl6421_attach,
+					fe0->dvb.frontend,
+					&dev->core->i2c_adap,
+					0x08, ISL6421_DCL, 0x00))
+				goto frontend_detach;
+		}
+		/* MFE frontend 2 */
+		fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+		if (!fe1)
+			goto frontend_detach;
+		/* DVB-T Init */
+		fe1->dvb.frontend = dvb_attach(cx22702_attach,
+					&hauppauge_hvr_config,
+					&dev->core->i2c_adap);
+		if (fe1->dvb.frontend) {
+			fe1->dvb.frontend->id = 1;
+			if (!dvb_attach(simple_tuner_attach,
+					fe1->dvb.frontend,
+					&dev->core->i2c_adap,
+					0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+					&hauppauge_hvr4000_config,
+					&dev->core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (!dvb_attach(isl6421_attach,
+					fe0->dvb.frontend,
+					&dev->core->i2c_adap,
+					0x08, ISL6421_DCL, 0x00))
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_PROF_6200:
+	case CX88_BOARD_TBS_8910:
+	case CX88_BOARD_TEVII_S420:
+		fe0->dvb.frontend = dvb_attach(stv0299_attach,
+						&tevii_tuner_sharp_config,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+					&core->i2c_adap, DVB_PLL_OPERA1))
+				goto frontend_detach;
+			core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+		} else {
+			fe0->dvb.frontend = dvb_attach(stv0288_attach,
+							    &tevii_tuner_earda_config,
+							    &core->i2c_adap);
+				if (fe0->dvb.frontend != NULL) {
+					if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
+						&core->i2c_adap))
+					goto frontend_detach;
+				core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+				fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+			}
+		}
+		break;
+	case CX88_BOARD_TEVII_S460:
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+					       &tevii_s460_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+		break;
+	case CX88_BOARD_TEVII_S464:
+		fe0->dvb.frontend = dvb_attach(ds3000_attach,
+						&tevii_ds3000_config,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			fe0->dvb.frontend->ops.set_voltage =
+							tevii_dvbs_set_voltage;
+		break;
+	case CX88_BOARD_OMICOM_SS4_PCI:
+	case CX88_BOARD_TBS_8920:
+	case CX88_BOARD_PROF_7300:
+	case CX88_BOARD_SATTRADE_ST4200:
+		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+					       &hauppauge_hvr4000_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL)
+			fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+		break;
+	case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &cx88_terratec_cinergy_ht_pci_mkii_config,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+			if (attach_xc3028(0x61, dev) < 0)
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_PROF_7301:{
+		struct dvb_tuner_ops *tuner_ops = NULL;
+
+		fe0->dvb.frontend = dvb_attach(stv0900_attach,
+						&prof_7301_stv0900_config,
+						&core->i2c_adap, 0);
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
+					&prof_7301_stb6100_config,
+					&core->i2c_adap))
+				goto frontend_detach;
+
+			tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
+			tuner_ops->set_frequency = stb6100_set_freq;
+			tuner_ops->get_frequency = stb6100_get_freq;
+			tuner_ops->set_bandwidth = stb6100_set_bandw;
+			tuner_ops->get_bandwidth = stb6100_get_bandw;
+
+			core->prev_set_voltage =
+					fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage =
+					tevii_dvbs_set_voltage;
+		}
+		break;
+		}
+	case CX88_BOARD_SAMSUNG_SMT_7020:
+		dev->ts_gen_cntrl = 0x08;
+
+		cx_set(MO_GP0_IO, 0x0101);
+
+		cx_clear(MO_GP0_IO, 0x01);
+		mdelay(100);
+		cx_set(MO_GP0_IO, 0x01);
+		mdelay(200);
+
+		fe0->dvb.frontend = dvb_attach(stv0299_attach,
+					&samsung_stv0299_config,
+					&dev->core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.tuner_ops.set_params =
+				samsung_smt_7020_tuner_set_params;
+			fe0->dvb.frontend->tuner_priv =
+				&dev->core->i2c_adap;
+			fe0->dvb.frontend->ops.set_voltage =
+				samsung_smt_7020_set_voltage;
+			fe0->dvb.frontend->ops.set_tone =
+				samsung_smt_7020_set_tone;
+		}
+
+		break;
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		dev->ts_gen_cntrl = 0x00;
+		fe0->dvb.frontend = dvb_attach(mb86a16_attach,
+						&twinhan_vp1027,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage =
+					fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage =
+					vp1027_set_voltage;
+		}
+		break;
+
+	default:
+		printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
+		       core->name);
+		break;
+	}
+
+	if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
+		printk(KERN_ERR
+		       "%s/2: frontend initialization failed\n",
+		       core->name);
+		goto frontend_detach;
+	}
+	/* define general-purpose callback pointer */
+	fe0->dvb.frontend->callback = cx88_tuner_callback;
+
+	/* Ensure all frontends negotiate bus access */
+	fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+	if (fe1)
+		fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+
+	/* Put the analog decoder in standby to keep it quiet */
+	call_all(core, core, s_power, 0);
+
+	/* register everything */
+	res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+		&dev->pci->dev, adapter_nr, mfe_shared);
+	if (res)
+		goto frontend_detach;
+	return res;
+
+frontend_detach:
+	core->gate_ctrl = NULL;
+	videobuf_dvb_dealloc_frontends(&dev->frontends);
+	return res;
+}
+
+/* ----------------------------------------------------------- */
+
+/* CX8802 MPEG -> mini driver - We have been given the hardware */
+static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+	dprintk( 1, "%s\n", __func__);
+
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* We arrive here with either the cx23416 or the cx22702
+		 * on the bus. Take the bus from the cx23416 and enable the
+		 * cx22702 demod
+		 */
+		/* Toggle reset on cx22702 leaving i2c active */
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		cx_clear(MO_GP0_IO, 0x00000080);
+		udelay(50);
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		/* enable the cx22702 pins */
+		cx_clear(MO_GP0_IO, 0x00000004);
+		udelay(1000);
+		break;
+
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		/* Toggle reset on cx22702 leaving i2c active */
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		cx_clear(MO_GP0_IO, 0x00000080);
+		udelay(50);
+		cx_set(MO_GP0_IO, 0x00000080);
+		udelay(1000);
+		switch (core->dvbdev->frontends.active_fe_id) {
+		case 1: /* DVB-S/S2 Enabled */
+			/* tri-state the cx22702 pins */
+			cx_set(MO_GP0_IO, 0x00000004);
+			/* Take the cx24116/cx24123 out of reset */
+			cx_write(MO_SRST_IO, 1);
+			core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
+			break;
+		case 2: /* DVB-T Enabled */
+			/* Put the cx24116/cx24123 into reset */
+			cx_write(MO_SRST_IO, 0);
+			/* enable the cx22702 pins */
+			cx_clear(MO_GP0_IO, 0x00000004);
+			core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
+			break;
+		}
+		udelay(1000);
+		break;
+
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		/* set RF input to AIR for DVB-T (GPIO 16) */
+		cx_write(MO_GP2_IO, 0x0101);
+		break;
+
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+/* CX8802 MPEG -> mini driver - We no longer have the hardware */
+static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+	dprintk( 1, "%s\n", __func__);
+
+	switch (core->boardnr) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* Do Nothing, leave the cx22702 on the bus. */
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+static int cx8802_dvb_probe(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = drv->core->dvbdev;
+	int err;
+	struct videobuf_dvb_frontend *fe;
+	int i;
+
+	dprintk( 1, "%s\n", __func__);
+	dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+		core->boardnr,
+		core->name,
+		core->pci_bus,
+		core->pci_slot);
+
+	err = -ENODEV;
+	if (!(core->board.mpeg & CX88_MPEG_DVB))
+		goto fail_core;
+
+	/* If vp3054 isn't enabled, a stub will just return 0 */
+	err = vp3054_i2c_probe(dev);
+	if (0 != err)
+		goto fail_core;
+
+	/* dvb stuff */
+	printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
+	dev->ts_gen_cntrl = 0x0c;
+
+	err = cx8802_alloc_frontends(dev);
+	if (err)
+		goto fail_core;
+
+	err = -ENODEV;
+	for (i = 1; i <= core->board.num_frontends; i++) {
+		fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+		if (fe == NULL) {
+			printk(KERN_ERR "%s() failed to get frontend(%d)\n",
+					__func__, i);
+			goto fail_probe;
+		}
+		videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
+				    &dev->pci->dev, &dev->slock,
+				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+				    V4L2_FIELD_TOP,
+				    sizeof(struct cx88_buffer),
+				    dev, NULL);
+		/* init struct videobuf_dvb */
+		fe->dvb.name = dev->core->name;
+	}
+
+	err = dvb_register(dev);
+	if (err)
+		/* frontends/adapter de-allocated in dvb_register */
+		printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
+		       core->name, err);
+	return err;
+fail_probe:
+	videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
+fail_core:
+	return err;
+}
+
+static int cx8802_dvb_remove(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = drv->core->dvbdev;
+
+	dprintk( 1, "%s\n", __func__);
+
+	videobuf_dvb_unregister_bus(&dev->frontends);
+
+	vp3054_i2c_remove(dev);
+
+	core->gate_ctrl = NULL;
+
+	return 0;
+}
+
+static struct cx8802_driver cx8802_dvb_driver = {
+	.type_id        = CX88_MPEG_DVB,
+	.hw_access      = CX8802_DRVCTL_SHARED,
+	.probe          = cx8802_dvb_probe,
+	.remove         = cx8802_dvb_remove,
+	.advise_acquire = cx8802_dvb_advise_acquire,
+	.advise_release = cx8802_dvb_advise_release,
+};
+
+static int __init dvb_init(void)
+{
+	printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
+	       CX88_VERSION);
+	return cx8802_register_driver(&cx8802_dvb_driver);
+}
+
+static void __exit dvb_fini(void)
+{
+	cx8802_unregister_driver(&cx8802_dvb_driver);
+}
+
+module_init(dvb_init);
+module_exit(dvb_fini);
diff --git a/drivers/media/pci/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c
new file mode 100644
index 000000000000..de0f1af74e41
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-i2c.c
@@ -0,0 +1,184 @@
+
+/*
+
+    cx88-i2c.c  --  all the i2c code is here
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+			   & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 2002 Yurij Sysoev <yurij@naturesoft.net>
+    (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+    (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+	- Multituner support and i2c address binding
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0644);
+MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs "
+		"(should be 5 or higher). Lower value means higher bus speed.");
+
+#define dprintk(level,fmt, arg...)	if (i2c_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
+/* ----------------------------------------------------------------------- */
+
+static void cx8800_bit_setscl(void *data, int state)
+{
+	struct cx88_core *core = data;
+
+	if (state)
+		core->i2c_state |= 0x02;
+	else
+		core->i2c_state &= ~0x02;
+	cx_write(MO_I2C, core->i2c_state);
+	cx_read(MO_I2C);
+}
+
+static void cx8800_bit_setsda(void *data, int state)
+{
+	struct cx88_core *core = data;
+
+	if (state)
+		core->i2c_state |= 0x01;
+	else
+		core->i2c_state &= ~0x01;
+	cx_write(MO_I2C, core->i2c_state);
+	cx_read(MO_I2C);
+}
+
+static int cx8800_bit_getscl(void *data)
+{
+	struct cx88_core *core = data;
+	u32 state;
+
+	state = cx_read(MO_I2C);
+	return state & 0x02 ? 1 : 0;
+}
+
+static int cx8800_bit_getsda(void *data)
+{
+	struct cx88_core *core = data;
+	u32 state;
+
+	state = cx_read(MO_I2C);
+	return state & 0x01;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_algo_bit_data cx8800_i2c_algo_template = {
+	.setsda  = cx8800_bit_setsda,
+	.setscl  = cx8800_bit_setscl,
+	.getsda  = cx8800_bit_getsda,
+	.getscl  = cx8800_bit_getscl,
+	.udelay  = 16,
+	.timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static const char * const i2c_devs[128] = {
+	[ 0x1c >> 1 ] = "lgdt330x",
+	[ 0x86 >> 1 ] = "tda9887/cx22702",
+	[ 0xa0 >> 1 ] = "eeprom",
+	[ 0xc0 >> 1 ] = "tuner (analog)",
+	[ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+	[ 0xc8 >> 1 ] = "xc5000",
+};
+
+static void do_i2c_scan(const char *name, struct i2c_client *c)
+{
+	unsigned char buf;
+	int i,rc;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+		c->addr = i;
+		rc = i2c_master_recv(c,&buf,0);
+		if (rc < 0)
+			continue;
+		printk("%s: i2c scan: found device @ 0x%x  [%s]\n",
+		       name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+	}
+}
+
+/* init + register i2c adapter */
+int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+{
+	/* Prevents usage of invalid delay values */
+	if (i2c_udelay<5)
+		i2c_udelay=5;
+
+	memcpy(&core->i2c_algo, &cx8800_i2c_algo_template,
+	       sizeof(core->i2c_algo));
+
+
+	core->i2c_adap.dev.parent = &pci->dev;
+	strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name));
+	core->i2c_adap.owner = THIS_MODULE;
+	core->i2c_algo.udelay = i2c_udelay;
+	core->i2c_algo.data = core;
+	i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev);
+	core->i2c_adap.algo_data = &core->i2c_algo;
+	core->i2c_client.adapter = &core->i2c_adap;
+	strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE);
+
+	cx8800_bit_setscl(core,1);
+	cx8800_bit_setsda(core,1);
+
+	core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
+	if (0 == core->i2c_rc) {
+		static u8 tuner_data[] =
+			{ 0x0b, 0xdc, 0x86, 0x52 };
+		static struct i2c_msg tuner_msg =
+			{ .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
+
+		dprintk(1, "i2c register ok\n");
+		switch( core->boardnr ) {
+			case CX88_BOARD_HAUPPAUGE_HVR1300:
+			case CX88_BOARD_HAUPPAUGE_HVR3000:
+			case CX88_BOARD_HAUPPAUGE_HVR4000:
+				printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
+					core->name);
+				i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
+				break;
+			default:
+				break;
+		}
+		if (i2c_scan)
+			do_i2c_scan(core->name,&core->i2c_client);
+	} else
+		printk("%s: i2c register FAILED\n", core->name);
+
+	return core->i2c_rc;
+}
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
new file mode 100644
index 000000000000..ebf448c48ca3
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -0,0 +1,635 @@
+/*
+ *
+ * Device driver for GPIO attached remote control interfaces
+ * on Conexant 2388x based TV/DVB cards.
+ *
+ * Copyright (c) 2003 Pavel Machek
+ * Copyright (c) 2004 Gerd Knorr
+ * Copyright (c) 2004, 2005 Chris Pascoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/hrtimer.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "cx88.h"
+#include <media/rc-core.h>
+
+#define MODULE_NAME "cx88xx"
+
+/* ---------------------------------------------------------------------- */
+
+struct cx88_IR {
+	struct cx88_core *core;
+	struct rc_dev *dev;
+
+	int users;
+
+	char name[32];
+	char phys[32];
+
+	/* sample from gpio pin 16 */
+	u32 sampling;
+
+	/* poll external decoder */
+	int polling;
+	struct hrtimer timer;
+	u32 gpio_addr;
+	u32 last_gpio;
+	u32 mask_keycode;
+	u32 mask_keydown;
+	u32 mask_keyup;
+};
+
+static unsigned ir_samplerate = 4;
+module_param(ir_samplerate, uint, 0444);
+MODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4");
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);	/* debug level [IR] */
+MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
+
+#define ir_dprintk(fmt, arg...)	if (ir_debug) \
+	printk(KERN_DEBUG "%s IR: " fmt , ir->core->name , ##arg)
+
+#define dprintk(fmt, arg...)	if (ir_debug) \
+	printk(KERN_DEBUG "cx88 IR: " fmt , ##arg)
+
+/* ---------------------------------------------------------------------- */
+
+static void cx88_ir_handle_key(struct cx88_IR *ir)
+{
+	struct cx88_core *core = ir->core;
+	u32 gpio, data, auxgpio;
+
+	/* read gpio value */
+	gpio = cx_read(ir->gpio_addr);
+	switch (core->boardnr) {
+	case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
+		/* This board apparently uses a combination of 2 GPIO
+		   to represent the keys. Additionally, the second GPIO
+		   can be used for parity.
+
+		   Example:
+
+		   for key "5"
+			gpio = 0x758, auxgpio = 0xe5 or 0xf5
+		   for key "Power"
+			gpio = 0x758, auxgpio = 0xed or 0xfd
+		 */
+
+		auxgpio = cx_read(MO_GP1_IO);
+		/* Take out the parity part */
+		gpio=(gpio & 0x7fd) + (auxgpio & 0xef);
+		break;
+	case CX88_BOARD_WINFAST_DTV1000:
+	case CX88_BOARD_WINFAST_DTV1800H:
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+		gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900);
+		auxgpio = gpio;
+		break;
+	default:
+		auxgpio = gpio;
+	}
+	if (ir->polling) {
+		if (ir->last_gpio == auxgpio)
+			return;
+		ir->last_gpio = auxgpio;
+	}
+
+	/* extract data */
+	data = ir_extract_bits(gpio, ir->mask_keycode);
+	ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+		   gpio, data,
+		   ir->polling ? "poll" : "irq",
+		   (gpio & ir->mask_keydown) ? " down" : "",
+		   (gpio & ir->mask_keyup) ? " up" : "");
+
+	if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) {
+		u32 gpio_key = cx_read(MO_GP0_IO);
+
+		data = (data << 4) | ((gpio_key & 0xf0) >> 4);
+
+		rc_keydown(ir->dev, data, 0);
+
+	} else if (ir->mask_keydown) {
+		/* bit set on keydown */
+		if (gpio & ir->mask_keydown)
+			rc_keydown_notimeout(ir->dev, data, 0);
+		else
+			rc_keyup(ir->dev);
+
+	} else if (ir->mask_keyup) {
+		/* bit cleared on keydown */
+		if (0 == (gpio & ir->mask_keyup))
+			rc_keydown_notimeout(ir->dev, data, 0);
+		else
+			rc_keyup(ir->dev);
+
+	} else {
+		/* can't distinguish keydown/up :-/ */
+		rc_keydown_notimeout(ir->dev, data, 0);
+		rc_keyup(ir->dev);
+	}
+}
+
+static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
+{
+	unsigned long missed;
+	struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
+
+	cx88_ir_handle_key(ir);
+	missed = hrtimer_forward_now(&ir->timer,
+				     ktime_set(0, ir->polling * 1000000));
+	if (missed > 1)
+		ir_dprintk("Missed ticks %ld\n", missed - 1);
+
+	return HRTIMER_RESTART;
+}
+
+static int __cx88_ir_start(void *priv)
+{
+	struct cx88_core *core = priv;
+	struct cx88_IR *ir;
+
+	if (!core || !core->ir)
+		return -EINVAL;
+
+	ir = core->ir;
+
+	if (ir->polling) {
+		hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		ir->timer.function = cx88_ir_work;
+		hrtimer_start(&ir->timer,
+			      ktime_set(0, ir->polling * 1000000),
+			      HRTIMER_MODE_REL);
+	}
+	if (ir->sampling) {
+		core->pci_irqmask |= PCI_INT_IR_SMPINT;
+		cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */
+		cx_write(MO_DDSCFG_IO, 0x5); /* enable */
+	}
+	return 0;
+}
+
+static void __cx88_ir_stop(void *priv)
+{
+	struct cx88_core *core = priv;
+	struct cx88_IR *ir;
+
+	if (!core || !core->ir)
+		return;
+
+	ir = core->ir;
+	if (ir->sampling) {
+		cx_write(MO_DDSCFG_IO, 0x0);
+		core->pci_irqmask &= ~PCI_INT_IR_SMPINT;
+	}
+
+	if (ir->polling)
+		hrtimer_cancel(&ir->timer);
+}
+
+int cx88_ir_start(struct cx88_core *core)
+{
+	if (core->ir->users)
+		return __cx88_ir_start(core);
+
+	return 0;
+}
+
+void cx88_ir_stop(struct cx88_core *core)
+{
+	if (core->ir->users)
+		__cx88_ir_stop(core);
+}
+
+static int cx88_ir_open(struct rc_dev *rc)
+{
+	struct cx88_core *core = rc->priv;
+
+	core->ir->users++;
+	return __cx88_ir_start(core);
+}
+
+static void cx88_ir_close(struct rc_dev *rc)
+{
+	struct cx88_core *core = rc->priv;
+
+	core->ir->users--;
+	if (!core->ir->users)
+		__cx88_ir_stop(core);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+{
+	struct cx88_IR *ir;
+	struct rc_dev *dev;
+	char *ir_codes = NULL;
+	u64 rc_type = RC_TYPE_OTHER;
+	int err = -ENOMEM;
+	u32 hardware_mask = 0;	/* For devices with a hardware mask, when
+				 * used with a full-code IR table
+				 */
+
+	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+	dev = rc_allocate_device();
+	if (!ir || !dev)
+		goto err_out_free;
+
+	ir->dev = dev;
+
+	/* detect & configure */
+	switch (core->boardnr) {
+	case CX88_BOARD_DNTV_LIVE_DVB_T:
+	case CX88_BOARD_KWORLD_DVB_T:
+	case CX88_BOARD_KWORLD_DVB_T_CX22702:
+		ir_codes = RC_MAP_DNTV_LIVE_DVB_T;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keycode = 0x1f;
+		ir->mask_keyup = 0x60;
+		ir->polling = 50; /* ms */
+		break;
+	case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
+		ir_codes = RC_MAP_CINERGY_1400;
+		ir->sampling = 0xeb04; /* address */
+		break;
+	case CX88_BOARD_HAUPPAUGE:
+	case CX88_BOARD_HAUPPAUGE_DVB_T1:
+	case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+	case CX88_BOARD_HAUPPAUGE_HVR1100:
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+	case CX88_BOARD_PCHDTV_HD3000:
+	case CX88_BOARD_PCHDTV_HD5500:
+	case CX88_BOARD_HAUPPAUGE_IRONLY:
+		ir_codes = RC_MAP_HAUPPAUGE;
+		ir->sampling = 1;
+		break;
+	case CX88_BOARD_WINFAST_DTV2000H:
+	case CX88_BOARD_WINFAST_DTV2000H_J:
+	case CX88_BOARD_WINFAST_DTV1800H:
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		ir_codes = RC_MAP_WINFAST;
+		ir->gpio_addr = MO_GP0_IO;
+		ir->mask_keycode = 0x8f8;
+		ir->mask_keyup = 0x100;
+		ir->polling = 50; /* ms */
+		break;
+	case CX88_BOARD_WINFAST2000XP_EXPERT:
+	case CX88_BOARD_WINFAST_DTV1000:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+		ir_codes = RC_MAP_WINFAST;
+		ir->gpio_addr = MO_GP0_IO;
+		ir->mask_keycode = 0x8f8;
+		ir->mask_keyup = 0x100;
+		ir->polling = 1; /* ms */
+		break;
+	case CX88_BOARD_IODATA_GVBCTV7E:
+		ir_codes = RC_MAP_IODATA_BCTV7E;
+		ir->gpio_addr = MO_GP0_IO;
+		ir->mask_keycode = 0xfd;
+		ir->mask_keydown = 0x02;
+		ir->polling = 5; /* ms */
+		break;
+	case CX88_BOARD_PROLINK_PLAYTVPVR:
+	case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO:
+		/*
+		 * It seems that this hardware is paired with NEC extended
+		 * address 0x866b. So, unfortunately, its usage with other
+		 * IR's with different address won't work. Still, there are
+		 * other IR's from the same manufacturer that works, like the
+		 * 002-T mini RC, provided with newer PV hardware
+		 */
+		ir_codes = RC_MAP_PIXELVIEW_MK12;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keyup = 0x80;
+		ir->polling = 10; /* ms */
+		hardware_mask = 0x3f;	/* Hardware returns only 6 bits from command part */
+		break;
+	case CX88_BOARD_PROLINK_PV_8000GT:
+	case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+		ir_codes = RC_MAP_PIXELVIEW_NEW;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keycode = 0x3f;
+		ir->mask_keyup = 0x80;
+		ir->polling = 1; /* ms */
+		break;
+	case CX88_BOARD_KWORLD_LTV883:
+		ir_codes = RC_MAP_PIXELVIEW;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keycode = 0x1f;
+		ir->mask_keyup = 0x60;
+		ir->polling = 1; /* ms */
+		break;
+	case CX88_BOARD_ADSTECH_DVB_T_PCI:
+		ir_codes = RC_MAP_ADSTECH_DVB_T_PCI;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keycode = 0xbf;
+		ir->mask_keyup = 0x40;
+		ir->polling = 50; /* ms */
+		break;
+	case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+		ir_codes = RC_MAP_MSI_TVANYWHERE;
+		ir->gpio_addr = MO_GP1_IO;
+		ir->mask_keycode = 0x1f;
+		ir->mask_keyup = 0x40;
+		ir->polling = 1; /* ms */
+		break;
+	case CX88_BOARD_AVERTV_303:
+	case CX88_BOARD_AVERTV_STUDIO_303:
+		ir_codes         = RC_MAP_AVERTV_303;
+		ir->gpio_addr    = MO_GP2_IO;
+		ir->mask_keycode = 0xfb;
+		ir->mask_keydown = 0x02;
+		ir->polling      = 50; /* ms */
+		break;
+	case CX88_BOARD_OMICOM_SS4_PCI:
+	case CX88_BOARD_SATTRADE_ST4200:
+	case CX88_BOARD_TBS_8920:
+	case CX88_BOARD_TBS_8910:
+	case CX88_BOARD_PROF_7300:
+	case CX88_BOARD_PROF_7301:
+	case CX88_BOARD_PROF_6200:
+		ir_codes = RC_MAP_TBS_NEC;
+		ir->sampling = 0xff00; /* address */
+		break;
+	case CX88_BOARD_TEVII_S464:
+	case CX88_BOARD_TEVII_S460:
+	case CX88_BOARD_TEVII_S420:
+		ir_codes = RC_MAP_TEVII_NEC;
+		ir->sampling = 0xff00; /* address */
+		break;
+	case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+		ir_codes         = RC_MAP_DNTV_LIVE_DVBT_PRO;
+		ir->sampling     = 0xff00; /* address */
+		break;
+	case CX88_BOARD_NORWOOD_MICRO:
+		ir_codes         = RC_MAP_NORWOOD;
+		ir->gpio_addr    = MO_GP1_IO;
+		ir->mask_keycode = 0x0e;
+		ir->mask_keyup   = 0x80;
+		ir->polling      = 50; /* ms */
+		break;
+	case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
+		ir_codes         = RC_MAP_NPGTECH;
+		ir->gpio_addr    = MO_GP0_IO;
+		ir->mask_keycode = 0xfa;
+		ir->polling      = 50; /* ms */
+		break;
+	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+		ir_codes         = RC_MAP_PINNACLE_PCTV_HD;
+		ir->sampling     = 1;
+		break;
+	case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+		ir_codes         = RC_MAP_POWERCOLOR_REAL_ANGEL;
+		ir->gpio_addr    = MO_GP2_IO;
+		ir->mask_keycode = 0x7e;
+		ir->polling      = 100; /* ms */
+		break;
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		ir_codes         = RC_MAP_TWINHAN_VP1027_DVBS;
+		rc_type          = RC_TYPE_NEC;
+		ir->sampling     = 0xff00; /* address */
+		break;
+	}
+
+	if (!ir_codes) {
+		err = -ENODEV;
+		goto err_out_free;
+	}
+
+	/*
+	 * The usage of mask_keycode were very convenient, due to several
+	 * reasons. Among others, the scancode tables were using the scancode
+	 * as the index elements. So, the less bits it was used, the smaller
+	 * the table were stored. After the input changes, the better is to use
+	 * the full scancodes, since it allows replacing the IR remote by
+	 * another one. Unfortunately, there are still some hardware, like
+	 * Pixelview Ultra Pro, where only part of the scancode is sent via
+	 * GPIO. So, there's no way to get the full scancode. Due to that,
+	 * hardware_mask were introduced here: it represents those hardware
+	 * that has such limits.
+	 */
+	if (hardware_mask && !ir->mask_keycode)
+		ir->mask_keycode = hardware_mask;
+
+	/* init input device */
+	snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
+	snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
+
+	dev->input_name = ir->name;
+	dev->input_phys = ir->phys;
+	dev->input_id.bustype = BUS_PCI;
+	dev->input_id.version = 1;
+	if (pci->subsystem_vendor) {
+		dev->input_id.vendor = pci->subsystem_vendor;
+		dev->input_id.product = pci->subsystem_device;
+	} else {
+		dev->input_id.vendor = pci->vendor;
+		dev->input_id.product = pci->device;
+	}
+	dev->dev.parent = &pci->dev;
+	dev->map_name = ir_codes;
+	dev->driver_name = MODULE_NAME;
+	dev->priv = core;
+	dev->open = cx88_ir_open;
+	dev->close = cx88_ir_close;
+	dev->scanmask = hardware_mask;
+
+	if (ir->sampling) {
+		dev->driver_type = RC_DRIVER_IR_RAW;
+		dev->timeout = 10 * 1000 * 1000; /* 10 ms */
+	} else {
+		dev->driver_type = RC_DRIVER_SCANCODE;
+		dev->allowed_protos = rc_type;
+	}
+
+	ir->core = core;
+	core->ir = ir;
+
+	/* all done */
+	err = rc_register_device(dev);
+	if (err)
+		goto err_out_free;
+
+	return 0;
+
+err_out_free:
+	rc_free_device(dev);
+	core->ir = NULL;
+	kfree(ir);
+	return err;
+}
+
+int cx88_ir_fini(struct cx88_core *core)
+{
+	struct cx88_IR *ir = core->ir;
+
+	/* skip detach on non attached boards */
+	if (NULL == ir)
+		return 0;
+
+	cx88_ir_stop(core);
+	rc_unregister_device(ir->dev);
+	kfree(ir);
+
+	/* done */
+	core->ir = NULL;
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void cx88_ir_irq(struct cx88_core *core)
+{
+	struct cx88_IR *ir = core->ir;
+	u32 samples;
+	unsigned todo, bits;
+	struct ir_raw_event ev;
+
+	if (!ir || !ir->sampling)
+		return;
+
+	/*
+	 * Samples are stored in a 32 bit register, oldest sample in
+	 * the msb. A set bit represents space and an unset bit
+	 * represents a pulse.
+	 */
+	samples = cx_read(MO_SAMPLE_IO);
+
+	if (samples == 0xff && ir->dev->idle)
+		return;
+
+	init_ir_raw_event(&ev);
+	for (todo = 32; todo > 0; todo -= bits) {
+		ev.pulse = samples & 0x80000000 ? false : true;
+		bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples));
+		ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate;
+		ir_raw_event_store_with_filter(ir->dev, &ev);
+		samples <<= bits;
+	}
+	ir_raw_event_handle(ir->dev);
+}
+
+static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	int flags, code;
+
+	/* poll IR chip */
+	flags = i2c_smbus_read_byte_data(ir->c, 0x10);
+	if (flags < 0) {
+		dprintk("read error\n");
+		return 0;
+	}
+	/* key pressed ? */
+	if (0 == (flags & 0x80))
+		return 0;
+
+	/* read actual key code */
+	code = i2c_smbus_read_byte_data(ir->c, 0x00);
+	if (code < 0) {
+		dprintk("read error\n");
+		return 0;
+	}
+
+	dprintk("IR Key/Flags: (0x%02x/0x%02x)\n",
+		   code & 0xff, flags & 0xff);
+
+	*ir_key = code & 0xff;
+	*ir_raw = code;
+	return 1;
+}
+
+void cx88_i2c_init_ir(struct cx88_core *core)
+{
+	struct i2c_board_info info;
+	const unsigned short default_addr_list[] = {
+		0x18, 0x6b, 0x71,
+		I2C_CLIENT_END
+	};
+	const unsigned short pvr2000_addr_list[] = {
+		0x18, 0x1a,
+		I2C_CLIENT_END
+	};
+	const unsigned short *addr_list = default_addr_list;
+	const unsigned short *addrp;
+	/* Instantiate the IR receiver device, if present */
+	if (0 != core->i2c_rc)
+		return;
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+	switch (core->boardnr) {
+	case CX88_BOARD_LEADTEK_PVR2000:
+		addr_list = pvr2000_addr_list;
+		core->init_data.name = "cx88 Leadtek PVR 2000 remote";
+		core->init_data.type = RC_TYPE_UNKNOWN;
+		core->init_data.get_key = get_key_pvr2000;
+		core->init_data.ir_codes = RC_MAP_EMPTY;
+		break;
+	}
+
+	/*
+	 * We can't call i2c_new_probed_device() because it uses
+	 * quick writes for probing and at least some RC receiver
+	 * devices only reply to reads.
+	 * Also, Hauppauge XVR needs to be specified, as address 0x71
+	 * conflicts with another remote type used with saa7134
+	 */
+	for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
+		info.platform_data = NULL;
+		memset(&core->init_data, 0, sizeof(core->init_data));
+
+		if (*addrp == 0x71) {
+			/* Hauppauge XVR */
+			core->init_data.name = "cx88 Hauppauge XVR remote";
+			core->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+			core->init_data.type = RC_TYPE_RC5;
+			core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+
+			info.platform_data = &core->init_data;
+		}
+		if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
+					I2C_SMBUS_READ, 0,
+					I2C_SMBUS_QUICK, NULL) >= 0) {
+			info.addr = *addrp;
+			i2c_new_device(&core->i2c_adap, &info);
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe");
+MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
new file mode 100644
index 000000000000..c04fb618e10b
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
@@ -0,0 +1,929 @@
+/*
+ *
+ *  Support for the mpeg transport stream transfers
+ *  PCI function #2 of the cx2388x.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.us>
+ *    (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.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/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <asm/delay.h>
+
+#include "cx88.h"
+
+/* ------------------------------------------------------------------ */
+
+MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
+
+#define dprintk(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
+
+#define mpeg_dbg(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
+
+	if (dev->core->board.mpeg & CX88_MPEG_DVB)
+		request_module("cx88-dvb");
+	if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
+		request_module("cx88-blackbird");
+}
+
+static void request_modules(struct cx8802_dev *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx8802_dev *dev)
+{
+	flush_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+static LIST_HEAD(cx8802_devlist);
+static DEFINE_MUTEX(cx8802_mutex);
+/* ------------------------------------------------------------------ */
+
+static int cx8802_start_dma(struct cx8802_dev    *dev,
+			    struct cx88_dmaqueue *q,
+			    struct cx88_buffer   *buf)
+{
+	struct cx88_core *core = dev->core;
+
+	dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
+		buf->vb.width, buf->vb.height, buf->vb.field);
+
+	/* setup fifo + format */
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
+				dev->ts_packet_size, buf->risc.dma);
+
+	/* write TS length to chip */
+	cx_write(MO_TS_LNGTH, buf->vb.width);
+
+	/* FIXME: this needs a review.
+	 * also: move to cx88-blackbird + cx88-dvb source files? */
+
+	dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
+
+	if ( (core->active_type_id == CX88_MPEG_DVB) &&
+		(core->board.mpeg & CX88_MPEG_DVB) ) {
+
+		dprintk( 1, "cx8802_start_dma doing .dvb\n");
+		/* negedge driven & software reset */
+		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);
+		switch (core->boardnr) {
+		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+		case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+		case CX88_BOARD_PCHDTV_HD5500:
+			cx_write(TS_SOP_STAT, 1<<13);
+			break;
+		case CX88_BOARD_SAMSUNG_SMT_7020:
+			cx_write(TS_SOP_STAT, 0x00);
+			break;
+		case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+		case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+			cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
+			udelay(100);
+			break;
+		case CX88_BOARD_HAUPPAUGE_HVR1300:
+			/* Enable MPEG parallel IO and video signal pins */
+			cx_write(MO_PINMUX_IO, 0x88);
+			cx_write(TS_SOP_STAT, 0);
+			cx_write(TS_VALERR_CNTRL, 0);
+			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;
+		}
+		cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
+		udelay(100);
+	} else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
+		(core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {
+		dprintk( 1, "cx8802_start_dma doing .blackbird\n");
+		cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
+
+		cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
+		udelay(100);
+
+		cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
+		cx_write(TS_VALERR_CNTRL, 0x2000);
+
+		cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
+		udelay(100);
+	} else {
+		printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__,
+			core->board.mpeg );
+		return -EINVAL;
+	}
+
+	/* reset counter */
+	cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
+	q->count = 1;
+
+	/* enable irqs */
+	dprintk( 1, "setting the interrupt mask\n" );
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
+	cx_set(MO_TS_INTMSK,  0x1f0011);
+
+	/* start dma */
+	cx_set(MO_DEV_CNTRL2, (1<<5));
+	cx_set(MO_TS_DMACNTRL, 0x11);
+	return 0;
+}
+
+static int cx8802_stop_dma(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	dprintk( 1, "cx8802_stop_dma\n" );
+
+	/* stop dma */
+	cx_clear(MO_TS_DMACNTRL, 0x11);
+
+	/* disable irqs */
+	cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
+	cx_clear(MO_TS_INTMSK, 0x1f0011);
+
+	/* Reset the controller */
+	cx_write(TS_GEN_CNTRL, 0xcd);
+	return 0;
+}
+
+static int cx8802_restart_queue(struct cx8802_dev    *dev,
+				struct cx88_dmaqueue *q)
+{
+	struct cx88_buffer *buf;
+
+	dprintk( 1, "cx8802_restart_queue\n" );
+	if (list_empty(&q->active))
+	{
+		struct cx88_buffer *prev;
+		prev = NULL;
+
+		dprintk(1, "cx8802_restart_queue: queue is empty\n" );
+
+		for (;;) {
+			if (list_empty(&q->queued))
+				return 0;
+			buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
+			if (NULL == prev) {
+				list_del(&buf->vb.queue);
+				list_add_tail(&buf->vb.queue,&q->active);
+				cx8802_start_dma(dev, q, buf);
+				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",
+					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,&q->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",
+					buf,buf->vb.i);
+			} else {
+				return 0;
+			}
+			prev = buf;
+		}
+		return 0;
+	}
+
+	buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+	dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+		buf, buf->vb.i);
+	cx8802_start_dma(dev, q, buf);
+	list_for_each_entry(buf, &q->active, vb.queue)
+		buf->count = q->count++;
+	mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
+			struct cx88_buffer *buf, enum v4l2_field field)
+{
+	int size = dev->ts_packet_size * dev->ts_packet_count;
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+	int rc;
+
+	dprintk(1, "%s: %p\n", __func__, buf);
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	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;
+		buf->vb.field  = field /*V4L2_FIELD_TOP*/;
+
+		if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+			goto fail;
+		cx88_risc_databuffer(dev->pci, &buf->risc,
+				     dma->sglist,
+				     buf->vb.width, buf->vb.height, 0);
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+ fail:
+	cx88_free_buffer(q,buf);
+	return rc;
+}
+
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+	struct cx88_buffer    *prev;
+	struct cx88_dmaqueue  *cx88q = &dev->mpegq;
+
+	dprintk( 1, "cx8802_buf_queue\n" );
+	/* 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(cx88q->stopper.dma);
+
+	if (list_empty(&cx88q->active)) {
+		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 = VIDEOBUF_ACTIVE;
+		buf->count    = cx88q->count++;
+		mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
+		dprintk(1,"[%p/%d] %s - first active\n",
+			buf, buf->vb.i, __func__);
+
+	} else {
+		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 = 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",
+			buf, buf->vb.i, __func__);
+	}
+}
+
+/* ----------------------------------------------------------- */
+
+static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
+{
+	struct cx88_dmaqueue *q = &dev->mpegq;
+	struct cx88_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->slock,flags);
+	while (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+		list_del(&buf->vb.queue);
+		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);
+	}
+	if (restart)
+	{
+		dprintk(1, "restarting queue\n" );
+		cx8802_restart_queue(dev,q);
+	}
+	spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+void cx8802_cancel_buffers(struct cx8802_dev *dev)
+{
+	struct cx88_dmaqueue *q = &dev->mpegq;
+
+	dprintk( 1, "cx8802_cancel_buffers" );
+	del_timer_sync(&q->timeout);
+	cx8802_stop_dma(dev);
+	do_cancel_buffers(dev,"cancel",0);
+}
+
+static void cx8802_timeout(unsigned long data)
+{
+	struct cx8802_dev *dev = (struct cx8802_dev*)data;
+
+	dprintk(1, "%s\n",__func__);
+
+	if (debug)
+		cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+	cx8802_stop_dma(dev);
+	do_cancel_buffers(dev,"timeout",1);
+}
+
+static const char * cx88_mpeg_irqs[32] = {
+	"ts_risci1", NULL, NULL, NULL,
+	"ts_risci2", NULL, NULL, NULL,
+	"ts_oflow",  NULL, NULL, NULL,
+	"ts_sync",   NULL, NULL, NULL,
+	"opc_err", "par_err", "rip_err", "pci_abort",
+	"ts_err?",
+};
+
+static void cx8802_mpeg_irq(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	u32 status, mask, count;
+
+	dprintk( 1, "cx8802_mpeg_irq\n" );
+	status = cx_read(MO_TS_INTSTAT);
+	mask   = cx_read(MO_TS_INTMSK);
+	if (0 == (status & mask))
+		return;
+
+	cx_write(MO_TS_INTSTAT, status);
+
+	if (debug || (status & mask & ~0xff))
+		cx88_print_irqbits(core->name, "irq mpeg ",
+				   cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
+				   status, mask);
+
+	/* risc op code error */
+	if (status & (1 << 16)) {
+		printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
+		cx_clear(MO_TS_DMACNTRL, 0x11);
+		cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+	}
+
+	/* risc1 y */
+	if (status & 0x01) {
+		dprintk( 1, "wake up\n" );
+		spin_lock(&dev->slock);
+		count = cx_read(MO_TS_GPCNT);
+		cx88_wakeup(dev->core, &dev->mpegq, count);
+		spin_unlock(&dev->slock);
+	}
+
+	/* risc2 y */
+	if (status & 0x10) {
+		spin_lock(&dev->slock);
+		cx8802_restart_queue(dev,&dev->mpegq);
+		spin_unlock(&dev->slock);
+	}
+
+	/* other general errors */
+	if (status & 0x1f0100) {
+		dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
+		spin_lock(&dev->slock);
+		cx8802_stop_dma(dev);
+		cx8802_restart_queue(dev,&dev->mpegq);
+		spin_unlock(&dev->slock);
+	}
+}
+
+#define MAX_IRQ_LOOP 10
+
+static irqreturn_t cx8802_irq(int irq, void *dev_id)
+{
+	struct cx8802_dev *dev = dev_id;
+	struct cx88_core *core = dev->core;
+	u32 status;
+	int loop, handled = 0;
+
+	for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+		status = cx_read(MO_PCI_INTSTAT) &
+			(core->pci_irqmask | PCI_INT_TSINT);
+		if (0 == status)
+			goto out;
+		dprintk( 1, "cx8802_irq\n" );
+		dprintk( 1, "    loop: %d/%d\n", loop, MAX_IRQ_LOOP );
+		dprintk( 1, "    status: %d\n", status );
+		handled = 1;
+		cx_write(MO_PCI_INTSTAT, status);
+
+		if (status & core->pci_irqmask)
+			cx88_core_irq(core,status);
+		if (status & PCI_INT_TSINT)
+			cx8802_mpeg_irq(dev);
+	};
+	if (MAX_IRQ_LOOP == loop) {
+		dprintk( 0, "clearing mask\n" );
+		printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+		       core->name);
+		cx_write(MO_PCI_INTMSK,0);
+	}
+
+ out:
+	return IRQ_RETVAL(handled);
+}
+
+static int cx8802_init_common(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	int err;
+
+	/* pci init */
+	if (pci_enable_device(dev->pci))
+		return -EIO;
+	pci_set_master(dev->pci);
+	if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) {
+		printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
+		return -EIO;
+	}
+
+	dev->pci_rev = dev->pci->revision;
+	pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER,  &dev->pci_lat);
+	printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", dev->core->name,
+	       pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
+	       dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
+
+	/* initialize driver struct */
+	spin_lock_init(&dev->slock);
+
+	/* init dma queue */
+	INIT_LIST_HEAD(&dev->mpegq.active);
+	INIT_LIST_HEAD(&dev->mpegq.queued);
+	dev->mpegq.timeout.function = cx8802_timeout;
+	dev->mpegq.timeout.data     = (unsigned long)dev;
+	init_timer(&dev->mpegq.timeout);
+	cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
+			  MO_TS_DMACNTRL,0x11,0x00);
+
+	/* get irq */
+	err = request_irq(dev->pci->irq, cx8802_irq,
+			  IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);
+	if (err < 0) {
+		printk(KERN_ERR "%s: can't get IRQ %d\n",
+		       dev->core->name, dev->pci->irq);
+		return err;
+	}
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+	/* everything worked */
+	pci_set_drvdata(dev->pci,dev);
+	return 0;
+}
+
+static void cx8802_fini_common(struct cx8802_dev *dev)
+{
+	dprintk( 2, "cx8802_fini_common\n" );
+	cx8802_stop_dma(dev);
+	pci_disable_device(dev->pci);
+
+	/* unregister stuff */
+	free_irq(dev->pci->irq, dev);
+	pci_set_drvdata(dev->pci, NULL);
+
+	/* free memory */
+	btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
+}
+
+/* ----------------------------------------------------------- */
+
+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;
+
+	/* stop mpeg dma */
+	spin_lock(&dev->slock);
+	if (!list_empty(&dev->mpegq.active)) {
+		dprintk( 2, "suspend\n" );
+		printk("%s: suspend mpeg\n", core->name);
+		cx8802_stop_dma(dev);
+		del_timer(&dev->mpegq.timeout);
+	}
+	spin_unlock(&dev->slock);
+
+	/* FIXME -- shutdown device */
+	cx88_shutdown(dev->core);
+
+	pci_save_state(pci_dev);
+	if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+		pci_disable_device(pci_dev);
+		dev->state.disabled = 1;
+	}
+	return 0;
+}
+
+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;
+	int err;
+
+	if (dev->state.disabled) {
+		err=pci_enable_device(pci_dev);
+		if (err) {
+			printk(KERN_ERR "%s: can't enable device\n",
+					       dev->core->name);
+			return err;
+		}
+		dev->state.disabled = 0;
+	}
+	err=pci_set_power_state(pci_dev, PCI_D0);
+	if (err) {
+		printk(KERN_ERR "%s: can't enable device\n",
+					       dev->core->name);
+		pci_disable_device(pci_dev);
+		dev->state.disabled = 1;
+
+		return err;
+	}
+	pci_restore_state(pci_dev);
+
+	/* FIXME: re-initialize hardware */
+	cx88_reset(dev->core);
+
+	/* restart video+vbi capture */
+	spin_lock(&dev->slock);
+	if (!list_empty(&dev->mpegq.active)) {
+		printk("%s: resume mpeg\n", core->name);
+		cx8802_restart_queue(dev,&dev->mpegq);
+	}
+	spin_unlock(&dev->slock);
+
+	return 0;
+}
+
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
+{
+	struct cx8802_driver *d;
+
+	list_for_each_entry(d, &dev->drvlist, drvlist)
+		if (d->type_id == btype)
+			return d;
+
+	return NULL;
+}
+
+/* Driver asked for hardware access. */
+static int cx8802_request_acquire(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	unsigned int	i;
+
+	/* Fail a request for hardware if the device is busy. */
+	if (core->active_type_id != CX88_BOARD_NONE &&
+	    core->active_type_id != drv->type_id)
+		return -EBUSY;
+
+	if (drv->type_id == CX88_MPEG_DVB) {
+		/* When switching to DVB, always set the input to the tuner */
+		core->last_analog_input = core->input;
+		core->input = 0;
+		for (i = 0;
+		     i < (sizeof(core->board.input) / sizeof(struct cx88_input));
+		     i++) {
+			if (core->board.input[i].type == CX88_VMUX_DVB) {
+				core->input = i;
+				break;
+			}
+		}
+	}
+
+	if (drv->advise_acquire)
+	{
+		core->active_ref++;
+		if (core->active_type_id == CX88_BOARD_NONE) {
+			core->active_type_id = drv->type_id;
+			drv->advise_acquire(drv);
+		}
+
+		mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+	}
+
+	return 0;
+}
+
+/* Driver asked to release hardware. */
+static int cx8802_request_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+
+	if (drv->advise_release && --core->active_ref == 0)
+	{
+		if (drv->type_id == CX88_MPEG_DVB) {
+			/* If the DVB driver is releasing, reset the input
+			   state to the last configured analog input */
+			core->input = core->last_analog_input;
+		}
+
+		drv->advise_release(drv);
+		core->active_type_id = CX88_BOARD_NONE;
+		mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+	}
+
+	return 0;
+}
+
+static int cx8802_check_driver(struct cx8802_driver *drv)
+{
+	if (drv == NULL)
+		return -ENODEV;
+
+	if ((drv->type_id != CX88_MPEG_DVB) &&
+		(drv->type_id != CX88_MPEG_BLACKBIRD))
+		return -EINVAL;
+
+	if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
+		(drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
+		return -EINVAL;
+
+	if ((drv->probe == NULL) ||
+		(drv->remove == NULL) ||
+		(drv->advise_acquire == NULL) ||
+		(drv->advise_release == NULL))
+		return -EINVAL;
+
+	return 0;
+}
+
+int cx8802_register_driver(struct cx8802_driver *drv)
+{
+	struct cx8802_dev *dev;
+	struct cx8802_driver *driver;
+	int err, i = 0;
+
+	printk(KERN_INFO
+	       "cx88/2: registering cx8802 driver, type: %s access: %s\n",
+	       drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+	       drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+	if ((err = cx8802_check_driver(drv)) != 0) {
+		printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");
+		return err;
+	}
+
+	mutex_lock(&cx8802_mutex);
+
+	list_for_each_entry(dev, &cx8802_devlist, devlist) {
+		printk(KERN_INFO
+		       "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+		       dev->core->name, dev->pci->subsystem_vendor,
+		       dev->pci->subsystem_device, dev->core->board.name,
+		       dev->core->boardnr);
+
+		/* Bring up a new struct for each driver instance */
+		driver = kzalloc(sizeof(*drv),GFP_KERNEL);
+		if (driver == NULL) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		/* Snapshot of the driver registration data */
+		drv->core = dev->core;
+		drv->suspend = cx8802_suspend_common;
+		drv->resume = cx8802_resume_common;
+		drv->request_acquire = cx8802_request_acquire;
+		drv->request_release = cx8802_request_release;
+		memcpy(driver, drv, sizeof(*driver));
+
+		mutex_lock(&drv->core->lock);
+		err = drv->probe(driver);
+		if (err == 0) {
+			i++;
+			list_add_tail(&driver->drvlist, &dev->drvlist);
+		} else {
+			printk(KERN_ERR
+			       "%s/2: cx8802 probe failed, err = %d\n",
+			       dev->core->name, err);
+		}
+		mutex_unlock(&drv->core->lock);
+	}
+
+	err = i ? 0 : -ENODEV;
+out:
+	mutex_unlock(&cx8802_mutex);
+	return err;
+}
+
+int cx8802_unregister_driver(struct cx8802_driver *drv)
+{
+	struct cx8802_dev *dev;
+	struct cx8802_driver *d, *dtmp;
+	int err = 0;
+
+	printk(KERN_INFO
+	       "cx88/2: unregistering cx8802 driver, type: %s access: %s\n",
+	       drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+	       drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+	mutex_lock(&cx8802_mutex);
+
+	list_for_each_entry(dev, &cx8802_devlist, devlist) {
+		printk(KERN_INFO
+		       "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+		       dev->core->name, dev->pci->subsystem_vendor,
+		       dev->pci->subsystem_device, dev->core->board.name,
+		       dev->core->boardnr);
+
+		mutex_lock(&dev->core->lock);
+
+		list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
+			/* only unregister the correct driver type */
+			if (d->type_id != drv->type_id)
+				continue;
+
+			err = d->remove(d);
+			if (err == 0) {
+				list_del(&d->drvlist);
+				kfree(d);
+			} else
+				printk(KERN_ERR "%s/2: cx8802 driver remove "
+				       "failed (%d)\n", dev->core->name, err);
+		}
+
+		mutex_unlock(&dev->core->lock);
+	}
+
+	mutex_unlock(&cx8802_mutex);
+
+	return err;
+}
+
+/* ----------------------------------------------------------- */
+static int __devinit cx8802_probe(struct pci_dev *pci_dev,
+			       const struct pci_device_id *pci_id)
+{
+	struct cx8802_dev *dev;
+	struct cx88_core  *core;
+	int err;
+
+	/* general setup */
+	core = cx88_core_get(pci_dev);
+	if (NULL == core)
+		return -EINVAL;
+
+	printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
+
+	err = -ENODEV;
+	if (!core->board.mpeg)
+		goto fail_core;
+
+	err = -ENOMEM;
+	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+	if (NULL == dev)
+		goto fail_core;
+	dev->pci = pci_dev;
+	dev->core = core;
+
+	/* Maintain a reference so cx88-video can query the 8802 device. */
+	core->dvbdev = dev;
+
+	err = cx8802_init_common(dev);
+	if (err != 0)
+		goto fail_free;
+
+	INIT_LIST_HEAD(&dev->drvlist);
+	mutex_lock(&cx8802_mutex);
+	list_add_tail(&dev->devlist,&cx8802_devlist);
+	mutex_unlock(&cx8802_mutex);
+
+	/* now autoload cx88-dvb or cx88-blackbird */
+	request_modules(dev);
+	return 0;
+
+ fail_free:
+	kfree(dev);
+ fail_core:
+	core->dvbdev = NULL;
+	cx88_core_put(core,pci_dev);
+	return err;
+}
+
+static void __devexit cx8802_remove(struct pci_dev *pci_dev)
+{
+	struct cx8802_dev *dev;
+
+	dev = pci_get_drvdata(pci_dev);
+
+	dprintk( 1, "%s\n", __func__);
+
+	flush_request_modules(dev);
+
+	mutex_lock(&dev->core->lock);
+
+	if (!list_empty(&dev->drvlist)) {
+		struct cx8802_driver *drv, *tmp;
+		int err;
+
+		printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "
+		       "while cx8802 sub-drivers still loaded?!\n",
+		       dev->core->name);
+
+		list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
+			err = drv->remove(drv);
+			if (err == 0) {
+				list_del(&drv->drvlist);
+			} else
+				printk(KERN_ERR "%s/2: cx8802 driver remove "
+				       "failed (%d)\n", dev->core->name, err);
+			kfree(drv);
+		}
+	}
+
+	mutex_unlock(&dev->core->lock);
+
+	/* Destroy any 8802 reference. */
+	dev->core->dvbdev = NULL;
+
+	/* common */
+	cx8802_fini_common(dev);
+	cx88_core_put(dev->core,dev->pci);
+	kfree(dev);
+}
+
+static const struct pci_device_id cx8802_pci_tbl[] = {
+	{
+		.vendor       = 0x14f1,
+		.device       = 0x8802,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	},{
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver cx8802_pci_driver = {
+	.name     = "cx88-mpeg driver manager",
+	.id_table = cx8802_pci_tbl,
+	.probe    = cx8802_probe,
+	.remove   = __devexit_p(cx8802_remove),
+};
+
+static int __init cx8802_init(void)
+{
+	printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
+	       CX88_VERSION);
+	return pci_register_driver(&cx8802_pci_driver);
+}
+
+static void __exit cx8802_fini(void)
+{
+	pci_unregister_driver(&cx8802_pci_driver);
+}
+
+module_init(cx8802_init);
+module_exit(cx8802_fini);
+EXPORT_SYMBOL(cx8802_buf_prepare);
+EXPORT_SYMBOL(cx8802_buf_queue);
+EXPORT_SYMBOL(cx8802_cancel_buffers);
+
+EXPORT_SYMBOL(cx8802_register_driver);
+EXPORT_SYMBOL(cx8802_unregister_driver);
+EXPORT_SYMBOL(cx8802_get_driver);
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/pci/cx88/cx88-reg.h b/drivers/media/pci/cx88/cx88-reg.h
new file mode 100644
index 000000000000..2ec52d1cdea0
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-reg.h
@@ -0,0 +1,836 @@
+/*
+
+    cx88x-hw.h - CX2388x register offsets
+
+    Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+		  2001 Michael Eskin
+		  2002 Yurij Sysoev <yurij@naturesoft.net>
+		  2003 Gerd Knorr <kraxel@bytesex.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 _CX88_REG_H_
+#define _CX88_REG_H_
+
+/* ---------------------------------------------------------------------- */
+/* PCI IDs and config space                                               */
+
+#ifndef PCI_VENDOR_ID_CONEXANT
+# define PCI_VENDOR_ID_CONEXANT		0x14F1
+#endif
+#ifndef PCI_DEVICE_ID_CX2300_VID
+# define PCI_DEVICE_ID_CX2300_VID	0x8800
+#endif
+
+#define CX88X_DEVCTRL 0x40
+#define CX88X_EN_TBFX 0x02
+#define CX88X_EN_VSFX 0x04
+
+/* ---------------------------------------------------------------------- */
+/* PCI controller registers                                               */
+
+/* Command and Status Register */
+#define F0_CMD_STAT_MM      0x2f0004
+#define F1_CMD_STAT_MM      0x2f0104
+#define F2_CMD_STAT_MM      0x2f0204
+#define F3_CMD_STAT_MM      0x2f0304
+#define F4_CMD_STAT_MM      0x2f0404
+
+/* Device Control #1 */
+#define F0_DEV_CNTRL1_MM    0x2f0040
+#define F1_DEV_CNTRL1_MM    0x2f0140
+#define F2_DEV_CNTRL1_MM    0x2f0240
+#define F3_DEV_CNTRL1_MM    0x2f0340
+#define F4_DEV_CNTRL1_MM    0x2f0440
+
+/* Device Control #1 */
+#define F0_BAR0_MM          0x2f0010
+#define F1_BAR0_MM          0x2f0110
+#define F2_BAR0_MM          0x2f0210
+#define F3_BAR0_MM          0x2f0310
+#define F4_BAR0_MM          0x2f0410
+
+/* ---------------------------------------------------------------------- */
+/* DMA Controller registers                                               */
+
+#define MO_PDMA_STHRSH      0x200000 // Source threshold
+#define MO_PDMA_STADRS      0x200004 // Source target address
+#define MO_PDMA_SIADRS      0x200008 // Source internal address
+#define MO_PDMA_SCNTRL      0x20000C // Source control
+#define MO_PDMA_DTHRSH      0x200010 // Destination threshold
+#define MO_PDMA_DTADRS      0x200014 // Destination target address
+#define MO_PDMA_DIADRS      0x200018 // Destination internal address
+#define MO_PDMA_DCNTRL      0x20001C // Destination control
+#define MO_LD_SSID          0x200030 // Load subsystem ID
+#define MO_DEV_CNTRL2       0x200034 // Device control
+#define MO_PCI_INTMSK       0x200040 // PCI interrupt mask
+#define MO_PCI_INTSTAT      0x200044 // PCI interrupt status
+#define MO_PCI_INTMSTAT     0x200048 // PCI interrupt masked status
+#define MO_VID_INTMSK       0x200050 // Video interrupt mask
+#define MO_VID_INTSTAT      0x200054 // Video interrupt status
+#define MO_VID_INTMSTAT     0x200058 // Video interrupt masked status
+#define MO_VID_INTSSTAT     0x20005C // Video interrupt set status
+#define MO_AUD_INTMSK       0x200060 // Audio interrupt mask
+#define MO_AUD_INTSTAT      0x200064 // Audio interrupt status
+#define MO_AUD_INTMSTAT     0x200068 // Audio interrupt masked status
+#define MO_AUD_INTSSTAT     0x20006C // Audio interrupt set status
+#define MO_TS_INTMSK        0x200070 // Transport stream interrupt mask
+#define MO_TS_INTSTAT       0x200074 // Transport stream interrupt status
+#define MO_TS_INTMSTAT      0x200078 // Transport stream interrupt mask status
+#define MO_TS_INTSSTAT      0x20007C // Transport stream interrupt set status
+#define MO_VIP_INTMSK       0x200080 // VIP interrupt mask
+#define MO_VIP_INTSTAT      0x200084 // VIP interrupt status
+#define MO_VIP_INTMSTAT     0x200088 // VIP interrupt masked status
+#define MO_VIP_INTSSTAT     0x20008C // VIP interrupt set status
+#define MO_GPHST_INTMSK     0x200090 // Host interrupt mask
+#define MO_GPHST_INTSTAT    0x200094 // Host interrupt status
+#define MO_GPHST_INTMSTAT   0x200098 // Host interrupt masked status
+#define MO_GPHST_INTSSTAT   0x20009C // Host interrupt set status
+
+// DMA Channels 1-6 belong to SPIPE
+#define MO_DMA7_PTR1        0x300018 // {24}RW* DMA Current Ptr : Ch#7
+#define MO_DMA8_PTR1        0x30001C // {24}RW* DMA Current Ptr : Ch#8
+
+// DMA Channels 9-20 belong to SPIPE
+#define MO_DMA21_PTR1       0x300080 // {24}R0* DMA Current Ptr : Ch#21
+#define MO_DMA22_PTR1       0x300084 // {24}R0* DMA Current Ptr : Ch#22
+#define MO_DMA23_PTR1       0x300088 // {24}R0* DMA Current Ptr : Ch#23
+#define MO_DMA24_PTR1       0x30008C // {24}R0* DMA Current Ptr : Ch#24
+#define MO_DMA25_PTR1       0x300090 // {24}R0* DMA Current Ptr : Ch#25
+#define MO_DMA26_PTR1       0x300094 // {24}R0* DMA Current Ptr : Ch#26
+#define MO_DMA27_PTR1       0x300098 // {24}R0* DMA Current Ptr : Ch#27
+#define MO_DMA28_PTR1       0x30009C // {24}R0* DMA Current Ptr : Ch#28
+#define MO_DMA29_PTR1       0x3000A0 // {24}R0* DMA Current Ptr : Ch#29
+#define MO_DMA30_PTR1       0x3000A4 // {24}R0* DMA Current Ptr : Ch#30
+#define MO_DMA31_PTR1       0x3000A8 // {24}R0* DMA Current Ptr : Ch#31
+#define MO_DMA32_PTR1       0x3000AC // {24}R0* DMA Current Ptr : Ch#32
+
+#define MO_DMA21_PTR2       0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21
+#define MO_DMA22_PTR2       0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22
+#define MO_DMA23_PTR2       0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23
+#define MO_DMA24_PTR2       0x3000CC // {24}RW* DMA Tab Ptr : Ch#24
+#define MO_DMA25_PTR2       0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25
+#define MO_DMA26_PTR2       0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26
+#define MO_DMA27_PTR2       0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27
+#define MO_DMA28_PTR2       0x3000DC // {24}RW* DMA Tab Ptr : Ch#28
+#define MO_DMA29_PTR2       0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29
+#define MO_DMA30_PTR2       0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30
+#define MO_DMA31_PTR2       0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31
+#define MO_DMA32_PTR2       0x3000EC // {24}RW* DMA Tab Ptr : Ch#32
+
+#define MO_DMA21_CNT1       0x300100 // {11}RW* DMA Buffer Size : Ch#21
+#define MO_DMA22_CNT1       0x300104 // {11}RW* DMA Buffer Size : Ch#22
+#define MO_DMA23_CNT1       0x300108 // {11}RW* DMA Buffer Size : Ch#23
+#define MO_DMA24_CNT1       0x30010C // {11}RW* DMA Buffer Size : Ch#24
+#define MO_DMA25_CNT1       0x300110 // {11}RW* DMA Buffer Size : Ch#25
+#define MO_DMA26_CNT1       0x300114 // {11}RW* DMA Buffer Size : Ch#26
+#define MO_DMA27_CNT1       0x300118 // {11}RW* DMA Buffer Size : Ch#27
+#define MO_DMA28_CNT1       0x30011C // {11}RW* DMA Buffer Size : Ch#28
+#define MO_DMA29_CNT1       0x300120 // {11}RW* DMA Buffer Size : Ch#29
+#define MO_DMA30_CNT1       0x300124 // {11}RW* DMA Buffer Size : Ch#30
+#define MO_DMA31_CNT1       0x300128 // {11}RW* DMA Buffer Size : Ch#31
+#define MO_DMA32_CNT1       0x30012C // {11}RW* DMA Buffer Size : Ch#32
+
+#define MO_DMA21_CNT2       0x300140 // {11}RW* DMA Table Size : Ch#21
+#define MO_DMA22_CNT2       0x300144 // {11}RW* DMA Table Size : Ch#22
+#define MO_DMA23_CNT2       0x300148 // {11}RW* DMA Table Size : Ch#23
+#define MO_DMA24_CNT2       0x30014C // {11}RW* DMA Table Size : Ch#24
+#define MO_DMA25_CNT2       0x300150 // {11}RW* DMA Table Size : Ch#25
+#define MO_DMA26_CNT2       0x300154 // {11}RW* DMA Table Size : Ch#26
+#define MO_DMA27_CNT2       0x300158 // {11}RW* DMA Table Size : Ch#27
+#define MO_DMA28_CNT2       0x30015C // {11}RW* DMA Table Size : Ch#28
+#define MO_DMA29_CNT2       0x300160 // {11}RW* DMA Table Size : Ch#29
+#define MO_DMA30_CNT2       0x300164 // {11}RW* DMA Table Size : Ch#30
+#define MO_DMA31_CNT2       0x300168 // {11}RW* DMA Table Size : Ch#31
+#define MO_DMA32_CNT2       0x30016C // {11}RW* DMA Table Size : Ch#32
+
+
+/* ---------------------------------------------------------------------- */
+/* Video registers                                                        */
+
+#define MO_VIDY_DMA         0x310000 // {64}RWp Video Y
+#define MO_VIDU_DMA         0x310008 // {64}RWp Video U
+#define MO_VIDV_DMA         0x310010 // {64}RWp Video V
+#define MO_VBI_DMA          0x310018 // {64}RWp VBI (Vertical blanking interval)
+
+#define MO_DEVICE_STATUS    0x310100
+#define MO_INPUT_FORMAT     0x310104
+#define MO_AGC_BURST        0x31010c
+#define MO_CONTR_BRIGHT     0x310110
+#define MO_UV_SATURATION    0x310114
+#define MO_HUE              0x310118
+#define MO_HTOTAL           0x310120
+#define MO_HDELAY_EVEN      0x310124
+#define MO_HDELAY_ODD       0x310128
+#define MO_VDELAY_ODD       0x31012c
+#define MO_VDELAY_EVEN      0x310130
+#define MO_HACTIVE_EVEN     0x31013c
+#define MO_HACTIVE_ODD      0x310140
+#define MO_VACTIVE_EVEN     0x310144
+#define MO_VACTIVE_ODD      0x310148
+#define MO_HSCALE_EVEN      0x31014c
+#define MO_HSCALE_ODD       0x310150
+#define MO_VSCALE_EVEN      0x310154
+#define MO_FILTER_EVEN      0x31015c
+#define MO_VSCALE_ODD       0x310158
+#define MO_FILTER_ODD       0x310160
+#define MO_OUTPUT_FORMAT    0x310164
+
+#define MO_PLL_REG          0x310168 // PLL register
+#define MO_PLL_ADJ_CTRL     0x31016c // PLL adjust control register
+#define MO_SCONV_REG        0x310170 // sample rate conversion register
+#define MO_SCONV_FIFO       0x310174 // sample rate conversion fifo
+#define MO_SUB_STEP         0x310178 // subcarrier step size
+#define MO_SUB_STEP_DR      0x31017c // subcarrier step size for DR line
+
+#define MO_CAPTURE_CTRL     0x310180 // capture control
+#define MO_COLOR_CTRL       0x310184
+#define MO_VBI_PACKET       0x310188 // vbi packet size / delay
+#define MO_FIELD_COUNT      0x310190 // field counter
+#define MO_VIP_CONFIG       0x310194
+#define MO_VBOS_CONTROL	    0x3101a8
+
+#define MO_AGC_BACK_VBI     0x310200
+#define MO_AGC_SYNC_TIP1    0x310208
+
+#define MO_VIDY_GPCNT       0x31C020 // {16}RO Video Y general purpose counter
+#define MO_VIDU_GPCNT       0x31C024 // {16}RO Video U general purpose counter
+#define MO_VIDV_GPCNT       0x31C028 // {16}RO Video V general purpose counter
+#define MO_VBI_GPCNT        0x31C02C // {16}RO VBI general purpose counter
+#define MO_VIDY_GPCNTRL     0x31C030 // {2}WO Video Y general purpose control
+#define MO_VIDU_GPCNTRL     0x31C034 // {2}WO Video U general purpose control
+#define MO_VIDV_GPCNTRL     0x31C038 // {2}WO Video V general purpose control
+#define MO_VBI_GPCNTRL      0x31C03C // {2}WO VBI general purpose counter
+#define MO_VID_DMACNTRL     0x31C040 // {8}RW Video DMA control
+#define MO_VID_XFR_STAT     0x31C044 // {1}RO Video transfer status
+
+
+/* ---------------------------------------------------------------------- */
+/* audio registers                                                        */
+
+#define MO_AUDD_DMA         0x320000 // {64}RWp Audio downstream
+#define MO_AUDU_DMA         0x320008 // {64}RWp Audio upstream
+#define MO_AUDR_DMA         0x320010 // {64}RWp Audio RDS (downstream)
+#define MO_AUDD_GPCNT       0x32C020 // {16}RO Audio down general purpose counter
+#define MO_AUDU_GPCNT       0x32C024 // {16}RO Audio up general purpose counter
+#define MO_AUDR_GPCNT       0x32C028 // {16}RO Audio RDS general purpose counter
+#define MO_AUDD_GPCNTRL     0x32C030 // {2}WO Audio down general purpose control
+#define MO_AUDU_GPCNTRL     0x32C034 // {2}WO Audio up general purpose control
+#define MO_AUDR_GPCNTRL     0x32C038 // {2}WO Audio RDS general purpose control
+#define MO_AUD_DMACNTRL     0x32C040 // {6}RW Audio DMA control
+#define MO_AUD_XFR_STAT     0x32C044 // {1}RO Audio transfer status
+#define MO_AUDD_LNGTH       0x32C048 // {12}RW Audio down line length
+#define MO_AUDR_LNGTH       0x32C04C // {12}RW Audio RDS line length
+
+#define AUD_INIT                 0x320100
+#define AUD_INIT_LD              0x320104
+#define AUD_SOFT_RESET           0x320108
+#define AUD_I2SINPUTCNTL         0x320120
+#define AUD_BAUDRATE             0x320124
+#define AUD_I2SOUTPUTCNTL        0x320128
+#define AAGC_HYST                0x320134
+#define AAGC_GAIN                0x320138
+#define AAGC_DEF                 0x32013c
+#define AUD_IIR1_0_SEL           0x320150
+#define AUD_IIR1_0_SHIFT         0x320154
+#define AUD_IIR1_1_SEL           0x320158
+#define AUD_IIR1_1_SHIFT         0x32015c
+#define AUD_IIR1_2_SEL           0x320160
+#define AUD_IIR1_2_SHIFT         0x320164
+#define AUD_IIR1_3_SEL           0x320168
+#define AUD_IIR1_3_SHIFT         0x32016c
+#define AUD_IIR1_4_SEL           0x320170
+#define AUD_IIR1_4_SHIFT         0x32017c
+#define AUD_IIR1_5_SEL           0x320180
+#define AUD_IIR1_5_SHIFT         0x320184
+#define AUD_IIR2_0_SEL           0x320190
+#define AUD_IIR2_0_SHIFT         0x320194
+#define AUD_IIR2_1_SEL           0x320198
+#define AUD_IIR2_1_SHIFT         0x32019c
+#define AUD_IIR2_2_SEL           0x3201a0
+#define AUD_IIR2_2_SHIFT         0x3201a4
+#define AUD_IIR2_3_SEL           0x3201a8
+#define AUD_IIR2_3_SHIFT         0x3201ac
+#define AUD_IIR3_0_SEL           0x3201c0
+#define AUD_IIR3_0_SHIFT         0x3201c4
+#define AUD_IIR3_1_SEL           0x3201c8
+#define AUD_IIR3_1_SHIFT         0x3201cc
+#define AUD_IIR3_2_SEL           0x3201d0
+#define AUD_IIR3_2_SHIFT         0x3201d4
+#define AUD_IIR4_0_SEL           0x3201e0
+#define AUD_IIR4_0_SHIFT         0x3201e4
+#define AUD_IIR4_1_SEL           0x3201e8
+#define AUD_IIR4_1_SHIFT         0x3201ec
+#define AUD_IIR4_2_SEL           0x3201f0
+#define AUD_IIR4_2_SHIFT         0x3201f4
+#define AUD_IIR4_0_CA0           0x320200
+#define AUD_IIR4_0_CA1           0x320204
+#define AUD_IIR4_0_CA2           0x320208
+#define AUD_IIR4_0_CB0           0x32020c
+#define AUD_IIR4_0_CB1           0x320210
+#define AUD_IIR4_1_CA0           0x320214
+#define AUD_IIR4_1_CA1           0x320218
+#define AUD_IIR4_1_CA2           0x32021c
+#define AUD_IIR4_1_CB0           0x320220
+#define AUD_IIR4_1_CB1           0x320224
+#define AUD_IIR4_2_CA0           0x320228
+#define AUD_IIR4_2_CA1           0x32022c
+#define AUD_IIR4_2_CA2           0x320230
+#define AUD_IIR4_2_CB0           0x320234
+#define AUD_IIR4_2_CB1           0x320238
+#define AUD_HP_MD_IIR4_1         0x320250
+#define AUD_HP_PROG_IIR4_1       0x320254
+#define AUD_FM_MODE_ENABLE       0x320258
+#define AUD_POLY0_DDS_CONSTANT   0x320270
+#define AUD_DN0_FREQ             0x320274
+#define AUD_DN1_FREQ             0x320278
+#define AUD_DN1_FREQ_SHIFT       0x32027c
+#define AUD_DN1_AFC              0x320280
+#define AUD_DN1_SRC_SEL          0x320284
+#define AUD_DN1_SHFT             0x320288
+#define AUD_DN2_FREQ             0x32028c
+#define AUD_DN2_FREQ_SHIFT       0x320290
+#define AUD_DN2_AFC              0x320294
+#define AUD_DN2_SRC_SEL          0x320298
+#define AUD_DN2_SHFT             0x32029c
+#define AUD_CRDC0_SRC_SEL        0x320300
+#define AUD_CRDC0_SHIFT          0x320304
+#define AUD_CORDIC_SHIFT_0       0x320308
+#define AUD_CRDC1_SRC_SEL        0x32030c
+#define AUD_CRDC1_SHIFT          0x320310
+#define AUD_CORDIC_SHIFT_1       0x320314
+#define AUD_DCOC_0_SRC           0x320320
+#define AUD_DCOC0_SHIFT          0x320324
+#define AUD_DCOC_0_SHIFT_IN0     0x320328
+#define AUD_DCOC_0_SHIFT_IN1     0x32032c
+#define AUD_DCOC_1_SRC           0x320330
+#define AUD_DCOC1_SHIFT          0x320334
+#define AUD_DCOC_1_SHIFT_IN0     0x320338
+#define AUD_DCOC_1_SHIFT_IN1     0x32033c
+#define AUD_DCOC_2_SRC           0x320340
+#define AUD_DCOC2_SHIFT          0x320344
+#define AUD_DCOC_2_SHIFT_IN0     0x320348
+#define AUD_DCOC_2_SHIFT_IN1     0x32034c
+#define AUD_DCOC_PASS_IN         0x320350
+#define AUD_PDET_SRC             0x320370
+#define AUD_PDET_SHIFT           0x320374
+#define AUD_PILOT_BQD_1_K0       0x320380
+#define AUD_PILOT_BQD_1_K1       0x320384
+#define AUD_PILOT_BQD_1_K2       0x320388
+#define AUD_PILOT_BQD_1_K3       0x32038c
+#define AUD_PILOT_BQD_1_K4       0x320390
+#define AUD_PILOT_BQD_2_K0       0x320394
+#define AUD_PILOT_BQD_2_K1       0x320398
+#define AUD_PILOT_BQD_2_K2       0x32039c
+#define AUD_PILOT_BQD_2_K3       0x3203a0
+#define AUD_PILOT_BQD_2_K4       0x3203a4
+#define AUD_THR_FR               0x3203c0
+#define AUD_X_PROG               0x3203c4
+#define AUD_Y_PROG               0x3203c8
+#define AUD_HARMONIC_MULT        0x3203cc
+#define AUD_C1_UP_THR            0x3203d0
+#define AUD_C1_LO_THR            0x3203d4
+#define AUD_C2_UP_THR            0x3203d8
+#define AUD_C2_LO_THR            0x3203dc
+#define AUD_PLL_EN               0x320400
+#define AUD_PLL_SRC              0x320404
+#define AUD_PLL_SHIFT            0x320408
+#define AUD_PLL_IF_SEL           0x32040c
+#define AUD_PLL_IF_SHIFT         0x320410
+#define AUD_BIQUAD_PLL_K0        0x320414
+#define AUD_BIQUAD_PLL_K1        0x320418
+#define AUD_BIQUAD_PLL_K2        0x32041c
+#define AUD_BIQUAD_PLL_K3        0x320420
+#define AUD_BIQUAD_PLL_K4        0x320424
+#define AUD_DEEMPH0_SRC_SEL      0x320440
+#define AUD_DEEMPH0_SHIFT        0x320444
+#define AUD_DEEMPH0_G0           0x320448
+#define AUD_DEEMPH0_A0           0x32044c
+#define AUD_DEEMPH0_B0           0x320450
+#define AUD_DEEMPH0_A1           0x320454
+#define AUD_DEEMPH0_B1           0x320458
+#define AUD_DEEMPH1_SRC_SEL      0x32045c
+#define AUD_DEEMPH1_SHIFT        0x320460
+#define AUD_DEEMPH1_G0           0x320464
+#define AUD_DEEMPH1_A0           0x320468
+#define AUD_DEEMPH1_B0           0x32046c
+#define AUD_DEEMPH1_A1           0x320470
+#define AUD_DEEMPH1_B1           0x320474
+#define AUD_OUT0_SEL             0x320490
+#define AUD_OUT0_SHIFT           0x320494
+#define AUD_OUT1_SEL             0x320498
+#define AUD_OUT1_SHIFT           0x32049c
+#define AUD_RDSI_SEL             0x3204a0
+#define AUD_RDSI_SHIFT           0x3204a4
+#define AUD_RDSQ_SEL             0x3204a8
+#define AUD_RDSQ_SHIFT           0x3204ac
+#define AUD_DBX_IN_GAIN          0x320500
+#define AUD_DBX_WBE_GAIN         0x320504
+#define AUD_DBX_SE_GAIN          0x320508
+#define AUD_DBX_RMS_WBE          0x32050c
+#define AUD_DBX_RMS_SE           0x320510
+#define AUD_DBX_SE_BYPASS        0x320514
+#define AUD_FAWDETCTL            0x320530
+#define AUD_FAWDETWINCTL         0x320534
+#define AUD_DEEMPHGAIN_R         0x320538
+#define AUD_DEEMPHNUMER1_R       0x32053c
+#define AUD_DEEMPHNUMER2_R       0x320540
+#define AUD_DEEMPHDENOM1_R       0x320544
+#define AUD_DEEMPHDENOM2_R       0x320548
+#define AUD_ERRLOGPERIOD_R       0x32054c
+#define AUD_ERRINTRPTTHSHLD1_R   0x320550
+#define AUD_ERRINTRPTTHSHLD2_R   0x320554
+#define AUD_ERRINTRPTTHSHLD3_R   0x320558
+#define AUD_NICAM_STATUS1        0x32055c
+#define AUD_NICAM_STATUS2        0x320560
+#define AUD_ERRLOG1              0x320564
+#define AUD_ERRLOG2              0x320568
+#define AUD_ERRLOG3              0x32056c
+#define AUD_DAC_BYPASS_L         0x320580
+#define AUD_DAC_BYPASS_R         0x320584
+#define AUD_DAC_BYPASS_CTL       0x320588
+#define AUD_CTL                  0x32058c
+#define AUD_STATUS               0x320590
+#define AUD_VOL_CTL              0x320594
+#define AUD_BAL_CTL              0x320598
+#define AUD_START_TIMER          0x3205b0
+#define AUD_MODE_CHG_TIMER       0x3205b4
+#define AUD_POLYPH80SCALEFAC     0x3205b8
+#define AUD_DMD_RA_DDS           0x3205bc
+#define AUD_I2S_RA_DDS           0x3205c0
+#define AUD_RATE_THRES_DMD       0x3205d0
+#define AUD_RATE_THRES_I2S       0x3205d4
+#define AUD_RATE_ADJ1            0x3205d8
+#define AUD_RATE_ADJ2            0x3205dc
+#define AUD_RATE_ADJ3            0x3205e0
+#define AUD_RATE_ADJ4            0x3205e4
+#define AUD_RATE_ADJ5            0x3205e8
+#define AUD_APB_IN_RATE_ADJ      0x3205ec
+#define AUD_I2SCNTL              0x3205ec
+#define AUD_PHASE_FIX_CTL        0x3205f0
+#define AUD_PLL_PRESCALE         0x320600
+#define AUD_PLL_DDS              0x320604
+#define AUD_PLL_INT              0x320608
+#define AUD_PLL_FRAC             0x32060c
+#define AUD_PLL_JTAG             0x320620
+#define AUD_PLL_SPMP             0x320624
+#define AUD_AFE_12DB_EN          0x320628
+
+// Audio QAM Register Addresses
+#define AUD_PDF_DDS_CNST_BYTE2   0x320d01
+#define AUD_PDF_DDS_CNST_BYTE1   0x320d02
+#define AUD_PDF_DDS_CNST_BYTE0   0x320d03
+#define AUD_PHACC_FREQ_8MSB      0x320d2a
+#define AUD_PHACC_FREQ_8LSB      0x320d2b
+#define AUD_QAM_MODE             0x320d04
+
+
+/* ---------------------------------------------------------------------- */
+/* transport stream registers                                             */
+
+#define MO_TS_DMA           0x330000 // {64}RWp Transport stream downstream
+#define MO_TS_GPCNT         0x33C020 // {16}RO TS general purpose counter
+#define MO_TS_GPCNTRL       0x33C030 // {2}WO TS general purpose control
+#define MO_TS_DMACNTRL      0x33C040 // {6}RW TS DMA control
+#define MO_TS_XFR_STAT      0x33C044 // {1}RO TS transfer status
+#define MO_TS_LNGTH         0x33C048 // {12}RW TS line length
+
+#define TS_HW_SOP_CNTRL     0x33C04C
+#define TS_GEN_CNTRL        0x33C050
+#define TS_BD_PKT_STAT      0x33C054
+#define TS_SOP_STAT         0x33C058
+#define TS_FIFO_OVFL_STAT   0x33C05C
+#define TS_VALERR_CNTRL     0x33C060
+
+
+/* ---------------------------------------------------------------------- */
+/* VIP registers                                                          */
+
+#define MO_VIPD_DMA         0x340000 // {64}RWp VIP downstream
+#define MO_VIPU_DMA         0x340008 // {64}RWp VIP upstream
+#define MO_VIPD_GPCNT       0x34C020 // {16}RO VIP down general purpose counter
+#define MO_VIPU_GPCNT       0x34C024 // {16}RO VIP up general purpose counter
+#define MO_VIPD_GPCNTRL     0x34C030 // {2}WO VIP down general purpose control
+#define MO_VIPU_GPCNTRL     0x34C034 // {2}WO VIP up general purpose control
+#define MO_VIP_DMACNTRL     0x34C040 // {6}RW VIP DMA control
+#define MO_VIP_XFR_STAT     0x34C044 // {1}RO VIP transfer status
+#define MO_VIP_CFG          0x340048 // VIP configuration
+#define MO_VIPU_CNTRL       0x34004C // VIP upstream control #1
+#define MO_VIPD_CNTRL       0x340050 // VIP downstream control #2
+#define MO_VIPD_LNGTH       0x340054 // VIP downstream line length
+#define MO_VIP_BRSTLN       0x340058 // VIP burst length
+#define MO_VIP_INTCNTRL     0x34C05C // VIP Interrupt Control
+#define MO_VIP_XFTERM       0x340060 // VIP transfer terminate
+
+
+/* ---------------------------------------------------------------------- */
+/* misc registers                                                         */
+
+#define MO_M2M_DMA          0x350000 // {64}RWp Mem2Mem DMA Bfr
+#define MO_GP0_IO           0x350010 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP1_IO           0x350014 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP2_IO           0x350018 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP3_IO           0x35001C // {32}RW* GPIO Mode/Ctrloutput enables
+#define MO_GPIO             0x350020 // {32}RW* GPIO I2C Ctrldata I/O
+#define MO_GPOE             0x350024 // {32}RW  GPIO I2C Ctrloutput enables
+#define MO_GP_ISM           0x350028 // {16}WO  GPIO Intr Sens/Pol
+
+#define MO_PLL_B            0x35C008 // {32}RW* PLL Control for ASB bus clks
+#define MO_M2M_CNT          0x35C024 // {32}RW  Mem2Mem DMA Cnt
+#define MO_M2M_XSUM         0x35C028 // {32}RO  M2M XOR-Checksum
+#define MO_CRC              0x35C02C // {16}RW  CRC16 init/result
+#define MO_CRC_D            0x35C030 // {32}WO  CRC16 new data in
+#define MO_TM_CNT_LDW       0x35C034 // {32}RO  Timer : Counter low dword
+#define MO_TM_CNT_UW        0x35C038 // {16}RO  Timer : Counter high word
+#define MO_TM_LMT_LDW       0x35C03C // {32}RW  Timer : Limit low dword
+#define MO_TM_LMT_UW        0x35C040 // {32}RW  Timer : Limit high word
+#define MO_PINMUX_IO        0x35C044 // {8}RW  Pin Mux Control
+#define MO_TSTSEL_IO        0x35C048 // {2}RW  Pin Mux Control
+#define MO_AFECFG_IO        0x35C04C // AFE configuration reg
+#define MO_DDS_IO           0x35C050 // DDS Increment reg
+#define MO_DDSCFG_IO        0x35C054 // DDS Configuration reg
+#define MO_SAMPLE_IO        0x35C058 // IRIn sample reg
+#define MO_SRST_IO          0x35C05C // Output system reset reg
+
+#define MO_INT1_MSK         0x35C060 // DMA RISC interrupt mask
+#define MO_INT1_STAT        0x35C064 // DMA RISC interrupt status
+#define MO_INT1_MSTAT       0x35C068 // DMA RISC interrupt masked status
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c bus registers                                                      */
+
+#define MO_I2C              0x368000 // I2C data/control
+#define MO_I2C_DIV          (0xf<<4)
+#define MO_I2C_SYNC         (1<<3)
+#define MO_I2C_W3B          (1<<2)
+#define MO_I2C_SCL          (1<<1)
+#define MO_I2C_SDA          (1<<0)
+
+
+/* ---------------------------------------------------------------------- */
+/* general purpose host registers                                         */
+/* FIXME: tyops?  s/0x35/0x38/ ??                                         */
+
+#define MO_GPHSTD_DMA       0x350000 // {64}RWp Host downstream
+#define MO_GPHSTU_DMA       0x350008 // {64}RWp Host upstream
+#define MO_GPHSTU_CNTRL     0x380048 // Host upstream control #1
+#define MO_GPHSTD_CNTRL     0x38004C // Host downstream control #2
+#define MO_GPHSTD_LNGTH     0x380050 // Host downstream line length
+#define MO_GPHST_WSC        0x380054 // Host wait state control
+#define MO_GPHST_XFR        0x380058 // Host transfer control
+#define MO_GPHST_WDTH       0x38005C // Host interface width
+#define MO_GPHST_HDSHK      0x380060 // Host peripheral handshake
+#define MO_GPHST_MUX16      0x380064 // Host muxed 16-bit transfer parameters
+#define MO_GPHST_MODE       0x380068 // Host mode select
+
+#define MO_GPHSTD_GPCNT     0x35C020 // Host down general purpose counter
+#define MO_GPHSTU_GPCNT     0x35C024 // Host up general purpose counter
+#define MO_GPHSTD_GPCNTRL   0x38C030 // Host down general purpose control
+#define MO_GPHSTU_GPCNTRL   0x38C034 // Host up general purpose control
+#define MO_GPHST_DMACNTRL   0x38C040 // Host DMA control
+#define MO_GPHST_XFR_STAT   0x38C044 // Host transfer status
+#define MO_GPHST_SOFT_RST   0x38C06C // Host software reset
+
+
+/* ---------------------------------------------------------------------- */
+/* RISC instructions                                                      */
+
+#define RISC_SYNC		 0x80000000
+#define RISC_SYNC_ODD		 0x80000000
+#define RISC_SYNC_EVEN		 0x80000200
+#define RISC_RESYNC		 0x80008000
+#define RISC_RESYNC_ODD		 0x80008000
+#define RISC_RESYNC_EVEN	 0x80008200
+#define RISC_WRITE		 0x10000000
+#define RISC_WRITEC		 0x50000000
+#define RISC_READ		 0x90000000
+#define RISC_READC		 0xA0000000
+#define RISC_JUMP		 0x70000000
+#define RISC_SKIP		 0x20000000
+#define RISC_WRITERM		 0xB0000000
+#define RISC_WRITECM		 0xC0000000
+#define RISC_WRITECR		 0xD0000000
+#define RISC_IMM		 0x00000001
+
+#define RISC_SOL		 0x08000000
+#define RISC_EOL		 0x04000000
+
+#define RISC_IRQ2		 0x02000000
+#define RISC_IRQ1		 0x01000000
+
+#define RISC_CNT_NONE		 0x00000000
+#define RISC_CNT_INC		 0x00010000
+#define RISC_CNT_RSVR		 0x00020000
+#define RISC_CNT_RESET		 0x00030000
+#define RISC_JMP_SRP         	 0x01
+
+
+/* ---------------------------------------------------------------------- */
+/* various constants                                                      */
+
+// DMA
+/* Interrupt mask/status */
+#define PCI_INT_VIDINT		(1 <<  0)
+#define PCI_INT_AUDINT		(1 <<  1)
+#define PCI_INT_TSINT		(1 <<  2)
+#define PCI_INT_VIPINT		(1 <<  3)
+#define PCI_INT_HSTINT		(1 <<  4)
+#define PCI_INT_TM1INT		(1 <<  5)
+#define PCI_INT_SRCDMAINT	(1 <<  6)
+#define PCI_INT_DSTDMAINT	(1 <<  7)
+#define PCI_INT_RISC_RD_BERRINT	(1 << 10)
+#define PCI_INT_RISC_WR_BERRINT	(1 << 11)
+#define PCI_INT_BRDG_BERRINT	(1 << 12)
+#define PCI_INT_SRC_DMA_BERRINT	(1 << 13)
+#define PCI_INT_DST_DMA_BERRINT	(1 << 14)
+#define PCI_INT_IPB_DMA_BERRINT	(1 << 15)
+#define PCI_INT_I2CDONE		(1 << 16)
+#define PCI_INT_I2CRACK		(1 << 17)
+#define PCI_INT_IR_SMPINT	(1 << 18)
+#define PCI_INT_GPIO_INT0	(1 << 19)
+#define PCI_INT_GPIO_INT1	(1 << 20)
+
+#define SEL_BTSC     0x01
+#define SEL_EIAJ     0x02
+#define SEL_A2       0x04
+#define SEL_SAP      0x08
+#define SEL_NICAM    0x10
+#define SEL_FMRADIO  0x20
+
+// AUD_CTL
+#define AUD_INT_DN_RISCI1	(1 <<  0)
+#define AUD_INT_UP_RISCI1	(1 <<  1)
+#define AUD_INT_RDS_DN_RISCI1	(1 <<  2)
+#define AUD_INT_DN_RISCI2	(1 <<  4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2	(1 <<  5)
+#define AUD_INT_RDS_DN_RISCI2	(1 <<  6)
+#define AUD_INT_DN_SYNC		(1 << 12)
+#define AUD_INT_UP_SYNC		(1 << 13)
+#define AUD_INT_RDS_DN_SYNC	(1 << 14)
+#define AUD_INT_OPC_ERR		(1 << 16)
+#define AUD_INT_BER_IRQ		(1 << 20)
+#define AUD_INT_MCHG_IRQ	(1 << 21)
+
+#define EN_BTSC_FORCE_MONO      0
+#define EN_BTSC_FORCE_STEREO    1
+#define EN_BTSC_FORCE_SAP       2
+#define EN_BTSC_AUTO_STEREO     3
+#define EN_BTSC_AUTO_SAP        4
+
+#define EN_A2_FORCE_MONO1       8
+#define EN_A2_FORCE_MONO2       9
+#define EN_A2_FORCE_STEREO      10
+#define EN_A2_AUTO_MONO2        11
+#define EN_A2_AUTO_STEREO       12
+
+#define EN_EIAJ_FORCE_MONO1     16
+#define EN_EIAJ_FORCE_MONO2     17
+#define EN_EIAJ_FORCE_STEREO    18
+#define EN_EIAJ_AUTO_MONO2      19
+#define EN_EIAJ_AUTO_STEREO     20
+
+#define EN_NICAM_FORCE_MONO1    32
+#define EN_NICAM_FORCE_MONO2    33
+#define EN_NICAM_FORCE_STEREO   34
+#define EN_NICAM_AUTO_MONO2     35
+#define EN_NICAM_AUTO_STEREO    36
+
+#define EN_FMRADIO_FORCE_MONO   24
+#define EN_FMRADIO_FORCE_STEREO 25
+#define EN_FMRADIO_AUTO_STEREO  26
+
+#define EN_NICAM_AUTO_FALLBACK  0x00000040
+#define EN_FMRADIO_EN_RDS       0x00000200
+#define EN_NICAM_TRY_AGAIN_BIT  0x00000400
+#define EN_DAC_ENABLE           0x00001000
+#define EN_I2SOUT_ENABLE        0x00002000
+#define EN_I2SIN_STR2DAC        0x00004000
+#define EN_I2SIN_ENABLE         0x00008000
+
+#define EN_DMTRX_SUMDIFF        (0 << 7)
+#define EN_DMTRX_SUMR           (1 << 7)
+#define EN_DMTRX_LR             (2 << 7)
+#define EN_DMTRX_MONO           (3 << 7)
+#define EN_DMTRX_BYPASS         (1 << 11)
+
+// Video
+#define VID_CAPTURE_CONTROL		0x310180
+
+#define CX23880_CAP_CTL_CAPTURE_VBI_ODD  (1<<3)
+#define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define CX23880_CAP_CTL_CAPTURE_ODD      (1<<1)
+#define CX23880_CAP_CTL_CAPTURE_EVEN     (1<<0)
+
+#define VideoInputMux0		 0x0
+#define VideoInputMux1		 0x1
+#define VideoInputMux2		 0x2
+#define VideoInputMux3		 0x3
+#define VideoInputTuner		 0x0
+#define VideoInputComposite	 0x1
+#define VideoInputSVideo	 0x2
+#define VideoInputOther		 0x3
+
+#define Xtal0		 0x1
+#define Xtal1		 0x2
+#define XtalAuto	 0x3
+
+#define VideoFormatAuto		 0x0
+#define VideoFormatNTSC		 0x1
+#define VideoFormatNTSCJapan	 0x2
+#define VideoFormatNTSC443	 0x3
+#define VideoFormatPAL		 0x4
+#define VideoFormatPALB		 0x4
+#define VideoFormatPALD		 0x4
+#define VideoFormatPALG		 0x4
+#define VideoFormatPALH		 0x4
+#define VideoFormatPALI		 0x4
+#define VideoFormatPALBDGHI	 0x4
+#define VideoFormatPALM		 0x5
+#define VideoFormatPALN		 0x6
+#define VideoFormatPALNC	 0x7
+#define VideoFormatPAL60	 0x8
+#define VideoFormatSECAM	 0x9
+
+#define VideoFormatAuto27MHz		 0x10
+#define VideoFormatNTSC27MHz		 0x11
+#define VideoFormatNTSCJapan27MHz	 0x12
+#define VideoFormatNTSC44327MHz		 0x13
+#define VideoFormatPAL27MHz		 0x14
+#define VideoFormatPALB27MHz		 0x14
+#define VideoFormatPALD27MHz		 0x14
+#define VideoFormatPALG27MHz		 0x14
+#define VideoFormatPALH27MHz		 0x14
+#define VideoFormatPALI27MHz		 0x14
+#define VideoFormatPALBDGHI27MHz	 0x14
+#define VideoFormatPALM27MHz		 0x15
+#define VideoFormatPALN27MHz		 0x16
+#define VideoFormatPALNC27MHz		 0x17
+#define VideoFormatPAL6027MHz		 0x18
+#define VideoFormatSECAM27MHz		 0x19
+
+#define NominalUSECAM	 0x87
+#define NominalVSECAM	 0x85
+#define NominalUNTSC	 0xFE
+#define NominalVNTSC	 0xB4
+
+#define NominalContrast  0xD8
+
+#define HFilterAutoFormat	 0x0
+#define HFilterCIF		 0x1
+#define HFilterQCIF		 0x2
+#define HFilterICON		 0x3
+
+#define VFilter2TapInterpolate  0
+#define VFilter3TapInterpolate  1
+#define VFilter4TapInterpolate  2
+#define VFilter5TapInterpolate  3
+#define VFilter2TapNoInterpolate  4
+#define VFilter3TapNoInterpolate  5
+#define VFilter4TapNoInterpolate  6
+#define VFilter5TapNoInterpolate  7
+
+#define ColorFormatRGB32	 0x0000
+#define ColorFormatRGB24	 0x0011
+#define ColorFormatRGB16	 0x0022
+#define ColorFormatRGB15	 0x0033
+#define ColorFormatYUY2		 0x0044
+#define ColorFormatBTYUV	 0x0055
+#define ColorFormatY8		 0x0066
+#define ColorFormatRGB8		 0x0077
+#define ColorFormatPL422	 0x0088
+#define ColorFormatPL411	 0x0099
+#define ColorFormatYUV12	 0x00AA
+#define ColorFormatYUV9		 0x00BB
+#define ColorFormatRAW		 0x00EE
+#define ColorFormatBSWAP         0x0300
+#define ColorFormatWSWAP         0x0c00
+#define ColorFormatEvenMask      0x050f
+#define ColorFormatOddMask       0x0af0
+#define ColorFormatGamma         0x1000
+
+#define Interlaced		 0x1
+#define NonInterlaced		 0x0
+
+#define FieldEven		 0x1
+#define FieldOdd		 0x0
+
+#define TGReadWriteMode		 0x0
+#define TGEnableMode		 0x1
+
+#define DV_CbAlign		 0x0
+#define DV_Y0Align		 0x1
+#define DV_CrAlign		 0x2
+#define DV_Y1Align		 0x3
+
+#define DVF_Analog		 0x0
+#define DVF_CCIR656		 0x1
+#define DVF_ByteStream		 0x2
+#define DVF_ExtVSYNC		 0x4
+#define DVF_ExtField		 0x5
+
+#define CHANNEL_VID_Y		 0x1
+#define CHANNEL_VID_U		 0x2
+#define CHANNEL_VID_V		 0x3
+#define CHANNEL_VID_VBI		 0x4
+#define CHANNEL_AUD_DN		 0x5
+#define CHANNEL_AUD_UP		 0x6
+#define CHANNEL_AUD_RDS_DN	 0x7
+#define CHANNEL_MPEG_DN		 0x8
+#define CHANNEL_VIP_DN		 0x9
+#define CHANNEL_VIP_UP		 0xA
+#define CHANNEL_HOST_DN		 0xB
+#define CHANNEL_HOST_UP		 0xC
+#define CHANNEL_FIRST		 0x1
+#define CHANNEL_LAST		 0xC
+
+#define GP_COUNT_CONTROL_NONE		 0x0
+#define GP_COUNT_CONTROL_INC		 0x1
+#define GP_COUNT_CONTROL_RESERVED	 0x2
+#define GP_COUNT_CONTROL_RESET		 0x3
+
+#define PLL_PRESCALE_BY_2  2
+#define PLL_PRESCALE_BY_3  3
+#define PLL_PRESCALE_BY_4  4
+#define PLL_PRESCALE_BY_5  5
+
+#define HLNotchFilter4xFsc	 0
+#define HLNotchFilterSquare	 1
+#define HLNotchFilter135NTSC	 2
+#define HLNotchFilter135PAL	 3
+
+#define NTSC_8x_SUB_CARRIER  28.63636E6
+#define PAL_8x_SUB_CARRIER  35.46895E6
+
+// Default analog settings
+#define DEFAULT_HUE_NTSC			0x00
+#define DEFAULT_BRIGHTNESS_NTSC			0x00
+#define DEFAULT_CONTRAST_NTSC			0x39
+#define DEFAULT_SAT_U_NTSC			0x7F
+#define DEFAULT_SAT_V_NTSC			0x5A
+
+typedef enum
+{
+	SOURCE_TUNER = 0,
+	SOURCE_COMPOSITE,
+	SOURCE_SVIDEO,
+	SOURCE_OTHER1,
+	SOURCE_OTHER2,
+	SOURCE_COMPVIASVIDEO,
+	SOURCE_CCIR656
+} VIDEOSOURCETYPE;
+
+#endif /* _CX88_REG_H_ */
diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c
new file mode 100644
index 000000000000..770ec05b5e9b
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-tvaudio.c
@@ -0,0 +1,1059 @@
+/*
+
+    cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
+
+     (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
+     (c) 2002 Yurij Sysoev <yurij@naturesoft.net>
+     (c) 2003 Gerd Knorr <kraxel@bytesex.org>
+
+    -----------------------------------------------------------------------
+
+    Lot of voodoo here.  Even the data sheet doesn't help to
+    understand what is going on here, the documentation for the audio
+    part of the cx2388x chip is *very* bad.
+
+    Some of this comes from party done linux driver sources I got from
+    [undocumented].
+
+    Some comes from the dscaler sources, one of the dscaler driver guy works
+    for Conexant ...
+
+    -----------------------------------------------------------------------
+
+    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/errno.h>
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+#include "cx88.h"
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]");
+
+static unsigned int always_analog;
+module_param(always_analog,int,0644);
+MODULE_PARM_DESC(always_analog,"force analog audio out");
+
+static unsigned int radio_deemphasis;
+module_param(radio_deemphasis,int,0644);
+MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant, "
+		 "0=None, 1=50us (elsewhere), 2=75us (USA)");
+
+#define dprintk(fmt, arg...)	if (audio_debug) \
+	printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+/* ----------------------------------------------------------- */
+
+static const char * const aud_ctl_names[64] = {
+	[EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO",
+	[EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO",
+	[EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP",
+	[EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO",
+	[EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP",
+	[EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1",
+	[EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2",
+	[EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO",
+	[EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2",
+	[EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO",
+	[EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1",
+	[EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2",
+	[EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO",
+	[EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2",
+	[EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO",
+	[EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1",
+	[EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2",
+	[EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO",
+	[EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2",
+	[EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO",
+	[EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO",
+	[EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO",
+	[EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO",
+};
+
+struct rlist {
+	u32 reg;
+	u32 val;
+};
+
+static void set_audio_registers(struct cx88_core *core, const struct rlist *l)
+{
+	int i;
+
+	for (i = 0; l[i].reg; i++) {
+		switch (l[i].reg) {
+		case AUD_PDF_DDS_CNST_BYTE2:
+		case AUD_PDF_DDS_CNST_BYTE1:
+		case AUD_PDF_DDS_CNST_BYTE0:
+		case AUD_QAM_MODE:
+		case AUD_PHACC_FREQ_8MSB:
+		case AUD_PHACC_FREQ_8LSB:
+			cx_writeb(l[i].reg, l[i].val);
+			break;
+		default:
+			cx_write(l[i].reg, l[i].val);
+			break;
+		}
+	}
+}
+
+static void set_audio_start(struct cx88_core *core, u32 mode)
+{
+	/* mute */
+	cx_write(AUD_VOL_CTL, (1 << 6));
+
+	/* start programming */
+	cx_write(AUD_INIT, mode);
+	cx_write(AUD_INIT_LD, 0x0001);
+	cx_write(AUD_SOFT_RESET, 0x0001);
+}
+
+static void set_audio_finish(struct cx88_core *core, u32 ctl)
+{
+	u32 volume;
+
+	/* restart dma; This avoids buzz in NICAM and is good in others  */
+	cx88_stop_audio_dma(core);
+	cx_write(AUD_RATE_THRES_DMD, 0x000000C0);
+	cx88_start_audio_dma(core);
+
+	if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {
+		cx_write(AUD_I2SINPUTCNTL, 4);
+		cx_write(AUD_BAUDRATE, 1);
+		/* 'pass-thru mode': this enables the i2s output to the mpeg encoder */
+		cx_set(AUD_CTL, EN_I2SOUT_ENABLE);
+		cx_write(AUD_I2SOUTPUTCNTL, 1);
+		cx_write(AUD_I2SCNTL, 0);
+		/* cx_write(AUD_APB_IN_RATE_ADJ, 0); */
+	}
+	if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) {
+		ctl |= EN_DAC_ENABLE;
+		cx_write(AUD_CTL, ctl);
+	}
+
+	/* finish programming */
+	cx_write(AUD_SOFT_RESET, 0x0000);
+
+	/* unmute */
+	volume = cx_sread(SHADOW_AUD_VOL_CTL);
+	cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
+
+	core->last_change = jiffies;
+}
+
+/* ----------------------------------------------------------- */
+
+static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap,
+				    u32 mode)
+{
+	static const struct rlist btsc[] = {
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AUD_OUT1_SEL, 0x00000013},
+		{AUD_OUT1_SHIFT, 0x00000000},
+		{AUD_POLY0_DDS_CONSTANT, 0x0012010c},
+		{AUD_DMD_RA_DDS, 0x00c3e7aa},
+		{AUD_DBX_IN_GAIN, 0x00004734},
+		{AUD_DBX_WBE_GAIN, 0x00004640},
+		{AUD_DBX_SE_GAIN, 0x00008d31},
+		{AUD_DCOC_0_SRC, 0x0000001a},
+		{AUD_IIR1_4_SEL, 0x00000021},
+		{AUD_DCOC_PASS_IN, 0x00000003},
+		{AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+		{AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+		{AUD_DN0_FREQ, 0x0000283b},
+		{AUD_DN2_SRC_SEL, 0x00000008},
+		{AUD_DN2_FREQ, 0x00003000},
+		{AUD_DN2_AFC, 0x00000002},
+		{AUD_DN2_SHFT, 0x00000000},
+		{AUD_IIR2_2_SEL, 0x00000020},
+		{AUD_IIR2_2_SHIFT, 0x00000000},
+		{AUD_IIR2_3_SEL, 0x0000001f},
+		{AUD_IIR2_3_SHIFT, 0x00000000},
+		{AUD_CRDC1_SRC_SEL, 0x000003ce},
+		{AUD_CRDC1_SHIFT, 0x00000000},
+		{AUD_CORDIC_SHIFT_1, 0x00000007},
+		{AUD_DCOC_1_SRC, 0x0000001b},
+		{AUD_DCOC1_SHIFT, 0x00000000},
+		{AUD_RDSI_SEL, 0x00000008},
+		{AUD_RDSQ_SEL, 0x00000008},
+		{AUD_RDSI_SHIFT, 0x00000000},
+		{AUD_RDSQ_SHIFT, 0x00000000},
+		{AUD_POLYPH80SCALEFAC, 0x00000003},
+		{ /* end of list */ },
+	};
+	static const struct rlist btsc_sap[] = {
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AUD_DBX_IN_GAIN, 0x00007200},
+		{AUD_DBX_WBE_GAIN, 0x00006200},
+		{AUD_DBX_SE_GAIN, 0x00006200},
+		{AUD_IIR1_1_SEL, 0x00000000},
+		{AUD_IIR1_3_SEL, 0x00000001},
+		{AUD_DN1_SRC_SEL, 0x00000007},
+		{AUD_IIR1_4_SHIFT, 0x00000006},
+		{AUD_IIR2_1_SHIFT, 0x00000000},
+		{AUD_IIR2_2_SHIFT, 0x00000000},
+		{AUD_IIR3_0_SHIFT, 0x00000000},
+		{AUD_IIR3_1_SHIFT, 0x00000000},
+		{AUD_IIR3_0_SEL, 0x0000000d},
+		{AUD_IIR3_1_SEL, 0x0000000e},
+		{AUD_DEEMPH1_SRC_SEL, 0x00000014},
+		{AUD_DEEMPH1_SHIFT, 0x00000000},
+		{AUD_DEEMPH1_G0, 0x00004000},
+		{AUD_DEEMPH1_A0, 0x00000000},
+		{AUD_DEEMPH1_B0, 0x00000000},
+		{AUD_DEEMPH1_A1, 0x00000000},
+		{AUD_DEEMPH1_B1, 0x00000000},
+		{AUD_OUT0_SEL, 0x0000003f},
+		{AUD_OUT1_SEL, 0x0000003f},
+		{AUD_DN1_AFC, 0x00000002},
+		{AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+		{AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+		{AUD_IIR1_0_SEL, 0x0000001d},
+		{AUD_IIR1_2_SEL, 0x0000001e},
+		{AUD_IIR2_1_SEL, 0x00000002},
+		{AUD_IIR2_2_SEL, 0x00000004},
+		{AUD_IIR3_2_SEL, 0x0000000f},
+		{AUD_DCOC2_SHIFT, 0x00000001},
+		{AUD_IIR3_2_SHIFT, 0x00000001},
+		{AUD_DEEMPH0_SRC_SEL, 0x00000014},
+		{AUD_CORDIC_SHIFT_1, 0x00000006},
+		{AUD_POLY0_DDS_CONSTANT, 0x000e4db2},
+		{AUD_DMD_RA_DDS, 0x00f696e6},
+		{AUD_IIR2_3_SEL, 0x00000025},
+		{AUD_IIR1_4_SEL, 0x00000021},
+		{AUD_DN1_FREQ, 0x0000c965},
+		{AUD_DCOC_PASS_IN, 0x00000003},
+		{AUD_DCOC_0_SRC, 0x0000001a},
+		{AUD_DCOC_1_SRC, 0x0000001b},
+		{AUD_DCOC1_SHIFT, 0x00000000},
+		{AUD_RDSI_SEL, 0x00000009},
+		{AUD_RDSQ_SEL, 0x00000009},
+		{AUD_RDSI_SHIFT, 0x00000000},
+		{AUD_RDSQ_SHIFT, 0x00000000},
+		{AUD_POLYPH80SCALEFAC, 0x00000003},
+		{ /* end of list */ },
+	};
+
+	mode |= EN_FMRADIO_EN_RDS;
+
+	if (sap) {
+		dprintk("%s SAP (status: unknown)\n", __func__);
+		set_audio_start(core, SEL_SAP);
+		set_audio_registers(core, btsc_sap);
+		set_audio_finish(core, mode);
+	} else {
+		dprintk("%s (status: known-good)\n", __func__);
+		set_audio_start(core, SEL_BTSC);
+		set_audio_registers(core, btsc);
+		set_audio_finish(core, mode);
+	}
+}
+
+static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode)
+{
+	static const struct rlist nicam_l[] = {
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AUD_RATE_ADJ1, 0x00000060},
+		{AUD_RATE_ADJ2, 0x000000F9},
+		{AUD_RATE_ADJ3, 0x000001CC},
+		{AUD_RATE_ADJ4, 0x000002B3},
+		{AUD_RATE_ADJ5, 0x00000726},
+		{AUD_DEEMPHDENOM1_R, 0x0000F3D0},
+		{AUD_DEEMPHDENOM2_R, 0x00000000},
+		{AUD_ERRLOGPERIOD_R, 0x00000064},
+		{AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
+		{AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
+		{AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
+		{AUD_POLYPH80SCALEFAC, 0x00000003},
+		{AUD_DMD_RA_DDS, 0x00C00000},
+		{AUD_PLL_INT, 0x0000001E},
+		{AUD_PLL_DDS, 0x00000000},
+		{AUD_PLL_FRAC, 0x0000E542},
+		{AUD_START_TIMER, 0x00000000},
+		{AUD_DEEMPHNUMER1_R, 0x000353DE},
+		{AUD_DEEMPHNUMER2_R, 0x000001B1},
+		{AUD_PDF_DDS_CNST_BYTE2, 0x06},
+		{AUD_PDF_DDS_CNST_BYTE1, 0x82},
+		{AUD_PDF_DDS_CNST_BYTE0, 0x12},
+		{AUD_QAM_MODE, 0x05},
+		{AUD_PHACC_FREQ_8MSB, 0x34},
+		{AUD_PHACC_FREQ_8LSB, 0x4C},
+		{AUD_DEEMPHGAIN_R, 0x00006680},
+		{AUD_RATE_THRES_DMD, 0x000000C0},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist nicam_bgdki_common[] = {
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AUD_RATE_ADJ1, 0x00000010},
+		{AUD_RATE_ADJ2, 0x00000040},
+		{AUD_RATE_ADJ3, 0x00000100},
+		{AUD_RATE_ADJ4, 0x00000400},
+		{AUD_RATE_ADJ5, 0x00001000},
+		{AUD_ERRLOGPERIOD_R, 0x00000fff},
+		{AUD_ERRINTRPTTHSHLD1_R, 0x000003ff},
+		{AUD_ERRINTRPTTHSHLD2_R, 0x000000ff},
+		{AUD_ERRINTRPTTHSHLD3_R, 0x0000003f},
+		{AUD_POLYPH80SCALEFAC, 0x00000003},
+		{AUD_DEEMPHGAIN_R, 0x000023c2},
+		{AUD_DEEMPHNUMER1_R, 0x0002a7bc},
+		{AUD_DEEMPHNUMER2_R, 0x0003023e},
+		{AUD_DEEMPHDENOM1_R, 0x0000f3d0},
+		{AUD_DEEMPHDENOM2_R, 0x00000000},
+		{AUD_PDF_DDS_CNST_BYTE2, 0x06},
+		{AUD_PDF_DDS_CNST_BYTE1, 0x82},
+		{AUD_QAM_MODE, 0x05},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist nicam_i[] = {
+		{AUD_PDF_DDS_CNST_BYTE0, 0x12},
+		{AUD_PHACC_FREQ_8MSB, 0x3a},
+		{AUD_PHACC_FREQ_8LSB, 0x93},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist nicam_default[] = {
+		{AUD_PDF_DDS_CNST_BYTE0, 0x16},
+		{AUD_PHACC_FREQ_8MSB, 0x34},
+		{AUD_PHACC_FREQ_8LSB, 0x4c},
+		{ /* end of list */ },
+	};
+
+	set_audio_start(core,SEL_NICAM);
+	switch (core->tvaudio) {
+	case WW_L:
+		dprintk("%s SECAM-L NICAM (status: devel)\n", __func__);
+		set_audio_registers(core, nicam_l);
+		break;
+	case WW_I:
+		dprintk("%s PAL-I NICAM (status: known-good)\n", __func__);
+		set_audio_registers(core, nicam_bgdki_common);
+		set_audio_registers(core, nicam_i);
+		break;
+	case WW_NONE:
+	case WW_BTSC:
+	case WW_BG:
+	case WW_DK:
+	case WW_EIAJ:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+	case WW_M:
+		dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__);
+		set_audio_registers(core, nicam_bgdki_common);
+		set_audio_registers(core, nicam_default);
+		break;
+	};
+
+	mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS;
+	set_audio_finish(core, mode);
+}
+
+static void set_audio_standard_A2(struct cx88_core *core, u32 mode)
+{
+	static const struct rlist a2_bgdk_common[] = {
+		{AUD_ERRLOGPERIOD_R, 0x00000064},
+		{AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
+		{AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
+		{AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
+		{AUD_PDF_DDS_CNST_BYTE2, 0x06},
+		{AUD_PDF_DDS_CNST_BYTE1, 0x82},
+		{AUD_PDF_DDS_CNST_BYTE0, 0x12},
+		{AUD_QAM_MODE, 0x05},
+		{AUD_PHACC_FREQ_8MSB, 0x34},
+		{AUD_PHACC_FREQ_8LSB, 0x4c},
+		{AUD_RATE_ADJ1, 0x00000100},
+		{AUD_RATE_ADJ2, 0x00000200},
+		{AUD_RATE_ADJ3, 0x00000300},
+		{AUD_RATE_ADJ4, 0x00000400},
+		{AUD_RATE_ADJ5, 0x00000500},
+		{AUD_THR_FR, 0x00000000},
+		{AAGC_HYST, 0x0000001a},
+		{AUD_PILOT_BQD_1_K0, 0x0000755b},
+		{AUD_PILOT_BQD_1_K1, 0x00551340},
+		{AUD_PILOT_BQD_1_K2, 0x006d30be},
+		{AUD_PILOT_BQD_1_K3, 0xffd394af},
+		{AUD_PILOT_BQD_1_K4, 0x00400000},
+		{AUD_PILOT_BQD_2_K0, 0x00040000},
+		{AUD_PILOT_BQD_2_K1, 0x002a4841},
+		{AUD_PILOT_BQD_2_K2, 0x00400000},
+		{AUD_PILOT_BQD_2_K3, 0x00000000},
+		{AUD_PILOT_BQD_2_K4, 0x00000000},
+		{AUD_MODE_CHG_TIMER, 0x00000040},
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AUD_CORDIC_SHIFT_0, 0x00000007},
+		{AUD_CORDIC_SHIFT_1, 0x00000007},
+		{AUD_DEEMPH0_G0, 0x00000380},
+		{AUD_DEEMPH1_G0, 0x00000380},
+		{AUD_DCOC_0_SRC, 0x0000001a},
+		{AUD_DCOC0_SHIFT, 0x00000000},
+		{AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+		{AUD_DCOC_PASS_IN, 0x00000003},
+		{AUD_IIR3_0_SEL, 0x00000021},
+		{AUD_DN2_AFC, 0x00000002},
+		{AUD_DCOC_1_SRC, 0x0000001b},
+		{AUD_DCOC1_SHIFT, 0x00000000},
+		{AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+		{AUD_IIR3_1_SEL, 0x00000023},
+		{AUD_RDSI_SEL, 0x00000017},
+		{AUD_RDSI_SHIFT, 0x00000000},
+		{AUD_RDSQ_SEL, 0x00000017},
+		{AUD_RDSQ_SHIFT, 0x00000000},
+		{AUD_PLL_INT, 0x0000001e},
+		{AUD_PLL_DDS, 0x00000000},
+		{AUD_PLL_FRAC, 0x0000e542},
+		{AUD_POLYPH80SCALEFAC, 0x00000001},
+		{AUD_START_TIMER, 0x00000000},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist a2_bg[] = {
+		{AUD_DMD_RA_DDS, 0x002a4f2f},
+		{AUD_C1_UP_THR, 0x00007000},
+		{AUD_C1_LO_THR, 0x00005400},
+		{AUD_C2_UP_THR, 0x00005400},
+		{AUD_C2_LO_THR, 0x00003000},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist a2_dk[] = {
+		{AUD_DMD_RA_DDS, 0x002a4f2f},
+		{AUD_C1_UP_THR, 0x00007000},
+		{AUD_C1_LO_THR, 0x00005400},
+		{AUD_C2_UP_THR, 0x00005400},
+		{AUD_C2_LO_THR, 0x00003000},
+		{AUD_DN0_FREQ, 0x00003a1c},
+		{AUD_DN2_FREQ, 0x0000d2e0},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist a1_i[] = {
+		{AUD_ERRLOGPERIOD_R, 0x00000064},
+		{AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
+		{AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
+		{AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
+		{AUD_PDF_DDS_CNST_BYTE2, 0x06},
+		{AUD_PDF_DDS_CNST_BYTE1, 0x82},
+		{AUD_PDF_DDS_CNST_BYTE0, 0x12},
+		{AUD_QAM_MODE, 0x05},
+		{AUD_PHACC_FREQ_8MSB, 0x3a},
+		{AUD_PHACC_FREQ_8LSB, 0x93},
+		{AUD_DMD_RA_DDS, 0x002a4f2f},
+		{AUD_PLL_INT, 0x0000001e},
+		{AUD_PLL_DDS, 0x00000004},
+		{AUD_PLL_FRAC, 0x0000e542},
+		{AUD_RATE_ADJ1, 0x00000100},
+		{AUD_RATE_ADJ2, 0x00000200},
+		{AUD_RATE_ADJ3, 0x00000300},
+		{AUD_RATE_ADJ4, 0x00000400},
+		{AUD_RATE_ADJ5, 0x00000500},
+		{AUD_THR_FR, 0x00000000},
+		{AUD_PILOT_BQD_1_K0, 0x0000755b},
+		{AUD_PILOT_BQD_1_K1, 0x00551340},
+		{AUD_PILOT_BQD_1_K2, 0x006d30be},
+		{AUD_PILOT_BQD_1_K3, 0xffd394af},
+		{AUD_PILOT_BQD_1_K4, 0x00400000},
+		{AUD_PILOT_BQD_2_K0, 0x00040000},
+		{AUD_PILOT_BQD_2_K1, 0x002a4841},
+		{AUD_PILOT_BQD_2_K2, 0x00400000},
+		{AUD_PILOT_BQD_2_K3, 0x00000000},
+		{AUD_PILOT_BQD_2_K4, 0x00000000},
+		{AUD_MODE_CHG_TIMER, 0x00000060},
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AAGC_HYST, 0x0000000a},
+		{AUD_CORDIC_SHIFT_0, 0x00000007},
+		{AUD_CORDIC_SHIFT_1, 0x00000007},
+		{AUD_C1_UP_THR, 0x00007000},
+		{AUD_C1_LO_THR, 0x00005400},
+		{AUD_C2_UP_THR, 0x00005400},
+		{AUD_C2_LO_THR, 0x00003000},
+		{AUD_DCOC_0_SRC, 0x0000001a},
+		{AUD_DCOC0_SHIFT, 0x00000000},
+		{AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+		{AUD_DCOC_PASS_IN, 0x00000003},
+		{AUD_IIR3_0_SEL, 0x00000021},
+		{AUD_DN2_AFC, 0x00000002},
+		{AUD_DCOC_1_SRC, 0x0000001b},
+		{AUD_DCOC1_SHIFT, 0x00000000},
+		{AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+		{AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+		{AUD_IIR3_1_SEL, 0x00000023},
+		{AUD_DN0_FREQ, 0x000035a3},
+		{AUD_DN2_FREQ, 0x000029c7},
+		{AUD_CRDC0_SRC_SEL, 0x00000511},
+		{AUD_IIR1_0_SEL, 0x00000001},
+		{AUD_IIR1_1_SEL, 0x00000000},
+		{AUD_IIR3_2_SEL, 0x00000003},
+		{AUD_IIR3_2_SHIFT, 0x00000000},
+		{AUD_IIR3_0_SEL, 0x00000002},
+		{AUD_IIR2_0_SEL, 0x00000021},
+		{AUD_IIR2_0_SHIFT, 0x00000002},
+		{AUD_DEEMPH0_SRC_SEL, 0x0000000b},
+		{AUD_DEEMPH1_SRC_SEL, 0x0000000b},
+		{AUD_POLYPH80SCALEFAC, 0x00000001},
+		{AUD_START_TIMER, 0x00000000},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist am_l[] = {
+		{AUD_ERRLOGPERIOD_R, 0x00000064},
+		{AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
+		{AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
+		{AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
+		{AUD_PDF_DDS_CNST_BYTE2, 0x48},
+		{AUD_PDF_DDS_CNST_BYTE1, 0x3D},
+		{AUD_QAM_MODE, 0x00},
+		{AUD_PDF_DDS_CNST_BYTE0, 0xf5},
+		{AUD_PHACC_FREQ_8MSB, 0x3a},
+		{AUD_PHACC_FREQ_8LSB, 0x4a},
+		{AUD_DEEMPHGAIN_R, 0x00006680},
+		{AUD_DEEMPHNUMER1_R, 0x000353DE},
+		{AUD_DEEMPHNUMER2_R, 0x000001B1},
+		{AUD_DEEMPHDENOM1_R, 0x0000F3D0},
+		{AUD_DEEMPHDENOM2_R, 0x00000000},
+		{AUD_FM_MODE_ENABLE, 0x00000007},
+		{AUD_POLYPH80SCALEFAC, 0x00000003},
+		{AUD_AFE_12DB_EN, 0x00000001},
+		{AAGC_GAIN, 0x00000000},
+		{AAGC_HYST, 0x00000018},
+		{AAGC_DEF, 0x00000020},
+		{AUD_DN0_FREQ, 0x00000000},
+		{AUD_POLY0_DDS_CONSTANT, 0x000E4DB2},
+		{AUD_DCOC_0_SRC, 0x00000021},
+		{AUD_IIR1_0_SEL, 0x00000000},
+		{AUD_IIR1_0_SHIFT, 0x00000007},
+		{AUD_IIR1_1_SEL, 0x00000002},
+		{AUD_IIR1_1_SHIFT, 0x00000000},
+		{AUD_DCOC_1_SRC, 0x00000003},
+		{AUD_DCOC1_SHIFT, 0x00000000},
+		{AUD_DCOC_PASS_IN, 0x00000000},
+		{AUD_IIR1_2_SEL, 0x00000023},
+		{AUD_IIR1_2_SHIFT, 0x00000000},
+		{AUD_IIR1_3_SEL, 0x00000004},
+		{AUD_IIR1_3_SHIFT, 0x00000007},
+		{AUD_IIR1_4_SEL, 0x00000005},
+		{AUD_IIR1_4_SHIFT, 0x00000007},
+		{AUD_IIR3_0_SEL, 0x00000007},
+		{AUD_IIR3_0_SHIFT, 0x00000000},
+		{AUD_DEEMPH0_SRC_SEL, 0x00000011},
+		{AUD_DEEMPH0_SHIFT, 0x00000000},
+		{AUD_DEEMPH0_G0, 0x00007000},
+		{AUD_DEEMPH0_A0, 0x00000000},
+		{AUD_DEEMPH0_B0, 0x00000000},
+		{AUD_DEEMPH0_A1, 0x00000000},
+		{AUD_DEEMPH0_B1, 0x00000000},
+		{AUD_DEEMPH1_SRC_SEL, 0x00000011},
+		{AUD_DEEMPH1_SHIFT, 0x00000000},
+		{AUD_DEEMPH1_G0, 0x00007000},
+		{AUD_DEEMPH1_A0, 0x00000000},
+		{AUD_DEEMPH1_B0, 0x00000000},
+		{AUD_DEEMPH1_A1, 0x00000000},
+		{AUD_DEEMPH1_B1, 0x00000000},
+		{AUD_OUT0_SEL, 0x0000003F},
+		{AUD_OUT1_SEL, 0x0000003F},
+		{AUD_DMD_RA_DDS, 0x00F5C285},
+		{AUD_PLL_INT, 0x0000001E},
+		{AUD_PLL_DDS, 0x00000000},
+		{AUD_PLL_FRAC, 0x0000E542},
+		{AUD_RATE_ADJ1, 0x00000100},
+		{AUD_RATE_ADJ2, 0x00000200},
+		{AUD_RATE_ADJ3, 0x00000300},
+		{AUD_RATE_ADJ4, 0x00000400},
+		{AUD_RATE_ADJ5, 0x00000500},
+		{AUD_RATE_THRES_DMD, 0x000000C0},
+		{ /* end of list */ },
+	};
+
+	static const struct rlist a2_deemph50[] = {
+		{AUD_DEEMPH0_G0, 0x00000380},
+		{AUD_DEEMPH1_G0, 0x00000380},
+		{AUD_DEEMPHGAIN_R, 0x000011e1},
+		{AUD_DEEMPHNUMER1_R, 0x0002a7bc},
+		{AUD_DEEMPHNUMER2_R, 0x0003023c},
+		{ /* end of list */ },
+	};
+
+	set_audio_start(core, SEL_A2);
+	switch (core->tvaudio) {
+	case WW_BG:
+		dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__);
+		set_audio_registers(core, a2_bgdk_common);
+		set_audio_registers(core, a2_bg);
+		set_audio_registers(core, a2_deemph50);
+		break;
+	case WW_DK:
+		dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__);
+		set_audio_registers(core, a2_bgdk_common);
+		set_audio_registers(core, a2_dk);
+		set_audio_registers(core, a2_deemph50);
+		break;
+	case WW_I:
+		dprintk("%s PAL-I A1 (status: known-good)\n", __func__);
+		set_audio_registers(core, a1_i);
+		set_audio_registers(core, a2_deemph50);
+		break;
+	case WW_L:
+		dprintk("%s AM-L (status: devel)\n", __func__);
+		set_audio_registers(core, am_l);
+		break;
+	case WW_NONE:
+	case WW_BTSC:
+	case WW_EIAJ:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+	case WW_M:
+		dprintk("%s Warning: wrong value\n", __func__);
+		return;
+		break;
+	};
+
+	mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF;
+	set_audio_finish(core, mode);
+}
+
+static void set_audio_standard_EIAJ(struct cx88_core *core)
+{
+	static const struct rlist eiaj[] = {
+		/* TODO: eiaj register settings are not there yet ... */
+
+		{ /* end of list */ },
+	};
+	dprintk("%s (status: unknown)\n", __func__);
+
+	set_audio_start(core, SEL_EIAJ);
+	set_audio_registers(core, eiaj);
+	set_audio_finish(core, EN_EIAJ_AUTO_STEREO);
+}
+
+static void set_audio_standard_FM(struct cx88_core *core,
+				  enum cx88_deemph_type deemph)
+{
+	static const struct rlist fm_deemph_50[] = {
+		{AUD_DEEMPH0_G0, 0x0C45},
+		{AUD_DEEMPH0_A0, 0x6262},
+		{AUD_DEEMPH0_B0, 0x1C29},
+		{AUD_DEEMPH0_A1, 0x3FC66},
+		{AUD_DEEMPH0_B1, 0x399A},
+
+		{AUD_DEEMPH1_G0, 0x0D80},
+		{AUD_DEEMPH1_A0, 0x6262},
+		{AUD_DEEMPH1_B0, 0x1C29},
+		{AUD_DEEMPH1_A1, 0x3FC66},
+		{AUD_DEEMPH1_B1, 0x399A},
+
+		{AUD_POLYPH80SCALEFAC, 0x0003},
+		{ /* end of list */ },
+	};
+	static const struct rlist fm_deemph_75[] = {
+		{AUD_DEEMPH0_G0, 0x091B},
+		{AUD_DEEMPH0_A0, 0x6B68},
+		{AUD_DEEMPH0_B0, 0x11EC},
+		{AUD_DEEMPH0_A1, 0x3FC66},
+		{AUD_DEEMPH0_B1, 0x399A},
+
+		{AUD_DEEMPH1_G0, 0x0AA0},
+		{AUD_DEEMPH1_A0, 0x6B68},
+		{AUD_DEEMPH1_B0, 0x11EC},
+		{AUD_DEEMPH1_A1, 0x3FC66},
+		{AUD_DEEMPH1_B1, 0x399A},
+
+		{AUD_POLYPH80SCALEFAC, 0x0003},
+		{ /* end of list */ },
+	};
+
+	/* It is enough to leave default values? */
+	/* No, it's not!  The deemphasis registers are reset to the 75us
+	 * values by default.  Analyzing the spectrum of the decoded audio
+	 * reveals that "no deemphasis" is the same as 75 us, while the 50 us
+	 * setting results in less deemphasis.  */
+	static const struct rlist fm_no_deemph[] = {
+
+		{AUD_POLYPH80SCALEFAC, 0x0003},
+		{ /* end of list */ },
+	};
+
+	dprintk("%s (status: unknown)\n", __func__);
+	set_audio_start(core, SEL_FMRADIO);
+
+	switch (deemph) {
+	default:
+	case FM_NO_DEEMPH:
+		set_audio_registers(core, fm_no_deemph);
+		break;
+
+	case FM_DEEMPH_50:
+		set_audio_registers(core, fm_deemph_50);
+		break;
+
+	case FM_DEEMPH_75:
+		set_audio_registers(core, fm_deemph_75);
+		break;
+	}
+
+	set_audio_finish(core, EN_FMRADIO_AUTO_STEREO);
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx88_detect_nicam(struct cx88_core *core)
+{
+	int i, j = 0;
+
+	dprintk("start nicam autodetect.\n");
+
+	for (i = 0; i < 6; i++) {
+		/* if bit1=1 then nicam is detected */
+		j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1);
+
+		if (j == 1) {
+			dprintk("nicam is detected.\n");
+			return 1;
+		}
+
+		/* wait a little bit for next reading status */
+		msleep(10);
+	}
+
+	dprintk("nicam is not detected.\n");
+	return 0;
+}
+
+void cx88_set_tvaudio(struct cx88_core *core)
+{
+	switch (core->tvaudio) {
+	case WW_BTSC:
+		set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
+		break;
+	case WW_BG:
+	case WW_DK:
+	case WW_M:
+	case WW_I:
+	case WW_L:
+		/* prepare all dsp registers */
+		set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+
+		/* set nicam mode - otherwise
+		   AUD_NICAM_STATUS2 contains wrong values */
+		set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO);
+		if (0 == cx88_detect_nicam(core)) {
+			/* fall back to fm / am mono */
+			set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+			core->audiomode_current = V4L2_TUNER_MODE_MONO;
+			core->use_nicam = 0;
+		} else {
+			core->use_nicam = 1;
+		}
+		break;
+	case WW_EIAJ:
+		set_audio_standard_EIAJ(core);
+		break;
+	case WW_FM:
+		set_audio_standard_FM(core, radio_deemphasis);
+		break;
+	case WW_I2SADC:
+		set_audio_start(core, 0x01);
+		/*
+		 * Slave/Philips/Autobaud
+		 * NB on Nova-S bit1 NPhilipsSony appears to be inverted:
+		 *	0= Sony, 1=Philips
+		 */
+		cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);
+		/* Switch to "I2S ADC mode" */
+		cx_write(AUD_I2SCNTL, 0x1);
+		set_audio_finish(core, EN_I2SIN_ENABLE);
+		break;
+	case WW_NONE:
+	case WW_I2SPT:
+		printk("%s/0: unknown tv audio mode [%d]\n",
+		       core->name, core->tvaudio);
+		break;
+	}
+	return;
+}
+
+void cx88_newstation(struct cx88_core *core)
+{
+	core->audiomode_manual = UNSET;
+	core->last_change = jiffies;
+}
+
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
+{
+	static const char * const m[] = { "stereo", "dual mono", "mono", "sap" };
+	static const char * const p[] = { "no pilot", "pilot c1", "pilot c2", "?" };
+	u32 reg, mode, pilot;
+
+	reg = cx_read(AUD_STATUS);
+	mode = reg & 0x03;
+	pilot = (reg >> 2) & 0x03;
+
+	if (core->astat != reg)
+		dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
+			reg, m[mode], p[pilot],
+			aud_ctl_names[cx_read(AUD_CTL) & 63]);
+	core->astat = reg;
+
+	t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
+	    V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+	t->rxsubchans = UNSET;
+	t->audmode = V4L2_TUNER_MODE_MONO;
+
+	switch (mode) {
+	case 0:
+		t->audmode = V4L2_TUNER_MODE_STEREO;
+		break;
+	case 1:
+		t->audmode = V4L2_TUNER_MODE_LANG2;
+		break;
+	case 2:
+		t->audmode = V4L2_TUNER_MODE_MONO;
+		break;
+	case 3:
+		t->audmode = V4L2_TUNER_MODE_SAP;
+		break;
+	}
+
+	switch (core->tvaudio) {
+	case WW_BTSC:
+	case WW_BG:
+	case WW_DK:
+	case WW_M:
+	case WW_EIAJ:
+		if (!core->use_nicam) {
+			t->rxsubchans = cx88_dsp_detect_stereo_sap(core);
+			break;
+		}
+		break;
+	case WW_NONE:
+	case WW_I:
+	case WW_L:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+		/* nothing */
+		break;
+	}
+
+	/* If software stereo detection is not supported... */
+	if (UNSET == t->rxsubchans) {
+		t->rxsubchans = V4L2_TUNER_SUB_MONO;
+		/* If the hardware itself detected stereo, also return
+		   stereo as an available subchannel */
+		if (V4L2_TUNER_MODE_STEREO == t->audmode)
+			t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+	}
+	return;
+}
+
+void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual)
+{
+	u32 ctl = UNSET;
+	u32 mask = UNSET;
+
+	if (manual) {
+		core->audiomode_manual = mode;
+	} else {
+		if (UNSET != core->audiomode_manual)
+			return;
+	}
+	core->audiomode_current = mode;
+
+	switch (core->tvaudio) {
+	case WW_BTSC:
+		switch (mode) {
+		case V4L2_TUNER_MODE_MONO:
+			set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO);
+			break;
+		case V4L2_TUNER_MODE_LANG1:
+			set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP);
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
+			set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO);
+			break;
+		}
+		break;
+	case WW_BG:
+	case WW_DK:
+	case WW_M:
+	case WW_I:
+	case WW_L:
+		if (1 == core->use_nicam) {
+			switch (mode) {
+			case V4L2_TUNER_MODE_MONO:
+			case V4L2_TUNER_MODE_LANG1:
+				set_audio_standard_NICAM(core,
+							 EN_NICAM_FORCE_MONO1);
+				break;
+			case V4L2_TUNER_MODE_LANG2:
+				set_audio_standard_NICAM(core,
+							 EN_NICAM_FORCE_MONO2);
+				break;
+			case V4L2_TUNER_MODE_STEREO:
+			case V4L2_TUNER_MODE_LANG1_LANG2:
+				set_audio_standard_NICAM(core,
+							 EN_NICAM_FORCE_STEREO);
+				break;
+			}
+		} else {
+			if ((core->tvaudio == WW_I) || (core->tvaudio == WW_L)) {
+				/* fall back to fm / am mono */
+				set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+			} else {
+				/* TODO: Add A2 autodection */
+				mask = 0x3f;
+				switch (mode) {
+				case V4L2_TUNER_MODE_MONO:
+				case V4L2_TUNER_MODE_LANG1:
+					ctl = EN_A2_FORCE_MONO1;
+					break;
+				case V4L2_TUNER_MODE_LANG2:
+					ctl = EN_A2_FORCE_MONO2;
+					break;
+				case V4L2_TUNER_MODE_STEREO:
+				case V4L2_TUNER_MODE_LANG1_LANG2:
+					ctl = EN_A2_FORCE_STEREO;
+					break;
+				}
+			}
+		}
+		break;
+	case WW_FM:
+		switch (mode) {
+		case V4L2_TUNER_MODE_MONO:
+			ctl = EN_FMRADIO_FORCE_MONO;
+			mask = 0x3f;
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+			ctl = EN_FMRADIO_AUTO_STEREO;
+			mask = 0x3f;
+			break;
+		}
+		break;
+	case WW_I2SADC:
+	case WW_NONE:
+	case WW_EIAJ:
+	case WW_I2SPT:
+		/* DO NOTHING */
+		break;
+	}
+
+	if (UNSET != ctl) {
+		dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x "
+			"[status=0x%x,ctl=0x%x,vol=0x%x]\n",
+			mask, ctl, cx_read(AUD_STATUS),
+			cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
+		cx_andor(AUD_CTL, mask, ctl);
+	}
+	return;
+}
+
+int cx88_audio_thread(void *data)
+{
+	struct cx88_core *core = data;
+	struct v4l2_tuner t;
+	u32 mode = 0;
+
+	dprintk("cx88: tvaudio thread started\n");
+	set_freezable();
+	for (;;) {
+		msleep_interruptible(1000);
+		if (kthread_should_stop())
+			break;
+		try_to_freeze();
+
+		switch (core->tvaudio) {
+		case WW_BG:
+		case WW_DK:
+		case WW_M:
+		case WW_I:
+		case WW_L:
+			if (core->use_nicam)
+				goto hw_autodetect;
+
+			/* just monitor the audio status for now ... */
+			memset(&t, 0, sizeof(t));
+			cx88_get_stereo(core, &t);
+
+			if (UNSET != core->audiomode_manual)
+				/* manually set, don't do anything. */
+				continue;
+
+			/* monitor signal and set stereo if available */
+			if (t.rxsubchans & V4L2_TUNER_SUB_STEREO)
+				mode = V4L2_TUNER_MODE_STEREO;
+			else
+				mode = V4L2_TUNER_MODE_MONO;
+			if (mode == core->audiomode_current)
+				continue;
+			/* automatically switch to best available mode */
+			cx88_set_stereo(core, mode, 0);
+			break;
+		case WW_NONE:
+		case WW_BTSC:
+		case WW_EIAJ:
+		case WW_I2SPT:
+		case WW_FM:
+		case WW_I2SADC:
+hw_autodetect:
+			/* stereo autodetection is supported by hardware so
+			   we don't need to do it manually. Do nothing. */
+			break;
+		}
+	}
+
+	dprintk("cx88: tvaudio thread exiting\n");
+	return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx88_set_tvaudio);
+EXPORT_SYMBOL(cx88_newstation);
+EXPORT_SYMBOL(cx88_set_stereo);
+EXPORT_SYMBOL(cx88_get_stereo);
+EXPORT_SYMBOL(cx88_audio_thread);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
new file mode 100644
index 000000000000..f8f8389c0362
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-vbi.c
@@ -0,0 +1,245 @@
+/*
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "cx88.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...)	if (vbi_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+int cx8800_vbi_fmt (struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct cx8800_fh  *fh   = priv;
+	struct cx8800_dev *dev  = fh->dev;
+
+	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset = 244;
+	f->fmt.vbi.count[0] = VBI_LINE_COUNT;
+	f->fmt.vbi.count[1] = VBI_LINE_COUNT;
+
+	if (dev->core->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->core->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 cx8800_start_vbi_dma(struct cx8800_dev    *dev,
+			 struct cx88_dmaqueue *q,
+			 struct cx88_buffer   *buf)
+{
+	struct cx88_core *core = dev->core;
+
+	/* setup fifo + format */
+	cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
+				buf->vb.width, buf->risc.dma);
+
+	cx_write(MO_VBOS_CONTROL, ( (1 << 18) |  // comb filter delay fixup
+				    (1 << 15) |  // enable vbi capture
+				    (1 << 11) ));
+
+	/* reset counter */
+	cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
+	q->count = 1;
+
+	/* enable irqs */
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
+	cx_set(MO_VID_INTMSK, 0x0f0088);
+
+	/* enable capture */
+	cx_set(VID_CAPTURE_CONTROL,0x18);
+
+	/* start dma */
+	cx_set(MO_DEV_CNTRL2, (1<<5));
+	cx_set(MO_VID_DMACNTRL, 0x88);
+
+	return 0;
+}
+
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+
+	/* stop dma */
+	cx_clear(MO_VID_DMACNTRL, 0x88);
+
+	/* disable capture */
+	cx_clear(VID_CAPTURE_CONTROL,0x18);
+
+	/* disable irqs */
+	cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
+	cx_clear(MO_VID_INTMSK, 0x0f0088);
+	return 0;
+}
+
+int cx8800_restart_vbi_queue(struct cx8800_dev    *dev,
+			     struct cx88_dmaqueue *q)
+{
+	struct cx88_buffer *buf;
+
+	if (list_empty(&q->active))
+		return 0;
+
+	buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+	dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+		buf, buf->vb.i);
+	cx8800_start_vbi_dma(dev, q, buf);
+	list_for_each_entry(buf, &q->active, vb.queue)
+		buf->count = q->count++;
+	mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+	return 0;
+}
+
+void cx8800_vbi_timeout(unsigned long data)
+{
+	struct cx8800_dev *dev = (struct cx8800_dev*)data;
+	struct cx88_core *core = dev->core;
+	struct cx88_dmaqueue *q = &dev->vbiq;
+	struct cx88_buffer *buf;
+	unsigned long flags;
+
+	cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]);
+
+	cx_clear(MO_VID_DMACNTRL, 0x88);
+	cx_clear(VID_CAPTURE_CONTROL, 0x18);
+
+	spin_lock_irqsave(&dev->slock,flags);
+	while (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx88_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->core->name,
+		       buf, buf->vb.i, (unsigned long)buf->risc.dma);
+	}
+	cx8800_restart_vbi_queue(dev,q);
+	spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+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 cx8800_fh   *fh  = q->priv_data;
+	struct cx8800_dev  *dev = fh->dev;
+	struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,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) {
+		struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+		buf->vb.width  = VBI_LINE_LENGTH;
+		buf->vb.height = VBI_LINE_COUNT;
+		buf->vb.size   = size;
+		buf->vb.field  = V4L2_FIELD_SEQ_TB;
+
+		if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+			goto fail;
+		cx88_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:
+	cx88_free_buffer(q,buf);
+	return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct cx88_buffer    *buf = container_of(vb,struct cx88_buffer,vb);
+	struct cx88_buffer    *prev;
+	struct cx8800_fh      *fh   = vq->priv_data;
+	struct cx8800_dev     *dev  = fh->dev;
+	struct cx88_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);
+
+	if (list_empty(&q->active)) {
+		list_add_tail(&buf->vb.queue,&q->active);
+		cx8800_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 cx88_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);
+		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 cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+
+	cx88_free_buffer(q,buf);
+}
+
+const struct videobuf_queue_ops cx8800_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/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
new file mode 100644
index 000000000000..a146d50d7795
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -0,0 +1,2075 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * video4linux video interface
+ *
+ * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *	- Multituner support
+ *	- video_ioctl2 conversion
+ *	- PAL/M fixes
+ *
+ *  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/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/wm8775.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[]   = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX88_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...)	if (video_debug >= level) \
+	printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+/* ------------------------------------------------------------------- */
+/* static data                                                         */
+
+static const struct cx8800_fmt formats[] = {
+	{
+		.name     = "8 bpp, gray",
+		.fourcc   = V4L2_PIX_FMT_GREY,
+		.cxformat = ColorFormatY8,
+		.depth    = 8,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "15 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB555,
+		.cxformat = ColorFormatRGB15,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "15 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB555X,
+		.cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "16 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB565,
+		.cxformat = ColorFormatRGB16,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "16 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB565X,
+		.cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "24 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR24,
+		.cxformat = ColorFormatRGB24,
+		.depth    = 24,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "32 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR32,
+		.cxformat = ColorFormatRGB32,
+		.depth    = 32,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "32 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB32,
+		.cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP,
+		.depth    = 32,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "4:2:2, packed, YUYV",
+		.fourcc   = V4L2_PIX_FMT_YUYV,
+		.cxformat = ColorFormatYUY2,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},{
+		.name     = "4:2:2, packed, UYVY",
+		.fourcc   = V4L2_PIX_FMT_UYVY,
+		.cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
+		.depth    = 16,
+		.flags    = FORMAT_FLAGS_PACKED,
+	},
+};
+
+static const struct cx8800_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;
+	return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+struct cx88_ctrl {
+	/* control information */
+	u32 id;
+	s32 minimum;
+	s32 maximum;
+	u32 step;
+	s32 default_value;
+
+	/* control register information */
+	u32 off;
+	u32 reg;
+	u32 sreg;
+	u32 mask;
+	u32 shift;
+};
+
+static const struct cx88_ctrl cx8800_vid_ctls[] = {
+	/* --- video --- */
+	{
+		.id            = V4L2_CID_BRIGHTNESS,
+		.minimum       = 0x00,
+		.maximum       = 0xff,
+		.step          = 1,
+		.default_value = 0x7f,
+		.off           = 128,
+		.reg           = MO_CONTR_BRIGHT,
+		.mask          = 0x00ff,
+		.shift         = 0,
+	},{
+		.id            = V4L2_CID_CONTRAST,
+		.minimum       = 0,
+		.maximum       = 0xff,
+		.step          = 1,
+		.default_value = 0x3f,
+		.off           = 0,
+		.reg           = MO_CONTR_BRIGHT,
+		.mask          = 0xff00,
+		.shift         = 8,
+	},{
+		.id            = V4L2_CID_HUE,
+		.minimum       = 0,
+		.maximum       = 0xff,
+		.step          = 1,
+		.default_value = 0x7f,
+		.off           = 128,
+		.reg           = MO_HUE,
+		.mask          = 0x00ff,
+		.shift         = 0,
+	},{
+		/* strictly, this only describes only U saturation.
+		 * V saturation is handled specially through code.
+		 */
+		.id            = V4L2_CID_SATURATION,
+		.minimum       = 0,
+		.maximum       = 0xff,
+		.step          = 1,
+		.default_value = 0x7f,
+		.off           = 0,
+		.reg           = MO_UV_SATURATION,
+		.mask          = 0x00ff,
+		.shift         = 0,
+	}, {
+		.id            = V4L2_CID_SHARPNESS,
+		.minimum       = 0,
+		.maximum       = 4,
+		.step          = 1,
+		.default_value = 0x0,
+		.off           = 0,
+		/* NOTE: the value is converted and written to both even
+		   and odd registers in the code */
+		.reg           = MO_FILTER_ODD,
+		.mask          = 7 << 7,
+		.shift         = 7,
+	}, {
+		.id            = V4L2_CID_CHROMA_AGC,
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0x1,
+		.reg           = MO_INPUT_FORMAT,
+		.mask          = 1 << 10,
+		.shift         = 10,
+	}, {
+		.id            = V4L2_CID_COLOR_KILLER,
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 0x1,
+		.reg           = MO_INPUT_FORMAT,
+		.mask          = 1 << 9,
+		.shift         = 9,
+	}, {
+		.id            = V4L2_CID_BAND_STOP_FILTER,
+		.minimum       = 0,
+		.maximum       = 1,
+		.step          = 1,
+		.default_value = 0x0,
+		.off           = 0,
+		.reg           = MO_HTOTAL,
+		.mask          = 3 << 11,
+		.shift         = 11,
+	}
+};
+
+static const struct cx88_ctrl cx8800_aud_ctls[] = {
+	{
+		/* --- audio --- */
+		.id            = V4L2_CID_AUDIO_MUTE,
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 1,
+		.reg           = AUD_VOL_CTL,
+		.sreg          = SHADOW_AUD_VOL_CTL,
+		.mask          = (1 << 6),
+		.shift         = 6,
+	},{
+		.id            = V4L2_CID_AUDIO_VOLUME,
+		.minimum       = 0,
+		.maximum       = 0x3f,
+		.step          = 1,
+		.default_value = 0x3f,
+		.reg           = AUD_VOL_CTL,
+		.sreg          = SHADOW_AUD_VOL_CTL,
+		.mask          = 0x3f,
+		.shift         = 0,
+	},{
+		.id            = V4L2_CID_AUDIO_BALANCE,
+		.minimum       = 0,
+		.maximum       = 0x7f,
+		.step          = 1,
+		.default_value = 0x40,
+		.reg           = AUD_BAL_CTL,
+		.sreg          = SHADOW_AUD_BAL_CTL,
+		.mask          = 0x7f,
+		.shift         = 0,
+	}
+};
+
+enum {
+	CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
+	CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
+};
+
+/* ------------------------------------------------------------------- */
+/* resource management                                                 */
+
+static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit)
+{
+	struct cx88_core *core = dev->core;
+	if (fh->resources & bit)
+		/* have it already allocated */
+		return 1;
+
+	/* is it free? */
+	mutex_lock(&core->lock);
+	if (dev->resources & bit) {
+		/* no, someone else uses it */
+		mutex_unlock(&core->lock);
+		return 0;
+	}
+	/* it's free, grab it */
+	fh->resources  |= bit;
+	dev->resources |= bit;
+	dprintk(1,"res: get %d\n",bit);
+	mutex_unlock(&core->lock);
+	return 1;
+}
+
+static
+int res_check(struct cx8800_fh *fh, unsigned int bit)
+{
+	return (fh->resources & bit);
+}
+
+static
+int res_locked(struct cx8800_dev *dev, unsigned int bit)
+{
+	return (dev->resources & bit);
+}
+
+static
+void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
+{
+	struct cx88_core *core = dev->core;
+	BUG_ON((fh->resources & bits) != bits);
+
+	mutex_lock(&core->lock);
+	fh->resources  &= ~bits;
+	dev->resources &= ~bits;
+	dprintk(1,"res: put %d\n",bits);
+	mutex_unlock(&core->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_video_mux(struct cx88_core *core, unsigned int input)
+{
+	/* struct cx88_core *core = dev->core; */
+
+	dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
+		input, INPUT(input).vmux,
+		INPUT(input).gpio0,INPUT(input).gpio1,
+		INPUT(input).gpio2,INPUT(input).gpio3);
+	core->input = input;
+	cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
+	cx_write(MO_GP3_IO, INPUT(input).gpio3);
+	cx_write(MO_GP0_IO, INPUT(input).gpio0);
+	cx_write(MO_GP1_IO, INPUT(input).gpio1);
+	cx_write(MO_GP2_IO, INPUT(input).gpio2);
+
+	switch (INPUT(input).type) {
+	case CX88_VMUX_SVIDEO:
+		cx_set(MO_AFECFG_IO,    0x00000001);
+		cx_set(MO_INPUT_FORMAT, 0x00010010);
+		cx_set(MO_FILTER_EVEN,  0x00002020);
+		cx_set(MO_FILTER_ODD,   0x00002020);
+		break;
+	default:
+		cx_clear(MO_AFECFG_IO,    0x00000001);
+		cx_clear(MO_INPUT_FORMAT, 0x00010010);
+		cx_clear(MO_FILTER_EVEN,  0x00002020);
+		cx_clear(MO_FILTER_ODD,   0x00002020);
+		break;
+	}
+
+	/* if there are audioroutes defined, we have an external
+	   ADC to deal with audio */
+	if (INPUT(input).audioroute) {
+		/* 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 == V4L2_IDENT_WM8775) {
+			call_all(core, audio, s_routing,
+				 INPUT(input).audioroute, 0, 0);
+		}
+		/* 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_VMUX_CABLE) {
+			/* "I2S ADC mode" */
+			core->tvaudio = WW_I2SADC;
+			cx88_set_tvaudio(core);
+		} else {
+			/* Normal mode */
+			cx_write(AUD_I2SCNTL, 0x0);
+			cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(cx88_video_mux);
+
+/* ------------------------------------------------------------------ */
+
+static int start_video_dma(struct cx8800_dev    *dev,
+			   struct cx88_dmaqueue *q,
+			   struct cx88_buffer   *buf)
+{
+	struct cx88_core *core = dev->core;
+
+	/* setup fifo + format */
+	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
+				buf->bpl, buf->risc.dma);
+	cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);
+	cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
+
+	/* reset counter */
+	cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
+	q->count = 1;
+
+	/* enable irqs */
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
+
+	/* Enables corresponding bits at PCI_INT_STAT:
+		bits 0 to 4: video, audio, transport stream, VIP, Host
+		bit 7: timer
+		bits 8 and 9: DMA complete for: SRC, DST
+		bits 10 and 11: BERR signal asserted for RISC: RD, WR
+		bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
+	 */
+	cx_set(MO_VID_INTMSK, 0x0f0011);
+
+	/* enable capture */
+	cx_set(VID_CAPTURE_CONTROL,0x06);
+
+	/* start dma */
+	cx_set(MO_DEV_CNTRL2, (1<<5));
+	cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int stop_video_dma(struct cx8800_dev    *dev)
+{
+	struct cx88_core *core = dev->core;
+
+	/* stop dma */
+	cx_clear(MO_VID_DMACNTRL, 0x11);
+
+	/* disable capture */
+	cx_clear(VID_CAPTURE_CONTROL,0x06);
+
+	/* disable irqs */
+	cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
+	cx_clear(MO_VID_INTMSK, 0x0f0011);
+	return 0;
+}
+#endif
+
+static int restart_video_queue(struct cx8800_dev    *dev,
+			       struct cx88_dmaqueue *q)
+{
+	struct cx88_core *core = dev->core;
+	struct cx88_buffer *buf, *prev;
+
+	if (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+		dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+			buf, buf->vb.i);
+		start_video_dma(dev, q, buf);
+		list_for_each_entry(buf, &q->active, 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 cx88_buffer, vb.queue);
+		if (NULL == prev) {
+			list_move_tail(&buf->vb.queue, &q->active);
+			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);
+			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 cx8800_fh *fh = q->priv_data;
+	struct cx8800_dev  *dev = fh->dev;
+
+	*size = dev->fmt->depth * dev->width * dev->height >> 3;
+	if (0 == *count)
+		*count = 32;
+	if (*size * *count > vid_limit * 1024 * 1024)
+		*count = (vid_limit * 1024 * 1024) / *size;
+	return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+	       enum v4l2_field field)
+{
+	struct cx8800_fh   *fh  = q->priv_data;
+	struct cx8800_dev  *dev = fh->dev;
+	struct cx88_core *core = dev->core;
+	struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+	int rc, init_buffer = 0;
+
+	BUG_ON(NULL == dev->fmt);
+	if (dev->width  < 48 || dev->width  > norm_maxw(core->tvnorm) ||
+	    dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
+		return -EINVAL;
+	buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+		return -EINVAL;
+
+	if (buf->fmt       != dev->fmt    ||
+	    buf->vb.width  != dev->width  ||
+	    buf->vb.height != dev->height ||
+	    buf->vb.field  != field) {
+		buf->fmt       = dev->fmt;
+		buf->vb.width  = dev->width;
+		buf->vb.height = dev->height;
+		buf->vb.field  = field;
+		init_buffer = 1;
+	}
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		init_buffer = 1;
+		if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+			goto fail;
+	}
+
+	if (init_buffer) {
+		buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			cx88_risc_buffer(dev->pci, &buf->risc,
+					 dma->sglist, 0, UNSET,
+					 buf->bpl, 0, buf->vb.height);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			cx88_risc_buffer(dev->pci, &buf->risc,
+					 dma->sglist, UNSET, 0,
+					 buf->bpl, 0, buf->vb.height);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			cx88_risc_buffer(dev->pci, &buf->risc,
+					 dma->sglist, 0, buf->bpl,
+					 buf->bpl, buf->bpl,
+					 buf->vb.height >> 1);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			cx88_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:
+			cx88_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_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+		buf, buf->vb.i,
+		dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
+		(unsigned long)buf->risc.dma);
+
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+ fail:
+	cx88_free_buffer(q,buf);
+	return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct cx88_buffer    *buf = container_of(vb,struct cx88_buffer,vb);
+	struct cx88_buffer    *prev;
+	struct cx8800_fh      *fh   = vq->priv_data;
+	struct cx8800_dev     *dev  = fh->dev;
+	struct cx88_core      *core = dev->core;
+	struct cx88_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);
+
+	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);
+		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 cx88_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);
+			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 cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+
+	cx88_free_buffer(q,buf);
+}
+
+static const struct videobuf_queue_ops cx8800_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 file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8800_fh *fh = file->private_data;
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		return &fh->vidq;
+	case VFL_TYPE_VBI:
+		return &fh->vbiq;
+	default:
+		BUG();
+		return NULL;
+	}
+}
+
+static int get_resource(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		return RESOURCE_VIDEO;
+	case VFL_TYPE_VBI:
+		return RESOURCE_VBI;
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static int video_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8800_dev *dev = video_drvdata(file);
+	struct cx88_core *core = dev->core;
+	struct cx8800_fh *fh;
+	enum v4l2_buf_type type = 0;
+	int radio = 0;
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		break;
+	case VFL_TYPE_VBI:
+		type = V4L2_BUF_TYPE_VBI_CAPTURE;
+		break;
+	case VFL_TYPE_RADIO:
+		radio = 1;
+		break;
+	}
+
+	dprintk(1, "open dev=%s radio=%d type=%s\n",
+		video_device_node_name(vdev), radio, v4l2_type_names[type]);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+	if (unlikely(!fh))
+		return -ENOMEM;
+
+	v4l2_fh_init(&fh->fh, vdev);
+	file->private_data = fh;
+	fh->dev      = dev;
+
+	mutex_lock(&core->lock);
+
+	videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx88_buffer),
+			    fh, NULL);
+	videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VBI_CAPTURE,
+			    V4L2_FIELD_SEQ_TB,
+			    sizeof(struct cx88_buffer),
+			    fh, NULL);
+
+	if (vdev->vfl_type == VFL_TYPE_RADIO) {
+		dprintk(1,"video_open: setting radio device\n");
+		cx_write(MO_GP3_IO, core->board.radio.gpio3);
+		cx_write(MO_GP0_IO, core->board.radio.gpio0);
+		cx_write(MO_GP1_IO, core->board.radio.gpio1);
+		cx_write(MO_GP2_IO, core->board.radio.gpio2);
+		if (core->board.radio.audioroute) {
+			if(core->board.audio_chip &&
+				core->board.audio_chip == V4L2_IDENT_WM8775) {
+				call_all(core, audio, s_routing,
+					core->board.radio.audioroute, 0, 0);
+			}
+			/* "I2S ADC mode" */
+			core->tvaudio = WW_I2SADC;
+			cx88_set_tvaudio(core);
+		} else {
+			/* FM Mode */
+			core->tvaudio = WW_FM;
+			cx88_set_tvaudio(core);
+			cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
+		}
+		call_all(core, tuner, s_radio);
+	}
+
+	core->users++;
+	mutex_unlock(&core->lock);
+	v4l2_fh_add(&fh->fh);
+
+	return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8800_fh *fh = file->private_data;
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		if (res_locked(fh->dev,RESOURCE_VIDEO))
+			return -EBUSY;
+		return videobuf_read_one(&fh->vidq, data, count, ppos,
+					 file->f_flags & O_NONBLOCK);
+	case VFL_TYPE_VBI:
+		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 video_device *vdev = video_devdata(file);
+	struct cx8800_fh *fh = file->private_data;
+	struct cx88_buffer *buf;
+	unsigned int rc = v4l2_ctrl_poll(file, wait);
+
+	if (vdev->vfl_type == VFL_TYPE_VBI) {
+		if (!res_get(fh->dev,fh,RESOURCE_VBI))
+			return rc | POLLERR;
+		return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
+	}
+	mutex_lock(&fh->vidq.vb_lock);
+	if (res_check(fh,RESOURCE_VIDEO)) {
+		/* streaming capture */
+		if (list_empty(&fh->vidq.stream))
+			goto done;
+		buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream);
+	} else {
+		/* read() capture */
+		buf = (struct cx88_buffer*)fh->vidq.read_buf;
+		if (NULL == buf)
+			goto done;
+	}
+	poll_wait(file, &buf->vb.done, wait);
+	if (buf->vb.state == VIDEOBUF_DONE ||
+	    buf->vb.state == VIDEOBUF_ERROR)
+		rc |= POLLIN|POLLRDNORM;
+done:
+	mutex_unlock(&fh->vidq.vb_lock);
+	return rc;
+}
+
+static int video_release(struct file *file)
+{
+	struct cx8800_fh  *fh  = file->private_data;
+	struct cx8800_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)) {
+		videobuf_stop(&fh->vbiq);
+		res_free(dev,fh,RESOURCE_VBI);
+	}
+
+	videobuf_mmap_free(&fh->vidq);
+	videobuf_mmap_free(&fh->vbiq);
+
+	mutex_lock(&dev->core->lock);
+	v4l2_fh_del(&fh->fh);
+	v4l2_fh_exit(&fh->fh);
+	file->private_data = NULL;
+	kfree(fh);
+
+	dev->core->users--;
+	if (!dev->core->users)
+		call_all(dev->core, core, s_power, 0);
+	mutex_unlock(&dev->core->lock);
+
+	return 0;
+}
+
+static int
+video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+	return videobuf_mmap_mapper(get_queue(file), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS                                                  */
+
+static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct cx88_core *core =
+		container_of(ctrl->handler, struct cx88_core, video_hdl);
+	const struct cx88_ctrl *cc = ctrl->priv;
+	u32 value, mask;
+
+	mask = cc->mask;
+	switch (ctrl->id) {
+	case V4L2_CID_SATURATION:
+		/* special v_sat handling */
+
+		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+
+		if (core->tvnorm & V4L2_STD_SECAM) {
+			/* For SECAM, both U and V sat should be equal */
+			value = value << 8 | value;
+		} else {
+			/* Keeps U Saturation proportional to V Sat */
+			value = (value * 0x5a) / 0x7f << 8 | value;
+		}
+		mask = 0xffff;
+		break;
+	case V4L2_CID_SHARPNESS:
+		/* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+		value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
+		/* needs to be set for both fields */
+		cx_andor(MO_FILTER_EVEN, mask, value);
+		break;
+	case V4L2_CID_CHROMA_AGC:
+		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+		break;
+	default:
+		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+		break;
+	}
+	dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+				ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+				mask, cc->sreg ? " [shadowed]" : "");
+	if (cc->sreg)
+		cx_sandor(cc->sreg, cc->reg, mask, value);
+	else
+		cx_andor(cc->reg, mask, value);
+	return 0;
+}
+
+static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct cx88_core *core =
+		container_of(ctrl->handler, struct cx88_core, audio_hdl);
+	const struct cx88_ctrl *cc = ctrl->priv;
+	u32 value,mask;
+
+	/* Pass changes onto any WM8775 */
+	if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_MUTE:
+			wm8775_s_ctrl(core, ctrl->id, ctrl->val);
+			break;
+		case V4L2_CID_AUDIO_VOLUME:
+			wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
+						(0x90 + ctrl->val) << 8 : 0);
+			break;
+		case V4L2_CID_AUDIO_BALANCE:
+			wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
+			break;
+		default:
+			break;
+		}
+	}
+
+	mask = cc->mask;
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_BALANCE:
+		value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		value = 0x3f - (ctrl->val & 0x3f);
+		break;
+	default:
+		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+		break;
+	}
+	dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+				ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+				mask, cc->sreg ? " [shadowed]" : "");
+	if (cc->sreg)
+		cx_sandor(cc->sreg, cc->reg, mask, value);
+	else
+		cx_andor(cc->reg, mask, value);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS                                                       */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct cx8800_fh  *fh   = priv;
+	struct cx8800_dev *dev = fh->dev;
+
+	f->fmt.pix.width        = dev->width;
+	f->fmt.pix.height       = dev->height;
+	f->fmt.pix.field        = fh->vidq.field;
+	f->fmt.pix.pixelformat  = dev->fmt->fourcc;
+	f->fmt.pix.bytesperline =
+		(f->fmt.pix.width * dev->fmt->depth) >> 3;
+	f->fmt.pix.sizeimage =
+		f->fmt.pix.height * f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+			struct v4l2_format *f)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+	const struct cx8800_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(core->tvnorm);
+	maxh  = norm_maxh(core->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;
+	v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+			      &f->fmt.pix.height, 32, maxh, 0, 0);
+	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_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct cx8800_fh  *fh   = priv;
+	struct cx8800_dev *dev = fh->dev;
+	int err = vidioc_try_fmt_vid_cap (file,priv,f);
+
+	if (0 != err)
+		return err;
+	dev->fmt        = format_by_fourcc(f->fmt.pix.pixelformat);
+	dev->width      = f->fmt.pix.width;
+	dev->height     = f->fmt.pix.height;
+	fh->vidq.field = f->fmt.pix.field;
+	return 0;
+}
+
+void cx88_querycap(struct file *file, struct cx88_core *core,
+		struct v4l2_capability *cap)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	strlcpy(cap->card, core->board.name, sizeof(cap->card));
+	cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+	if (UNSET != core->board.tuner_type)
+		cap->device_caps |= V4L2_CAP_TUNER;
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_RADIO:
+		cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
+		break;
+	case VFL_TYPE_GRABBER:
+		cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+		break;
+	case VFL_TYPE_VBI:
+		cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+		break;
+	}
+	cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
+	if (core->board.radio.type == CX88_RADIO)
+		cap->capabilities |= V4L2_CAP_RADIO;
+}
+EXPORT_SYMBOL(cx88_querycap);
+
+static int vidioc_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *cap)
+{
+	struct cx8800_dev *dev  = ((struct cx8800_fh *)priv)->dev;
+	struct cx88_core  *core = dev->core;
+
+	strcpy(cap->driver, "cx8800");
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+	cx88_querycap(file, core, cap);
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_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;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+	return videobuf_reqbufs(get_queue(file), p);
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	return videobuf_querybuf(get_queue(file), p);
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	return videobuf_qbuf(get_queue(file), p);
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	return videobuf_dqbuf(get_queue(file), p,
+				file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8800_fh  *fh   = priv;
+	struct cx8800_dev *dev  = fh->dev;
+
+	if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+	    (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	if (unlikely(!res_get(dev, fh, get_resource(file))))
+		return -EBUSY;
+	return videobuf_streamon(get_queue(file));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct cx8800_fh  *fh   = priv;
+	struct cx8800_dev *dev  = fh->dev;
+	int               err, res;
+
+	if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+	    (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	res = get_resource(file);
+	err = videobuf_streamoff(get_queue(file));
+	if (err < 0)
+		return err;
+	res_free(dev,fh,res);
+	return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+	struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	*tvnorm = core->tvnorm;
+	return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	mutex_lock(&core->lock);
+	cx88_set_tvnorm(core,*tvnorms);
+	mutex_unlock(&core->lock);
+
+	return 0;
+}
+
+/* only one input in this sample driver */
+int cx88_enum_input (struct cx88_core  *core,struct v4l2_input *i)
+{
+	static const char * const iname[] = {
+		[ CX88_VMUX_COMPOSITE1 ] = "Composite1",
+		[ CX88_VMUX_COMPOSITE2 ] = "Composite2",
+		[ CX88_VMUX_COMPOSITE3 ] = "Composite3",
+		[ CX88_VMUX_COMPOSITE4 ] = "Composite4",
+		[ CX88_VMUX_SVIDEO     ] = "S-Video",
+		[ CX88_VMUX_TELEVISION ] = "Television",
+		[ CX88_VMUX_CABLE      ] = "Cable TV",
+		[ CX88_VMUX_DVB        ] = "DVB",
+		[ CX88_VMUX_DEBUG      ] = "for debug only",
+	};
+	unsigned int n = i->index;
+
+	if (n >= 4)
+		return -EINVAL;
+	if (0 == INPUT(n).type)
+		return -EINVAL;
+	i->type  = V4L2_INPUT_TYPE_CAMERA;
+	strcpy(i->name,iname[INPUT(n).type]);
+	if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
+	    (CX88_VMUX_CABLE      == INPUT(n).type)) {
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	}
+	i->std = CX88_NORMS;
+	return 0;
+}
+EXPORT_SYMBOL(cx88_enum_input);
+
+static int vidioc_enum_input (struct file *file, void *priv,
+				struct v4l2_input *i)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+	return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	*i = core->input;
+	return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	if (i >= 4)
+		return -EINVAL;
+	if (0 == INPUT(i).type)
+		return -EINVAL;
+
+	mutex_lock(&core->lock);
+	cx88_newstation(core);
+	cx88_video_mux(core,i);
+	mutex_unlock(&core->lock);
+	return 0;
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+	u32 reg;
+
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "Television");
+	t->capability = V4L2_TUNER_CAP_NORM;
+	t->rangehigh  = 0xffffffffUL;
+	call_all(core, tuner, g_tuner, t);
+
+	cx88_get_stereo(core ,t);
+	reg = cx_read(MO_DEVICE_STATUS);
+	t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+	return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	if (UNSET == core->board.tuner_type)
+		return -EINVAL;
+	if (0 != t->index)
+		return -EINVAL;
+
+	cx88_set_stereo(core, t->audmode, 1);
+	return 0;
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct cx8800_fh  *fh   = priv;
+	struct cx88_core  *core = fh->dev->core;
+
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (f->tuner)
+		return -EINVAL;
+
+	f->frequency = core->freq;
+
+	call_all(core, tuner, g_frequency, f);
+
+	return 0;
+}
+
+int cx88_set_freq (struct cx88_core  *core,
+				struct v4l2_frequency *f)
+{
+	if (unlikely(UNSET == core->board.tuner_type))
+		return -EINVAL;
+	if (unlikely(f->tuner != 0))
+		return -EINVAL;
+
+	mutex_lock(&core->lock);
+	cx88_newstation(core);
+	call_all(core, tuner, s_frequency, f);
+	call_all(core, tuner, g_frequency, f);
+	core->freq = f->frequency;
+
+	/* When changing channels it is required to reset TVAUDIO */
+	msleep (10);
+	cx88_set_tvaudio(core);
+
+	mutex_unlock(&core->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(cx88_set_freq);
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	struct cx8800_fh  *fh   = priv;
+	struct cx88_core  *core = fh->dev->core;
+
+	return cx88_set_freq(core, f);
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+				struct v4l2_dbg_chip_ident *chip)
+{
+	if (!v4l2_chip_match_host(&chip->match))
+		return -EINVAL;
+	chip->revision = 0;
+	chip->ident = V4L2_IDENT_UNKNOWN;
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *fh,
+				struct v4l2_dbg_register *reg)
+{
+	struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+	/* cx2388x has a 24-bit register space */
+	reg->val = cx_read(reg->reg & 0xffffff);
+	reg->size = 4;
+	return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *fh,
+				struct v4l2_dbg_register *reg)
+{
+	struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+	cx_write(reg->reg & 0xffffff, reg->val);
+	return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS                                      */
+/* ----------------------------------------------------------- */
+
+static int radio_g_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	if (unlikely(t->index > 0))
+		return -EINVAL;
+
+	strcpy(t->name, "Radio");
+
+	call_all(core, tuner, g_tuner, t);
+	return 0;
+}
+
+/* FIXME: Should add a standard for radio */
+
+static int radio_s_tuner (struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
+
+	if (0 != t->index)
+		return -EINVAL;
+	if (t->audmode > V4L2_TUNER_MODE_STEREO)
+		t->audmode = V4L2_TUNER_MODE_STEREO;
+
+	call_all(core, tuner, s_tuner, t);
+
+	return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_vid_timeout(unsigned long data)
+{
+	struct cx8800_dev *dev = (struct cx8800_dev*)data;
+	struct cx88_core *core = dev->core;
+	struct cx88_dmaqueue *q = &dev->vidq;
+	struct cx88_buffer *buf;
+	unsigned long flags;
+
+	cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+
+	cx_clear(MO_VID_DMACNTRL, 0x11);
+	cx_clear(VID_CAPTURE_CONTROL, 0x06);
+
+	spin_lock_irqsave(&dev->slock,flags);
+	while (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx88_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", core->name,
+		       buf, buf->vb.i, (unsigned long)buf->risc.dma);
+	}
+	restart_video_queue(dev,q);
+	spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+static const char *cx88_vid_irqs[32] = {
+	"y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
+	"y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
+	"y_oflow",  "u_oflow",  "v_oflow",  "vbi_oflow",
+	"y_sync",   "u_sync",   "v_sync",   "vbi_sync",
+	"opc_err",  "par_err",  "rip_err",  "pci_abort",
+};
+
+static void cx8800_vid_irq(struct cx8800_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	u32 status, mask, count;
+
+	status = cx_read(MO_VID_INTSTAT);
+	mask   = cx_read(MO_VID_INTMSK);
+	if (0 == (status & mask))
+		return;
+	cx_write(MO_VID_INTSTAT, status);
+	if (irq_debug  ||  (status & mask & ~0xff))
+		cx88_print_irqbits(core->name, "irq vid",
+				   cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
+				   status, mask);
+
+	/* risc op code error */
+	if (status & (1 << 16)) {
+		printk(KERN_WARNING "%s/0: video risc op code error\n",core->name);
+		cx_clear(MO_VID_DMACNTRL, 0x11);
+		cx_clear(VID_CAPTURE_CONTROL, 0x06);
+		cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+	}
+
+	/* risc1 y */
+	if (status & 0x01) {
+		spin_lock(&dev->slock);
+		count = cx_read(MO_VIDY_GPCNT);
+		cx88_wakeup(core, &dev->vidq, count);
+		spin_unlock(&dev->slock);
+	}
+
+	/* risc1 vbi */
+	if (status & 0x08) {
+		spin_lock(&dev->slock);
+		count = cx_read(MO_VBI_GPCNT);
+		cx88_wakeup(core, &dev->vbiq, count);
+		spin_unlock(&dev->slock);
+	}
+
+	/* risc2 y */
+	if (status & 0x10) {
+		dprintk(2,"stopper video\n");
+		spin_lock(&dev->slock);
+		restart_video_queue(dev,&dev->vidq);
+		spin_unlock(&dev->slock);
+	}
+
+	/* risc2 vbi */
+	if (status & 0x80) {
+		dprintk(2,"stopper vbi\n");
+		spin_lock(&dev->slock);
+		cx8800_restart_vbi_queue(dev,&dev->vbiq);
+		spin_unlock(&dev->slock);
+	}
+}
+
+static irqreturn_t cx8800_irq(int irq, void *dev_id)
+{
+	struct cx8800_dev *dev = dev_id;
+	struct cx88_core *core = dev->core;
+	u32 status;
+	int loop, handled = 0;
+
+	for (loop = 0; loop < 10; loop++) {
+		status = cx_read(MO_PCI_INTSTAT) &
+			(core->pci_irqmask | PCI_INT_VIDINT);
+		if (0 == status)
+			goto out;
+		cx_write(MO_PCI_INTSTAT, status);
+		handled = 1;
+
+		if (status & core->pci_irqmask)
+			cx88_core_irq(core,status);
+		if (status & PCI_INT_VIDINT)
+			cx8800_vid_irq(dev);
+	};
+	if (10 == loop) {
+		printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+		       core->name);
+		cx_write(MO_PCI_INTMSK,0);
+	}
+
+ out:
+	return IRQ_RETVAL(handled);
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff                                              */
+
+static const struct v4l2_file_operations video_fops =
+{
+	.owner	       = THIS_MODULE,
+	.open	       = video_open,
+	.release       = video_release,
+	.read	       = video_read,
+	.poll          = video_poll,
+	.mmap	       = video_mmap,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs       = vidioc_reqbufs,
+	.vidioc_querybuf      = vidioc_querybuf,
+	.vidioc_qbuf          = vidioc_qbuf,
+	.vidioc_dqbuf         = vidioc_dqbuf,
+	.vidioc_g_std         = vidioc_g_std,
+	.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_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,
+	.vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
+	.vidioc_g_chip_ident  = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register    = vidioc_g_register,
+	.vidioc_s_register    = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_video_template = {
+	.name                 = "cx8800-video",
+	.fops                 = &video_fops,
+	.ioctl_ops 	      = &video_ioctl_ops,
+	.tvnorms              = CX88_NORMS,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_g_fmt_vbi_cap     = cx8800_vbi_fmt,
+	.vidioc_try_fmt_vbi_cap   = cx8800_vbi_fmt,
+	.vidioc_s_fmt_vbi_cap     = cx8800_vbi_fmt,
+	.vidioc_reqbufs       = vidioc_reqbufs,
+	.vidioc_querybuf      = vidioc_querybuf,
+	.vidioc_qbuf          = vidioc_qbuf,
+	.vidioc_dqbuf         = vidioc_dqbuf,
+	.vidioc_g_std         = vidioc_g_std,
+	.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_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,
+	.vidioc_g_chip_ident  = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register    = vidioc_g_register,
+	.vidioc_s_register    = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_vbi_template = {
+	.name                 = "cx8800-vbi",
+	.fops                 = &video_fops,
+	.ioctl_ops	      = &vbi_ioctl_ops,
+	.tvnorms              = CX88_NORMS,
+};
+
+static const struct v4l2_file_operations radio_fops =
+{
+	.owner         = THIS_MODULE,
+	.open          = video_open,
+	.poll          = v4l2_ctrl_poll,
+	.release       = video_release,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+	.vidioc_querycap      = vidioc_querycap,
+	.vidioc_g_tuner       = radio_g_tuner,
+	.vidioc_s_tuner       = radio_s_tuner,
+	.vidioc_g_frequency   = vidioc_g_frequency,
+	.vidioc_s_frequency   = vidioc_s_frequency,
+	.vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
+	.vidioc_g_chip_ident  = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register    = vidioc_g_register,
+	.vidioc_s_register    = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_radio_template = {
+	.name                 = "cx8800-radio",
+	.fops                 = &radio_fops,
+	.ioctl_ops 	      = &radio_ioctl_ops,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
+	.s_ctrl = cx8800_s_vid_ctrl,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
+	.s_ctrl = cx8800_s_aud_ctrl,
+};
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_unregister_video(struct cx8800_dev *dev)
+{
+	if (dev->radio_dev) {
+		if (video_is_registered(dev->radio_dev))
+			video_unregister_device(dev->radio_dev);
+		else
+			video_device_release(dev->radio_dev);
+		dev->radio_dev = NULL;
+	}
+	if (dev->vbi_dev) {
+		if (video_is_registered(dev->vbi_dev))
+			video_unregister_device(dev->vbi_dev);
+		else
+			video_device_release(dev->vbi_dev);
+		dev->vbi_dev = NULL;
+	}
+	if (dev->video_dev) {
+		if (video_is_registered(dev->video_dev))
+			video_unregister_device(dev->video_dev);
+		else
+			video_device_release(dev->video_dev);
+		dev->video_dev = NULL;
+	}
+}
+
+static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
+				    const struct pci_device_id *pci_id)
+{
+	struct cx8800_dev *dev;
+	struct cx88_core *core;
+	int err;
+	int i;
+
+	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+
+	/* pci init */
+	dev->pci = pci_dev;
+	if (pci_enable_device(pci_dev)) {
+		err = -EIO;
+		goto fail_free;
+	}
+	core = cx88_core_get(dev->pci);
+	if (NULL == core) {
+		err = -EINVAL;
+		goto fail_free;
+	}
+	dev->core = core;
+
+	/* print pci info */
+	dev->pci_rev = pci_dev->revision;
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+	printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", core->name,
+	       pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+	       dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+
+	pci_set_master(pci_dev);
+	if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) {
+		printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
+		err = -EIO;
+		goto fail_core;
+	}
+
+	/* initialize driver struct */
+	spin_lock_init(&dev->slock);
+	core->tvnorm = V4L2_STD_NTSC_M;
+
+	/* init video dma queues */
+	INIT_LIST_HEAD(&dev->vidq.active);
+	INIT_LIST_HEAD(&dev->vidq.queued);
+	dev->vidq.timeout.function = cx8800_vid_timeout;
+	dev->vidq.timeout.data     = (unsigned long)dev;
+	init_timer(&dev->vidq.timeout);
+	cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
+			  MO_VID_DMACNTRL,0x11,0x00);
+
+	/* init vbi dma queues */
+	INIT_LIST_HEAD(&dev->vbiq.active);
+	INIT_LIST_HEAD(&dev->vbiq.queued);
+	dev->vbiq.timeout.function = cx8800_vbi_timeout;
+	dev->vbiq.timeout.data     = (unsigned long)dev;
+	init_timer(&dev->vbiq.timeout);
+	cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
+			  MO_VID_DMACNTRL,0x88,0x00);
+
+	/* get irq */
+	err = request_irq(pci_dev->irq, cx8800_irq,
+			  IRQF_SHARED | IRQF_DISABLED, core->name, dev);
+	if (err < 0) {
+		printk(KERN_ERR "%s/0: can't get IRQ %d\n",
+		       core->name,pci_dev->irq);
+		goto fail_core;
+	}
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+	for (i = 0; i < CX8800_AUD_CTLS; i++) {
+		const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
+		struct v4l2_ctrl *vc;
+
+		vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
+			cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+		if (vc == NULL) {
+			err = core->audio_hdl.error;
+			goto fail_core;
+		}
+		vc->priv = (void *)cc;
+	}
+
+	for (i = 0; i < CX8800_VID_CTLS; i++) {
+		const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
+		struct v4l2_ctrl *vc;
+
+		vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
+			cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+		if (vc == NULL) {
+			err = core->video_hdl.error;
+			goto fail_core;
+		}
+		vc->priv = (void *)cc;
+		if (vc->id == V4L2_CID_CHROMA_AGC)
+			core->chroma_agc = vc;
+	}
+	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL);
+
+	/* load and configure helper modules */
+
+	if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+		struct i2c_board_info wm8775_info = {
+			.type = "wm8775",
+			.addr = 0x36 >> 1,
+			.platform_data = &core->wm8775_data,
+		};
+		struct v4l2_subdev *sd;
+
+		if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
+			core->wm8775_data.is_nova_s = true;
+		else
+			core->wm8775_data.is_nova_s = false;
+
+		sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
+				&wm8775_info, NULL);
+		if (sd != NULL) {
+			core->sd_wm8775 = sd;
+			sd->grp_id = WM8775_GID;
+		}
+	}
+
+	if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
+		/* This probes for a tda9874 as is used on some
+		   Pixelview Ultra boards. */
+		v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+				"tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
+	}
+
+	switch (core->boardnr) {
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
+		static const struct i2c_board_info rtc_info = {
+			I2C_BOARD_INFO("isl1208", 0x6f)
+		};
+
+		request_module("rtc-isl1208");
+		core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
+	}
+		/* break intentionally omitted */
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+		request_module("ir-kbd-i2c");
+	}
+
+	/* Sets device info at pci_dev */
+	pci_set_drvdata(pci_dev, dev);
+
+	dev->width   = 320;
+	dev->height  = 240;
+	dev->fmt     = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+	/* initial device configuration */
+	mutex_lock(&core->lock);
+	cx88_set_tvnorm(core, core->tvnorm);
+	v4l2_ctrl_handler_setup(&core->video_hdl);
+	v4l2_ctrl_handler_setup(&core->audio_hdl);
+	cx88_video_mux(core, 0);
+
+	/* register v4l devices */
+	dev->video_dev = cx88_vdev_init(core,dev->pci,
+					&cx8800_video_template,"video");
+	video_set_drvdata(dev->video_dev, dev);
+	dev->video_dev->ctrl_handler = &core->video_hdl;
+	err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+				    video_nr[core->nr]);
+	if (err < 0) {
+		printk(KERN_ERR "%s/0: can't register video device\n",
+		       core->name);
+		goto fail_unreg;
+	}
+	printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+	       core->name, video_device_node_name(dev->video_dev));
+
+	dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
+	video_set_drvdata(dev->vbi_dev, dev);
+	err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+				    vbi_nr[core->nr]);
+	if (err < 0) {
+		printk(KERN_ERR "%s/0: can't register vbi device\n",
+		       core->name);
+		goto fail_unreg;
+	}
+	printk(KERN_INFO "%s/0: registered device %s\n",
+	       core->name, video_device_node_name(dev->vbi_dev));
+
+	if (core->board.radio.type == CX88_RADIO) {
+		dev->radio_dev = cx88_vdev_init(core,dev->pci,
+						&cx8800_radio_template,"radio");
+		video_set_drvdata(dev->radio_dev, dev);
+		dev->radio_dev->ctrl_handler = &core->audio_hdl;
+		err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+					    radio_nr[core->nr]);
+		if (err < 0) {
+			printk(KERN_ERR "%s/0: can't register radio device\n",
+			       core->name);
+			goto fail_unreg;
+		}
+		printk(KERN_INFO "%s/0: registered device %s\n",
+		       core->name, video_device_node_name(dev->radio_dev));
+	}
+
+	/* start tvaudio thread */
+	if (core->board.tuner_type != TUNER_ABSENT) {
+		core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
+		if (IS_ERR(core->kthread)) {
+			err = PTR_ERR(core->kthread);
+			printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n",
+			       core->name, err);
+		}
+	}
+	mutex_unlock(&core->lock);
+
+	return 0;
+
+fail_unreg:
+	cx8800_unregister_video(dev);
+	free_irq(pci_dev->irq, dev);
+	mutex_unlock(&core->lock);
+fail_core:
+	cx88_core_put(core,dev->pci);
+fail_free:
+	kfree(dev);
+	return err;
+}
+
+static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
+{
+	struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+	struct cx88_core *core = dev->core;
+
+	/* stop thread */
+	if (core->kthread) {
+		kthread_stop(core->kthread);
+		core->kthread = NULL;
+	}
+
+	if (core->ir)
+		cx88_ir_stop(core);
+
+	cx88_shutdown(core); /* FIXME */
+	pci_disable_device(pci_dev);
+
+	/* unregister stuff */
+
+	free_irq(pci_dev->irq, dev);
+	cx8800_unregister_video(dev);
+	pci_set_drvdata(pci_dev, NULL);
+
+	/* free memory */
+	btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
+	cx88_core_put(core,dev->pci);
+	kfree(dev);
+}
+
+#ifdef CONFIG_PM
+static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+	struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+	struct cx88_core *core = dev->core;
+
+	/* stop video+vbi capture */
+	spin_lock(&dev->slock);
+	if (!list_empty(&dev->vidq.active)) {
+		printk("%s/0: suspend video\n", core->name);
+		stop_video_dma(dev);
+		del_timer(&dev->vidq.timeout);
+	}
+	if (!list_empty(&dev->vbiq.active)) {
+		printk("%s/0: suspend vbi\n", core->name);
+		cx8800_stop_vbi_dma(dev);
+		del_timer(&dev->vbiq.timeout);
+	}
+	spin_unlock(&dev->slock);
+
+	if (core->ir)
+		cx88_ir_stop(core);
+	/* FIXME -- shutdown device */
+	cx88_shutdown(core);
+
+	pci_save_state(pci_dev);
+	if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+		pci_disable_device(pci_dev);
+		dev->state.disabled = 1;
+	}
+	return 0;
+}
+
+static int cx8800_resume(struct pci_dev *pci_dev)
+{
+	struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+	struct cx88_core *core = dev->core;
+	int err;
+
+	if (dev->state.disabled) {
+		err=pci_enable_device(pci_dev);
+		if (err) {
+			printk(KERN_ERR "%s/0: can't enable device\n",
+			       core->name);
+			return err;
+		}
+
+		dev->state.disabled = 0;
+	}
+	err= pci_set_power_state(pci_dev, PCI_D0);
+	if (err) {
+		printk(KERN_ERR "%s/0: can't set power state\n", core->name);
+		pci_disable_device(pci_dev);
+		dev->state.disabled = 1;
+
+		return err;
+	}
+	pci_restore_state(pci_dev);
+
+	/* FIXME: re-initialize hardware */
+	cx88_reset(core);
+	if (core->ir)
+		cx88_ir_start(core);
+
+	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+	/* restart video+vbi capture */
+	spin_lock(&dev->slock);
+	if (!list_empty(&dev->vidq.active)) {
+		printk("%s/0: resume video\n", core->name);
+		restart_video_queue(dev,&dev->vidq);
+	}
+	if (!list_empty(&dev->vbiq.active)) {
+		printk("%s/0: resume vbi\n", core->name);
+		cx8800_restart_vbi_queue(dev,&dev->vbiq);
+	}
+	spin_unlock(&dev->slock);
+
+	return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static const struct pci_device_id cx8800_pci_tbl[] = {
+	{
+		.vendor       = 0x14f1,
+		.device       = 0x8800,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	},{
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
+
+static struct pci_driver cx8800_pci_driver = {
+	.name     = "cx8800",
+	.id_table = cx8800_pci_tbl,
+	.probe    = cx8800_initdev,
+	.remove   = __devexit_p(cx8800_finidev),
+#ifdef CONFIG_PM
+	.suspend  = cx8800_suspend,
+	.resume   = cx8800_resume,
+#endif
+};
+
+static int __init cx8800_init(void)
+{
+	printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
+	       CX88_VERSION);
+	return pci_register_driver(&cx8800_pci_driver);
+}
+
+static void __exit cx8800_fini(void)
+{
+	pci_unregister_driver(&cx8800_pci_driver);
+}
+
+module_init(cx8800_init);
+module_exit(cx8800_fini);
diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c
new file mode 100644
index 000000000000..d77f8ecab9d7
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c
@@ -0,0 +1,159 @@
+/*
+
+    cx88-vp3054-i2c.c  --  support for the secondary I2C bus of the
+			   DNTV Live! DVB-T Pro (VP-3054), wired as:
+			   GPIO[0] -> SCL, GPIO[1] -> SDA
+
+    (c) 2005 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 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/slab.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include "cx88.h"
+#include "cx88-vp3054-i2c.h"
+
+MODULE_DESCRIPTION("driver for cx2388x VP3054 design");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_LICENSE("GPL");
+
+/* ----------------------------------------------------------------------- */
+
+static void vp3054_bit_setscl(void *data, int state)
+{
+	struct cx8802_dev *dev = data;
+	struct cx88_core *core = dev->core;
+	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+	if (state) {
+		vp3054_i2c->state |=  0x0001;	/* SCL high */
+		vp3054_i2c->state &= ~0x0100;	/* external pullup */
+	} else {
+		vp3054_i2c->state &= ~0x0001;	/* SCL low */
+		vp3054_i2c->state |=  0x0100;	/* drive pin */
+	}
+	cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state);
+	cx_read(MO_GP0_IO);
+}
+
+static void vp3054_bit_setsda(void *data, int state)
+{
+	struct cx8802_dev *dev = data;
+	struct cx88_core *core = dev->core;
+	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+	if (state) {
+		vp3054_i2c->state |=  0x0002;	/* SDA high */
+		vp3054_i2c->state &= ~0x0200;	/* tristate pin */
+	} else {
+		vp3054_i2c->state &= ~0x0002;	/* SDA low */
+		vp3054_i2c->state |=  0x0200;	/* drive pin */
+	}
+	cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state);
+	cx_read(MO_GP0_IO);
+}
+
+static int vp3054_bit_getscl(void *data)
+{
+	struct cx8802_dev *dev = data;
+	struct cx88_core *core = dev->core;
+	u32 state;
+
+	state = cx_read(MO_GP0_IO);
+	return (state & 0x01) ? 1 : 0;
+}
+
+static int vp3054_bit_getsda(void *data)
+{
+	struct cx8802_dev *dev = data;
+	struct cx88_core *core = dev->core;
+	u32 state;
+
+	state = cx_read(MO_GP0_IO);
+	return (state & 0x02) ? 1 : 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_algo_bit_data vp3054_i2c_algo_template = {
+	.setsda  = vp3054_bit_setsda,
+	.setscl  = vp3054_bit_setscl,
+	.getsda  = vp3054_bit_getsda,
+	.getscl  = vp3054_bit_getscl,
+	.udelay  = 16,
+	.timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+
+int vp3054_i2c_probe(struct cx8802_dev *dev)
+{
+	struct cx88_core *core = dev->core;
+	struct vp3054_i2c_state *vp3054_i2c;
+	int rc;
+
+	if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
+		return 0;
+
+	vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL);
+	if (vp3054_i2c == NULL)
+		return -ENOMEM;
+	dev->vp3054 = vp3054_i2c;
+
+	memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template,
+	       sizeof(vp3054_i2c->algo));
+
+	vp3054_i2c->adap.dev.parent = &dev->pci->dev;
+	strlcpy(vp3054_i2c->adap.name, core->name,
+		sizeof(vp3054_i2c->adap.name));
+	vp3054_i2c->adap.owner = THIS_MODULE;
+	vp3054_i2c->algo.data = dev;
+	i2c_set_adapdata(&vp3054_i2c->adap, dev);
+	vp3054_i2c->adap.algo_data = &vp3054_i2c->algo;
+
+	vp3054_bit_setscl(dev,1);
+	vp3054_bit_setsda(dev,1);
+
+	rc = i2c_bit_add_bus(&vp3054_i2c->adap);
+	if (0 != rc) {
+		printk("%s: vp3054_i2c register FAILED\n", core->name);
+
+		kfree(dev->vp3054);
+		dev->vp3054 = NULL;
+	}
+
+	return rc;
+}
+
+void vp3054_i2c_remove(struct cx8802_dev *dev)
+{
+	struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+	if (vp3054_i2c == NULL ||
+	    dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
+		return;
+
+	i2c_del_adapter(&vp3054_i2c->adap);
+	kfree(vp3054_i2c);
+}
+
+EXPORT_SYMBOL(vp3054_i2c_probe);
+EXPORT_SYMBOL(vp3054_i2c_remove);
diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.h b/drivers/media/pci/cx88/cx88-vp3054-i2c.h
new file mode 100644
index 000000000000..be99c931dc3e
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.h
@@ -0,0 +1,41 @@
+/*
+
+    cx88-vp3054-i2c.h  --  support for the secondary I2C bus of the
+			   DNTV Live! DVB-T Pro (VP-3054), wired as:
+			   GPIO[0] -> SCL, GPIO[1] -> SDA
+
+    (c) 2005 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 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.
+
+*/
+
+/* ----------------------------------------------------------------------- */
+struct vp3054_i2c_state {
+	struct i2c_adapter         adap;
+	struct i2c_algo_bit_data   algo;
+	u32                        state;
+};
+
+/* ----------------------------------------------------------------------- */
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+int  vp3054_i2c_probe(struct cx8802_dev *dev);
+void vp3054_i2c_remove(struct cx8802_dev *dev);
+#else
+static inline int  vp3054_i2c_probe(struct cx8802_dev *dev)
+{ return 0; }
+static inline void vp3054_i2c_remove(struct cx8802_dev *dev)
+{ }
+#endif
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
new file mode 100644
index 000000000000..44ffc8b3d45f
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88.h
@@ -0,0 +1,748 @@
+/*
+ *
+ * v4l2 device driver for cx2388x based TV cards
+ *
+ * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/cx2341x.h>
+#include <media/videobuf-dvb.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/wm8775.h>
+
+#include "btcx-risc.h"
+#include "cx88-reg.h"
+#include "tuner-xc2028.h"
+
+#include <linux/mutex.h>
+
+#define CX88_VERSION "0.0.9"
+
+#define UNSET (-1U)
+
+#define CX88_MAXBOARDS 8
+
+/* Max number of inputs by card */
+#define MAX_CX88_INPUT 8
+
+/* ----------------------------------------------------------- */
+/* defines and enums                                           */
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
+#define CX88_NORMS (V4L2_STD_ALL 		\
+		    & ~V4L2_STD_PAL_H		\
+		    & ~V4L2_STD_NTSC_M_KR	\
+		    & ~V4L2_STD_SECAM_LC)
+
+#define FORMAT_FLAGS_PACKED       0x01
+#define FORMAT_FLAGS_PLANAR       0x02
+
+#define VBI_LINE_COUNT              17
+#define VBI_LINE_LENGTH           2048
+
+#define AUD_RDS_LINES		     4
+
+/* need "shadow" registers for some write-only ones ... */
+#define SHADOW_AUD_VOL_CTL           1
+#define SHADOW_AUD_BAL_CTL           2
+#define SHADOW_MAX                   3
+
+/* FM Radio deemphasis type */
+enum cx88_deemph_type {
+	FM_NO_DEEMPH = 0,
+	FM_DEEMPH_50,
+	FM_DEEMPH_75
+};
+
+enum cx88_board_type {
+	CX88_BOARD_NONE = 0,
+	CX88_MPEG_DVB,
+	CX88_MPEG_BLACKBIRD
+};
+
+enum cx8802_board_access {
+	CX8802_DRVCTL_SHARED    = 1,
+	CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
+/* ----------------------------------------------------------- */
+/* tv norms                                                    */
+
+static unsigned int inline norm_maxw(v4l2_std_id norm)
+{
+	return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+
+static unsigned int inline norm_maxh(v4l2_std_id norm)
+{
+	return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+/* ----------------------------------------------------------- */
+/* static data                                                 */
+
+struct cx8800_fmt {
+	const char  *name;
+	u32   fourcc;          /* v4l2 format id */
+	int   depth;
+	int   flags;
+	u32   cxformat;
+};
+
+/* ----------------------------------------------------------- */
+/* SRAM memory management data (see cx88-core.c)               */
+
+#define SRAM_CH21 0   /* video */
+#define SRAM_CH22 1
+#define SRAM_CH23 2
+#define SRAM_CH24 3   /* vbi   */
+#define SRAM_CH25 4   /* audio */
+#define SRAM_CH26 5
+#define SRAM_CH28 6   /* mpeg */
+#define SRAM_CH27 7   /* audio rds */
+/* more */
+
+struct sram_channel {
+	const char *name;
+	u32  cmds_start;
+	u32  ctrl_start;
+	u32  cdt;
+	u32  fifo_start;
+	u32  fifo_size;
+	u32  ptr1_reg;
+	u32  ptr2_reg;
+	u32  cnt1_reg;
+	u32  cnt2_reg;
+};
+extern const struct sram_channel cx88_sram_channels[];
+
+/* ----------------------------------------------------------- */
+/* card configuration                                          */
+
+#define CX88_BOARD_NOAUTO               UNSET
+#define CX88_BOARD_UNKNOWN                  0
+#define CX88_BOARD_HAUPPAUGE                1
+#define CX88_BOARD_GDI                      2
+#define CX88_BOARD_PIXELVIEW                3
+#define CX88_BOARD_ATI_WONDER_PRO           4
+#define CX88_BOARD_WINFAST2000XP_EXPERT     5
+#define CX88_BOARD_AVERTV_STUDIO_303        6
+#define CX88_BOARD_MSI_TVANYWHERE_MASTER    7
+#define CX88_BOARD_WINFAST_DV2000           8
+#define CX88_BOARD_LEADTEK_PVR2000          9
+#define CX88_BOARD_IODATA_GVVCP3PCI        10
+#define CX88_BOARD_PROLINK_PLAYTVPVR       11
+#define CX88_BOARD_ASUS_PVR_416            12
+#define CX88_BOARD_MSI_TVANYWHERE          13
+#define CX88_BOARD_KWORLD_DVB_T            14
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
+#define CX88_BOARD_KWORLD_LTV883           16
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q  17
+#define CX88_BOARD_HAUPPAUGE_DVB_T1        18
+#define CX88_BOARD_CONEXANT_DVB_T1         19
+#define CX88_BOARD_PROVIDEO_PV259          20
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21
+#define CX88_BOARD_PCHDTV_HD3000           22
+#define CX88_BOARD_DNTV_LIVE_DVB_T         23
+#define CX88_BOARD_HAUPPAUGE_ROSLYN        24
+#define CX88_BOARD_DIGITALLOGIC_MEC        25
+#define CX88_BOARD_IODATA_GVBCTV7E         26
+#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T  28
+#define CX88_BOARD_ADSTECH_DVB_T_PCI          29
+#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1  30
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31
+#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32
+#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33
+#define CX88_BOARD_ATI_HDTVWONDER          34
+#define CX88_BOARD_WINFAST_DTV1000         35
+#define CX88_BOARD_AVERTV_303              36
+#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1  37
+#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1    38
+#define CX88_BOARD_KWORLD_DVBS_100         39
+#define CX88_BOARD_HAUPPAUGE_HVR1100       40
+#define CX88_BOARD_HAUPPAUGE_HVR1100LP     41
+#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO     42
+#define CX88_BOARD_KWORLD_DVB_T_CX22702    43
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44
+#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46
+#define CX88_BOARD_PCHDTV_HD5500           47
+#define CX88_BOARD_KWORLD_MCE200_DELUXE    48
+#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000  49
+#define CX88_BOARD_NPGTECH_REALTV_TOP10FM  50
+#define CX88_BOARD_WINFAST_DTV2000H        51
+#define CX88_BOARD_GENIATECH_DVBS          52
+#define CX88_BOARD_HAUPPAUGE_HVR3000       53
+#define CX88_BOARD_NORWOOD_MICRO           54
+#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
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59
+#define CX88_BOARD_PINNACLE_HYBRID_PCTV    60
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61
+#define CX88_BOARD_POWERCOLOR_REAL_ANGEL   62
+#define CX88_BOARD_GENIATECH_X8000_MT      63
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
+#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
+#define CX88_BOARD_PROLINK_PV_8000GT       66
+#define CX88_BOARD_KWORLD_ATSC_120         67
+#define CX88_BOARD_HAUPPAUGE_HVR4000       68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE   69
+#define CX88_BOARD_TEVII_S460              70
+#define CX88_BOARD_OMICOM_SS4_PCI          71
+#define CX88_BOARD_TBS_8920                72
+#define CX88_BOARD_TEVII_S420              73
+#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
+#define CX88_BOARD_PROF_7300               75
+#define CX88_BOARD_SATTRADE_ST4200         76
+#define CX88_BOARD_TBS_8910                77
+#define CX88_BOARD_PROF_6200               78
+#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
+#define CX88_BOARD_HAUPPAUGE_IRONLY        80
+#define CX88_BOARD_WINFAST_DTV1800H        81
+#define CX88_BOARD_WINFAST_DTV2000H_J      82
+#define CX88_BOARD_PROF_7301               83
+#define CX88_BOARD_SAMSUNG_SMT_7020        84
+#define CX88_BOARD_TWINHAN_VP1027_DVBS     85
+#define CX88_BOARD_TEVII_S464              86
+#define CX88_BOARD_WINFAST_DTV2000H_PLUS   87
+#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
+
+enum cx88_itype {
+	CX88_VMUX_COMPOSITE1 = 1,
+	CX88_VMUX_COMPOSITE2,
+	CX88_VMUX_COMPOSITE3,
+	CX88_VMUX_COMPOSITE4,
+	CX88_VMUX_SVIDEO,
+	CX88_VMUX_TELEVISION,
+	CX88_VMUX_CABLE,
+	CX88_VMUX_DVB,
+	CX88_VMUX_DEBUG,
+	CX88_RADIO,
+};
+
+struct cx88_input {
+	enum cx88_itype type;
+	u32             gpio0, gpio1, gpio2, gpio3;
+	unsigned int    vmux:2;
+	unsigned int    audioroute:4;
+};
+
+struct cx88_board {
+	const char              *name;
+	unsigned int            tuner_type;
+	unsigned int		radio_type;
+	unsigned char		tuner_addr;
+	unsigned char		radio_addr;
+	int                     tda9887_conf;
+	struct cx88_input       input[MAX_CX88_INPUT];
+	struct cx88_input       radio;
+	enum cx88_board_type    mpeg;
+	unsigned int            audio_chip;
+	int			num_frontends;
+
+	/* Used for I2S devices */
+	int			i2sinputcntl;
+};
+
+struct cx88_subid {
+	u16     subvendor;
+	u16     subdevice;
+	u32     card;
+};
+
+enum cx88_tvaudio {
+	WW_NONE = 1,
+	WW_BTSC,
+	WW_BG,
+	WW_DK,
+	WW_I,
+	WW_L,
+	WW_EIAJ,
+	WW_I2SPT,
+	WW_FM,
+	WW_I2SADC,
+	WW_M
+};
+
+#define INPUT(nr) (core->board.input[nr])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status                                 */
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_VIDEO         2
+#define RESOURCE_VBI           4
+
+#define BUFFER_TIMEOUT     msecs_to_jiffies(2000)
+
+/* buffer for one video frame */
+struct cx88_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer vb;
+
+	/* cx88 specific */
+	unsigned int           bpl;
+	struct btcx_riscmem    risc;
+	const struct cx8800_fmt *fmt;
+	u32                    count;
+};
+
+struct cx88_dmaqueue {
+	struct list_head       active;
+	struct list_head       queued;
+	struct timer_list      timeout;
+	struct btcx_riscmem    stopper;
+	u32                    count;
+};
+
+struct cx88_core {
+	struct list_head           devlist;
+	atomic_t                   refcount;
+
+	/* board name */
+	int                        nr;
+	char                       name[32];
+
+	/* pci stuff */
+	int                        pci_bus;
+	int                        pci_slot;
+	u32                        __iomem *lmmio;
+	u8                         __iomem *bmmio;
+	u32                        shadow[SHADOW_MAX];
+	int                        pci_irqmask;
+
+	/* i2c i/o */
+	struct i2c_adapter         i2c_adap;
+	struct i2c_algo_bit_data   i2c_algo;
+	struct i2c_client          i2c_client;
+	u32                        i2c_state, i2c_rc;
+
+	/* config info -- analog */
+	struct v4l2_device 	   v4l2_dev;
+	struct v4l2_ctrl_handler   video_hdl;
+	struct v4l2_ctrl	   *chroma_agc;
+	struct v4l2_ctrl_handler   audio_hdl;
+	struct v4l2_subdev	   *sd_wm8775;
+	struct i2c_client 	   *i2c_rtc;
+	unsigned int               boardnr;
+	struct cx88_board	   board;
+
+	/* Supported V4L _STD_ tuner formats */
+	unsigned int               tuner_formats;
+
+	/* config info -- dvb */
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+	int 			   (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+#endif
+	void			   (*gate_ctrl)(struct cx88_core  *core, int open);
+
+	/* state info */
+	struct task_struct         *kthread;
+	v4l2_std_id                tvnorm;
+	enum cx88_tvaudio          tvaudio;
+	u32                        audiomode_manual;
+	u32                        audiomode_current;
+	u32                        input;
+	u32                        last_analog_input;
+	u32                        astat;
+	u32			   use_nicam;
+	unsigned long		   last_change;
+
+	/* IR remote control state */
+	struct cx88_IR             *ir;
+
+	/* I2C remote data */
+	struct IR_i2c_init_data    init_data;
+	struct wm8775_platform_data wm8775_data;
+
+	struct mutex               lock;
+	/* various v4l controls */
+	u32                        freq;
+	int                        users;
+	int                        mpeg_users;
+
+	/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
+	struct cx8802_dev          *dvbdev;
+	enum cx88_board_type       active_type_id;
+	int			   active_ref;
+	int			   active_fe_id;
+};
+
+static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
+}
+
+#define call_hw(core, grpid, o, f, args...) \
+	do {							\
+		if (!core->i2c_rc) {				\
+			if (core->gate_ctrl)			\
+				core->gate_ctrl(core, 1);	\
+			v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
+			if (core->gate_ctrl)			\
+				core->gate_ctrl(core, 0);	\
+		}						\
+	} while (0)
+
+#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+
+#define WM8775_GID      (1 << 0)
+
+#define wm8775_s_ctrl(core, id, val) \
+	do {									\
+		struct v4l2_ctrl *ctrl_ =					\
+			v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id);	\
+		if (ctrl_ && !core->i2c_rc) {					\
+			if (core->gate_ctrl)					\
+				core->gate_ctrl(core, 1);			\
+			v4l2_ctrl_s_ctrl(ctrl_, val);				\
+			if (core->gate_ctrl)					\
+				core->gate_ctrl(core, 0);			\
+		}								\
+	} while (0)
+
+#define wm8775_g_ctrl(core, id) \
+	({									\
+		struct v4l2_ctrl *ctrl_ =					\
+			v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id);	\
+		s32 val = 0;							\
+		if (ctrl_ && !core->i2c_rc) {					\
+			if (core->gate_ctrl)					\
+				core->gate_ctrl(core, 1);			\
+			val = v4l2_ctrl_g_ctrl(ctrl_);				\
+			if (core->gate_ctrl)					\
+				core->gate_ctrl(core, 0);			\
+		}								\
+		val;								\
+	})
+
+struct cx8800_dev;
+struct cx8802_dev;
+
+/* ----------------------------------------------------------- */
+/* function 0: video stuff                                     */
+
+struct cx8800_fh {
+	struct v4l2_fh		   fh;
+	struct cx8800_dev          *dev;
+	unsigned int               resources;
+
+	/* video capture */
+	struct videobuf_queue      vidq;
+
+	/* vbi capture */
+	struct videobuf_queue      vbiq;
+};
+
+struct cx8800_suspend_state {
+	int                        disabled;
+};
+
+struct cx8800_dev {
+	struct cx88_core           *core;
+	spinlock_t                 slock;
+
+	/* various device info */
+	unsigned int               resources;
+	struct video_device        *video_dev;
+	struct video_device        *vbi_dev;
+	struct video_device        *radio_dev;
+
+	/* pci i/o */
+	struct pci_dev             *pci;
+	unsigned char              pci_rev,pci_lat;
+
+	const struct cx8800_fmt    *fmt;
+	unsigned int               width, height;
+
+	/* capture queues */
+	struct cx88_dmaqueue       vidq;
+	struct cx88_dmaqueue       vbiq;
+
+	/* various v4l controls */
+
+	/* other global state info */
+	struct cx8800_suspend_state state;
+};
+
+/* ----------------------------------------------------------- */
+/* function 1: audio/alsa stuff                                */
+/* =============> moved to cx88-alsa.c <====================== */
+
+
+/* ----------------------------------------------------------- */
+/* function 2: mpeg stuff                                      */
+
+struct cx8802_fh {
+	struct v4l2_fh		   fh;
+	struct cx8802_dev          *dev;
+	struct videobuf_queue      mpegq;
+};
+
+struct cx8802_suspend_state {
+	int                        disabled;
+};
+
+struct cx8802_driver {
+	struct cx88_core *core;
+
+	/* List of drivers attached to device */
+	struct list_head drvlist;
+
+	/* Type of driver and access required */
+	enum cx88_board_type type_id;
+	enum cx8802_board_access hw_access;
+
+	/* MPEG 8802 internal only */
+	int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
+	int (*resume)(struct pci_dev *pci_dev);
+
+	/* Callers to the following functions must hold core->lock */
+
+	/* MPEG 8802 -> mini driver - Driver probe and configuration */
+	int (*probe)(struct cx8802_driver *drv);
+	int (*remove)(struct cx8802_driver *drv);
+
+	/* MPEG 8802 -> mini driver - Access for hardware control */
+	int (*advise_acquire)(struct cx8802_driver *drv);
+	int (*advise_release)(struct cx8802_driver *drv);
+
+	/* MPEG 8802 <- mini driver - Access for hardware control */
+	int (*request_acquire)(struct cx8802_driver *drv);
+	int (*request_release)(struct cx8802_driver *drv);
+};
+
+struct cx8802_dev {
+	struct cx88_core           *core;
+	spinlock_t                 slock;
+
+	/* pci i/o */
+	struct pci_dev             *pci;
+	unsigned char              pci_rev,pci_lat;
+
+	/* dma queues */
+	struct cx88_dmaqueue       mpegq;
+	u32                        ts_packet_size;
+	u32                        ts_packet_count;
+
+	/* other global state info */
+	struct cx8802_suspend_state state;
+
+	/* for blackbird only */
+	struct list_head           devlist;
+#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
+    defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
+	struct video_device        *mpeg_dev;
+	u32                        mailbox;
+	int                        width;
+	int                        height;
+	unsigned char              mpeg_active; /* nonzero if mpeg encoder is active */
+
+	/* mpeg params */
+	struct cx2341x_handler     cxhdl;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+	/* for dvb only */
+	struct videobuf_dvb_frontends frontends;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || \
+    defined(CONFIG_VIDEO_CX88_VP3054_MODULE)
+	/* For VP3045 secondary I2C bus support */
+	struct vp3054_i2c_state	   *vp3054;
+#endif
+	/* for switching modulation types */
+	unsigned char              ts_gen_cntrl;
+
+	/* List of attached drivers; must hold core->lock to access */
+	struct list_head	   drvlist;
+
+	struct work_struct	   request_module_wk;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg)             readl(core->lmmio + ((reg)>>2))
+#define cx_write(reg,value)      writel((value), core->lmmio + ((reg)>>2))
+#define cx_writeb(reg,value)     writeb((value), core->bmmio + (reg))
+
+#define cx_andor(reg,mask,value) \
+  writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\
+  ((value) & (mask)), core->lmmio+((reg)>>2))
+#define cx_set(reg,bit)          cx_andor((reg),(bit),(bit))
+#define cx_clear(reg,bit)        cx_andor((reg),(bit),0)
+
+#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
+
+/* shadow registers */
+#define cx_sread(sreg)		    (core->shadow[sreg])
+#define cx_swrite(sreg,reg,value) \
+  (core->shadow[sreg] = value, \
+   writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+#define cx_sandor(sreg,reg,mask,value) \
+  (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
+   writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+
+/* ----------------------------------------------------------- */
+/* cx88-core.c                                                 */
+
+extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+			       int len, u32 bits, u32 mask);
+
+extern int cx88_core_irq(struct cx88_core *core, u32 status);
+extern void cx88_wakeup(struct cx88_core *core,
+			struct cx88_dmaqueue *q, u32 count);
+extern void cx88_shutdown(struct cx88_core *core);
+extern int cx88_reset(struct cx88_core *core);
+
+extern int
+cx88_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);
+extern int
+cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+		     struct scatterlist *sglist, unsigned int bpl,
+		     unsigned int lines, unsigned int lpi);
+extern int
+cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+		  u32 reg, u32 mask, u32 value);
+extern void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
+
+extern void cx88_risc_disasm(struct cx88_core *core,
+			     struct btcx_riscmem *risc);
+extern int cx88_sram_channel_setup(struct cx88_core *core,
+				   const struct sram_channel *ch,
+				   unsigned int bpl, u32 risc);
+extern void cx88_sram_channel_dump(struct cx88_core *core,
+				   const struct sram_channel *ch);
+
+extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
+			  unsigned int height, enum v4l2_field field);
+extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
+
+extern struct video_device *cx88_vdev_init(struct cx88_core *core,
+					   struct pci_dev *pci,
+					   const struct video_device *template_,
+					   const char *type);
+extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
+extern void cx88_core_put(struct cx88_core *core,
+			  struct pci_dev *pci);
+
+extern int cx88_start_audio_dma(struct cx88_core *core);
+extern int cx88_stop_audio_dma(struct cx88_core *core);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-vbi.c                                                  */
+
+/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */
+int cx8800_vbi_fmt (struct file *file, void *priv,
+					struct v4l2_format *f);
+
+/*
+int cx8800_start_vbi_dma(struct cx8800_dev    *dev,
+			 struct cx88_dmaqueue *q,
+			 struct cx88_buffer   *buf);
+*/
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
+int cx8800_restart_vbi_queue(struct cx8800_dev    *dev,
+			     struct cx88_dmaqueue *q);
+void cx8800_vbi_timeout(unsigned long data);
+
+extern const struct videobuf_queue_ops cx8800_vbi_qops;
+
+/* ----------------------------------------------------------- */
+/* cx88-i2c.c                                                  */
+
+extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-cards.c                                                */
+
+extern int cx88_tuner_callback(void *dev, int component, 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);
+extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
+
+/* ----------------------------------------------------------- */
+/* cx88-tvaudio.c                                              */
+
+void cx88_set_tvaudio(struct cx88_core *core);
+void cx88_newstation(struct cx88_core *core);
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
+void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
+int cx88_audio_thread(void *data);
+
+int cx8802_register_driver(struct cx8802_driver *drv);
+int cx8802_unregister_driver(struct cx8802_driver *drv);
+
+/* Caller must hold core->lock */
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
+
+/* ----------------------------------------------------------- */
+/* cx88-dsp.c                                                  */
+
+s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-input.c                                                */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);
+int cx88_ir_fini(struct cx88_core *core);
+void cx88_ir_irq(struct cx88_core *core);
+int cx88_ir_start(struct cx88_core *core);
+void cx88_ir_stop(struct cx88_core *core);
+extern void cx88_i2c_init_ir(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-mpeg.c                                                 */
+
+int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
+			struct cx88_buffer *buf, enum v4l2_field field);
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
+void cx8802_cancel_buffers(struct cx8802_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* cx88-video.c*/
+int cx88_enum_input (struct cx88_core  *core,struct v4l2_input *i);
+int cx88_set_freq (struct cx88_core  *core,struct v4l2_frequency *f);
+int cx88_video_mux(struct cx88_core *core, unsigned int input);
+void cx88_querycap(struct file *file, struct cx88_core *core,
+		struct v4l2_capability *cap);
diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig
new file mode 100644
index 000000000000..44e5dc15e60a
--- /dev/null
+++ b/drivers/media/pci/ddbridge/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DDBRIDGE
+	tristate "Digital Devices bridge support"
+	depends on DVB_CORE && PCI && I2C
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  Support for cards with the Digital Devices PCI express bridge:
+	  - Octopus PCIe Bridge
+	  - Octopus mini PCIe Bridge
+	  - Octopus LE
+	  - DuoFlex S2 Octopus
+	  - DuoFlex CT Octopus
+	  - cineS2(v6)
+
+	  Say Y if you own such a card and want to use it.
diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile
new file mode 100644
index 000000000000..7446c8b677b5
--- /dev/null
+++ b/drivers/media/pci/ddbridge/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the ddbridge device driver
+#
+
+ddbridge-objs := ddbridge-core.o
+
+obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
+
+# For the staging CI driver cxd2099
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
new file mode 100644
index 000000000000..feff57ee5a08
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -0,0 +1,1730 @@
+/*
+ * ddbridge.c: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <linux/swab.h>
+#include <linux/vmalloc.h>
+#include "ddbridge.h"
+
+#include "ddbridge-regs.h"
+
+#include "tda18271c2dd.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "drxk.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* MSI had problems with lost interrupts, fixed but needs testing */
+#undef CONFIG_PCI_MSI
+
+/******************************************************************************/
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+	struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
+				   .buf  = &reg, .len   = 1 },
+				  {.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+			  u16 reg, u8 *val)
+{
+	u8 msg[2] = {reg>>8, reg&0xff};
+	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+				   .buf  = msg, .len   = 2},
+				  {.addr = adr, .flags = I2C_M_RD,
+				   .buf  = val, .len   = 1} };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
+{
+	struct ddb *dev = i2c->dev;
+	int stat;
+	u32 val;
+
+	i2c->done = 0;
+	ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
+	stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
+	if (stat <= 0) {
+		printk(KERN_ERR "I2C timeout\n");
+		{ /* MSI debugging*/
+			u32 istat = ddbreadl(INTERRUPT_STATUS);
+			printk(KERN_ERR "IRS %08x\n", istat);
+			ddbwritel(istat, INTERRUPT_ACK);
+		}
+		return -EIO;
+	}
+	val = ddbreadl(i2c->regs+I2C_COMMAND);
+	if (val & 0x70000)
+		return -EIO;
+	return 0;
+}
+
+static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
+			       struct i2c_msg msg[], int num)
+{
+	struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
+	struct ddb *dev = i2c->dev;
+	u8 addr = 0;
+
+	if (num)
+		addr = msg[0].addr;
+
+	if (num == 2 && msg[1].flags & I2C_M_RD &&
+	    !(msg[0].flags & I2C_M_RD)) {
+		memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
+			    msg[0].buf, msg[0].len);
+		ddbwritel(msg[0].len|(msg[1].len << 16),
+			  i2c->regs+I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 1)) {
+			memcpy_fromio(msg[1].buf,
+				      dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
+				      msg[1].len);
+			return num;
+		}
+	}
+
+	if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+		ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
+		ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 2))
+			return num;
+	}
+	if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+		ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 3)) {
+			ddbcpyfrom(msg[0].buf,
+				   I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
+			return num;
+		}
+	}
+	return -EIO;
+}
+
+
+static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm ddb_i2c_algo = {
+	.master_xfer   = ddb_i2c_master_xfer,
+	.functionality = ddb_i2c_functionality,
+};
+
+static void ddb_i2c_release(struct ddb *dev)
+{
+	int i;
+	struct ddb_i2c *i2c;
+	struct i2c_adapter *adap;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		i2c = &dev->i2c[i];
+		adap = &i2c->adap;
+		i2c_del_adapter(adap);
+	}
+}
+
+static int ddb_i2c_init(struct ddb *dev)
+{
+	int i, j, stat = 0;
+	struct ddb_i2c *i2c;
+	struct i2c_adapter *adap;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		i2c = &dev->i2c[i];
+		i2c->dev = dev;
+		i2c->nr = i;
+		i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
+		i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
+		i2c->regs = 0x80 + i * 0x20;
+		ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
+		ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
+			  i2c->regs + I2C_TASKADDRESS);
+		init_waitqueue_head(&i2c->wq);
+
+		adap = &i2c->adap;
+		i2c_set_adapdata(adap, i2c);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+		adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
+#else
+#ifdef I2C_CLASS_TV_ANALOG
+		adap->class = I2C_CLASS_TV_ANALOG;
+#endif
+#endif
+		strcpy(adap->name, "ddbridge");
+		adap->algo = &ddb_i2c_algo;
+		adap->algo_data = (void *)i2c;
+		adap->dev.parent = &dev->pdev->dev;
+		stat = i2c_add_adapter(adap);
+		if (stat)
+			break;
+	}
+	if (stat)
+		for (j = 0; j < i; j++) {
+			i2c = &dev->i2c[j];
+			adap = &i2c->adap;
+			i2c_del_adapter(adap);
+		}
+	return stat;
+}
+
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static void set_table(struct ddb *dev, u32 off,
+		      dma_addr_t *pbuf, u32 num)
+{
+	u32 i, base;
+	u64 mem;
+
+	base = DMA_BASE_ADDRESS_TABLE + off;
+	for (i = 0; i < num; i++) {
+		mem = pbuf[i];
+		ddbwritel(mem & 0xffffffff, base + i * 8);
+		ddbwritel(mem >> 32, base + i * 8 + 4);
+	}
+}
+#endif
+
+static void ddb_address_table(struct ddb *dev)
+{
+	u32 i, j, base;
+	u64 mem;
+	dma_addr_t *pbuf;
+
+	for (i = 0; i < dev->info->port_num * 2; i++) {
+		base = DMA_BASE_ADDRESS_TABLE + i * 0x100;
+		pbuf = dev->input[i].pbuf;
+		for (j = 0; j < dev->input[i].dma_buf_num; j++) {
+			mem = pbuf[j];
+			ddbwritel(mem & 0xffffffff, base + j * 8);
+			ddbwritel(mem >> 32, base + j * 8 + 4);
+		}
+	}
+	for (i = 0; i < dev->info->port_num; i++) {
+		base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100;
+		pbuf = dev->output[i].pbuf;
+		for (j = 0; j < dev->output[i].dma_buf_num; j++) {
+			mem = pbuf[j];
+			ddbwritel(mem & 0xffffffff, base + j * 8);
+			ddbwritel(mem >> 32, base + j * 8 + 4);
+		}
+	}
+}
+
+static void io_free(struct pci_dev *pdev, u8 **vbuf,
+		    dma_addr_t *pbuf, u32 size, int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (vbuf[i]) {
+			pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
+			vbuf[i] = 0;
+		}
+	}
+}
+
+static int io_alloc(struct pci_dev *pdev, u8 **vbuf,
+		    dma_addr_t *pbuf, u32 size, int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]);
+		if (!vbuf[i])
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static int ddb_buffers_alloc(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		switch (port->class) {
+		case DDB_PORT_TUNER:
+			if (io_alloc(dev->pdev, port->input[0]->vbuf,
+				     port->input[0]->pbuf,
+				     port->input[0]->dma_buf_size,
+				     port->input[0]->dma_buf_num) < 0)
+				return -1;
+			if (io_alloc(dev->pdev, port->input[1]->vbuf,
+				     port->input[1]->pbuf,
+				     port->input[1]->dma_buf_size,
+				     port->input[1]->dma_buf_num) < 0)
+				return -1;
+			break;
+		case DDB_PORT_CI:
+			if (io_alloc(dev->pdev, port->input[0]->vbuf,
+				     port->input[0]->pbuf,
+				     port->input[0]->dma_buf_size,
+				     port->input[0]->dma_buf_num) < 0)
+				return -1;
+			if (io_alloc(dev->pdev, port->output->vbuf,
+				     port->output->pbuf,
+				     port->output->dma_buf_size,
+				     port->output->dma_buf_num) < 0)
+				return -1;
+			break;
+		default:
+			break;
+		}
+	}
+	ddb_address_table(dev);
+	return 0;
+}
+
+static void ddb_buffers_free(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		io_free(dev->pdev, port->input[0]->vbuf,
+			port->input[0]->pbuf,
+			port->input[0]->dma_buf_size,
+			port->input[0]->dma_buf_num);
+		io_free(dev->pdev, port->input[1]->vbuf,
+			port->input[1]->pbuf,
+			port->input[1]->dma_buf_size,
+			port->input[1]->dma_buf_num);
+		io_free(dev->pdev, port->output->vbuf,
+			port->output->pbuf,
+			port->output->dma_buf_size,
+			port->output->dma_buf_num);
+	}
+}
+
+static void ddb_input_start(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+
+	spin_lock_irq(&input->lock);
+	input->cbuf = 0;
+	input->coff = 0;
+
+	/* reset */
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(2, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+
+	ddbwritel((1 << 16) |
+		  (input->dma_buf_num << 11) |
+		  (input->dma_buf_size >> 7),
+		  DMA_BUFFER_SIZE(input->nr));
+	ddbwritel(0, DMA_BUFFER_ACK(input->nr));
+
+	ddbwritel(1, DMA_BASE_WRITE);
+	ddbwritel(3, DMA_BUFFER_CONTROL(input->nr));
+	ddbwritel(9, TS_INPUT_CONTROL(input->nr));
+	input->running = 1;
+	spin_unlock_irq(&input->lock);
+}
+
+static void ddb_input_stop(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+
+	spin_lock_irq(&input->lock);
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(0, DMA_BUFFER_CONTROL(input->nr));
+	input->running = 0;
+	spin_unlock_irq(&input->lock);
+}
+
+static void ddb_output_start(struct ddb_output *output)
+{
+	struct ddb *dev = output->port->dev;
+
+	spin_lock_irq(&output->lock);
+	output->cbuf = 0;
+	output->coff = 0;
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(2, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel((1 << 16) |
+		  (output->dma_buf_num << 11) |
+		  (output->dma_buf_size >> 7),
+		  DMA_BUFFER_SIZE(output->nr + 8));
+	ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8));
+
+	ddbwritel(1, DMA_BASE_READ);
+	ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8));
+	/* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */
+	ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr));
+	output->running = 1;
+	spin_unlock_irq(&output->lock);
+}
+
+static void ddb_output_stop(struct ddb_output *output)
+{
+	struct ddb *dev = output->port->dev;
+
+	spin_lock_irq(&output->lock);
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8));
+	output->running = 0;
+	spin_unlock_irq(&output->lock);
+}
+
+static u32 ddb_output_free(struct ddb_output *output)
+{
+	u32 idx, off, stat = output->stat;
+	s32 diff;
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	if (output->cbuf != idx) {
+		if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+		    (output->dma_buf_size - output->coff <= 188))
+			return 0;
+		return 188;
+	}
+	diff = off - output->coff;
+	if (diff <= 0 || diff > 188)
+		return 188;
+	return 0;
+}
+
+static ssize_t ddb_output_write(struct ddb_output *output,
+				const u8 *buf, size_t count)
+{
+	struct ddb *dev = output->port->dev;
+	u32 idx, off, stat = output->stat;
+	u32 left = count, len;
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	while (left) {
+		len = output->dma_buf_size - output->coff;
+		if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+		    (off == 0)) {
+			if (len <= 188)
+				break;
+			len -= 188;
+		}
+		if (output->cbuf == idx) {
+			if (off > output->coff) {
+#if 1
+				len = off - output->coff;
+				len -= (len % 188);
+				if (len <= 188)
+
+#endif
+					break;
+				len -= 188;
+			}
+		}
+		if (len > left)
+			len = left;
+		if (copy_from_user(output->vbuf[output->cbuf] + output->coff,
+				   buf, len))
+			return -EIO;
+		left -= len;
+		buf += len;
+		output->coff += len;
+		if (output->coff == output->dma_buf_size) {
+			output->coff = 0;
+			output->cbuf = ((output->cbuf + 1) % output->dma_buf_num);
+		}
+		ddbwritel((output->cbuf << 11) | (output->coff >> 7),
+			  DMA_BUFFER_ACK(output->nr + 8));
+	}
+	return count - left;
+}
+
+static u32 ddb_input_avail(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+	u32 idx, off, stat = input->stat;
+	u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr));
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	if (ctrl & 4) {
+		printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
+		ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
+		return 0;
+	}
+	if (input->cbuf != idx)
+		return 188;
+	return 0;
+}
+
+static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+{
+	struct ddb *dev = input->port->dev;
+	u32 left = count;
+	u32 idx, free, stat = input->stat;
+	int ret;
+
+	idx = (stat >> 11) & 0x1f;
+
+	while (left) {
+		if (input->cbuf == idx)
+			return count - left;
+		free = input->dma_buf_size - input->coff;
+		if (free > left)
+			free = left;
+		ret = copy_to_user(buf, input->vbuf[input->cbuf] +
+				   input->coff, free);
+		if (ret)
+			return -EFAULT;
+		input->coff += free;
+		if (input->coff == input->dma_buf_size) {
+			input->coff = 0;
+			input->cbuf = (input->cbuf+1) % input->dma_buf_num;
+		}
+		left -= free;
+		ddbwritel((input->cbuf << 11) | (input->coff >> 7),
+			  DMA_BUFFER_ACK(input->nr));
+	}
+	return count;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe)
+{
+	int i;
+
+	for (i = 0; i < dev->info->port_num * 2; i++) {
+		if (dev->input[i].fe == fe)
+			return &dev->input[i];
+	}
+	return NULL;
+}
+#endif
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct ddb_input *input = fe->sec_priv;
+	struct ddb_port *port = input->port;
+	int status;
+
+	if (enable) {
+		mutex_lock(&port->i2c_gate_lock);
+		status = input->gate_ctrl(fe, 1);
+	} else {
+		status = input->gate_ctrl(fe, 0);
+		mutex_unlock(&port->i2c_gate_lock);
+	}
+	return status;
+}
+
+static int demod_attach_drxk(struct ddb_input *input)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct dvb_frontend *fe;
+	struct drxk_config config;
+
+	memset(&config, 0, sizeof(config));
+	config.microcode_name = "drxk_a3.mc";
+	config.qam_demod_parameter_count = 4;
+	config.adr = 0x29 + (input->nr & 1);
+
+	fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
+	if (!input->fe) {
+		printk(KERN_ERR "No DRXK found!\n");
+		return -ENODEV;
+	}
+	fe->sec_priv = input;
+	input->gate_ctrl = fe->ops.i2c_gate_ctrl;
+	fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+	return 0;
+}
+
+static int tuner_attach_tda18271(struct ddb_input *input)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct dvb_frontend *fe;
+
+	if (input->fe->ops.i2c_gate_ctrl)
+		input->fe->ops.i2c_gate_ctrl(input->fe, 1);
+	fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
+	if (!fe) {
+		printk(KERN_ERR "No TDA18271 found!\n");
+		return -ENODEV;
+	}
+	if (input->fe->ops.i2c_gate_ctrl)
+		input->fe->ops.i2c_gate_ctrl(input->fe, 0);
+	return 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct stv090x_config stv0900 = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x69,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+};
+
+static struct stv090x_config stv0900_aa = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x68,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+};
+
+static struct stv6110x_config stv6110a = {
+	.addr    = 0x60,
+	.refclk	 = 27000000,
+	.clk_div = 1,
+};
+
+static struct stv6110x_config stv6110b = {
+	.addr    = 0x63,
+	.refclk	 = 27000000,
+	.clk_div = 1,
+};
+
+static int demod_attach_stv0900(struct ddb_input *input, int type)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+
+	input->fe = dvb_attach(stv090x_attach, feconf, i2c,
+			       (input->nr & 1) ? STV090x_DEMODULATOR_1
+			       : STV090x_DEMODULATOR_0);
+	if (!input->fe) {
+		printk(KERN_ERR "No STV0900 found!\n");
+		return -ENODEV;
+	}
+	if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
+			0, (input->nr & 1) ?
+			(0x09 - type) : (0x0b - type))) {
+		printk(KERN_ERR "No LNBH24 found!\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int tuner_attach_stv6110(struct ddb_input *input, int type)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+	struct stv6110x_config *tunerconf = (input->nr & 1) ?
+		&stv6110b : &stv6110a;
+	struct stv6110x_devctl *ctl;
+
+	ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
+	if (!ctl) {
+		printk(KERN_ERR "No STV6110X found!\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "attach tuner input %d adr %02x\n",
+			 input->nr, tunerconf->addr);
+
+	feconf->tuner_init          = ctl->tuner_init;
+	feconf->tuner_sleep         = ctl->tuner_sleep;
+	feconf->tuner_set_mode      = ctl->tuner_set_mode;
+	feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+	feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+	feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+	feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+	feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
+	feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
+	feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
+	feconf->tuner_get_status    = ctl->tuner_get_status;
+
+	return 0;
+}
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+			    int (*start_feed)(struct dvb_demux_feed *),
+			    int (*stop_feed)(struct dvb_demux_feed *),
+			    void *priv)
+{
+	dvbdemux->priv = priv;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = start_feed;
+	dvbdemux->stop_feed = stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+				      DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+	return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+			       struct dvb_demux *dvbdemux,
+			       struct dmx_frontend *hw_frontend,
+			       struct dmx_frontend *mem_frontend,
+			       struct dvb_adapter *dvb_adapter)
+{
+	int ret;
+
+	dmxdev->filternum = 256;
+	dmxdev->demux = &dvbdemux->dmx;
+	dmxdev->capabilities = 0;
+	ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+	if (ret < 0)
+		return ret;
+
+	hw_frontend->source = DMX_FRONTEND_0;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+	mem_frontend->source = DMX_MEMORY_FE;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+	return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ddb_input *input = dvbdmx->priv;
+
+	if (!input->users)
+		ddb_input_start(input);
+
+	return ++input->users;
+}
+
+static int stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ddb_input *input = dvbdmx->priv;
+
+	if (--input->users)
+		return input->users;
+
+	ddb_input_stop(input);
+	return 0;
+}
+
+
+static void dvb_input_detach(struct ddb_input *input)
+{
+	struct dvb_adapter *adap = &input->adap;
+	struct dvb_demux *dvbdemux = &input->demux;
+
+	switch (input->attached) {
+	case 5:
+		if (input->fe2)
+			dvb_unregister_frontend(input->fe2);
+		if (input->fe) {
+			dvb_unregister_frontend(input->fe);
+			dvb_frontend_detach(input->fe);
+			input->fe = NULL;
+		}
+	case 4:
+		dvb_net_release(&input->dvbnet);
+
+	case 3:
+		dvbdemux->dmx.close(&dvbdemux->dmx);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &input->hw_frontend);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &input->mem_frontend);
+		dvb_dmxdev_release(&input->dmxdev);
+
+	case 2:
+		dvb_dmx_release(&input->demux);
+
+	case 1:
+		dvb_unregister_adapter(adap);
+	}
+	input->attached = 0;
+}
+
+static int dvb_input_attach(struct ddb_input *input)
+{
+	int ret;
+	struct ddb_port *port = input->port;
+	struct dvb_adapter *adap = &input->adap;
+	struct dvb_demux *dvbdemux = &input->demux;
+
+	ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
+				   &input->port->dev->pdev->dev,
+				   adapter_nr);
+	if (ret < 0) {
+		printk(KERN_ERR "ddbridge: Could not register adapter."
+		       "Check if you enabled enough adapters in dvb-core!\n");
+		return ret;
+	}
+	input->attached = 1;
+
+	ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+				      start_feed,
+				      stop_feed, input);
+	if (ret < 0)
+		return ret;
+	input->attached = 2;
+
+	ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux,
+					 &input->hw_frontend,
+					 &input->mem_frontend, adap);
+	if (ret < 0)
+		return ret;
+	input->attached = 3;
+
+	ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux);
+	if (ret < 0)
+		return ret;
+	input->attached = 4;
+
+	input->fe = 0;
+	switch (port->type) {
+	case DDB_TUNER_DVBS_ST:
+		if (demod_attach_stv0900(input, 0) < 0)
+			return -ENODEV;
+		if (tuner_attach_stv6110(input, 0) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		break;
+	case DDB_TUNER_DVBS_ST_AA:
+		if (demod_attach_stv0900(input, 1) < 0)
+			return -ENODEV;
+		if (tuner_attach_stv6110(input, 1) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		break;
+	case DDB_TUNER_DVBCT_TR:
+		if (demod_attach_drxk(input) < 0)
+			return -ENODEV;
+		if (tuner_attach_tda18271(input) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		if (input->fe2) {
+			if (dvb_register_frontend(adap, input->fe2) < 0)
+				return -ENODEV;
+			input->fe2->tuner_priv = input->fe->tuner_priv;
+			memcpy(&input->fe2->ops.tuner_ops,
+			       &input->fe->ops.tuner_ops,
+			       sizeof(struct dvb_tuner_ops));
+		}
+		break;
+	}
+	input->attached = 5;
+	return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static ssize_t ts_write(struct file *file, const char *buf,
+			size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	size_t left = count;
+	int stat;
+
+	while (left) {
+		if (ddb_output_free(output) < 188) {
+			if (file->f_flags & O_NONBLOCK)
+				break;
+			if (wait_event_interruptible(
+				    output->wq, ddb_output_free(output) >= 188) < 0)
+				break;
+		}
+		stat = ddb_output_write(output, buf, left);
+		if (stat < 0)
+			break;
+		buf += stat;
+		left -= stat;
+	}
+	return (left == count) ? -EAGAIN : (count - left);
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+		       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	struct ddb_input *input = output->port->input[0];
+	int left, read;
+
+	count -= count % 188;
+	left = count;
+	while (left) {
+		if (ddb_input_avail(input) < 188) {
+			if (file->f_flags & O_NONBLOCK)
+				break;
+			if (wait_event_interruptible(
+				    input->wq, ddb_input_avail(input) >= 188) < 0)
+				break;
+		}
+		read = ddb_input_read(input, buf, left);
+		if (read < 0)
+			return read;
+		left -= read;
+		buf += read;
+	}
+	return (left == count) ? -EAGAIN : (count - left);
+}
+
+static unsigned int ts_poll(struct file *file, poll_table *wait)
+{
+	/*
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	struct ddb_input *input = output->port->input[0];
+	*/
+	unsigned int mask = 0;
+
+#if 0
+	if (data_avail_to_read)
+		mask |= POLLIN | POLLRDNORM;
+	if (data_avail_to_write)
+		mask |= POLLOUT | POLLWRNORM;
+
+	poll_wait(file, &read_queue, wait);
+	poll_wait(file, &write_queue, wait);
+#endif
+	return mask;
+}
+
+static const struct file_operations ci_fops = {
+	.owner   = THIS_MODULE,
+	.read    = ts_read,
+	.write   = ts_write,
+	.open    = dvb_generic_open,
+	.release = dvb_generic_release,
+	.poll    = ts_poll,
+	.mmap    = 0,
+};
+
+static struct dvb_device dvbdev_ci = {
+	.priv    = 0,
+	.readers = -1,
+	.writers = -1,
+	.users   = -1,
+	.fops    = &ci_fops,
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void input_tasklet(unsigned long data)
+{
+	struct ddb_input *input = (struct ddb_input *) data;
+	struct ddb *dev = input->port->dev;
+
+	spin_lock(&input->lock);
+	if (!input->running) {
+		spin_unlock(&input->lock);
+		return;
+	}
+	input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+
+	if (input->port->class == DDB_PORT_TUNER) {
+		if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
+			printk(KERN_ERR "Overflow input %d\n", input->nr);
+		while (input->cbuf != ((input->stat >> 11) & 0x1f)
+		       || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
+			dvb_dmx_swfilter_packets(&input->demux,
+						 input->vbuf[input->cbuf],
+						 input->dma_buf_size / 188);
+
+			input->cbuf = (input->cbuf + 1) % input->dma_buf_num;
+			ddbwritel((input->cbuf << 11),
+				  DMA_BUFFER_ACK(input->nr));
+			input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+		       }
+	}
+	if (input->port->class == DDB_PORT_CI)
+		wake_up(&input->wq);
+	spin_unlock(&input->lock);
+}
+
+static void output_tasklet(unsigned long data)
+{
+	struct ddb_output *output = (struct ddb_output *) data;
+	struct ddb *dev = output->port->dev;
+
+	spin_lock(&output->lock);
+	if (!output->running) {
+		spin_unlock(&output->lock);
+		return;
+	}
+	output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8));
+	wake_up(&output->wq);
+	spin_unlock(&output->lock);
+}
+
+
+struct cxd2099_cfg cxd_cfg = {
+	.bitrate =  62000,
+	.adr     =  0x40,
+	.polarity = 1,
+	.clock_mode = 1,
+};
+
+static int ddb_ci_attach(struct ddb_port *port)
+{
+	int ret;
+
+	ret = dvb_register_adapter(&port->output->adap,
+				   "DDBridge",
+				   THIS_MODULE,
+				   &port->dev->pdev->dev,
+				   adapter_nr);
+	if (ret < 0)
+		return ret;
+	port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
+	if (!port->en) {
+		dvb_unregister_adapter(&port->output->adap);
+		return -ENODEV;
+	}
+	ddb_input_start(port->input[0]);
+	ddb_output_start(port->output);
+	dvb_ca_en50221_init(&port->output->adap,
+			    port->en, 0, 1);
+	ret = dvb_register_device(&port->output->adap, &port->output->dev,
+				  &dvbdev_ci, (void *) port->output,
+				  DVB_DEVICE_SEC);
+	return ret;
+}
+
+static int ddb_port_attach(struct ddb_port *port)
+{
+	int ret = 0;
+
+	switch (port->class) {
+	case DDB_PORT_TUNER:
+		ret = dvb_input_attach(port->input[0]);
+		if (ret < 0)
+			break;
+		ret = dvb_input_attach(port->input[1]);
+		break;
+	case DDB_PORT_CI:
+		ret = ddb_ci_attach(port);
+		break;
+	default:
+		break;
+	}
+	if (ret < 0)
+		printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
+	return ret;
+}
+
+static int ddb_ports_attach(struct ddb *dev)
+{
+	int i, ret = 0;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		ret = ddb_port_attach(port);
+		if (ret < 0)
+			break;
+	}
+	return ret;
+}
+
+static void ddb_ports_detach(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		switch (port->class) {
+		case DDB_PORT_TUNER:
+			dvb_input_detach(port->input[0]);
+			dvb_input_detach(port->input[1]);
+			break;
+		case DDB_PORT_CI:
+			if (port->output->dev)
+				dvb_unregister_device(port->output->dev);
+			if (port->en) {
+				ddb_input_stop(port->input[0]);
+				ddb_output_stop(port->output);
+				dvb_ca_en50221_release(port->en);
+				kfree(port->en);
+				port->en = 0;
+				dvb_unregister_adapter(&port->output->adap);
+			}
+			break;
+		}
+	}
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static int port_has_ci(struct ddb_port *port)
+{
+	u8 val;
+	return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1;
+}
+
+static int port_has_stv0900(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_stv0900_aa(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_drxks(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read(&port->i2c->adap, 0x29, &val) < 0)
+		return 0;
+	if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static void ddb_port_probe(struct ddb_port *port)
+{
+	struct ddb *dev = port->dev;
+	char *modname = "NO MODULE";
+
+	port->class = DDB_PORT_NONE;
+
+	if (port_has_ci(port)) {
+		modname = "CI";
+		port->class = DDB_PORT_CI;
+		ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_stv0900(port)) {
+		modname = "DUAL DVB-S2";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBS_ST;
+		ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_stv0900_aa(port)) {
+		modname = "DUAL DVB-S2";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBS_ST_AA;
+		ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_drxks(port)) {
+		modname = "DUAL DVB-C/T";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBCT_TR;
+		ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+	}
+	printk(KERN_INFO "Port %d (TAB %d): %s\n",
+			 port->nr, port->nr+1, modname);
+}
+
+static void ddb_input_init(struct ddb_port *port, int nr)
+{
+	struct ddb *dev = port->dev;
+	struct ddb_input *input = &dev->input[nr];
+
+	input->nr = nr;
+	input->port = port;
+	input->dma_buf_num = INPUT_DMA_BUFS;
+	input->dma_buf_size = INPUT_DMA_SIZE;
+	ddbwritel(0, TS_INPUT_CONTROL(nr));
+	ddbwritel(2, TS_INPUT_CONTROL(nr));
+	ddbwritel(0, TS_INPUT_CONTROL(nr));
+	ddbwritel(0, DMA_BUFFER_ACK(nr));
+	tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input);
+	spin_lock_init(&input->lock);
+	init_waitqueue_head(&input->wq);
+}
+
+static void ddb_output_init(struct ddb_port *port, int nr)
+{
+	struct ddb *dev = port->dev;
+	struct ddb_output *output = &dev->output[nr];
+	output->nr = nr;
+	output->port = port;
+	output->dma_buf_num = OUTPUT_DMA_BUFS;
+	output->dma_buf_size = OUTPUT_DMA_SIZE;
+
+	ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+	ddbwritel(2, TS_OUTPUT_CONTROL(nr));
+	ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+	tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output);
+	init_waitqueue_head(&output->wq);
+}
+
+static void ddb_ports_init(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		port->dev = dev;
+		port->nr = i;
+		port->i2c = &dev->i2c[i];
+		port->input[0] = &dev->input[2 * i];
+		port->input[1] = &dev->input[2 * i + 1];
+		port->output = &dev->output[i];
+
+		mutex_init(&port->i2c_gate_lock);
+		ddb_port_probe(port);
+		ddb_input_init(port, 2 * i);
+		ddb_input_init(port, 2 * i + 1);
+		ddb_output_init(port, i);
+	}
+}
+
+static void ddb_ports_release(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		port->dev = dev;
+		tasklet_kill(&port->input[0]->tasklet);
+		tasklet_kill(&port->input[1]->tasklet);
+		tasklet_kill(&port->output->tasklet);
+	}
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void irq_handle_i2c(struct ddb *dev, int n)
+{
+	struct ddb_i2c *i2c = &dev->i2c[n];
+
+	i2c->done = 1;
+	wake_up(&i2c->wq);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+	struct ddb *dev = (struct ddb *) dev_id;
+	u32 s = ddbreadl(INTERRUPT_STATUS);
+
+	if (!s)
+		return IRQ_NONE;
+
+	do {
+		ddbwritel(s, INTERRUPT_ACK);
+
+		if (s & 0x00000001)
+			irq_handle_i2c(dev, 0);
+		if (s & 0x00000002)
+			irq_handle_i2c(dev, 1);
+		if (s & 0x00000004)
+			irq_handle_i2c(dev, 2);
+		if (s & 0x00000008)
+			irq_handle_i2c(dev, 3);
+
+		if (s & 0x00000100)
+			tasklet_schedule(&dev->input[0].tasklet);
+		if (s & 0x00000200)
+			tasklet_schedule(&dev->input[1].tasklet);
+		if (s & 0x00000400)
+			tasklet_schedule(&dev->input[2].tasklet);
+		if (s & 0x00000800)
+			tasklet_schedule(&dev->input[3].tasklet);
+		if (s & 0x00001000)
+			tasklet_schedule(&dev->input[4].tasklet);
+		if (s & 0x00002000)
+			tasklet_schedule(&dev->input[5].tasklet);
+		if (s & 0x00004000)
+			tasklet_schedule(&dev->input[6].tasklet);
+		if (s & 0x00008000)
+			tasklet_schedule(&dev->input[7].tasklet);
+
+		if (s & 0x00010000)
+			tasklet_schedule(&dev->output[0].tasklet);
+		if (s & 0x00020000)
+			tasklet_schedule(&dev->output[1].tasklet);
+		if (s & 0x00040000)
+			tasklet_schedule(&dev->output[2].tasklet);
+		if (s & 0x00080000)
+			tasklet_schedule(&dev->output[3].tasklet);
+
+		/* if (s & 0x000f0000)	printk(KERN_DEBUG "%08x\n", istat); */
+	} while ((s = ddbreadl(INTERRUPT_STATUS)));
+
+	return IRQ_HANDLED;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+	u32 data, shift;
+
+	if (wlen > 4)
+		ddbwritel(1, SPI_CONTROL);
+	while (wlen > 4) {
+		/* FIXME: check for big-endian */
+		data = swab32(*(u32 *)wbuf);
+		wbuf += 4;
+		wlen -= 4;
+		ddbwritel(data, SPI_DATA);
+		while (ddbreadl(SPI_CONTROL) & 0x0004)
+			;
+	}
+
+	if (rlen)
+		ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+	else
+		ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+
+	data = 0;
+	shift = ((4 - wlen) * 8);
+	while (wlen) {
+		data <<= 8;
+		data |= *wbuf;
+		wlen--;
+		wbuf++;
+	}
+	if (shift)
+		data <<= shift;
+	ddbwritel(data, SPI_DATA);
+	while (ddbreadl(SPI_CONTROL) & 0x0004)
+		;
+
+	if (!rlen) {
+		ddbwritel(0, SPI_CONTROL);
+		return 0;
+	}
+	if (rlen > 4)
+		ddbwritel(1, SPI_CONTROL);
+
+	while (rlen > 4) {
+		ddbwritel(0xffffffff, SPI_DATA);
+		while (ddbreadl(SPI_CONTROL) & 0x0004)
+			;
+		data = ddbreadl(SPI_DATA);
+		*(u32 *) rbuf = swab32(data);
+		rbuf += 4;
+		rlen -= 4;
+	}
+	ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
+	ddbwritel(0xffffffff, SPI_DATA);
+	while (ddbreadl(SPI_CONTROL) & 0x0004)
+		;
+
+	data = ddbreadl(SPI_DATA);
+	ddbwritel(0, SPI_CONTROL);
+
+	if (rlen < 4)
+		data <<= ((4 - rlen) * 8);
+
+	while (rlen > 0) {
+		*rbuf = ((data >> 24) & 0xff);
+		data <<= 8;
+		rbuf++;
+		rlen--;
+	}
+	return 0;
+}
+
+#define DDB_MAGIC 'd'
+
+struct ddb_flashio {
+	__u8 *write_buf;
+	__u32 write_len;
+	__u8 *read_buf;
+	__u32 read_len;
+};
+
+#define IOCTL_DDB_FLASHIO  _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
+
+#define DDB_NAME "ddbridge"
+
+static u32 ddb_num;
+static struct ddb *ddbs[32];
+static struct class *ddb_class;
+static int ddb_major;
+
+static int ddb_open(struct inode *inode, struct file *file)
+{
+	struct ddb *dev = ddbs[iminor(inode)];
+
+	file->private_data = dev;
+	return 0;
+}
+
+static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ddb *dev = file->private_data;
+	void *parg = (void *)arg;
+	int res;
+
+	switch (cmd) {
+	case IOCTL_DDB_FLASHIO:
+	{
+		struct ddb_flashio fio;
+		u8 *rbuf, *wbuf;
+
+		if (copy_from_user(&fio, parg, sizeof(fio)))
+			return -EFAULT;
+
+		if (fio.write_len > 1028 || fio.read_len > 1028)
+			return -EINVAL;
+		if (fio.write_len + fio.read_len > 1028)
+			return -EINVAL;
+
+		wbuf = &dev->iobuf[0];
+		rbuf = wbuf + fio.write_len;
+
+		if (copy_from_user(wbuf, fio.write_buf, fio.write_len))
+			return -EFAULT;
+		res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len);
+		if (res)
+			return res;
+		if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
+			return -EFAULT;
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static const struct file_operations ddb_fops = {
+	.unlocked_ioctl = ddb_ioctl,
+	.open           = ddb_open,
+};
+
+static char *ddb_devnode(struct device *device, umode_t *mode)
+{
+	struct ddb *dev = dev_get_drvdata(device);
+
+	return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr);
+}
+
+static int ddb_class_create(void)
+{
+	ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
+	if (ddb_major < 0)
+		return ddb_major;
+
+	ddb_class = class_create(THIS_MODULE, DDB_NAME);
+	if (IS_ERR(ddb_class)) {
+		unregister_chrdev(ddb_major, DDB_NAME);
+		return PTR_ERR(ddb_class);
+	}
+	ddb_class->devnode = ddb_devnode;
+	return 0;
+}
+
+static void ddb_class_destroy(void)
+{
+	class_destroy(ddb_class);
+	unregister_chrdev(ddb_major, DDB_NAME);
+}
+
+static int ddb_device_create(struct ddb *dev)
+{
+	dev->nr = ddb_num++;
+	dev->ddb_dev = device_create(ddb_class, NULL,
+				     MKDEV(ddb_major, dev->nr),
+				     dev, "ddbridge%d", dev->nr);
+	ddbs[dev->nr] = dev;
+	if (IS_ERR(dev->ddb_dev))
+		return -1;
+	return 0;
+}
+
+static void ddb_device_destroy(struct ddb *dev)
+{
+	ddb_num--;
+	if (IS_ERR(dev->ddb_dev))
+		return;
+	device_destroy(ddb_class, MKDEV(ddb_major, 0));
+}
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void ddb_unmap(struct ddb *dev)
+{
+	if (dev->regs)
+		iounmap(dev->regs);
+	vfree(dev);
+}
+
+
+static void __devexit ddb_remove(struct pci_dev *pdev)
+{
+	struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
+
+	ddb_ports_detach(dev);
+	ddb_i2c_release(dev);
+
+	ddbwritel(0, INTERRUPT_ENABLE);
+	free_irq(dev->pdev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+	if (dev->msi)
+		pci_disable_msi(dev->pdev);
+#endif
+	ddb_ports_release(dev);
+	ddb_buffers_free(dev);
+	ddb_device_destroy(dev);
+
+	ddb_unmap(dev);
+	pci_set_drvdata(pdev, 0);
+	pci_disable_device(pdev);
+}
+
+
+static int __devinit ddb_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	struct ddb *dev;
+	int stat = 0;
+	int irq_flag = IRQF_SHARED;
+
+	if (pci_enable_device(pdev) < 0)
+		return -ENODEV;
+
+	dev = vmalloc(sizeof(struct ddb));
+	if (dev == NULL)
+		return -ENOMEM;
+	memset(dev, 0, sizeof(struct ddb));
+
+	dev->pdev = pdev;
+	pci_set_drvdata(pdev, dev);
+	dev->info = (struct ddb_info *) id->driver_data;
+	printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
+
+	dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
+			    pci_resource_len(dev->pdev, 0));
+	if (!dev->regs) {
+		stat = -ENOMEM;
+		goto fail;
+	}
+	printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
+
+#ifdef CONFIG_PCI_MSI
+	if (pci_msi_enabled())
+		stat = pci_enable_msi(dev->pdev);
+	if (stat) {
+		printk(KERN_INFO ": MSI not available.\n");
+	} else {
+		irq_flag = 0;
+		dev->msi = 1;
+	}
+#endif
+	stat = request_irq(dev->pdev->irq, irq_handler,
+			   irq_flag, "DDBridge", (void *) dev);
+	if (stat < 0)
+		goto fail1;
+	ddbwritel(0, DMA_BASE_WRITE);
+	ddbwritel(0, DMA_BASE_READ);
+	ddbwritel(0xffffffff, INTERRUPT_ACK);
+	ddbwritel(0xfff0f, INTERRUPT_ENABLE);
+	ddbwritel(0, MSI1_ENABLE);
+
+	if (ddb_i2c_init(dev) < 0)
+		goto fail1;
+	ddb_ports_init(dev);
+	if (ddb_buffers_alloc(dev) < 0) {
+		printk(KERN_INFO ": Could not allocate buffer memory\n");
+		goto fail2;
+	}
+	if (ddb_ports_attach(dev) < 0)
+		goto fail3;
+	ddb_device_create(dev);
+	return 0;
+
+fail3:
+	ddb_ports_detach(dev);
+	printk(KERN_ERR "fail3\n");
+	ddb_ports_release(dev);
+fail2:
+	printk(KERN_ERR "fail2\n");
+	ddb_buffers_free(dev);
+fail1:
+	printk(KERN_ERR "fail1\n");
+	if (dev->msi)
+		pci_disable_msi(dev->pdev);
+	free_irq(dev->pdev->irq, dev);
+fail:
+	printk(KERN_ERR "fail\n");
+	ddb_unmap(dev);
+	pci_set_drvdata(pdev, 0);
+	pci_disable_device(pdev);
+	return -1;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct ddb_info ddb_none = {
+	.type     = DDB_NONE,
+	.name     = "Digital Devices PCIe bridge",
+};
+
+static struct ddb_info ddb_octopus = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Octopus DVB adapter",
+	.port_num = 4,
+};
+
+static struct ddb_info ddb_octopus_le = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Octopus LE DVB adapter",
+	.port_num = 2,
+};
+
+static struct ddb_info ddb_v6 = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Cine S2 V6 DVB adapter",
+	.port_num = 3,
+};
+
+#define DDVID 0xdd01 /* Digital Devices Vendor ID */
+
+#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) {	\
+	.vendor      = _vend,    .device    = _dev, \
+	.subvendor   = _subvend, .subdevice = _subdev, \
+	.driver_data = (unsigned long)&_driverdata }
+
+static const struct pci_device_id ddb_id_tbl[] __devinitdata = {
+	DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
+	/* in case sub-ids got deleted in flash */
+	DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+	{0}
+};
+MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
+
+
+static struct pci_driver ddb_pci_driver = {
+	.name        = "DDBridge",
+	.id_table    = ddb_id_tbl,
+	.probe       = ddb_probe,
+	.remove      = __devexit_p(ddb_remove),
+};
+
+static __init int module_init_ddbridge(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Digital Devices PCIE bridge driver, "
+	       "Copyright (C) 2010-11 Digital Devices GmbH\n");
+
+	ret = ddb_class_create();
+	if (ret < 0)
+		return ret;
+	ret = pci_register_driver(&ddb_pci_driver);
+	if (ret < 0)
+		ddb_class_destroy();
+	return ret;
+}
+
+static __exit void module_exit_ddbridge(void)
+{
+	pci_unregister_driver(&ddb_pci_driver);
+	ddb_class_destroy();
+}
+
+module_init(module_init_ddbridge);
+module_exit(module_exit_ddbridge);
+
+MODULE_DESCRIPTION("Digital Devices PCIe Bridge");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h
new file mode 100644
index 000000000000..a3ccb318b500
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-regs.h
@@ -0,0 +1,151 @@
+/*
+ * ddbridge-regs.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */
+
+/* Register Definitions */
+
+#define CUR_REGISTERMAP_VERSION 0x10000
+
+#define HARDWARE_VERSION       0x00
+#define REGISTERMAP_VERSION    0x04
+
+/* ------------------------------------------------------------------------- */
+/* SPI Controller */
+
+#define SPI_CONTROL     0x10
+#define SPI_DATA        0x14
+
+/* ------------------------------------------------------------------------- */
+
+/* Interrupt controller                                     */
+/* How many MSI's are available depends on HW (Min 2 max 8) */
+/* How many are usable also depends on Host platform        */
+
+#define INTERRUPT_BASE   (0x40)
+
+#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00)
+#define MSI0_ENABLE      (INTERRUPT_BASE + 0x00)
+#define MSI1_ENABLE      (INTERRUPT_BASE + 0x04)
+#define MSI2_ENABLE      (INTERRUPT_BASE + 0x08)
+#define MSI3_ENABLE      (INTERRUPT_BASE + 0x0C)
+#define MSI4_ENABLE      (INTERRUPT_BASE + 0x10)
+#define MSI5_ENABLE      (INTERRUPT_BASE + 0x14)
+#define MSI6_ENABLE      (INTERRUPT_BASE + 0x18)
+#define MSI7_ENABLE      (INTERRUPT_BASE + 0x1C)
+
+#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20)
+#define INTERRUPT_ACK    (INTERRUPT_BASE + 0x20)
+
+#define INTMASK_I2C1        (0x00000001)
+#define INTMASK_I2C2        (0x00000002)
+#define INTMASK_I2C3        (0x00000004)
+#define INTMASK_I2C4        (0x00000008)
+
+#define INTMASK_CIRQ1       (0x00000010)
+#define INTMASK_CIRQ2       (0x00000020)
+#define INTMASK_CIRQ3       (0x00000040)
+#define INTMASK_CIRQ4       (0x00000080)
+
+#define INTMASK_TSINPUT1    (0x00000100)
+#define INTMASK_TSINPUT2    (0x00000200)
+#define INTMASK_TSINPUT3    (0x00000400)
+#define INTMASK_TSINPUT4    (0x00000800)
+#define INTMASK_TSINPUT5    (0x00001000)
+#define INTMASK_TSINPUT6    (0x00002000)
+#define INTMASK_TSINPUT7    (0x00004000)
+#define INTMASK_TSINPUT8    (0x00008000)
+
+#define INTMASK_TSOUTPUT1   (0x00010000)
+#define INTMASK_TSOUTPUT2   (0x00020000)
+#define INTMASK_TSOUTPUT3   (0x00040000)
+#define INTMASK_TSOUTPUT4   (0x00080000)
+
+/* ------------------------------------------------------------------------- */
+/* I2C Master Controller */
+
+#define I2C_BASE        (0x80)  /* Byte offset */
+
+#define I2C_COMMAND     (0x00)
+#define I2C_TIMING      (0x04)
+#define I2C_TASKLENGTH  (0x08)     /* High read, low write */
+#define I2C_TASKADDRESS (0x0C)     /* High read, low write */
+
+#define I2C_MONITOR     (0x1C)
+
+#define I2C_BASE_1      (I2C_BASE + 0x00)
+#define I2C_BASE_2      (I2C_BASE + 0x20)
+#define I2C_BASE_3      (I2C_BASE + 0x40)
+#define I2C_BASE_4      (I2C_BASE + 0x60)
+
+#define I2C_BASE_N(i)   (I2C_BASE + (i) * 0x20)
+
+#define I2C_TASKMEM_BASE    (0x1000)    /* Byte offset */
+#define I2C_TASKMEM_SIZE    (0x1000)
+
+#define I2C_SPEED_400   (0x04030404)
+#define I2C_SPEED_200   (0x09080909)
+#define I2C_SPEED_154   (0x0C0B0C0C)
+#define I2C_SPEED_100   (0x13121313)
+#define I2C_SPEED_77    (0x19181919)
+#define I2C_SPEED_50    (0x27262727)
+
+
+/* ------------------------------------------------------------------------- */
+/* DMA  Controller */
+
+#define DMA_BASE_WRITE        (0x100)
+#define DMA_BASE_READ         (0x140)
+
+#define DMA_CONTROL     (0x00)                  /* 64 */
+#define DMA_ERROR       (0x04)                  /* 65 ( only read instance ) */
+
+#define DMA_DIAG_CONTROL                (0x1C)  /* 71 */
+#define DMA_DIAG_PACKETCOUNTER_LOW      (0x20)  /* 72 */
+#define DMA_DIAG_PACKETCOUNTER_HIGH     (0x24)  /* 73 */
+#define DMA_DIAG_TIMECOUNTER_LOW        (0x28)  /* 74 */
+#define DMA_DIAG_TIMECOUNTER_HIGH       (0x2C)  /* 75 */
+#define DMA_DIAG_RECHECKCOUNTER         (0x30)  /* 76  ( Split completions on read ) */
+#define DMA_DIAG_WAITTIMEOUTINIT        (0x34)  /* 77 */
+#define DMA_DIAG_WAITOVERFLOWCOUNTER    (0x38)  /* 78 */
+#define DMA_DIAG_WAITCOUNTER            (0x3C)  /* 79 */
+
+/* ------------------------------------------------------------------------- */
+/* DMA  Buffer */
+
+#define TS_INPUT_BASE       (0x200)
+#define TS_INPUT_CONTROL(i)         (TS_INPUT_BASE + (i) * 16 + 0x00)
+
+#define TS_OUTPUT_BASE       (0x280)
+#define TS_OUTPUT_CONTROL(i)         (TS_OUTPUT_BASE + (i) * 16 + 0x00)
+
+#define DMA_BUFFER_BASE     (0x300)
+
+#define DMA_BUFFER_CONTROL(i)       (DMA_BUFFER_BASE + (i) * 16 + 0x00)
+#define DMA_BUFFER_ACK(i)           (DMA_BUFFER_BASE + (i) * 16 + 0x04)
+#define DMA_BUFFER_CURRENT(i)       (DMA_BUFFER_BASE + (i) * 16 + 0x08)
+#define DMA_BUFFER_SIZE(i)          (DMA_BUFFER_BASE + (i) * 16 + 0x0c)
+
+#define DMA_BASE_ADDRESS_TABLE  (0x2000)
+#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512)
+
diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h
new file mode 100644
index 000000000000..8b1b41d2a52d
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge.h
@@ -0,0 +1,185 @@
+/*
+ * ddbridge.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _DDBRIDGE_H_
+#define _DDBRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/dma.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/ca.h>
+#include <linux/socket.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_ca_en50221.h"
+#include "dvb_net.h"
+#include "cxd2099.h"
+
+#define DDB_MAX_I2C     4
+#define DDB_MAX_PORT    4
+#define DDB_MAX_INPUT   8
+#define DDB_MAX_OUTPUT  4
+
+struct ddb_info {
+	int   type;
+#define DDB_NONE         0
+#define DDB_OCTOPUS      1
+	char *name;
+	int   port_num;
+	u32   port_type[DDB_MAX_PORT];
+};
+
+/* DMA_SIZE MUST be divisible by 188 and 128 !!! */
+
+#define INPUT_DMA_MAX_BUFS 32      /* hardware table limit */
+#define INPUT_DMA_BUFS 8
+#define INPUT_DMA_SIZE (128*47*21)
+
+#define OUTPUT_DMA_MAX_BUFS 32
+#define OUTPUT_DMA_BUFS 8
+#define OUTPUT_DMA_SIZE (128*47*21)
+
+struct ddb;
+struct ddb_port;
+
+struct ddb_input {
+	struct ddb_port       *port;
+	u32                    nr;
+	int                    attached;
+
+	dma_addr_t             pbuf[INPUT_DMA_MAX_BUFS];
+	u8                    *vbuf[INPUT_DMA_MAX_BUFS];
+	u32                    dma_buf_num;
+	u32                    dma_buf_size;
+
+	struct tasklet_struct  tasklet;
+	spinlock_t             lock;
+	wait_queue_head_t      wq;
+	int                    running;
+	u32                    stat;
+	u32                    cbuf;
+	u32                    coff;
+
+	struct dvb_adapter     adap;
+	struct dvb_device     *dev;
+	struct dvb_frontend   *fe;
+	struct dvb_frontend   *fe2;
+	struct dmxdev          dmxdev;
+	struct dvb_demux       demux;
+	struct dvb_net         dvbnet;
+	struct dmx_frontend    hw_frontend;
+	struct dmx_frontend    mem_frontend;
+	int                    users;
+	int (*gate_ctrl)(struct dvb_frontend *, int);
+};
+
+struct ddb_output {
+	struct ddb_port       *port;
+	u32                    nr;
+	dma_addr_t             pbuf[OUTPUT_DMA_MAX_BUFS];
+	u8                    *vbuf[OUTPUT_DMA_MAX_BUFS];
+	u32                    dma_buf_num;
+	u32                    dma_buf_size;
+	struct tasklet_struct  tasklet;
+	spinlock_t             lock;
+	wait_queue_head_t      wq;
+	int                    running;
+	u32                    stat;
+	u32                    cbuf;
+	u32                    coff;
+
+	struct dvb_adapter     adap;
+	struct dvb_device     *dev;
+};
+
+struct ddb_i2c {
+	struct ddb            *dev;
+	u32                    nr;
+	struct i2c_adapter     adap;
+	struct i2c_adapter     adap2;
+	u32                    regs;
+	u32                    rbuf;
+	u32                    wbuf;
+	int                    done;
+	wait_queue_head_t      wq;
+};
+
+struct ddb_port {
+	struct ddb            *dev;
+	u32                    nr;
+	struct ddb_i2c        *i2c;
+	struct mutex           i2c_gate_lock;
+	u32                    class;
+#define DDB_PORT_NONE           0
+#define DDB_PORT_CI             1
+#define DDB_PORT_TUNER          2
+	u32                    type;
+#define DDB_TUNER_NONE          0
+#define DDB_TUNER_DVBS_ST       1
+#define DDB_TUNER_DVBS_ST_AA    2
+#define DDB_TUNER_DVBCT_TR     16
+#define DDB_TUNER_DVBCT_ST     17
+	u32                    adr;
+
+	struct ddb_input      *input[2];
+	struct ddb_output     *output;
+	struct dvb_ca_en50221 *en;
+};
+
+struct ddb {
+	struct pci_dev        *pdev;
+	unsigned char         *regs;
+	struct ddb_port        port[DDB_MAX_PORT];
+	struct ddb_i2c         i2c[DDB_MAX_I2C];
+	struct ddb_input       input[DDB_MAX_INPUT];
+	struct ddb_output      output[DDB_MAX_OUTPUT];
+
+	struct device         *ddb_dev;
+	int                    nr;
+	u8                     iobuf[1028];
+
+	struct ddb_info       *info;
+	int                    msi;
+};
+
+/****************************************************************************/
+
+#define ddbwritel(_val, _adr)        writel((_val), \
+				     (char *) (dev->regs+(_adr)))
+#define ddbreadl(_adr)               readl((char *) (dev->regs+(_adr)))
+#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *)	\
+				     (dev->regs+(_adr)), (_src), (_count))
+#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \
+				       (dev->regs+(_adr)), (_count))
+
+/****************************************************************************/
+
+#endif
diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig
new file mode 100644
index 000000000000..013df4e015cd
--- /dev/null
+++ b/drivers/media/pci/dm1105/Kconfig
@@ -0,0 +1,20 @@
+config DVB_DM1105
+	tristate "SDMC DM1105 based PCI cards"
+	depends on DVB_CORE && PCI && I2C
+	select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
+	depends on RC_CORE
+	help
+	  Support for cards based on the SDMC DM1105 PCI chip like
+	  DvbWorld 2002
+
+	  Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/dm1105/Makefile b/drivers/media/pci/dm1105/Makefile
new file mode 100644
index 000000000000..327585143c83
--- /dev/null
+++ b/drivers/media/pci/dm1105/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DM1105) += dm1105.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
new file mode 100644
index 000000000000..a609b3a9b146
--- /dev/null
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -0,0 +1,1248 @@
+/*
+ * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip
+ *
+ * Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ *
+ * 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/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+#include "dvb-pll.h"
+
+#include "stv0299.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "si21xx.h"
+#include "cx24116.h"
+#include "z0194a.h"
+#include "ds3000.h"
+
+#define MODULE_NAME "dm1105"
+
+#define UNSET (-1U)
+
+#define DM1105_BOARD_NOAUTO			UNSET
+#define DM1105_BOARD_UNKNOWN			0
+#define DM1105_BOARD_DVBWORLD_2002		1
+#define DM1105_BOARD_DVBWORLD_2004		2
+#define DM1105_BOARD_AXESS_DM05			3
+#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO	4
+
+/* ----------------------------------------------- */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_VENDOR_ID_TRIGEM
+#define PCI_VENDOR_ID_TRIGEM	0x109f
+#endif
+#ifndef PCI_VENDOR_ID_AXESS
+#define PCI_VENDOR_ID_AXESS	0x195d
+#endif
+#ifndef PCI_DEVICE_ID_DM1105
+#define PCI_DEVICE_ID_DM1105	0x036f
+#endif
+#ifndef PCI_DEVICE_ID_DW2002
+#define PCI_DEVICE_ID_DW2002	0x2002
+#endif
+#ifndef PCI_DEVICE_ID_DW2004
+#define PCI_DEVICE_ID_DW2004	0x2004
+#endif
+#ifndef PCI_DEVICE_ID_DM05
+#define PCI_DEVICE_ID_DM05	0x1105
+#endif
+/* ----------------------------------------------- */
+/* sdmc dm1105 registers */
+
+/* TS Control */
+#define DM1105_TSCTR				0x00
+#define DM1105_DTALENTH				0x04
+
+/* GPIO Interface */
+#define DM1105_GPIOVAL				0x08
+#define DM1105_GPIOCTR				0x0c
+
+/* PID serial number */
+#define DM1105_PIDN				0x10
+
+/* Odd-even secret key select */
+#define DM1105_CWSEL				0x14
+
+/* Host Command Interface */
+#define DM1105_HOST_CTR				0x18
+#define DM1105_HOST_AD				0x1c
+
+/* PCI Interface */
+#define DM1105_CR				0x30
+#define DM1105_RST				0x34
+#define DM1105_STADR				0x38
+#define DM1105_RLEN				0x3c
+#define DM1105_WRP				0x40
+#define DM1105_INTCNT				0x44
+#define DM1105_INTMAK				0x48
+#define DM1105_INTSTS				0x4c
+
+/* CW Value */
+#define DM1105_ODD				0x50
+#define DM1105_EVEN				0x58
+
+/* PID Value */
+#define DM1105_PID				0x60
+
+/* IR Control */
+#define DM1105_IRCTR				0x64
+#define DM1105_IRMODE				0x68
+#define DM1105_SYSTEMCODE			0x6c
+#define DM1105_IRCODE				0x70
+
+/* Unknown Values */
+#define DM1105_ENCRYPT				0x74
+#define DM1105_VER				0x7c
+
+/* I2C Interface */
+#define DM1105_I2CCTR				0x80
+#define DM1105_I2CSTS				0x81
+#define DM1105_I2CDAT				0x82
+#define DM1105_I2C_RA				0x83
+/* ----------------------------------------------- */
+/* Interrupt Mask Bits */
+
+#define INTMAK_TSIRQM				0x01
+#define INTMAK_HIRQM				0x04
+#define INTMAK_IRM				0x08
+#define INTMAK_ALLMASK				(INTMAK_TSIRQM | \
+						INTMAK_HIRQM | \
+						INTMAK_IRM)
+#define INTMAK_NONEMASK				0x00
+
+/* Interrupt Status Bits */
+#define INTSTS_TSIRQ				0x01
+#define INTSTS_HIRQ				0x04
+#define INTSTS_IR				0x08
+
+/* IR Control Bits */
+#define DM1105_IR_EN				0x01
+#define DM1105_SYS_CHK				0x02
+#define DM1105_REP_FLG				0x08
+
+/* EEPROM addr */
+#define IIC_24C01_addr				0xa0
+/* Max board count */
+#define DM1105_MAX				0x04
+
+#define DRIVER_NAME				"dm1105"
+#define DM1105_I2C_GPIO_NAME			"dm1105-gpio"
+
+#define DM1105_DMA_PACKETS			47
+#define DM1105_DMA_PACKET_LENGTH		(128*4)
+#define DM1105_DMA_BYTES			(128 * 4 * DM1105_DMA_PACKETS)
+
+/*  */
+#define GPIO08					(1 << 8)
+#define GPIO13					(1 << 13)
+#define GPIO14					(1 << 14)
+#define GPIO15					(1 << 15)
+#define GPIO16					(1 << 16)
+#define GPIO17					(1 << 17)
+#define GPIO_ALL				0x03ffff
+
+/* GPIO's for LNB power control */
+#define DM1105_LNB_MASK				(GPIO_ALL & ~(GPIO14 | GPIO13))
+#define DM1105_LNB_OFF				GPIO17
+#define DM1105_LNB_13V				(GPIO16 | GPIO08)
+#define DM1105_LNB_18V				GPIO08
+
+/* GPIO's for LNB power control for Axess DM05 */
+#define DM05_LNB_MASK				(GPIO_ALL & ~(GPIO14 | GPIO13))
+#define DM05_LNB_OFF				GPIO17/* actually 13v */
+#define DM05_LNB_13V				GPIO17
+#define DM05_LNB_18V				(GPIO17 | GPIO16)
+
+/* GPIO's for LNB power control for unbranded with I2C on GPIO */
+#define UNBR_LNB_MASK				(GPIO17 | GPIO16)
+#define UNBR_LNB_OFF				0
+#define UNBR_LNB_13V				GPIO17
+#define UNBR_LNB_18V				(GPIO17 | GPIO16)
+
+static unsigned int card[]  = {[0 ... 3] = UNSET };
+module_param_array(card,  int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+static unsigned int dm1105_devcount;
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct dm1105_board {
+	char	*name;
+	struct	{
+		u32	mask, off, v13, v18;
+	} lnb;
+	u32	gpio_scl, gpio_sda;
+};
+
+struct dm1105_subid {
+	u16     subvendor;
+	u16     subdevice;
+	u32     card;
+};
+
+static const struct dm1105_board dm1105_boards[] = {
+	[DM1105_BOARD_UNKNOWN] = {
+		.name		= "UNKNOWN/GENERIC",
+		.lnb = {
+			.mask = DM1105_LNB_MASK,
+			.off = DM1105_LNB_OFF,
+			.v13 = DM1105_LNB_13V,
+			.v18 = DM1105_LNB_18V,
+		},
+	},
+	[DM1105_BOARD_DVBWORLD_2002] = {
+		.name		= "DVBWorld PCI 2002",
+		.lnb = {
+			.mask = DM1105_LNB_MASK,
+			.off = DM1105_LNB_OFF,
+			.v13 = DM1105_LNB_13V,
+			.v18 = DM1105_LNB_18V,
+		},
+	},
+	[DM1105_BOARD_DVBWORLD_2004] = {
+		.name		= "DVBWorld PCI 2004",
+		.lnb = {
+			.mask = DM1105_LNB_MASK,
+			.off = DM1105_LNB_OFF,
+			.v13 = DM1105_LNB_13V,
+			.v18 = DM1105_LNB_18V,
+		},
+	},
+	[DM1105_BOARD_AXESS_DM05] = {
+		.name		= "Axess/EasyTv DM05",
+		.lnb = {
+			.mask = DM05_LNB_MASK,
+			.off = DM05_LNB_OFF,
+			.v13 = DM05_LNB_13V,
+			.v18 = DM05_LNB_18V,
+		},
+	},
+	[DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = {
+		.name		= "Unbranded DM1105 with i2c on GPIOs",
+		.lnb = {
+			.mask = UNBR_LNB_MASK,
+			.off = UNBR_LNB_OFF,
+			.v13 = UNBR_LNB_13V,
+			.v18 = UNBR_LNB_18V,
+		},
+		.gpio_scl	= GPIO14,
+		.gpio_sda	= GPIO13,
+	},
+};
+
+static const struct dm1105_subid dm1105_subids[] = {
+	{
+		.subvendor = 0x0000,
+		.subdevice = 0x2002,
+		.card      = DM1105_BOARD_DVBWORLD_2002,
+	}, {
+		.subvendor = 0x0001,
+		.subdevice = 0x2002,
+		.card      = DM1105_BOARD_DVBWORLD_2002,
+	}, {
+		.subvendor = 0x0000,
+		.subdevice = 0x2004,
+		.card      = DM1105_BOARD_DVBWORLD_2004,
+	}, {
+		.subvendor = 0x0001,
+		.subdevice = 0x2004,
+		.card      = DM1105_BOARD_DVBWORLD_2004,
+	}, {
+		.subvendor = 0x195d,
+		.subdevice = 0x1105,
+		.card      = DM1105_BOARD_AXESS_DM05,
+	},
+};
+
+static void dm1105_card_list(struct pci_dev *pci)
+{
+	int i;
+
+	if (0 == pci->subsystem_vendor &&
+			0 == pci->subsystem_device) {
+		printk(KERN_ERR
+			"dm1105: Your board has no valid PCI Subsystem ID\n"
+			"dm1105: and thus can't be autodetected\n"
+			"dm1105: Please pass card=<n> insmod option to\n"
+			"dm1105: workaround that.  Redirect complaints to\n"
+			"dm1105: the vendor of the TV card.  Best regards,\n"
+			"dm1105: -- tux\n");
+	} else {
+		printk(KERN_ERR
+			"dm1105: Your board isn't known (yet) to the driver.\n"
+			"dm1105: You can try to pick one of the existing\n"
+			"dm1105: card configs via card=<n> insmod option.\n"
+			"dm1105: Updating to the latest version might help\n"
+			"dm1105: as well.\n");
+	}
+	printk(KERN_ERR "Here is a list of valid choices for the card=<n> "
+		   "insmod option:\n");
+	for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++)
+		printk(KERN_ERR "dm1105:    card=%d -> %s\n",
+				i, dm1105_boards[i].name);
+}
+
+/* infrared remote control */
+struct infrared {
+	struct rc_dev		*dev;
+	char			input_phys[32];
+	struct work_struct	work;
+	u32			ir_command;
+};
+
+struct dm1105_dev {
+	/* pci */
+	struct pci_dev *pdev;
+	u8 __iomem *io_mem;
+
+	/* ir */
+	struct infrared ir;
+
+	/* dvb */
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct dmxdev dmxdev;
+	struct dvb_adapter dvb_adapter;
+	struct dvb_demux demux;
+	struct dvb_frontend *fe;
+	struct dvb_net dvbnet;
+	unsigned int full_ts_users;
+	unsigned int boardnr;
+	int nr;
+
+	/* i2c */
+	struct i2c_adapter i2c_adap;
+	struct i2c_adapter i2c_bb_adap;
+	struct i2c_algo_bit_data i2c_bit;
+
+	/* irq */
+	struct work_struct work;
+	struct workqueue_struct *wq;
+	char wqn[16];
+
+	/* dma */
+	dma_addr_t dma_addr;
+	unsigned char *ts_buf;
+	u32 wrp;
+	u32 nextwrp;
+	u32 buffer_size;
+	unsigned int	PacketErrorCount;
+	unsigned int dmarst;
+	spinlock_t lock;
+};
+
+#define dm_io_mem(reg)	((unsigned long)(&dev->io_mem[reg]))
+
+#define dm_readb(reg)		inb(dm_io_mem(reg))
+#define dm_writeb(reg, value)	outb((value), (dm_io_mem(reg)))
+
+#define dm_readw(reg)		inw(dm_io_mem(reg))
+#define dm_writew(reg, value)	outw((value), (dm_io_mem(reg)))
+
+#define dm_readl(reg)		inl(dm_io_mem(reg))
+#define dm_writel(reg, value)	outl((value), (dm_io_mem(reg)))
+
+#define dm_andorl(reg, mask, value) \
+	outl((inl(dm_io_mem(reg)) & ~(mask)) |\
+		((value) & (mask)), (dm_io_mem(reg)))
+
+#define dm_setl(reg, bit)	dm_andorl((reg), (bit), (bit))
+#define dm_clearl(reg, bit)	dm_andorl((reg), (bit), 0)
+
+/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines,
+ so we can use only 3 GPIO's from GPIO15 to GPIO17.
+ Here I don't check whether HOST is enebled as it is not implemented yet.
+ */
+static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask)
+{
+	if (mask & 0xfffc0000)
+		printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+	if (mask & 0x0003ffff)
+		dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff);
+
+}
+
+static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask)
+{
+	if (mask & 0xfffc0000)
+		printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+	if (mask & 0x0003ffff)
+		dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff);
+
+}
+
+static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val)
+{
+	if (mask & 0xfffc0000)
+		printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+	if (mask & 0x0003ffff)
+		dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val);
+
+}
+
+static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask)
+{
+	if (mask & 0xfffc0000)
+		printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+	if (mask & 0x0003ffff)
+		return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff;
+
+	return 0;
+}
+
+static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput)
+{
+	if (mask & 0xfffc0000)
+		printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+	if ((mask & 0x0003ffff) && asoutput)
+		dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff);
+	else if ((mask & 0x0003ffff) && !asoutput)
+		dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff);
+
+}
+
+static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state)
+{
+	if (state)
+		dm1105_gpio_enable(dev, line, 0);
+	else {
+		dm1105_gpio_enable(dev, line, 1);
+		dm1105_gpio_clear(dev, line);
+	}
+}
+
+static void dm1105_setsda(void *data, int state)
+{
+	struct dm1105_dev *dev = data;
+
+	dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state);
+}
+
+static void dm1105_setscl(void *data, int state)
+{
+	struct dm1105_dev *dev = data;
+
+	dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state);
+}
+
+static int dm1105_getsda(void *data)
+{
+	struct dm1105_dev *dev = data;
+
+	return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda)
+									? 1 : 0;
+}
+
+static int dm1105_getscl(void *data)
+{
+	struct dm1105_dev *dev = data;
+
+	return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl)
+									? 1 : 0;
+}
+
+static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
+			    struct i2c_msg *msgs, int num)
+{
+	struct dm1105_dev *dev ;
+
+	int addr, rc, i, j, k, len, byte, data;
+	u8 status;
+
+	dev = i2c_adap->algo_data;
+	for (i = 0; i < num; i++) {
+		dm_writeb(DM1105_I2CCTR, 0x00);
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read bytes */
+			addr  = msgs[i].addr << 1;
+			addr |= 1;
+			dm_writeb(DM1105_I2CDAT, addr);
+			for (byte = 0; byte < msgs[i].len; byte++)
+				dm_writeb(DM1105_I2CDAT + byte + 1, 0);
+
+			dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len);
+			for (j = 0; j < 55; j++) {
+				mdelay(10);
+				status = dm_readb(DM1105_I2CSTS);
+				if ((status & 0xc0) == 0x40)
+					break;
+			}
+			if (j >= 55)
+				return -1;
+
+			for (byte = 0; byte < msgs[i].len; byte++) {
+				rc = dm_readb(DM1105_I2CDAT + byte + 1);
+				if (rc < 0)
+					goto err;
+				msgs[i].buf[byte] = rc;
+			}
+		} else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
+			/* prepaired for cx24116 firmware */
+			/* Write in small blocks */
+			len = msgs[i].len - 1;
+			k = 1;
+			do {
+				dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1);
+				dm_writeb(DM1105_I2CDAT + 1, 0xf7);
+				for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
+					data = msgs[i].buf[k + byte];
+					dm_writeb(DM1105_I2CDAT + byte + 2, data);
+				}
+				dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len));
+				for (j = 0; j < 25; j++) {
+					mdelay(10);
+					status = dm_readb(DM1105_I2CSTS);
+					if ((status & 0xc0) == 0x40)
+						break;
+				}
+
+				if (j >= 25)
+					return -1;
+
+				k += 48;
+				len -= 48;
+			} while (len > 0);
+		} else {
+			/* write bytes */
+			dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1);
+			for (byte = 0; byte < msgs[i].len; byte++) {
+				data = msgs[i].buf[byte];
+				dm_writeb(DM1105_I2CDAT + byte + 1, data);
+			}
+			dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len);
+			for (j = 0; j < 25; j++) {
+				mdelay(10);
+				status = dm_readb(DM1105_I2CSTS);
+				if ((status & 0xc0) == 0x40)
+					break;
+			}
+
+			if (j >= 25)
+				return -1;
+		}
+	}
+	return num;
+ err:
+	return rc;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dm1105_algo = {
+	.master_xfer   = dm1105_i2c_xfer,
+	.functionality = functionality,
+};
+
+static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed)
+{
+	return container_of(feed->demux, struct dm1105_dev, demux);
+}
+
+static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe)
+{
+	return container_of(fe->dvb, struct dm1105_dev, dvb_adapter);
+}
+
+static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct dm1105_dev *dev = frontend_to_dm1105_dev(fe);
+
+	dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1);
+	if (voltage == SEC_VOLTAGE_18)
+		dm1105_gpio_andor(dev,
+				dm1105_boards[dev->boardnr].lnb.mask,
+				dm1105_boards[dev->boardnr].lnb.v18);
+	else if (voltage == SEC_VOLTAGE_13)
+		dm1105_gpio_andor(dev,
+				dm1105_boards[dev->boardnr].lnb.mask,
+				dm1105_boards[dev->boardnr].lnb.v13);
+	else
+		dm1105_gpio_andor(dev,
+				dm1105_boards[dev->boardnr].lnb.mask,
+				dm1105_boards[dev->boardnr].lnb.off);
+
+	return 0;
+}
+
+static void dm1105_set_dma_addr(struct dm1105_dev *dev)
+{
+	dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr));
+}
+
+static int __devinit dm1105_dma_map(struct dm1105_dev *dev)
+{
+	dev->ts_buf = pci_alloc_consistent(dev->pdev,
+					6 * DM1105_DMA_BYTES,
+					&dev->dma_addr);
+
+	return !dev->ts_buf;
+}
+
+static void dm1105_dma_unmap(struct dm1105_dev *dev)
+{
+	pci_free_consistent(dev->pdev,
+			6 * DM1105_DMA_BYTES,
+			dev->ts_buf,
+			dev->dma_addr);
+}
+
+static void dm1105_enable_irqs(struct dm1105_dev *dev)
+{
+	dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK);
+	dm_writeb(DM1105_CR, 1);
+}
+
+static void dm1105_disable_irqs(struct dm1105_dev *dev)
+{
+	dm_writeb(DM1105_INTMAK, INTMAK_IRM);
+	dm_writeb(DM1105_CR, 0);
+}
+
+static int dm1105_start_feed(struct dvb_demux_feed *f)
+{
+	struct dm1105_dev *dev = feed_to_dm1105_dev(f);
+
+	if (dev->full_ts_users++ == 0)
+		dm1105_enable_irqs(dev);
+
+	return 0;
+}
+
+static int dm1105_stop_feed(struct dvb_demux_feed *f)
+{
+	struct dm1105_dev *dev = feed_to_dm1105_dev(f);
+
+	if (--dev->full_ts_users == 0)
+		dm1105_disable_irqs(dev);
+
+	return 0;
+}
+
+/* ir work handler */
+static void dm1105_emit_key(struct work_struct *work)
+{
+	struct infrared *ir = container_of(work, struct infrared, work);
+	u32 ircom = ir->ir_command;
+	u8 data;
+
+	if (ir_debug)
+		printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom);
+
+	data = (ircom >> 8) & 0x7f;
+
+	rc_keydown(ir->dev, data, 0);
+}
+
+/* work handler */
+static void dm1105_dmx_buffer(struct work_struct *work)
+{
+	struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work);
+	unsigned int nbpackets;
+	u32 oldwrp = dev->wrp;
+	u32 nextwrp = dev->nextwrp;
+
+	if (!((dev->ts_buf[oldwrp] == 0x47) &&
+			(dev->ts_buf[oldwrp + 188] == 0x47) &&
+			(dev->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+		dev->PacketErrorCount++;
+		/* bad packet found */
+		if ((dev->PacketErrorCount >= 2) &&
+				(dev->dmarst == 0)) {
+			dm_writeb(DM1105_RST, 1);
+			dev->wrp = 0;
+			dev->PacketErrorCount = 0;
+			dev->dmarst = 0;
+			return;
+		}
+	}
+
+	if (nextwrp < oldwrp) {
+		memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp);
+		nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188;
+	} else
+		nbpackets = (nextwrp - oldwrp) / 188;
+
+	dev->wrp = nextwrp;
+	dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets);
+}
+
+static irqreturn_t dm1105_irq(int irq, void *dev_id)
+{
+	struct dm1105_dev *dev = dev_id;
+
+	/* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
+	unsigned int intsts = dm_readb(DM1105_INTSTS);
+	dm_writeb(DM1105_INTSTS, intsts);
+
+	switch (intsts) {
+	case INTSTS_TSIRQ:
+	case (INTSTS_TSIRQ | INTSTS_IR):
+		dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR);
+		queue_work(dev->wq, &dev->work);
+		break;
+	case INTSTS_IR:
+		dev->ir.ir_command = dm_readl(DM1105_IRCODE);
+		schedule_work(&dev->ir.work);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+int __devinit dm1105_ir_init(struct dm1105_dev *dm1105)
+{
+	struct rc_dev *dev;
+	int err = -ENOMEM;
+
+	dev = rc_allocate_device();
+	if (!dev)
+		return -ENOMEM;
+
+	snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
+		"pci-%s/ir0", pci_name(dm1105->pdev));
+
+	dev->driver_name = MODULE_NAME;
+	dev->map_name = RC_MAP_DM1105_NEC;
+	dev->driver_type = RC_DRIVER_SCANCODE;
+	dev->input_name = "DVB on-card IR receiver";
+	dev->input_phys = dm1105->ir.input_phys;
+	dev->input_id.bustype = BUS_PCI;
+	dev->input_id.version = 1;
+	if (dm1105->pdev->subsystem_vendor) {
+		dev->input_id.vendor = dm1105->pdev->subsystem_vendor;
+		dev->input_id.product = dm1105->pdev->subsystem_device;
+	} else {
+		dev->input_id.vendor = dm1105->pdev->vendor;
+		dev->input_id.product = dm1105->pdev->device;
+	}
+	dev->dev.parent = &dm1105->pdev->dev;
+
+	INIT_WORK(&dm1105->ir.work, dm1105_emit_key);
+
+	err = rc_register_device(dev);
+	if (err < 0) {
+		rc_free_device(dev);
+		return err;
+	}
+
+	dm1105->ir.dev = dev;
+	return 0;
+}
+
+void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105)
+{
+	rc_unregister_device(dm1105->ir.dev);
+}
+
+static int __devinit dm1105_hw_init(struct dm1105_dev *dev)
+{
+	dm1105_disable_irqs(dev);
+
+	dm_writeb(DM1105_HOST_CTR, 0);
+
+	/*DATALEN 188,*/
+	dm_writeb(DM1105_DTALENTH, 188);
+	/*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/
+	dm_writew(DM1105_TSCTR, 0xc10a);
+
+	/* map DMA and set address */
+	dm1105_dma_map(dev);
+	dm1105_set_dma_addr(dev);
+	/* big buffer */
+	dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES);
+	dm_writeb(DM1105_INTCNT, 47);
+
+	/* IR NEC mode enable */
+	dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK));
+	dm_writeb(DM1105_IRMODE, 0);
+	dm_writew(DM1105_SYSTEMCODE, 0);
+
+	return 0;
+}
+
+static void dm1105_hw_exit(struct dm1105_dev *dev)
+{
+	dm1105_disable_irqs(dev);
+
+	/* IR disable */
+	dm_writeb(DM1105_IRCTR, 0);
+	dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK);
+
+	dm1105_dma_unmap(dev);
+}
+
+static struct stv0299_config sharp_z0194a_config = {
+	.demod_address = 0x68,
+	.inittab = sharp_z0194a_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = sharp_z0194a_set_symbol_rate,
+};
+
+static struct stv0288_config earda_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+};
+
+static struct si21xx_config serit_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+
+};
+
+static struct cx24116_config serit_sp2633_config = {
+	.demod_address = 0x55,
+};
+
+static struct ds3000_config dvbworld_ds3000_config = {
+	.demod_address = 0x68,
+};
+
+static int __devinit frontend_init(struct dm1105_dev *dev)
+{
+	int ret;
+
+	switch (dev->boardnr) {
+	case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO:
+		dm1105_gpio_enable(dev, GPIO15, 1);
+		dm1105_gpio_clear(dev, GPIO15);
+		msleep(100);
+		dm1105_gpio_set(dev, GPIO15);
+		msleep(200);
+		dev->fe = dvb_attach(
+			stv0299_attach, &sharp_z0194a_config,
+			&dev->i2c_bb_adap);
+		if (dev->fe) {
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+			dvb_attach(dvb_pll_attach, dev->fe, 0x60,
+					&dev->i2c_bb_adap, DVB_PLL_OPERA1);
+			break;
+		}
+
+		dev->fe = dvb_attach(
+			stv0288_attach, &earda_config,
+			&dev->i2c_bb_adap);
+		if (dev->fe) {
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+			dvb_attach(stb6000_attach, dev->fe, 0x61,
+					&dev->i2c_bb_adap);
+			break;
+		}
+
+		dev->fe = dvb_attach(
+			si21xx_attach, &serit_config,
+			&dev->i2c_bb_adap);
+		if (dev->fe)
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+		break;
+	case DM1105_BOARD_DVBWORLD_2004:
+		dev->fe = dvb_attach(
+			cx24116_attach, &serit_sp2633_config,
+			&dev->i2c_adap);
+		if (dev->fe) {
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+			break;
+		}
+
+		dev->fe = dvb_attach(
+			ds3000_attach, &dvbworld_ds3000_config,
+			&dev->i2c_adap);
+		if (dev->fe)
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+
+		break;
+	case DM1105_BOARD_DVBWORLD_2002:
+	case DM1105_BOARD_AXESS_DM05:
+	default:
+		dev->fe = dvb_attach(
+			stv0299_attach, &sharp_z0194a_config,
+			&dev->i2c_adap);
+		if (dev->fe) {
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+			dvb_attach(dvb_pll_attach, dev->fe, 0x60,
+					&dev->i2c_adap, DVB_PLL_OPERA1);
+			break;
+		}
+
+		dev->fe = dvb_attach(
+			stv0288_attach, &earda_config,
+			&dev->i2c_adap);
+		if (dev->fe) {
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+			dvb_attach(stb6000_attach, dev->fe, 0x61,
+					&dev->i2c_adap);
+			break;
+		}
+
+		dev->fe = dvb_attach(
+			si21xx_attach, &serit_config,
+			&dev->i2c_adap);
+		if (dev->fe)
+			dev->fe->ops.set_voltage = dm1105_set_voltage;
+
+	}
+
+	if (!dev->fe) {
+		dev_err(&dev->pdev->dev, "could not attach frontend\n");
+		return -ENODEV;
+	}
+
+	ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe);
+	if (ret < 0) {
+		if (dev->fe->ops.release)
+			dev->fe->ops.release(dev->fe);
+		dev->fe = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac)
+{
+	static u8 command[1] = { 0x28 };
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = IIC_24C01_addr >> 1,
+			.flags = 0,
+			.buf = command,
+			.len = 1
+		}, {
+			.addr = IIC_24C01_addr >> 1,
+			.flags = I2C_M_RD,
+			.buf = mac,
+			.len = 6
+		},
+	};
+
+	dm1105_i2c_xfer(&dev->i2c_adap, msg , 2);
+	dev_info(&dev->pdev->dev, "MAC %pM\n", mac);
+}
+
+static int __devinit dm1105_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	struct dm1105_dev *dev;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret = -ENOMEM;
+	int i;
+
+	dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	/* board config */
+	dev->nr = dm1105_devcount;
+	dev->boardnr = UNSET;
+	if (card[dev->nr] < ARRAY_SIZE(dm1105_boards))
+		dev->boardnr = card[dev->nr];
+	for (i = 0; UNSET == dev->boardnr &&
+				i < ARRAY_SIZE(dm1105_subids); i++)
+		if (pdev->subsystem_vendor ==
+			dm1105_subids[i].subvendor &&
+				pdev->subsystem_device ==
+					dm1105_subids[i].subdevice)
+			dev->boardnr = dm1105_subids[i].card;
+
+	if (UNSET == dev->boardnr) {
+		dev->boardnr = DM1105_BOARD_UNKNOWN;
+		dm1105_card_list(pdev);
+	}
+
+	dm1105_devcount++;
+	dev->pdev = pdev;
+	dev->buffer_size = 5 * DM1105_DMA_BYTES;
+	dev->PacketErrorCount = 0;
+	dev->dmarst = 0;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_kfree;
+
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+	if (!dev->io_mem) {
+		ret = -EIO;
+		goto err_pci_release_regions;
+	}
+
+	spin_lock_init(&dev->lock);
+	pci_set_drvdata(pdev, dev);
+
+	ret = dm1105_hw_init(dev);
+	if (ret < 0)
+		goto err_pci_iounmap;
+
+	/* i2c */
+	i2c_set_adapdata(&dev->i2c_adap, dev);
+	strcpy(dev->i2c_adap.name, DRIVER_NAME);
+	dev->i2c_adap.owner = THIS_MODULE;
+	dev->i2c_adap.dev.parent = &pdev->dev;
+	dev->i2c_adap.algo = &dm1105_algo;
+	dev->i2c_adap.algo_data = dev;
+	ret = i2c_add_adapter(&dev->i2c_adap);
+
+	if (ret < 0)
+		goto err_dm1105_hw_exit;
+
+	i2c_set_adapdata(&dev->i2c_bb_adap, dev);
+	strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME);
+	dev->i2c_bb_adap.owner = THIS_MODULE;
+	dev->i2c_bb_adap.dev.parent = &pdev->dev;
+	dev->i2c_bb_adap.algo_data = &dev->i2c_bit;
+	dev->i2c_bit.data = dev;
+	dev->i2c_bit.setsda = dm1105_setsda;
+	dev->i2c_bit.setscl = dm1105_setscl;
+	dev->i2c_bit.getsda = dm1105_getsda;
+	dev->i2c_bit.getscl = dm1105_getscl;
+	dev->i2c_bit.udelay = 10;
+	dev->i2c_bit.timeout = 10;
+
+	/* Raise SCL and SDA */
+	dm1105_setsda(dev, 1);
+	dm1105_setscl(dev, 1);
+
+	ret = i2c_bit_add_bus(&dev->i2c_bb_adap);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	/* dvb */
+	ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME,
+					THIS_MODULE, &pdev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_i2c_del_adapters;
+
+	dvb_adapter = &dev->dvb_adapter;
+
+	dm1105_read_mac(dev, dvb_adapter->proposed_mac);
+
+	dvbdemux = &dev->demux;
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = dm1105_start_feed;
+	dvbdemux->stop_feed = dm1105_stop_feed;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+			DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+	ret = dvb_dmx_init(dvbdemux);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter;
+
+	dmx = &dvbdemux->dmx;
+	dev->dmxdev.filternum = 256;
+	dev->dmxdev.demux = dmx;
+	dev->dmxdev.capabilities = 0;
+
+	ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter);
+	if (ret < 0)
+		goto err_dvb_dmx_release;
+
+	dev->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dmx->add_frontend(dmx, &dev->hw_frontend);
+	if (ret < 0)
+		goto err_dvb_dmxdev_release;
+
+	dev->mem_frontend.source = DMX_MEMORY_FE;
+
+	ret = dmx->add_frontend(dmx, &dev->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &dev->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	ret = dvb_net_init(dvb_adapter, &dev->dvbnet, dmx);
+	if (ret < 0)
+		goto err_disconnect_frontend;
+
+	ret = frontend_init(dev);
+	if (ret < 0)
+		goto err_dvb_net;
+
+	dm1105_ir_init(dev);
+
+	INIT_WORK(&dev->work, dm1105_dmx_buffer);
+	sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num);
+	dev->wq = create_singlethread_workqueue(dev->wqn);
+	if (!dev->wq)
+		goto err_dvb_net;
+
+	ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED,
+						DRIVER_NAME, dev);
+	if (ret < 0)
+		goto err_workqueue;
+
+	return 0;
+
+err_workqueue:
+	destroy_workqueue(dev->wq);
+err_dvb_net:
+	dvb_net_release(&dev->dvbnet);
+err_disconnect_frontend:
+	dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+	dmx->remove_frontend(dmx, &dev->mem_frontend);
+err_remove_hw_frontend:
+	dmx->remove_frontend(dmx, &dev->hw_frontend);
+err_dvb_dmxdev_release:
+	dvb_dmxdev_release(&dev->dmxdev);
+err_dvb_dmx_release:
+	dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+	dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapters:
+	i2c_del_adapter(&dev->i2c_bb_adap);
+err_i2c_del_adapter:
+	i2c_del_adapter(&dev->i2c_adap);
+err_dm1105_hw_exit:
+	dm1105_hw_exit(dev);
+err_pci_iounmap:
+	pci_iounmap(pdev, dev->io_mem);
+err_pci_release_regions:
+	pci_release_regions(pdev);
+err_pci_disable_device:
+	pci_disable_device(pdev);
+err_kfree:
+	pci_set_drvdata(pdev, NULL);
+	kfree(dev);
+	return ret;
+}
+
+static void __devexit dm1105_remove(struct pci_dev *pdev)
+{
+	struct dm1105_dev *dev = pci_get_drvdata(pdev);
+	struct dvb_adapter *dvb_adapter = &dev->dvb_adapter;
+	struct dvb_demux *dvbdemux = &dev->demux;
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dm1105_ir_exit(dev);
+	dmx->close(dmx);
+	dvb_net_release(&dev->dvbnet);
+	if (dev->fe)
+		dvb_unregister_frontend(dev->fe);
+
+	dmx->disconnect_frontend(dmx);
+	dmx->remove_frontend(dmx, &dev->mem_frontend);
+	dmx->remove_frontend(dmx, &dev->hw_frontend);
+	dvb_dmxdev_release(&dev->dmxdev);
+	dvb_dmx_release(dvbdemux);
+	dvb_unregister_adapter(dvb_adapter);
+	if (&dev->i2c_adap)
+		i2c_del_adapter(&dev->i2c_adap);
+
+	dm1105_hw_exit(dev);
+	synchronize_irq(pdev->irq);
+	free_irq(pdev->irq, dev);
+	pci_iounmap(pdev, dev->io_mem);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	dm1105_devcount--;
+	kfree(dev);
+}
+
+static struct pci_device_id dm1105_id_table[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_TRIGEM,
+		.device = PCI_DEVICE_ID_DM1105,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	}, {
+		.vendor = PCI_VENDOR_ID_AXESS,
+		.device = PCI_DEVICE_ID_DM05,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	}, {
+		/* empty */
+	},
+};
+
+MODULE_DEVICE_TABLE(pci, dm1105_id_table);
+
+static struct pci_driver dm1105_driver = {
+	.name = DRIVER_NAME,
+	.id_table = dm1105_id_table,
+	.probe = dm1105_probe,
+	.remove = __devexit_p(dm1105_remove),
+};
+
+static int __init dm1105_init(void)
+{
+	return pci_register_driver(&dm1105_driver);
+}
+
+static void __exit dm1105_exit(void)
+{
+	pci_unregister_driver(&dm1105_driver);
+}
+
+module_init(dm1105_init);
+module_exit(dm1105_exit);
+
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_DESCRIPTION("SDMC DM1105 DVB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig
new file mode 100644
index 000000000000..dd6ee57e3a4c
--- /dev/null
+++ b/drivers/media/pci/ivtv/Kconfig
@@ -0,0 +1,61 @@
+config VIDEO_IVTV
+	tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+	depends on VIDEO_V4L2 && PCI && I2C
+	select I2C_ALGOBIT
+	depends on RC_CORE
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_CX2341X
+	select VIDEO_CX25840
+	select VIDEO_MSP3400
+	select VIDEO_SAA711X
+	select VIDEO_SAA717X
+	select VIDEO_SAA7127
+	select VIDEO_CS53L32A
+	select VIDEO_M52790
+	select VIDEO_WM8775
+	select VIDEO_WM8739
+	select VIDEO_VP27SMPX
+	select VIDEO_UPD64031A
+	select VIDEO_UPD64083
+	---help---
+	  This is a video4linux driver for Conexant cx23416 or cx23415 based
+	  PCI personal video recorder devices.
+
+	  This is used in devices such as the Hauppauge PVR-150/250/350/500
+	  cards. There is a driver homepage at <http://www.ivtvdriver.org>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ivtv.
+
+config VIDEO_IVTV_ALSA
+	tristate "Conexant cx23415/cx23416 ALSA interface for PCM audio capture"
+	depends on VIDEO_IVTV && SND
+	select SND_PCM
+	---help---
+	  This driver provides an ALSA interface as another method for user
+	  applications to obtain PCM audio data from Conexant cx23415/cx23416
+	  based PCI TV cards supported by the ivtv driver.
+
+	  The ALSA interface has much wider use in user applications performing
+	  PCM audio capture, than the V4L2 "/dev/video24" PCM audio interface
+	  provided by the main ivtv driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ivtv-alsa.
+
+config VIDEO_FB_IVTV
+	tristate "Conexant cx23415 framebuffer support"
+	depends on VIDEO_IVTV && FB
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	---help---
+	  This is a framebuffer driver for the Conexant cx23415 MPEG
+	  encoder/decoder.
+
+	  This is used in the Hauppauge PVR-350 card. There is a driver
+	  homepage at <http://www.ivtvdriver.org>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ivtvfb.
diff --git a/drivers/media/pci/ivtv/Makefile b/drivers/media/pci/ivtv/Makefile
new file mode 100644
index 000000000000..0eaa88298b7e
--- /dev/null
+++ b/drivers/media/pci/ivtv/Makefile
@@ -0,0 +1,16 @@
+ivtv-objs	:= ivtv-routing.o ivtv-cards.o ivtv-controls.o \
+		   ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+		   ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+		   ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+		   ivtv-vbi.o ivtv-yuv.o
+ivtv-alsa-objs := ivtv-alsa-main.o ivtv-alsa-pcm.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
+obj-$(CONFIG_VIDEO_IVTV_ALSA) += ivtv-alsa.o
+obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c
new file mode 100644
index 000000000000..8deab1629b3b
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c
@@ -0,0 +1,303 @@
+/*
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
+ *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-alsa.h"
+#include "ivtv-alsa-mixer.h"
+#include "ivtv-alsa-pcm.h"
+
+int ivtv_alsa_debug;
+
+#define IVTV_DEBUG_ALSA_INFO(fmt, arg...) \
+	do { \
+		if (ivtv_alsa_debug & 2) \
+			pr_info("%s: " fmt, "ivtv-alsa", ## arg); \
+	} while (0)
+
+module_param_named(debug, ivtv_alsa_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: 0\n"
+		 "\t\t\t  1/0x0001: warning\n"
+		 "\t\t\t  2/0x0002: info\n");
+
+MODULE_AUTHOR("Andy Walls");
+MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface");
+MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+static inline
+struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev)
+{
+	return to_ivtv(v4l2_dev)->alsa;
+}
+
+static inline
+struct snd_ivtv_card *p_to_snd_ivtv_card(struct v4l2_device **v4l2_dev)
+{
+	return container_of(v4l2_dev, struct snd_ivtv_card, v4l2_dev);
+}
+
+static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc)
+{
+	if (itvsc == NULL)
+		return;
+
+	if (itvsc->v4l2_dev != NULL)
+		to_ivtv(itvsc->v4l2_dev)->alsa = NULL;
+
+	/* FIXME - take any other stopping actions needed */
+
+	kfree(itvsc);
+}
+
+static void snd_ivtv_card_private_free(struct snd_card *sc)
+{
+	if (sc == NULL)
+		return;
+	snd_ivtv_card_free(sc->private_data);
+	sc->private_data = NULL;
+	sc->private_free = NULL;
+}
+
+static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev,
+				       struct snd_card *sc,
+				       struct snd_ivtv_card **itvsc)
+{
+	*itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL);
+	if (*itvsc == NULL)
+		return -ENOMEM;
+
+	(*itvsc)->v4l2_dev = v4l2_dev;
+	(*itvsc)->sc = sc;
+
+	sc->private_data = *itvsc;
+	sc->private_free = snd_ivtv_card_private_free;
+
+	return 0;
+}
+
+static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc)
+{
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+	struct snd_card *sc = itvsc->sc;
+
+	/* sc->driver is used by alsa-lib's configurator: simple, unique */
+	strlcpy(sc->driver, "CX2341[56]", sizeof(sc->driver));
+
+	/* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */
+	snprintf(sc->shortname,  sizeof(sc->shortname), "IVTV-%d",
+		 itv->instance);
+
+	/* sc->longname is read from /proc/asound/cards */
+	snprintf(sc->longname, sizeof(sc->longname),
+		 "CX2341[56] #%d %s TV/FM Radio/Line-In Capture",
+		 itv->instance, itv->card_name);
+
+	return 0;
+}
+
+static int snd_ivtv_init(struct v4l2_device *v4l2_dev)
+{
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	struct snd_card *sc = NULL;
+	struct snd_ivtv_card *itvsc;
+	int ret;
+
+	/* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
+
+	/* (1) Check and increment the device index */
+	/* This is a no-op for us.  We'll use the itv->instance */
+
+	/* (2) Create a card instance */
+	ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
+			      SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
+			      THIS_MODULE, 0, &sc);
+	if (ret) {
+		IVTV_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit;
+	}
+
+	/* (3) Create a main component */
+	ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc);
+	if (ret) {
+		IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+
+	/* (4) Set the driver ID and name strings */
+	snd_ivtv_card_set_names(itvsc);
+
+	/* (5) Create other components: mixer, PCM, & proc files */
+#if 0
+	ret = snd_ivtv_mixer_create(itvsc);
+	if (ret) {
+		IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d:"
+			       " proceeding anyway\n", __func__, ret);
+	}
+#endif
+
+	ret = snd_ivtv_pcm_create(itvsc);
+	if (ret) {
+		IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+	/* FIXME - proc files */
+
+	/* (7) Set the driver data and return 0 */
+	/* We do this out of normal order for PCI drivers to avoid races */
+	itv->alsa = itvsc;
+
+	/* (6) Register the card instance */
+	ret = snd_card_register(sc);
+	if (ret) {
+		itv->alsa = NULL;
+		IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit_free;
+	}
+
+	return 0;
+
+err_exit_free:
+	if (sc != NULL)
+		snd_card_free(sc);
+	kfree(itvsc);
+err_exit:
+	return ret;
+}
+
+int ivtv_alsa_load(struct ivtv *itv)
+{
+	struct v4l2_device *v4l2_dev = &itv->v4l2_dev;
+	struct ivtv_stream *s;
+
+	if (v4l2_dev == NULL) {
+		pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
+		       __func__);
+		return 0;
+	}
+
+	itv = to_ivtv(v4l2_dev);
+	if (itv == NULL) {
+		pr_err("ivtv-alsa itv is NULL\n");
+		return 0;
+	}
+
+	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+	if (s->vdev == NULL) {
+		IVTV_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
+				     "skipping\n", __func__);
+		return 0;
+	}
+
+	if (itv->alsa != NULL) {
+		IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n",
+			      __func__);
+		return 0;
+	}
+
+	if (snd_ivtv_init(v4l2_dev)) {
+		IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n",
+			      __func__);
+	} else {
+		IVTV_DEBUG_ALSA_INFO("%s: created ivtv ALSA interface instance "
+				     "\n", __func__);
+	}
+	return 0;
+}
+
+static int __init ivtv_alsa_init(void)
+{
+	pr_info("ivtv-alsa: module loading...\n");
+	ivtv_ext_init = &ivtv_alsa_load;
+	return 0;
+}
+
+static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc)
+{
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+
+	/* FIXME - pointer checks & shutdown itvsc */
+
+	snd_card_free(itvsc->sc);
+	itv->alsa = NULL;
+}
+
+static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+	struct snd_ivtv_card *itvsc;
+
+	if (v4l2_dev == NULL) {
+		pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
+		       __func__);
+		return 0;
+	}
+
+	itvsc = to_snd_ivtv_card(v4l2_dev);
+	if (itvsc == NULL) {
+		IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n",
+			       __func__);
+		return 0;
+	}
+
+	snd_ivtv_exit(itvsc);
+	return 0;
+}
+
+static void __exit ivtv_alsa_exit(void)
+{
+	struct device_driver *drv;
+	int ret;
+
+	pr_info("ivtv-alsa: module unloading...\n");
+
+	drv = driver_find("ivtv", &pci_bus_type);
+	ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback);
+	(void)ret;	/* suppress compiler warning */
+
+	ivtv_ext_init = NULL;
+	pr_info("ivtv-alsa: module unload complete\n");
+}
+
+module_init(ivtv_alsa_init);
+module_exit(ivtv_alsa_exit);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
new file mode 100644
index 000000000000..33ec05b09af3
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
@@ -0,0 +1,175 @@
+/*
+ *  ALSA mixer controls for the
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.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
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "ivtv-alsa.h"
+#include "ivtv-driver.h"
+
+/*
+ * Note the cx25840-core volume scale is funny, due to the alignment of the
+ * scale with another chip's range:
+ *
+ * v4l2_control value	/512	indicated dB	actual dB	reg 0x8d4
+ * 0x0000 - 0x01ff	  0	-119		-96		228
+ * 0x0200 - 0x02ff	  1	-118		-96		228
+ * ...
+ * 0x2c00 - 0x2dff	 22	 -97		-96		228
+ * 0x2e00 - 0x2fff	 23	 -96		-96		228
+ * 0x3000 - 0x31ff	 24	 -95		-95		226
+ * ...
+ * 0xee00 - 0xefff	119	   0		  0		 36
+ * ...
+ * 0xfe00 - 0xffff	127	  +8		 +8		 20
+ */
+static inline int dB_to_cx25840_vol(int dB)
+{
+	if (dB < -96)
+		dB = -96;
+	else if (dB > 8)
+		dB = 8;
+	return (dB + 119) << 9;
+}
+
+static inline int cx25840_vol_to_dB(int v)
+{
+	if (v < (23 << 9))
+		v = (23 << 9);
+	else if (v > (127 << 9))
+		v = (127 << 9);
+	return (v >> 9) - 119;
+}
+
+static int snd_ivtv_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	/* We're already translating values, just keep this control in dB */
+	uinfo->value.integer.min  = -96;
+	uinfo->value.integer.max  =   8;
+	uinfo->value.integer.step =   1;
+	return 0;
+}
+
+static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+	struct v4l2_control vctrl;
+	int ret;
+
+	vctrl.id = V4L2_CID_AUDIO_VOLUME;
+	vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+
+	snd_ivtv_lock(itvsc);
+	ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
+	snd_ivtv_unlock(itvsc);
+
+	if (!ret)
+		uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value);
+	return ret;
+}
+
+static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+	struct v4l2_control vctrl;
+	int ret;
+
+	vctrl.id = V4L2_CID_AUDIO_VOLUME;
+	vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+
+	snd_ivtv_lock(itvsc);
+
+	/* Fetch current state */
+	ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
+
+	if (ret ||
+	    (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
+
+		/* Set, if needed */
+		vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+		ret = v4l2_subdev_call(itv->sd_audio, core, s_ctrl, &vctrl);
+		if (!ret)
+			ret = 1; /* Indicate control was changed w/o error */
+	}
+	snd_ivtv_unlock(itvsc);
+
+	return ret;
+}
+
+
+/* This is a bit of overkill, the slider is already in dB internally */
+static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0);
+
+static struct snd_kcontrol_new snd_ivtv_mixer_tv_vol __initdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog TV Capture Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	.info = snd_ivtv_mixer_tv_volume_info,
+	.get = snd_ivtv_mixer_tv_volume_get,
+	.put = snd_ivtv_mixer_tv_volume_put,
+	.tlv.p = snd_ivtv_mixer_tv_vol_db_scale
+};
+
+/* FIXME - add mute switch and balance, bass, treble sliders:
+	V4L2_CID_AUDIO_MUTE
+
+	V4L2_CID_AUDIO_BALANCE
+
+	V4L2_CID_AUDIO_BASS
+	V4L2_CID_AUDIO_TREBLE
+*/
+
+/* FIXME - add stereo, lang1, lang2, mono menu */
+/* FIXME - add I2S volume */
+
+int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc)
+{
+	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+	struct snd_card *sc = itvsc->sc;
+	int ret;
+
+	strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername));
+
+	ret = snd_ctl_add(sc, snd_ctl_new1(snd_ivtv_mixer_tv_vol, itvsc));
+	if (ret) {
+		IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n",
+			       __func__, snd_ivtv_mixer_tv_vol.name, ret);
+	}
+	return ret;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
new file mode 100644
index 000000000000..cdde36704d53
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
@@ -0,0 +1,23 @@
+/*
+ *  ALSA mixer controls for the
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.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
+ */
+
+int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
new file mode 100644
index 000000000000..f7022bd58ffd
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
@@ -0,0 +1,356 @@
+/*
+ *  ALSA PCM device for the
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
+ *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-streams.h"
+#include "ivtv-fileops.h"
+#include "ivtv-alsa.h"
+
+static unsigned int pcm_debug;
+module_param(pcm_debug, int, 0644);
+MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
+
+#define dprintk(fmt, arg...) \
+	do { \
+		if (pcm_debug) \
+			pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \
+	} while (0)
+
+static struct snd_pcm_hardware snd_ivtv_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_48000,
+
+	.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, */
+};
+
+void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, u8 *pcm_data,
+				 size_t num_bytes)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int oldptr;
+	unsigned int stride;
+	int period_elapsed = 0;
+	int length;
+
+	dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc,
+		pcm_data, num_bytes);
+
+	substream = itvsc->capture_pcm_substream;
+	if (substream == NULL) {
+		dprintk("substream was NULL\n");
+		return;
+	}
+
+	runtime = substream->runtime;
+	if (runtime == NULL) {
+		dprintk("runtime was NULL\n");
+		return;
+	}
+
+	stride = runtime->frame_bits >> 3;
+	if (stride == 0) {
+		dprintk("stride is zero\n");
+		return;
+	}
+
+	length = num_bytes / stride;
+	if (length == 0) {
+		dprintk("%s: length was zero\n", __func__);
+		return;
+	}
+
+	if (runtime->dma_area == NULL) {
+		dprintk("dma area was NULL - ignoring\n");
+		return;
+	}
+
+	oldptr = itvsc->hwptr_done_capture;
+	if (oldptr + length >= runtime->buffer_size) {
+		unsigned int cnt =
+			runtime->buffer_size - oldptr;
+		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+		       cnt * stride);
+		memcpy(runtime->dma_area, pcm_data + cnt * stride,
+		       length * stride - cnt * stride);
+	} else {
+		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+		       length * stride);
+	}
+	snd_pcm_stream_lock(substream);
+
+	itvsc->hwptr_done_capture += length;
+	if (itvsc->hwptr_done_capture >=
+	    runtime->buffer_size)
+		itvsc->hwptr_done_capture -=
+			runtime->buffer_size;
+
+	itvsc->capture_transfer_done += length;
+	if (itvsc->capture_transfer_done >=
+	    runtime->period_size) {
+		itvsc->capture_transfer_done -=
+			runtime->period_size;
+		period_elapsed = 1;
+	}
+
+	snd_pcm_stream_unlock(substream);
+
+	if (period_elapsed)
+		snd_pcm_period_elapsed(substream);
+}
+
+static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	struct ivtv_stream *s;
+	struct ivtv_open_id item;
+	int ret;
+
+	/* Instruct the CX2341[56] to start sending packets */
+	snd_ivtv_lock(itvsc);
+	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+
+	v4l2_fh_init(&item.fh, s->vdev);
+	item.itv = itv;
+	item.type = s->type;
+
+	/* See if the stream is available */
+	if (ivtv_claim_stream(&item, item.type)) {
+		/* No, it's already in use */
+		snd_ivtv_unlock(itvsc);
+		return -EBUSY;
+	}
+
+	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) ||
+	    test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		/* We're already streaming.  No additional action required */
+		snd_ivtv_unlock(itvsc);
+		return 0;
+	}
+
+
+	runtime->hw = snd_ivtv_hw_capture;
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	itvsc->capture_pcm_substream = substream;
+	runtime->private_data = itv;
+
+	itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data;
+
+	/* Not currently streaming, so start it up */
+	set_bit(IVTV_F_S_STREAMING, &s->s_flags);
+	ret = ivtv_start_v4l2_encode_stream(s);
+	snd_ivtv_unlock(itvsc);
+
+	return ret;
+}
+
+static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	struct ivtv_stream *s;
+
+	/* Instruct the ivtv to stop sending packets */
+	snd_ivtv_lock(itvsc);
+	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+	ivtv_stop_v4l2_encode_stream(s, 0);
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+	ivtv_release_stream(s);
+
+	itv->pcm_announce_callback = NULL;
+	snd_ivtv_unlock(itvsc);
+
+	return 0;
+}
+
+static int snd_ivtv_pcm_ioctl(struct snd_pcm_substream *substream,
+		     unsigned int cmd, void *arg)
+{
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+	int ret;
+
+	snd_ivtv_lock(itvsc);
+	ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+	snd_ivtv_unlock(itvsc);
+	return ret;
+}
+
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+					size_t size)
+{
+	struct snd_pcm_runtime *runtime = subs->runtime;
+
+	dprintk("Allocating vbuffer\n");
+	if (runtime->dma_area) {
+		if (runtime->dma_bytes > size)
+			return 0;
+
+		vfree(runtime->dma_area);
+	}
+	runtime->dma_area = vmalloc(size);
+	if (!runtime->dma_area)
+		return -ENOMEM;
+
+	runtime->dma_bytes = size;
+
+	return 0;
+}
+
+static int snd_ivtv_pcm_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	dprintk("%s called\n", __func__);
+
+	return snd_pcm_alloc_vmalloc_buffer(substream,
+					   params_buffer_bytes(params));
+}
+
+static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&itvsc->slock, flags);
+	if (substream->runtime->dma_area) {
+		dprintk("freeing pcm capture region\n");
+		vfree(substream->runtime->dma_area);
+		substream->runtime->dma_area = NULL;
+	}
+	spin_unlock_irqrestore(&itvsc->slock, flags);
+
+	return 0;
+}
+
+static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+
+	itvsc->hwptr_done_capture = 0;
+	itvsc->capture_transfer_done = 0;
+
+	return 0;
+}
+
+static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	unsigned long flags;
+	snd_pcm_uframes_t hwptr_done;
+	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+
+	spin_lock_irqsave(&itvsc->slock, flags);
+	hwptr_done = itvsc->hwptr_done_capture;
+	spin_unlock_irqrestore(&itvsc->slock, flags);
+
+	return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+					     unsigned long offset)
+{
+	void *pageptr = subs->runtime->dma_area + offset;
+
+	return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_ivtv_pcm_capture_ops = {
+	.open		= snd_ivtv_pcm_capture_open,
+	.close		= snd_ivtv_pcm_capture_close,
+	.ioctl		= snd_ivtv_pcm_ioctl,
+	.hw_params	= snd_ivtv_pcm_hw_params,
+	.hw_free	= snd_ivtv_pcm_hw_free,
+	.prepare	= snd_ivtv_pcm_prepare,
+	.trigger	= snd_ivtv_pcm_trigger,
+	.pointer	= snd_ivtv_pcm_pointer,
+	.page		= snd_pcm_get_vmalloc_page,
+};
+
+int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc)
+{
+	struct snd_pcm *sp;
+	struct snd_card *sc = itvsc->sc;
+	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	int ret;
+
+	ret = snd_pcm_new(sc, "CX2341[56] PCM",
+			  0, /* PCM device 0, the only one for this card */
+			  0, /* 0 playback substreams */
+			  1, /* 1 capture substream */
+			  &sp);
+	if (ret) {
+		IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
+			      __func__, ret);
+		goto err_exit;
+	}
+
+	spin_lock_init(&itvsc->slock);
+
+	snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_ivtv_pcm_capture_ops);
+	sp->info_flags = 0;
+	sp->private_data = itvsc;
+	strlcpy(sp->name, itv->card_name, sizeof(sp->name));
+
+	return 0;
+
+err_exit:
+	return ret;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
new file mode 100644
index 000000000000..5ab18319ea4d
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
@@ -0,0 +1,27 @@
+/*
+ *  ALSA PCM device for the
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.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
+ */
+
+int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc);
+
+/* Used by ivtv driver to announce the PCM data to the module */
+void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *card, u8 *pcm_data,
+				 size_t num_bytes);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa.h b/drivers/media/pci/ivtv/ivtv-alsa.h
new file mode 100644
index 000000000000..4a0d8f2c254d
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa.h
@@ -0,0 +1,75 @@
+/*
+ *  ALSA interface to ivtv PCM capture streams
+ *
+ *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
+ *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.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., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307  USA
+ */
+
+struct snd_card;
+
+struct snd_ivtv_card {
+	struct v4l2_device *v4l2_dev;
+	struct snd_card *sc;
+	unsigned int capture_transfer_done;
+	unsigned int hwptr_done_capture;
+	struct snd_pcm_substream *capture_pcm_substream;
+	spinlock_t slock;
+};
+
+extern int ivtv_alsa_debug;
+
+/*
+ * File operations that manipulate the encoder or video or audio subdevices
+ * need to be serialized.  Use the same lock we use for v4l2 file ops.
+ */
+static inline void snd_ivtv_lock(struct snd_ivtv_card *itvsc)
+{
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+	mutex_lock(&itv->serialize_lock);
+}
+
+static inline void snd_ivtv_unlock(struct snd_ivtv_card *itvsc)
+{
+	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+	mutex_unlock(&itv->serialize_lock);
+}
+
+#define IVTV_ALSA_DBGFLG_WARN  (1 << 0)
+#define IVTV_ALSA_DBGFLG_INFO  (1 << 1)
+
+#define IVTV_ALSA_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & ivtv_alsa_debug) \
+			pr_info("%s-alsa: " type ": " fmt, \
+				v4l2_dev->name , ## args); \
+	} while (0)
+
+#define IVTV_ALSA_DEBUG_WARN(fmt, args...) \
+	IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_WARN, "warning", fmt , ## args)
+
+#define IVTV_ALSA_DEBUG_INFO(fmt, args...) \
+	IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_INFO, "info", fmt , ## args)
+
+#define IVTV_ALSA_ERR(fmt, args...) \
+	pr_err("%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define IVTV_ALSA_WARN(fmt, args...) \
+	pr_warn("%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define IVTV_ALSA_INFO(fmt, args...) \
+	pr_info("%s-alsa: " fmt, v4l2_dev->name , ## args)
diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c
new file mode 100644
index 000000000000..145e4749a69d
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-cards.c
@@ -0,0 +1,1370 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+
+#include <media/msp3400.h>
+#include <media/m52790.h>
+#include <media/wm8775.h>
+#include <media/cs53l32a.h>
+#include <media/cx25840.h>
+#include <media/upd64031a.h>
+
+#define MSP_TUNER  MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+				MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
+#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_MONO   MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
+				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
+
+/* usual i2c tuner addresses to probe */
+static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
+	.radio = { I2C_CLIENT_END },
+	.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/ 
+   This keeps the PCI ID database up to date. Note that the entries
+   must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+   New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge PVR-250 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
+static const struct ivtv_card ivtv_card_pvr250 = {
+	.type = IVTV_CARD_PVR_250,
+	.name = "Hauppauge WinTV PVR-250",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-350 cards */
+
+/* Outputs for Hauppauge PVR350 cards */
+static struct ivtv_card_output ivtv_pvr350_outputs[] = {
+	{
+		.name = "S-Video + Composite",
+		.video_output = 0,
+	}, {
+		.name = "Composite",
+		.video_output = 1,
+	}, {
+		.name = "S-Video",
+		.video_output = 2,
+	}, {
+		.name = "RGB",
+		.video_output = 3,
+	}, {
+		.name = "YUV C",
+		.video_output = 4,
+	}, {
+		.name = "YUV V",
+		.video_output = 5,
+	}
+};
+
+static const struct ivtv_card ivtv_card_pvr350 = {
+	.type = IVTV_CARD_PVR_350,
+	.name = "Hauppauge WinTV PVR-350",
+	.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+	.video_outputs = ivtv_pvr350_outputs,
+	.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+		  IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ 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
+   saa7114 instead of a saa7115.
+   Note that the info below comes from a pre-production model so it may
+   not be correct. Especially the audio behaves strangely (mono only it seems) */
+static const struct ivtv_card ivtv_card_pvr350_v1 = {
+	.type = IVTV_CARD_PVR_350_V1,
+	.name = "Hauppauge WinTV PVR-350 (V1)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+	.video_outputs = ivtv_pvr350_outputs,
+	.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
+		  IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  MSP_MONO   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+		{ IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-150/PVR-500 cards */
+
+static const struct ivtv_card ivtv_card_pvr150 = {
+	.type = IVTV_CARD_PVR_150,
+	.name = "Hauppauge WinTV PVR-150",
+	.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_WM8775,
+	.hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
+		  IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+		  IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT |
+		  IVTV_HW_Z8F0811_IR_HAUP,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE7 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+		{ IVTV_CARD_INPUT_SVIDEO2,    2, CX25840_SVIDEO2    },
+		{ IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,
+		  CX25840_AUDIO8, WM8775_AIN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,
+		  CX25840_AUDIO_SERIAL, WM8775_AIN2 },
+		{ IVTV_CARD_INPUT_LINE_IN2,
+		  CX25840_AUDIO_SERIAL, WM8775_AIN3 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
+			 CX25840_AUDIO_SERIAL, WM8775_AIN4 },
+	/* apparently needed for the IR blaster */
+	.gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M179 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_m179 = {
+	.type = IVTV_CARD_M179,
+	.name = "AVerMedia M179",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
+	.gpio_audio_input  = { .mask = 0x8040, .tuner  = 0x8000, .linein = 0x0000 },
+	.gpio_audio_mute   = { .mask = 0x2000, .mute   = 0x2000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0200, .lang2  = 0x0100, .both   = 0x0000 },
+	.gpio_audio_freq   = { .mask = 0x0018, .f32000 = 0x0000,
+			     .f44100 = 0x0008, .f48000 = 0x0010 },
+	.gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
+	.tuners = {
+		/* As far as we know all M179 cards use this tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
+	},
+	.pci_list = ivtv_pci_m179,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg600 = {
+	.type = IVTV_CARD_MPG600,
+	.name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
+	.gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+	.gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+	.gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+			      .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+	.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+	.tuners = {
+		/* The PAL tuner is confirmed */
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_mpg600,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
+	{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg160 = {
+	.type = IVTV_CARD_MPG160,
+	.name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
+	.gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+	.gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+	.gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+			      .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+	.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_mpg160,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600/Diamond PVR-550 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600 = {
+	.type = IVTV_CARD_PG600,
+	.name = "Yuan PG600, Diamond PVR-550",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_pg600,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2410 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2410 = {
+	.type = IVTV_CARD_AVC2410,
+	.name = "Adaptec VideOh! AVC-2410",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_muxer = IVTV_HW_CS53L32A,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
+		  IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+		  IVTV_HW_I2C_IR_RX_ADAPTEC,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,
+		  MSP_TUNER, CS53L32A_IN0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,
+		  MSP_SCART1, CS53L32A_IN2 },
+	},
+	/* This card has no eeprom and in fact the Windows driver relies
+	   on the country/region setting of the user to decide which tuner
+	   is available. */
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP,
+			.tuner = TUNER_PHILIPS_FM1236_MK3 },
+		{ .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_avc2410,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2010 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2010 = {
+	.type = IVTV_CARD_AVC2010,
+	.name = "Adaptec VideOh! AVC-2010",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_CS53L32A,
+	.hw_audio_ctrl = IVTV_HW_CS53L32A,
+	.hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_SVIDEO1,    0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_LINE_IN1,   CS53L32A_IN2 },
+	},
+	/* Does not have a tuner */
+	.pci_list = ivtv_pci_avc2010,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Nagase Transgear 5000TV card */
+
+static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_tg5000tv = {
+	.type = IVTV_CARD_TG5000TV,
+	.name = "Nagase Transgear 5000TV",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+	IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gr_config = UPD64031A_VERTICAL_EXTERNAL,
+	.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+	.gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+	.gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+			  .composite = 0x0010, .svideo = 0x0020 },
+	.tuners = {
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_tg5000tv,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AOpen VA2000MAX-SNT6 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
+	{ PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_va2000 = {
+	.type = IVTV_CARD_VA2000MAX_SNT6,
+	.name = "AOpen VA2000MAX-SNT6",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_MSP34XX,
+	.hw_audio_ctrl = IVTV_HW_MSP34XX,
+	.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+		  IVTV_HW_UPD6408X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+	},
+	.tuners = {
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_va2000,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc = {
+	.type = IVTV_CARD_CX23416GYC,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
+		IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO3 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gr_config = UPD64031A_VERTICAL_EXTERNAL,
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .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 = {
+	.type = IVTV_CARD_CX23416GYC_NOGR,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+		  IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .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 = {
+	.type = IVTV_CARD_CX23416GYC_NOGRYCS,
+	.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+						 IVTV_SAA717X_TUNER_FLAG },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+	},
+	.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+	.gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+			       .composite = 0x0020, .svideo = 0x0020 },
+	.gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+			     .f44100 = 0x4000, .f48000 = 0x8000 },
+	.tuners = {
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx = {
+	.type = IVTV_CARD_GV_MVPRX,
+	.name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_WM8739,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_VP27SMPX |
+		  IVTV_HW_TUNER | IVTV_HW_WM8739 |
+		  IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO1    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2    },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+	.gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+	.tuners = {
+		/* This card has the Panasonic VP27 tuner */
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+	},
+	.pci_list = ivtv_pci_gv_mvprx,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX2E card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
+	{0, 0, 0}
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx2e = {
+	.type = IVTV_CARD_GV_MVPRX2E,
+	.name = "I/O Data GV-MVP/RX2E",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_WM8739,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+		  IVTV_HW_VP27SMPX | IVTV_HW_WM8739,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+	.gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+	.tuners = {
+		/* This card has the Panasonic VP27 tuner */
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+	},
+	.pci_list = ivtv_pci_gv_mvprx2e,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
+	.type = IVTV_CARD_GOTVIEW_PCI_DVD,
+	.name = "GotView PCI DVD",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA717X,
+	.hw_audio = IVTV_HW_SAA717X,
+	.hw_audio_ctrl = IVTV_HW_SAA717X,
+	.hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE1 },  /* pin 116 */
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },     /* pin 114/109 */
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },  /* pin 118 */
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN2 },
+	},
+	.gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
+	.tuners = {
+		/* This card has a Philips FQ1216ME MK3 tuner */
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+	},
+	.pci_list = ivtv_pci_gotview_pci_dvd,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD2 Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
+	.type = IVTV_CARD_GOTVIEW_PCI_DVD2,
+	.name = "GotView PCI DVD2 Deluxe",
+	.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 = {
+		/* This card has a Philips FQ1216ME MK5 tuner */
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+	},
+	.pci_list = ivtv_pci_gotview_pci_dvd2,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC622 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_yuan_mpc622 = {
+	.type = IVTV_CARD_YUAN_MPC622,
+	.name = "Yuan MPC622",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
+	.tuners = {
+		/* This card has the TDA8290/TDA8275 tuner chips */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
+	},
+	.pci_list = ivtv_pci_yuan_mpc622,
+	.i2c = &ivtv_i2c_tda8290,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* DIGITAL COWBOY DCT-MTVP1 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_dctmvtvp1 = {
+	.type = IVTV_CARD_DCTMTVP1,
+	.name = "Digital Cowboy DCT-MTVP1",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+		IVTV_HW_GPIO,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+		IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+		{ IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+	.gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+	.gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+	.gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+			      .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+			       .composite = 0x0010, .svideo = 0x0020},
+	.tuners = {
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+	},
+	.pci_list = ivtv_pci_dctmvtvp1,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600-2/GotView PCI DVD Lite cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2,  0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600v2 = {
+	.type = IVTV_CARD_PG600V2,
+	.name = "Yuan PG600-2, GotView PCI DVD Lite",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	/* XC2028 support apparently works for the Yuan, it's still
+	   uncertain whether it also works with the GotView. */
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+	.xceive_pin = 12,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_pg600v2,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Club3D ZAP-TV1x01 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_club3d[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_club3d = {
+	.type = IVTV_CARD_CLUB3D,
+	.name = "Club3D ZAP-TV1x01",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+		  CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_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 },
+	.xceive_pin = 12,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_club3d,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerTV MCE 116 Plus (M116) card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avertv_mce116[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc439 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avertv_mce116 = {
+	.type = IVTV_CARD_AVERTV_MCE116,
+	.name = "AVerTV MCE 116 Plus",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 |
+		  IVTV_HW_I2C_IR_RX_AVER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5 },
+	/* enable line-in */
+	.gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
+	.xceive_pin = 10,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_avertv_mce116,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_pvr150 = {
+	.type = IVTV_CARD_AVER_PVR150PLUS,
+	.name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner",
+	.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 |
+		  IVTV_HW_WM8739 | IVTV_HW_GPIO,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5,       0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+	/* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */
+	.gpio_init = { .direction = 0xc000, .initial_value = 0 },
+	.gpio_audio_input  = { .mask   = 0xc000,
+			       .tuner  = 0x0000,
+			       .linein = 0x4000,
+			       .radio  = 0x8000 },
+	.tuners = {
+		/* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 },
+	},
+	.pci_list = ivtv_pci_aver_pvr150,
+	/* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */
+	.i2c = &ivtv_i2c_radio,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
+	.type = IVTV_CARD_AVER_ULTRA1500MCE,
+	.name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner",
+	.comment = "For non-NTSC tuners, use the pal= or secam= module options",
+	.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 |
+		  IVTV_HW_WM8739 | IVTV_HW_GPIO,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5,       0 },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+	/* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */
+	.gpio_init = { .direction = 0xc000, .initial_value = 0 },
+	.gpio_audio_input  = { .mask   = 0xc000,
+			       .tuner  = 0x0000,
+			       .linein = 0x4000,
+			       .radio  = 0x8000 },
+	.tuners = {
+		/* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */
+		{ .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+		{ .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 },
+	},
+	.pci_list = ivtv_pci_aver_ultra1500mce,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* 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_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+	},
+	.pci_list = ivtv_pci_asus_falcon2,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M104 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_m104 = {
+	.type = IVTV_CARD_AVER_M104,
+	.name = "AVerMedia M104",
+	.comment = "Not yet supported!\n",
+	.v4l2_capabilities = 0, /*IVTV_CAP_ENCODER,*/
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_SVIDEO1,    0, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+	/* enable line-in + reset tuner */
+	.gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
+	.xceive_pin = 10,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_aver_m104,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Buffalo PC-MV5L/PCI cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_buffalo = {
+	.type = IVTV_CARD_BUFFALO_MV5L,
+	.name = "Buffalo PC-MV5L/PCI",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_CX25840,
+	.hw_audio = IVTV_HW_CX25840,
+	.hw_audio_ctrl = IVTV_HW_CX25840,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1,
+			CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+	},
+	.xceive_pin = 12,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_buffalo,
+	.i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+/* Sony Kikyou */
+
+static const struct ivtv_card_pci_info ivtv_pci_kikyou[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_SONY, 0x813d },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_kikyou = {
+	.type = IVTV_CARD_KIKYOU,
+	.name = "Sony VAIO Giga Pocket (ENX Kikyou)",
+	.v4l2_capabilities = IVTV_CAP_ENCODER,
+	.hw_video = IVTV_HW_SAA7115,
+	.hw_audio = IVTV_HW_GPIO,
+	.hw_audio_ctrl = IVTV_HW_GPIO,
+	.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+	.video_inputs = {
+	{ IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE1 },
+	{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE1 },
+	{ IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO1 },
+	},
+	.audio_inputs = {
+	     { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER },
+	     { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+	     { IVTV_CARD_INPUT_LINE_IN2,   IVTV_GPIO_LINE_IN },
+	},
+	.gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 },
+	.gpio_audio_input = { .mask   = 0x0060,
+			      .tuner  = 0x0020,
+			      .linein = 0x0000,
+			      .radio  = 0x0060 },
+	.gpio_audio_mute  = { .mask = 0x0000,
+			      .mute = 0x0000 }, /* 0x200? Disable for now. */
+	.gpio_audio_mode  = { .mask   = 0x0080,
+			      .mono   = 0x0000,
+			      .stereo = 0x0000, /* SAP */
+			      .lang1  = 0x0080,
+			      .lang2  = 0x0000,
+			      .both   = 0x0080 },
+	.tuners = {
+	     { .std = V4L2_STD_ALL, .tuner = TUNER_SONY_BTF_PXN01Z },
+	},
+	.pci_list = ivtv_pci_kikyou,
+	.i2c = &ivtv_i2c_std,
+};
+
+
+static const struct ivtv_card *ivtv_card_list[] = {
+	&ivtv_card_pvr250,
+	&ivtv_card_pvr350,
+	&ivtv_card_pvr150,
+	&ivtv_card_m179,
+	&ivtv_card_mpg600,
+	&ivtv_card_mpg160,
+	&ivtv_card_pg600,
+	&ivtv_card_avc2410,
+	&ivtv_card_avc2010,
+	&ivtv_card_tg5000tv,
+	&ivtv_card_va2000,
+	&ivtv_card_cx23416gyc,
+	&ivtv_card_gv_mvprx,
+	&ivtv_card_gv_mvprx2e,
+	&ivtv_card_gotview_pci_dvd,
+	&ivtv_card_gotview_pci_dvd2,
+	&ivtv_card_yuan_mpc622,
+	&ivtv_card_dctmvtvp1,
+	&ivtv_card_pg600v2,
+	&ivtv_card_club3d,
+	&ivtv_card_avertv_mce116,
+	&ivtv_card_asus_falcon2,
+	&ivtv_card_aver_pvr150,
+	&ivtv_card_aver_ezmaker,
+	&ivtv_card_aver_m104,
+	&ivtv_card_buffalo,
+	&ivtv_card_aver_ultra1500mce,
+	&ivtv_card_kikyou,
+
+	/* Variations of standard cards but with the same PCI IDs.
+	   These cards must come last in this list. */
+	&ivtv_card_pvr350_v1,
+	&ivtv_card_cx23416gyc_nogr,
+	&ivtv_card_cx23416gyc_nogrycs,
+};
+
+const struct ivtv_card *ivtv_get_card(u16 index)
+{
+	if (index >= ARRAY_SIZE(ivtv_card_list))
+		return NULL;
+	return ivtv_card_list[index];
+}
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
+{
+	const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"S-Video 1",
+		"S-Video 2",
+		"Composite 1",
+		"Composite 2",
+		"Composite 3"
+	};
+
+	if (index >= itv->nof_inputs)
+		return -EINVAL;
+	input->index = index;
+	strlcpy(input->name, input_strs[card_input->video_type - 1],
+			sizeof(input->name));
+	input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
+			V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+	input->audioset = (1 << itv->nof_audio_inputs) - 1;
+	input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+				itv->tuner_std : V4L2_STD_ALL;
+	return 0;
+}
+
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
+{
+	const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
+
+	if (index >= itv->card->nof_outputs)
+		return -EINVAL;
+	output->index = index;
+	strlcpy(output->name, card_output->name, sizeof(output->name));
+	output->type = V4L2_OUTPUT_TYPE_ANALOG;
+	output->audioset = 1;
+	output->std = V4L2_STD_ALL;
+	return 0;
+}
+
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
+{
+	const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
+	static const char * const input_strs[] = {
+		"Tuner 1",
+		"Line In 1",
+		"Line In 2"
+	};
+
+	memset(audio, 0, sizeof(*audio));
+	if (index >= itv->nof_audio_inputs)
+		return -EINVAL;
+	strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+			sizeof(audio->name));
+	audio->index = index;
+	audio->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
+
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
+{
+	memset(aud_output, 0, sizeof(*aud_output));
+	if (itv->card->video_outputs == NULL || index != 0)
+		return -EINVAL;
+	strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name));
+	return 0;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-cards.h b/drivers/media/pci/ivtv/ivtv-cards.h
new file mode 100644
index 000000000000..e6f5c02981f1
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-cards.h
@@ -0,0 +1,309 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_CARDS_H
+#define IVTV_CARDS_H
+
+/* Supported cards */
+#define IVTV_CARD_PVR_250 	      0	/* WinTV PVR 250 */
+#define IVTV_CARD_PVR_350 	      1	/* encoder, decoder, tv-out */
+#define IVTV_CARD_PVR_150 	      2	/* WinTV PVR 150 and PVR 500 (really just two
+					   PVR150s on one PCI board) */
+#define IVTV_CARD_M179    	      3	/* AVerMedia M179 (encoder only) */
+#define IVTV_CARD_MPG600  	      4	/* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
+#define IVTV_CARD_MPG160  	      5	/* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
+					   cx23415 based, but does not have tv-out */
+#define IVTV_CARD_PG600 	      6	/* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
+#define IVTV_CARD_AVC2410 	      7	/* Adaptec AVC-2410 */
+#define IVTV_CARD_AVC2010 	      8	/* Adaptec AVD-2010 (No Tuner) */
+#define IVTV_CARD_TG5000TV   	      9 /* NAGASE TRANSGEAR 5000TV, encoder only */
+#define IVTV_CARD_VA2000MAX_SNT6     10 /* VA2000MAX-STN6 */
+#define IVTV_CARD_CX23416GYC 	     11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_GV_MVPRX   	     12 /* I/O Data GV-MVP/RX, RX2, RX2W */
+#define IVTV_CARD_GV_MVPRX2E 	     13 /* I/O Data GV-MVP/RX2E */
+#define IVTV_CARD_GOTVIEW_PCI_DVD    14	/* GotView PCI DVD */
+#define IVTV_CARD_GOTVIEW_PCI_DVD2   15	/* GotView PCI DVD2 */
+#define IVTV_CARD_YUAN_MPC622        16	/* Yuan MPC622 miniPCI */
+#define IVTV_CARD_DCTMTVP1 	     17 /* DIGITAL COWBOY DCT-MTVP1 */
+#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_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_AVER_M104          24 /* AverMedia M104 miniPCI card */
+#define IVTV_CARD_BUFFALO_MV5L       25 /* Buffalo PC-MV5L/PCI card */
+#define IVTV_CARD_AVER_ULTRA1500MCE  26 /* AVerMedia UltraTV 1500 MCE */
+#define IVTV_CARD_KIKYOU             27 /* Sony VAIO Giga Pocket (ENX Kikyou) */
+#define IVTV_CARD_LAST 		     27
+
+/* Variants of existing cards but with the same PCI IDs. The driver
+   detects these based on other device information.
+   These cards must always come last.
+   New cards must be inserted above, and the indices of the cards below
+   must be adjusted accordingly. */
+
+/* PVR-350 V1 (uses saa7114) */
+#define IVTV_CARD_PVR_350_V1 	     (IVTV_CARD_LAST+1)
+/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_CX23416GYC_NOGR    (IVTV_CARD_LAST+2)
+#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_ICOMP  0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+
+/* subsystem vendor ID */
+#define IVTV_PCI_ID_HAUPPAUGE 		0x0070
+#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
+#define IVTV_PCI_ID_YUAN3 		0xffab
+#define IVTV_PCI_ID_YUAN4 		0xfbab
+#define IVTV_PCI_ID_DIAMONDMM 		0xff92
+#define IVTV_PCI_ID_IODATA 		0x10fc
+#define IVTV_PCI_ID_MELCO 		0x1154
+#define IVTV_PCI_ID_GOTVIEW1		0xffac
+#define IVTV_PCI_ID_GOTVIEW2 		0xffad
+#define IVTV_PCI_ID_SONY 		0x104d
+
+/* hardware flags, no gaps allowed */
+#define IVTV_HW_CX25840			(1 << 0)
+#define IVTV_HW_SAA7115			(1 << 1)
+#define IVTV_HW_SAA7127			(1 << 2)
+#define IVTV_HW_MSP34XX			(1 << 3)
+#define IVTV_HW_TUNER			(1 << 4)
+#define IVTV_HW_WM8775			(1 << 5)
+#define IVTV_HW_CS53L32A		(1 << 6)
+#define IVTV_HW_TVEEPROM		(1 << 7)
+#define IVTV_HW_SAA7114			(1 << 8)
+#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_I2C_IR_RX_AVER		(1 << 16)
+#define IVTV_HW_I2C_IR_RX_HAUP_EXT	(1 << 17) /* External before internal */
+#define IVTV_HW_I2C_IR_RX_HAUP_INT	(1 << 18)
+#define IVTV_HW_Z8F0811_IR_TX_HAUP	(1 << 19)
+#define IVTV_HW_Z8F0811_IR_RX_HAUP	(1 << 20)
+#define IVTV_HW_I2C_IR_RX_ADAPTEC	(1 << 21)
+
+#define IVTV_HW_Z8F0811_IR_HAUP	(IVTV_HW_Z8F0811_IR_RX_HAUP | \
+				 IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_SAA711X   (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+
+#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \
+			   IVTV_HW_I2C_IR_RX_HAUP_EXT | \
+			   IVTV_HW_I2C_IR_RX_HAUP_INT | \
+			   IVTV_HW_Z8F0811_IR_RX_HAUP | \
+			   IVTV_HW_I2C_IR_RX_ADAPTEC)
+
+#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_IR_ANY	  (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY)
+
+/* video inputs */
+#define	IVTV_CARD_INPUT_VID_TUNER	1
+#define	IVTV_CARD_INPUT_SVIDEO1 	2
+#define	IVTV_CARD_INPUT_SVIDEO2 	3
+#define	IVTV_CARD_INPUT_COMPOSITE1 	4
+#define	IVTV_CARD_INPUT_COMPOSITE2 	5
+#define	IVTV_CARD_INPUT_COMPOSITE3 	6
+
+/* audio inputs */
+#define	IVTV_CARD_INPUT_AUD_TUNER	1
+#define	IVTV_CARD_INPUT_LINE_IN1 	2
+#define	IVTV_CARD_INPUT_LINE_IN2 	3
+
+#define IVTV_CARD_MAX_VIDEO_INPUTS 6
+#define IVTV_CARD_MAX_AUDIO_INPUTS 3
+#define IVTV_CARD_MAX_TUNERS  	   3
+
+/* SAA71XX HW inputs */
+#define IVTV_SAA71XX_COMPOSITE0 0
+#define IVTV_SAA71XX_COMPOSITE1 1
+#define IVTV_SAA71XX_COMPOSITE2 2
+#define IVTV_SAA71XX_COMPOSITE3 3
+#define IVTV_SAA71XX_COMPOSITE4 4
+#define IVTV_SAA71XX_COMPOSITE5 5
+#define IVTV_SAA71XX_SVIDEO0    6
+#define IVTV_SAA71XX_SVIDEO1    7
+#define IVTV_SAA71XX_SVIDEO2    8
+#define IVTV_SAA71XX_SVIDEO3    9
+
+/* SAA717X needs to mark the tuner input by ORing with this flag */
+#define IVTV_SAA717X_TUNER_FLAG 0x80
+
+/* Dummy HW input */
+#define IVTV_DUMMY_AUDIO        0
+
+/* GPIO HW inputs */
+#define IVTV_GPIO_TUNER   0
+#define IVTV_GPIO_LINE_IN 1
+
+/* SAA717X HW inputs */
+#define IVTV_SAA717X_IN0 0
+#define IVTV_SAA717X_IN1 1
+#define IVTV_SAA717X_IN2 2
+
+/* V4L2 capability aliases */
+#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+			  V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
+			  V4L2_CAP_SLICED_VBI_CAPTURE)
+#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \
+			  V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
+
+struct ivtv_card_video_input {
+	u8  video_type; 	/* video input type */
+	u8  audio_index;	/* index in ivtv_card_audio_input array */
+	u16 video_input;	/* hardware video input */
+};
+
+struct ivtv_card_audio_input {
+	u8  audio_type;		/* audio input type */
+	u32 audio_input;	/* hardware audio input */
+	u16 muxer_input;	/* hardware muxer input for boards with a
+				   multiplexer chip */
+};
+
+struct ivtv_card_output {
+	u8  name[32];
+	u16 video_output;  /* hardware video output */
+};
+
+struct ivtv_card_pci_info {
+	u16 device;
+	u16 subsystem_vendor;
+	u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct ivtv_gpio_init { 	/* set initial GPIO DIR and OUT values */
+	u16 direction; 		/* DIR setting. Leave to 0 if no init is needed */
+	u16 initial_value;
+};
+
+struct ivtv_gpio_video_input { 	/* select tuner/line in input */
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 tuner;
+	u16 composite;
+	u16 svideo;
+};
+
+struct ivtv_gpio_audio_input { 	/* select tuner/line in input */
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 tuner;
+	u16 linein;
+	u16 radio;
+};
+
+struct ivtv_gpio_audio_mute {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 mute;		/* set this value to mute, 0 to unmute */
+};
+
+struct ivtv_gpio_audio_mode {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 mono; 		/* set audio to mono */
+	u16 stereo; 		/* set audio to stereo */
+	u16 lang1;		/* set audio to the first language */
+	u16 lang2;		/* set audio to the second language */
+	u16 both; 		/* both languages are output */
+};
+
+struct ivtv_gpio_audio_freq {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 f32000;
+	u16 f44100;
+	u16 f48000;
+};
+
+struct ivtv_gpio_audio_detect {
+	u16 mask; 		/* leave to 0 if not supported */
+	u16 stereo; 		/* if the input matches this value then
+				   stereo is detected */
+};
+
+struct ivtv_card_tuner {
+	v4l2_std_id std; 	/* standard for which the tuner is suitable */
+	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;
+	char *name;
+	char *comment;
+	u32 v4l2_capabilities;
+	u32 hw_video;		/* hardware used to process video */
+	u32 hw_audio;		/* hardware used to process audio */
+	u32 hw_audio_ctrl;	/* hardware used for the V4L2 controls (only 1 dev allowed) */
+	u32 hw_muxer;		/* hardware used to multiplex audio input */
+	u32 hw_all;		/* all hardware used by the board */
+	struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
+	struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
+	struct ivtv_card_audio_input radio_input;
+	int nof_outputs;
+	const struct ivtv_card_output *video_outputs;
+	u8 gr_config; 		/* config byte for the ghost reduction device */
+	u8 xceive_pin; 		/* XCeive tuner GPIO reset pin */
+
+	/* GPIO card-specific settings */
+	struct ivtv_gpio_init 		gpio_init;
+	struct ivtv_gpio_video_input	gpio_video_input;
+	struct ivtv_gpio_audio_input 	gpio_audio_input;
+	struct ivtv_gpio_audio_mute 	gpio_audio_mute;
+	struct ivtv_gpio_audio_mode 	gpio_audio_mode;
+	struct ivtv_gpio_audio_freq 	gpio_audio_freq;
+	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. */
+	const struct ivtv_card_pci_info *pci_list;
+};
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
+const struct ivtv_card *ivtv_get_card(u16 index);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
new file mode 100644
index 000000000000..c60424601cb9
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-controls.c
@@ -0,0 +1,163 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-controls.h"
+#include "ivtv-mailbox.h"
+
+static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
+{
+	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+	/* First try to allocate sliced VBI buffers if needed. */
+	if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
+		int i;
+
+		for (i = 0; i < IVTV_VBI_FRAMES; i++) {
+			/* Yuck, hardcoded. Needs to be a define */
+			itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+			if (itv->vbi.sliced_mpeg_data[i] == NULL) {
+				while (--i >= 0) {
+					kfree(itv->vbi.sliced_mpeg_data[i]);
+					itv->vbi.sliced_mpeg_data[i] = NULL;
+				}
+				return -ENOMEM;
+			}
+		}
+	}
+
+	itv->vbi.insert_mpeg = fmt;
+
+	if (itv->vbi.insert_mpeg == 0) {
+		return 0;
+	}
+	/* Need sliced data for mpeg insertion */
+	if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) {
+		if (itv->is_60hz)
+			itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+		else
+			itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+		ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
+	}
+	return 0;
+}
+
+static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
+{
+	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+	int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+	struct v4l2_mbus_framefmt fmt;
+
+	/* fix videodecoder resolution */
+	fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+	fmt.height = cxhdl->height;
+	fmt.code = V4L2_MBUS_FMT_FIXED;
+	v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
+	return 0;
+}
+
+static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
+{
+	static const u32 freqs[3] = { 44100, 48000, 32000 };
+	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+	/* The audio clock of the digitizer must match the codec sample
+	   rate otherwise you get some very strange effects. */
+	if (idx < ARRAY_SIZE(freqs))
+		ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
+	return 0;
+}
+
+static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
+{
+	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+	itv->dualwatch_stereo_mode = val;
+	return 0;
+}
+
+struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+	.s_audio_mode = ivtv_s_audio_mode,
+	.s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
+	.s_video_encoding = ivtv_s_video_encoding,
+	.s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
+};
+
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+		*pts = (s64)((u64)itv->last_dec_timing[2] << 32) |
+			(u64)itv->last_dec_timing[1];
+		*frame = itv->last_dec_timing[0];
+		return 0;
+	}
+	*pts = 0;
+	*frame = 0;
+	if (atomic_read(&itv->decoding)) {
+		if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+			IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+			return -EIO;
+		}
+		memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+		set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+		*pts = (s64)((u64) data[2] << 32) | (u64) data[1];
+		*frame = data[0];
+		/*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+	}
+	return 0;
+}
+
+static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+	switch (ctrl->id) {
+	/* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME
+	   control cluster */
+	case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+		return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64,
+					     &itv->ctrl_frame->val64);
+	}
+	return 0;
+}
+
+static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+	switch (ctrl->id) {
+	/* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK
+	   control cluster */
+	case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+		itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1;
+		itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1;
+		ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+		break;
+	}
+	return 0;
+}
+
+const struct v4l2_ctrl_ops ivtv_hdl_out_ops = {
+	.s_ctrl = ivtv_s_ctrl,
+	.g_volatile_ctrl = ivtv_g_volatile_ctrl,
+};
diff --git a/drivers/media/pci/ivtv/ivtv-controls.h b/drivers/media/pci/ivtv/ivtv-controls.h
new file mode 100644
index 000000000000..3999e6358312
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-controls.h
@@ -0,0 +1,28 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_CONTROLS_H
+#define IVTV_CONTROLS_H
+
+extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
+extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops;
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
new file mode 100644
index 000000000000..74e9a5032364
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -0,0 +1,1536 @@
+/*
+    ivtv driver initialization and card probing
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ *                using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ *                version by T.Adachi. Special thanks  Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-controls.h"
+#include "ivtv-gpio.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+#include <media/saa7115.h>
+#include <media/v4l2-chip-ident.h>
+#include "tuner-xc2028.h"
+
+/* If you have already X v4l cards, then set this to X. This way
+   the device numbers stay matched. Example: you have a WinTV card
+   without radio and a PVR-350 with. Normally this would give a
+   video1 device together with a radio0 device for the PVR. By
+   setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor;
+
+/* Callback for registering extensions */
+int (*ivtv_ext_init)(struct ivtv *);
+EXPORT_SYMBOL(ivtv_ext_init);
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1 };
+static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+					       -1, -1, -1, -1, -1, -1, -1, -1,
+					       -1, -1, -1, -1, -1, -1, -1, -1,
+					       -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static unsigned int cardtype_c = 1;
+static unsigned int tuner_c = 1;
+static int radio_c = 1;
+static unsigned int i2c_clock_period_c = 1;
+static char pal[] = "---";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+/* Exception: size in kB for this stream (MB is overkill) */
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 320
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+/* Exception: size in kB for this stream (MB is way overkill) */
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 64
+
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode;
+static int ivtv_yuv_threshold = -1;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int ivtv_fw_debug;
+#endif
+
+static int tunertype = -1;
+static int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+module_param_named(fw_debug, ivtv_fw_debug, int, 0644);
+#endif
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(tunertype, int, 0644);
+module_param(newi2c, int, 0644);
+module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+			"\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+		 "Enable or disable the radio. Use only if autodetection\n"
+		 "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+		 "Only use this option if your card is not detected properly.\n"
+		 "\t\tSpecify card type:\n"
+		 "\t\t\t 1 = WinTV PVR 250\n"
+		 "\t\t\t 2 = WinTV PVR 350\n"
+		 "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+		 "\t\t\t 4 = AVerMedia M179\n"
+		 "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+		 "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+		 "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+		 "\t\t\t 8 = Adaptec AVC-2410\n"
+		 "\t\t\t 9 = Adaptec AVC-2010\n"
+		 "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+		 "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+		 "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+		 "\t\t\t13 = I/O Data GV-MVP/RX\n"
+		 "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+		 "\t\t\t15 = GOTVIEW PCI DVD\n"
+		 "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+		 "\t\t\t17 = Yuan MPC622\n"
+		 "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+		 "\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\t25 = AverMedia M104 (not yet working)\n"
+		 "\t\t\t26 = Buffalo PC-MV5L/PCI\n"
+		 "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n"
+		 "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n"
+		 "\t\t\t 0 = Autodetect (default)\n"
+		 "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)");
+MODULE_PARM_DESC(tunertype,
+		"Specify tuner type:\n"
+		"\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n"
+		"\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n"
+		"\t\t\t-1 = Autodetect (default)\n");
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: 0\n"
+		 "\t\t\t   1/0x0001: warning\n"
+		 "\t\t\t   2/0x0002: info\n"
+		 "\t\t\t   4/0x0004: mailbox\n"
+		 "\t\t\t   8/0x0008: ioctl\n"
+		 "\t\t\t  16/0x0010: file\n"
+		 "\t\t\t  32/0x0020: dma\n"
+		 "\t\t\t  64/0x0040: irq\n"
+		 "\t\t\t 128/0x0080: decoder\n"
+		 "\t\t\t 256/0x0100: yuv\n"
+		 "\t\t\t 512/0x0200: i2c\n"
+		 "\t\t\t1024/0x0400: high volume\n");
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+MODULE_PARM_DESC(fw_debug,
+		 "Enable code for debugging firmware problems.  Default: 0\n");
+#endif
+MODULE_PARM_DESC(ivtv_pci_latency,
+		 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+		 "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+		 "Specify the yuv playback mode:\n"
+		 "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+		 "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+		 "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+		 "\t\t\tDefault: 480");
+MODULE_PARM_DESC(enc_mpg_buffers,
+		 "Encoder MPG Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+		 "Encoder YUV Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+		 "Encoder VBI Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+		 "Encoder PCM buffers (in kB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+		 "Decoder MPG buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+		 "Decoder YUV buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+		 "Decoder VBI buffers (in kB)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+		 "Use new I2C implementation\n"
+		 "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+		 "\t\t\tDefault is autodetect");
+MODULE_PARM_DESC(i2c_clock_period,
+		 "Period of SCL for the I2C bus controlled by the CX23415/6\n"
+		 "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
+		 "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+    ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+		"\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	struct ivtv *dev = container_of(work, struct ivtv, request_module_wk);
+
+	/* Make sure ivtv-alsa module is loaded */
+	request_module("ivtv-alsa");
+
+	/* Initialize ivtv-alsa for this instance of the cx18 device */
+	if (ivtv_ext_init != NULL)
+		ivtv_ext_init(dev);
+}
+
+static void request_modules(struct ivtv *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct ivtv *dev)
+{
+	flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+	itv->irqmask &= ~mask;
+	write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+	itv->irqmask |= mask;
+	write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+    int old_mode;
+
+    spin_lock(&itv->lock);
+    old_mode = itv->output_mode;
+    if (old_mode == 0)
+	itv->output_mode = old_mode = mode;
+    spin_unlock(&itv->lock);
+    return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+	switch (itv->output_mode) {
+	case OUT_MPG:
+		return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+	case OUT_YUV:
+		return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+	default:
+		return NULL;
+	}
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+	DEFINE_WAIT(wait);
+
+	prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+	schedule();
+	finish_wait(waitq, &wait);
+	return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_msleep_timeout(unsigned int msecs, int intr)
+{
+	int timeout = msecs_to_jiffies(msecs);
+
+	do {
+		set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		timeout = schedule_timeout(timeout);
+		if (intr) {
+			int ret = signal_pending(current);
+
+			if (ret)
+				return ret;
+		}
+	} while (timeout);
+	return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+	if (itv == NULL)
+		return;
+
+	/* Release registers memory */
+	if (itv->reg_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing reg_mem\n");
+		iounmap(itv->reg_mem);
+		itv->reg_mem = NULL;
+	}
+	/* Release io memory */
+	if (itv->has_cx23415 && itv->dec_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing dec_mem\n");
+		iounmap(itv->dec_mem);
+	}
+	itv->dec_mem = NULL;
+
+	/* Release io memory */
+	if (itv->enc_mem != NULL) {
+		IVTV_DEBUG_INFO("releasing enc_mem\n");
+		iounmap(itv->enc_mem);
+		itv->enc_mem = NULL;
+	}
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+	u8 eedata[256];
+
+	itv->i2c_client.addr = 0xA0 >> 1;
+	tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+	tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+	struct tveeprom tv;
+	int pci_slot = PCI_SLOT(itv->pdev->devfn);
+
+	ivtv_read_eeprom(itv, &tv);
+
+	/* Many thanks to Steven Toth from Hauppauge for providing the
+	   model numbers */
+	switch (tv.model) {
+		/* In a few cases the PCI subsystem IDs do not correctly
+		   identify the card. A better method is to check the
+		   model number from the eeprom instead. */
+		case 30012 ... 30039:  /* Low profile PVR250 */
+		case 32000 ... 32999:
+		case 48000 ... 48099:  /* 48??? range are PVR250s with a cx23415 */
+		case 48400 ... 48599:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+			break;
+		case 48100 ... 48399:
+		case 48600 ... 48999:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+			break;
+		case 23000 ... 23999:  /* PVR500 */
+		case 25000 ... 25999:  /* Low profile PVR150 */
+		case 26000 ... 26999:  /* Regular PVR150 */
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+			break;
+		case 0:
+			IVTV_ERR("Invalid EEPROM\n");
+			return;
+		default:
+			IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+			break;
+	}
+
+	switch (tv.model) {
+		/* Old style PVR350 (with an saa7114) uses this input for
+		   the tuner. */
+		case 48254:
+			itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+			break;
+		default:
+			break;
+	}
+
+	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
+	   set, then it is the second unit (according to info from Hauppauge).
+
+	   However, while this works for most cards, I have seen a few PVR500 cards
+	   where both units have the same subsystem ID.
+
+	   So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+	   PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+	   it is the second unit. It is possible that it is a different slot when ivtv is
+	   used in Xen, in that case I ignore this card here. The worst that can happen
+	   is that the card presents itself with a non-working radio device.
+
+	   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;
+
+			itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+						    "WinTV PVR 500 (unit #2)";
+			if (!is_first) {
+				IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+				tv.has_radio = 0;
+			}
+		}
+	}
+	IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+	switch (tv.tuner_hauppauge_model) {
+		case 85:
+		case 99:
+		case 112:
+			itv->pvr150_workaround = 1;
+			break;
+		default:
+			break;
+	}
+	if (tv.tuner_type == TUNER_ABSENT)
+		IVTV_ERR("tveeprom cannot autodetect tuner!\n");
+
+	if (itv->options.tuner == -1)
+		itv->options.tuner = tv.tuner_type;
+	if (itv->options.radio == -1)
+		itv->options.radio = (tv.has_radio != 0);
+	/* only enable newi2c if an IR blaster is present */
+	if (itv->options.newi2c == -1 && tv.has_ir) {
+		itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0;
+		if (itv->options.newi2c) {
+		    IVTV_INFO("Reopen i2c bus for IR-blaster support\n");
+		    exit_ivtv_i2c(itv);
+		    init_ivtv_i2c(itv);
+		}
+	}
+
+	if (itv->std != 0)
+		/* user specified tuner standard */
+		return;
+
+	/* autodetect tuner standard */
+	if (tv.tuner_formats & V4L2_STD_PAL) {
+		IVTV_DEBUG_INFO("PAL tuner detected\n");
+		itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+	} else if (tv.tuner_formats & V4L2_STD_NTSC) {
+		IVTV_DEBUG_INFO("NTSC tuner detected\n");
+		itv->std |= V4L2_STD_NTSC_M;
+	} else if (tv.tuner_formats & V4L2_STD_SECAM) {
+		IVTV_DEBUG_INFO("SECAM tuner detected\n");
+		itv->std |= V4L2_STD_SECAM_L;
+	} else {
+		IVTV_INFO("No tuner detected, default to NTSC-M\n");
+		itv->std |= V4L2_STD_NTSC_M;
+	}
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+	switch (pal[0]) {
+		case '6':
+			tunertype = 0;
+			return V4L2_STD_PAL_60;
+		case 'b':
+		case 'B':
+		case 'g':
+		case 'G':
+		case 'h':
+		case 'H':
+			tunertype = 0;
+			return V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+		case 'n':
+		case 'N':
+			tunertype = 1;
+			if (pal[1] == 'c' || pal[1] == 'C')
+				return V4L2_STD_PAL_Nc;
+			return V4L2_STD_PAL_N;
+		case 'i':
+		case 'I':
+			tunertype = 0;
+			return V4L2_STD_PAL_I;
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			tunertype = 0;
+			return V4L2_STD_PAL_DK;
+		case 'M':
+		case 'm':
+			tunertype = 1;
+			return V4L2_STD_PAL_M;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("pal= argument not recognised\n");
+			return 0;
+	}
+
+	switch (secam[0]) {
+		case 'b':
+		case 'B':
+		case 'g':
+		case 'G':
+		case 'h':
+		case 'H':
+			tunertype = 0;
+			return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			tunertype = 0;
+			return V4L2_STD_SECAM_DK;
+		case 'l':
+		case 'L':
+			tunertype = 0;
+			if (secam[1] == 'C' || secam[1] == 'c')
+				return V4L2_STD_SECAM_LC;
+			return V4L2_STD_SECAM_L;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("secam= argument not recognised\n");
+			return 0;
+	}
+
+	switch (ntsc[0]) {
+		case 'm':
+		case 'M':
+			tunertype = 1;
+			return V4L2_STD_NTSC_M;
+		case 'j':
+		case 'J':
+			tunertype = 1;
+			return V4L2_STD_NTSC_M_JP;
+		case 'k':
+		case 'K':
+			tunertype = 1;
+			return V4L2_STD_NTSC_M_KR;
+		case '-':
+			break;
+		default:
+			IVTV_WARN("ntsc= argument not recognised\n");
+			return 0;
+	}
+
+	/* no match found */
+	return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+	const char *chipname;
+	int i, j;
+
+	itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers * 1024;
+	itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers * 1024;
+	itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers * 1024;
+	itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+	itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
+	itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
+	itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+	itv->options.cardtype = cardtype[itv->instance];
+	itv->options.tuner = tuner[itv->instance];
+	itv->options.radio = radio[itv->instance];
+
+	itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
+	if (itv->options.i2c_clock_period == -1)
+		itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
+	else if (itv->options.i2c_clock_period < 10)
+		itv->options.i2c_clock_period = 10;
+	else if (itv->options.i2c_clock_period > 4500)
+		itv->options.i2c_clock_period = 4500;
+
+	itv->options.newi2c = newi2c;
+	if (tunertype < -1 || tunertype > 1) {
+		IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
+		tunertype = -1;
+	}
+	itv->std = ivtv_parse_std(itv);
+	if (itv->std == 0 && tunertype >= 0)
+		itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN);
+	itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15);
+	chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+	if (itv->options.cardtype == -1) {
+		IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+		return;
+	}
+	if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+		IVTV_INFO("User specified %s card (detected %s based chip)\n",
+				itv->card->name, chipname);
+	} else if (itv->options.cardtype != 0) {
+		IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+	}
+	if (itv->card == NULL) {
+		if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+		    itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+		    itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+			itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+			IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+					chipname);
+		}
+	}
+	if (itv->card == NULL) {
+		for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+			if (itv->card->pci_list == NULL)
+				continue;
+			for (j = 0; itv->card->pci_list[j].device; j++) {
+				if (itv->pdev->device !=
+				    itv->card->pci_list[j].device)
+					continue;
+				if (itv->pdev->subsystem_vendor !=
+				    itv->card->pci_list[j].subsystem_vendor)
+					continue;
+				if (itv->pdev->subsystem_device !=
+				    itv->card->pci_list[j].subsystem_device)
+					continue;
+				IVTV_INFO("Autodetected %s card (%s based)\n",
+						itv->card->name, chipname);
+				goto done;
+			}
+		}
+	}
+done:
+
+	if (itv->card == NULL) {
+		itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+		IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+		     itv->pdev->vendor, itv->pdev->device);
+		IVTV_ERR("              subsystem vendor/device: [%04x:%04x]\n",
+		     itv->pdev->subsystem_vendor, itv->pdev->subsystem_device);
+		IVTV_ERR("              %s based\n", chipname);
+		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 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
+   the dev and num fields have been filled in.
+   No assumptions on the card type may be made here (see ivtv_init_struct2
+   for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+	struct sched_param param = { .sched_priority = 99 };
+
+	itv->base_addr = pci_resource_start(itv->pdev, 0);
+	itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+	itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+	mutex_init(&itv->serialize_lock);
+	mutex_init(&itv->i2c_bus_lock);
+	mutex_init(&itv->udma.lock);
+
+	spin_lock_init(&itv->lock);
+	spin_lock_init(&itv->dma_reg_lock);
+
+	init_kthread_worker(&itv->irq_worker);
+	itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker,
+					   itv->v4l2_dev.name);
+	if (IS_ERR(itv->irq_worker_task)) {
+		IVTV_ERR("Could not create ivtv task\n");
+		return -1;
+	}
+	/* must use the FIFO scheduler as it is realtime sensitive */
+	sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, &param);
+
+	init_kthread_work(&itv->irq_work, ivtv_irq_work_handler);
+
+	/* Initial settings */
+	itv->cxhdl.port = CX2341X_PORT_MEMORY;
+	itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+	init_waitqueue_head(&itv->eos_waitq);
+	init_waitqueue_head(&itv->event_waitq);
+	init_waitqueue_head(&itv->vsync_waitq);
+	init_waitqueue_head(&itv->dma_waitq);
+	init_timer(&itv->dma_timer);
+	itv->dma_timer.function = ivtv_unfinished_dma;
+	itv->dma_timer.data = (unsigned long)itv;
+
+	itv->cur_dma_stream = -1;
+	itv->cur_pio_stream = -1;
+
+	/* Ctrls */
+	itv->speed = 1000;
+
+	/* VBI */
+	itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+	/* Init the sg table for osd/yuv output */
+	sg_init_table(itv->udma.SGlist, IVTV_DMA_SG_OSD_ENT);
+
+	/* OSD */
+	itv->osd_global_alpha_state = 1;
+	itv->osd_global_alpha = 255;
+
+	/* YUV */
+	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;
+	itv->yuv_info.track_osd = 1;
+	return 0;
+}
+
+/* Second initialization part. Here the card type has been
+   autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+	int i;
+
+	for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+		if (itv->card->video_inputs[i].video_type == 0)
+			break;
+	itv->nof_inputs = i;
+	for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+		if (itv->card->audio_inputs[i].audio_type == 0)
+			break;
+	itv->nof_audio_inputs = i;
+
+	if (itv->card->hw_all & IVTV_HW_CX25840) {
+		itv->vbi.sliced_size = 288;  /* multiple of 16, real size = 284 */
+	} else {
+		itv->vbi.sliced_size = 64;   /* multiple of 16, real size = 52 */
+	}
+
+	/* Find tuner input */
+	for (i = 0; i < itv->nof_inputs; i++) {
+		if (itv->card->video_inputs[i].video_type ==
+				IVTV_CARD_INPUT_VID_TUNER)
+			break;
+	}
+	if (i == itv->nof_inputs)
+		i = 0;
+	itv->active_input = i;
+	itv->audio_input = itv->card->video_inputs[i].audio_index;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
+			  const struct pci_device_id *pci_id)
+{
+	u16 cmd;
+	unsigned char pci_latency;
+
+	IVTV_DEBUG_INFO("Enabling pci device\n");
+
+	if (pci_enable_device(pdev)) {
+		IVTV_ERR("Can't enable device!\n");
+		return -EIO;
+	}
+	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+		IVTV_ERR("No suitable DMA available.\n");
+		return -EIO;
+	}
+	if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+		IVTV_ERR("Cannot request encoder memory region.\n");
+		return -EIO;
+	}
+
+	if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+				IVTV_REG_SIZE, "ivtv registers")) {
+		IVTV_ERR("Cannot request register memory region.\n");
+		release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+		return -EIO;
+	}
+
+	if (itv->has_cx23415 &&
+	    !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+				IVTV_DECODER_SIZE, "ivtv decoder")) {
+		IVTV_ERR("Cannot request decoder memory region.\n");
+		release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+		release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+		return -EIO;
+	}
+
+	/* Check for bus mastering */
+	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+	if (!(cmd & PCI_COMMAND_MASTER)) {
+		IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+		pci_set_master(pdev);
+		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+		if (!(cmd & PCI_COMMAND_MASTER)) {
+			IVTV_ERR("Bus Mastering is not enabled\n");
+			return -ENXIO;
+		}
+	}
+	IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+
+	if (pci_latency < 64 && ivtv_pci_latency) {
+		IVTV_INFO("Unreasonably low latency timer, "
+			       "setting to 64 (was %d)\n", pci_latency);
+		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+		pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+	}
+	/* This config space value relates to DMA latencies. The
+	   default value 0x8080 is too low however and will lead
+	   to DMA errors. 0xffff is the max value which solves
+	   these problems. */
+	pci_write_config_dword(pdev, 0x40, 0xffff);
+
+	IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+		   "irq: %d, latency: %d, memory: 0x%llx\n",
+		   pdev->device, pdev->revision, pdev->bus->number,
+		   PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+		   pdev->irq, pci_latency, (u64)itv->base_addr);
+
+	return 0;
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+	u32 hw = itv->card->hw_all;
+	unsigned i;
+
+	/* check which i2c devices are actually found */
+	for (i = 0; i < 32; i++) {
+		u32 device = 1 << i;
+
+		if (!(device & hw))
+			continue;
+		if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) {
+			/* GPIO and TVEEPROM do not use i2c probing */
+			itv->hw_flags |= device;
+			continue;
+		}
+		if (ivtv_i2c_register(itv, i) == 0)
+			itv->hw_flags |= device;
+	}
+
+	/* probe for legacy IR controllers that aren't in card definitions */
+	if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
+		ivtv_i2c_new_ir_legacy(itv);
+
+	if (itv->card->hw_all & IVTV_HW_CX25840)
+		itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+	else if (itv->card->hw_all & IVTV_HW_SAA717X)
+		itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+	else if (itv->card->hw_all & IVTV_HW_SAA7114)
+		itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+	else
+		itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+	itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+	itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
+	hw = itv->hw_flags;
+
+	if (itv->card->type == IVTV_CARD_CX23416GYC) {
+		/* Several variations of this card exist, detect which card
+		   type should be used. */
+		if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+			itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+		else if ((hw & IVTV_HW_UPD64031A) == 0)
+			itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+	}
+	else if (itv->card->type == IVTV_CARD_GV_MVPRX ||
+		 itv->card->type == IVTV_CARD_GV_MVPRX2E) {
+		/* The crystal frequency of GVMVPRX is 24.576MHz */
+		v4l2_subdev_call(itv->sd_video, video, s_crystal_freq,
+			SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC);
+	}
+
+	if (hw & IVTV_HW_CX25840) {
+		itv->vbi.raw_decoder_line_size = 1444;
+		itv->vbi.raw_decoder_sav_odd_field = 0x20;
+		itv->vbi.raw_decoder_sav_even_field = 0x60;
+		itv->vbi.sliced_decoder_line_size = 272;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+		itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+	}
+
+	if (hw & IVTV_HW_SAA711X) {
+		struct v4l2_dbg_chip_ident v;
+
+		/* determine the exact saa711x model */
+		itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+		v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;
+		strlcpy(v.match.name, "saa7115", sizeof(v.match.name));
+		ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
+		if (v.ident == V4L2_IDENT_SAA7114) {
+			itv->hw_flags |= IVTV_HW_SAA7114;
+			/* VBI is not yet supported by the saa7114 driver. */
+			itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+		} else {
+			itv->hw_flags |= IVTV_HW_SAA7115;
+		}
+		itv->vbi.raw_decoder_line_size = 1443;
+		itv->vbi.raw_decoder_sav_odd_field = 0x25;
+		itv->vbi.raw_decoder_sav_even_field = 0x62;
+		itv->vbi.sliced_decoder_line_size = 51;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+		itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+	}
+
+	if (hw & IVTV_HW_SAA717X) {
+		itv->vbi.raw_decoder_line_size = 1443;
+		itv->vbi.raw_decoder_sav_odd_field = 0x25;
+		itv->vbi.raw_decoder_sav_even_field = 0x62;
+		itv->vbi.sliced_decoder_line_size = 51;
+		itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+		itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+	}
+}
+
+static int __devinit ivtv_probe(struct pci_dev *pdev,
+				const struct pci_device_id *pci_id)
+{
+	int retval = 0;
+	int vbi_buf_size;
+	struct ivtv *itv;
+
+	itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+	if (itv == NULL)
+		return -ENOMEM;
+	itv->pdev = pdev;
+	itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv",
+						&ivtv_instance);
+
+	retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev);
+	if (retval) {
+		kfree(itv);
+		return retval;
+	}
+	IVTV_INFO("Initializing card %d\n", itv->instance);
+
+	ivtv_process_options(itv);
+	if (itv->options.cardtype == -1) {
+		retval = -ENODEV;
+		goto err;
+	}
+	if (ivtv_init_struct1(itv)) {
+		retval = -ENOMEM;
+		goto err;
+	}
+	retval = cx2341x_handler_init(&itv->cxhdl, 50);
+	if (retval)
+		goto err;
+	itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+	itv->cxhdl.ops = &ivtv_cxhdl_ops;
+	itv->cxhdl.priv = itv;
+	itv->cxhdl.func = ivtv_api_func;
+
+	IVTV_DEBUG_INFO("base addr: 0x%llx\n", (u64)itv->base_addr);
+
+	/* PCI Device Setup */
+	retval = ivtv_setup_pci(itv, pdev, pci_id);
+	if (retval == -EIO)
+		goto free_worker;
+	if (retval == -ENXIO)
+		goto free_mem;
+
+	/* map io memory */
+	IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+		   (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+	itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+				       IVTV_ENCODER_SIZE);
+	if (!itv->enc_mem) {
+		IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+			 "encoder memory\n");
+		IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of "
+			 "vmalloc address space for this window\n");
+		IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+		IVTV_ERR("Use the vmalloc= kernel command line option to set "
+			 "VmallocTotal to a larger value\n");
+		retval = -ENOMEM;
+		goto free_mem;
+	}
+
+	if (itv->has_cx23415) {
+		IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+				(u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+		itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+				IVTV_DECODER_SIZE);
+		if (!itv->dec_mem) {
+			IVTV_ERR("ioremap failed. Can't get a window into "
+				 "CX23415 decoder memory\n");
+			IVTV_ERR("Each capture card with a CX23415 needs 8 MB "
+				 "of vmalloc address space for this window\n");
+			IVTV_ERR("Check the output of 'grep Vmalloc "
+				 "/proc/meminfo'\n");
+			IVTV_ERR("Use the vmalloc= kernel command line option "
+				 "to set VmallocTotal to a larger value\n");
+			retval = -ENOMEM;
+			goto free_mem;
+		}
+	}
+	else {
+		itv->dec_mem = itv->enc_mem;
+	}
+
+	/* map registers memory */
+	IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+		   (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	itv->reg_mem =
+	    ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (!itv->reg_mem) {
+		IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+			 "register space\n");
+		IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of "
+			 "vmalloc address space for this window\n");
+		IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+		IVTV_ERR("Use the vmalloc= kernel command line option to set "
+			 "VmallocTotal to a larger value\n");
+		retval = -ENOMEM;
+		goto free_io;
+	}
+
+	retval = ivtv_gpio_init(itv);
+	if (retval)
+		goto free_io;
+
+	/* active i2c  */
+	IVTV_DEBUG_INFO("activating i2c...\n");
+	if (init_ivtv_i2c(itv)) {
+		IVTV_ERR("Could not initialize i2c\n");
+		goto free_io;
+	}
+
+	if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+		/* Based on the model number the cardtype may be changed.
+		   The PCI IDs are not always reliable. */
+		ivtv_process_eeprom(itv);
+	}
+	if (itv->card->comment)
+		IVTV_INFO("%s", itv->card->comment);
+	if (itv->card->v4l2_capabilities == 0) {
+		/* card was detected but is not supported */
+		retval = -ENODEV;
+		goto free_i2c;
+	}
+
+	if (itv->std == 0) {
+		itv->std = V4L2_STD_NTSC_M;
+	}
+
+	if (itv->options.tuner == -1) {
+		int i;
+
+		for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+			if ((itv->std & itv->card->tuners[i].std) == 0)
+				continue;
+			itv->options.tuner = itv->card->tuners[i].tuner;
+			break;
+		}
+	}
+	/* if no tuner was found, then pick the first tuner in the card list */
+	if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+		itv->std = itv->card->tuners[0].std;
+		if (itv->std & V4L2_STD_PAL)
+			itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+		else if (itv->std & V4L2_STD_NTSC)
+			itv->std = V4L2_STD_NTSC_M;
+		else if (itv->std & V4L2_STD_SECAM)
+			itv->std = V4L2_STD_SECAM_L;
+		itv->options.tuner = itv->card->tuners[0].tuner;
+	}
+	if (itv->options.radio == -1)
+		itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+	/* The card is now fully identified, continue with card-specific
+	   initialization. */
+	ivtv_init_struct2(itv);
+
+	ivtv_load_and_init_modules(itv);
+
+	if (itv->std & V4L2_STD_525_60) {
+		itv->is_60hz = 1;
+		itv->is_out_60hz = 1;
+	} else {
+		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;
+
+	cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+
+	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;
+	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
+	   take the largest size here. */
+	/* 1456 is multiple of 16, real size = 1444 */
+	itv->vbi.raw_size = 1456;
+	/* We use a buffer size of 1/2 of the total size needed for a
+	   frame. This is actually very useful, since we now receive
+	   a field at a time and that makes 'compressing' the raw data
+	   down to size by stripping off the SAV codes a lot easier.
+	   Note: having two different buffer sizes prevents standard
+	   switching on the fly. We need to find a better solution... */
+	vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+	itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+	if (itv->options.radio > 0)
+		itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+	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 */
+		if (itv->options.radio > 0)
+			setup.mode_mask |= T_RADIO;
+		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+			ivtv_reset_tuner_gpio : NULL;
+		ivtv_call_all(itv, tuner, s_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_all(itv, tuner, s_config, &cfg);
+		}
+	}
+
+	/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+	   are not. */
+	itv->tuner_std = itv->std;
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
+
+		itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+				V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0);
+		itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+				V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0);
+		/* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
+		   mask that menu item. */
+		itv->ctrl_audio_playback =
+			v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+				V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
+				V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+				1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+				V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
+		itv->ctrl_audio_multilingual_playback =
+			v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+				V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
+				V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+				1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+				V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
+		if (hdl->error) {
+			retval = hdl->error;
+			goto free_i2c;
+		}
+		v4l2_ctrl_cluster(2, &itv->ctrl_pts);
+		v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
+		ivtv_call_all(itv, video, s_std_output, itv->std);
+		/* Turn off the output signal. The mpeg decoder is not yet
+		   active so without this you would get a green image until the
+		   mpeg decoder becomes active. */
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+	}
+
+	/* clear interrupt mask, effectively disabling interrupts */
+	ivtv_set_irq_mask(itv, 0xffffffff);
+
+	/* Register IRQ */
+	retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
+	     IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
+	if (retval) {
+		IVTV_ERR("Failed to register irq %d\n", retval);
+		goto free_i2c;
+	}
+
+	retval = ivtv_streams_setup(itv);
+	if (retval) {
+		IVTV_ERR("Error %d setting up streams\n", retval);
+		goto free_irq;
+	}
+	retval = ivtv_streams_register(itv);
+	if (retval) {
+		IVTV_ERR("Error %d registering devices\n", retval);
+		goto free_streams;
+	}
+	IVTV_INFO("Initialized card: %s\n", itv->card_name);
+
+	/* Load ivtv submodules (ivtv-alsa) */
+	request_modules(itv);
+	return 0;
+
+free_streams:
+	ivtv_streams_cleanup(itv, 1);
+free_irq:
+	free_irq(itv->pdev->irq, (void *)itv);
+free_i2c:
+	v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+	exit_ivtv_i2c(itv);
+free_io:
+	ivtv_iounmap(itv);
+free_mem:
+	release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+	release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (itv->has_cx23415)
+		release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+free_worker:
+	kthread_stop(itv->irq_worker_task);
+err:
+	if (retval == 0)
+		retval = -ENODEV;
+	IVTV_ERR("Error %d on initialization\n", retval);
+
+	v4l2_device_unregister(&itv->v4l2_dev);
+	kfree(itv);
+	return retval;
+}
+
+int ivtv_init_on_first_open(struct ivtv *itv)
+{
+	struct v4l2_frequency vf;
+	/* Needed to call ioctls later */
+	struct ivtv_open_id fh;
+	int fw_retry_count = 3;
+	int video_input;
+
+	fh.itv = itv;
+	fh.type = IVTV_ENC_STREAM_TYPE_MPG;
+
+	if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
+		return -ENXIO;
+
+	if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags))
+		return 0;
+
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (ivtv_firmware_init(itv) == 0)
+			break;
+		if (fw_retry_count > 1)
+			IVTV_WARN("Retry loading firmware\n");
+	}
+
+	if (fw_retry_count == 0) {
+		set_bit(IVTV_F_I_FAILED, &itv->i_flags);
+		return -ENXIO;
+	}
+
+	/* Try and get firmware versions */
+	IVTV_DEBUG_INFO("Getting firmware version..\n");
+	ivtv_firmware_versions(itv);
+
+	if (itv->card->hw_all & IVTV_HW_CX25840)
+		v4l2_subdev_call(itv->sd_video, core, load_fw);
+
+	vf.tuner = 0;
+	vf.type = V4L2_TUNER_ANALOG_TV;
+	vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+	/* Set initial frequency. For PAL/SECAM broadcasts no
+	   'default' channel exists AFAIK. */
+	if (itv->std == V4L2_STD_NTSC_M_JP) {
+		vf.frequency = 1460;	/* ch. 1 91250*16/1000 */
+	}
+	else if (itv->std & V4L2_STD_NTSC_M) {
+		vf.frequency = 1076;	/* ch. 4 67250*16/1000 */
+	}
+
+	video_input = itv->active_input;
+	itv->active_input++;	/* Force update of input */
+	ivtv_s_input(NULL, &fh, video_input);
+
+	/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+	   in one place. */
+	itv->std++;		/* Force full standard initialization */
+	itv->std_out = itv->std;
+	ivtv_s_frequency(NULL, &fh, &vf);
+
+	if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+		/* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
+		   the mpeg decoder so now the saa7127 receives a proper
+		   signal. */
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+		ivtv_init_mpeg_decoder(itv);
+	}
+
+	/* On a cx23416 this seems to be able to enable DMA to the chip? */
+	if (!itv->has_cx23415)
+		write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+	ivtv_s_std_enc(itv, &itv->tuner_std);
+
+	/* Default interrupts enabled. For the PVR350 this includes the
+	   decoder VSYNC interrupt, which is always on. It is not only used
+	   during decoding but also by the OSD.
+	   Some old PVR250 cards had a cx23415, so testing for that is too
+	   general. Instead test if the card has video output capability. */
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+		ivtv_set_osd_alpha(itv);
+		ivtv_s_std_dec(itv, &itv->tuner_std);
+	} else {
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+	}
+
+	/* Setup initial controls */
+	cx2341x_handler_setup(&itv->cxhdl);
+	return 0;
+}
+
+static void ivtv_remove(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	int i;
+
+	IVTV_DEBUG_INFO("Removing card\n");
+
+	flush_request_modules(itv);
+
+	if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
+		/* Stop all captures */
+		IVTV_DEBUG_INFO("Stopping all streams\n");
+		if (atomic_read(&itv->capturing) > 0)
+			ivtv_stop_all_captures(itv);
+
+		/* Stop all decoding */
+		IVTV_DEBUG_INFO("Stopping decoding\n");
+
+		/* Turn off the TV-out */
+		if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+			ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+		if (atomic_read(&itv->decoding) > 0) {
+			int type;
+
+			if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+				type = IVTV_DEC_STREAM_TYPE_YUV;
+			else
+				type = IVTV_DEC_STREAM_TYPE_MPG;
+			ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+				V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+		}
+		ivtv_halt_firmware(itv);
+	}
+
+	/* Interrupts */
+	ivtv_set_irq_mask(itv, 0xffffffff);
+	del_timer_sync(&itv->dma_timer);
+
+	/* Kill irq worker */
+	flush_kthread_worker(&itv->irq_worker);
+	kthread_stop(itv->irq_worker_task);
+
+	ivtv_streams_cleanup(itv, 1);
+	ivtv_udma_free(itv);
+
+	v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+
+	exit_ivtv_i2c(itv);
+
+	free_irq(itv->pdev->irq, (void *)itv);
+	ivtv_iounmap(itv);
+
+	release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+	release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (itv->has_cx23415)
+		release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+	pci_disable_device(itv->pdev);
+	for (i = 0; i < IVTV_VBI_FRAMES; i++)
+		kfree(itv->vbi.sliced_mpeg_data[i]);
+
+	printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+
+	v4l2_device_unregister(&itv->v4l2_dev);
+	kfree(itv);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+      .name =     "ivtv",
+      .id_table = ivtv_pci_tbl,
+      .probe =    ivtv_probe,
+      .remove =   ivtv_remove,
+};
+
+static int __init module_start(void)
+{
+	printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
+
+	/* Validate parameters */
+	if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+		printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+		     IVTV_MAX_CARDS - 1);
+		return -1;
+	}
+
+	if (ivtv_debug < 0 || ivtv_debug > 2047) {
+		ivtv_debug = 0;
+		printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+	}
+
+	if (pci_register_driver(&ivtv_pci_driver)) {
+		printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "ivtv: End initialization\n");
+	return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+	pci_unregister_driver(&ivtv_pci_driver);
+}
+
+/* Note: These symbols are exported because they are used by the ivtvfb
+   framebuffer module and an infrared module for the IR-blaster. */
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+EXPORT_SYMBOL(ivtv_fw_debug);
+#endif
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+EXPORT_SYMBOL(ivtv_init_on_first_open);
+EXPORT_SYMBOL(ivtv_firmware_check);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h
new file mode 100644
index 000000000000..bc309f42c8ed
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-driver.h
@@ -0,0 +1,850 @@
+/*
+    ivtv driver internal defines and structures
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
+
+#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 */
+#define IVTV_DECODER_OFFSET	0x01000000
+#define IVTV_DECODER_SIZE	0x00800000	/* Total size is 0x01000000, but only first half is used */
+#define IVTV_REG_OFFSET 	0x02000000
+#define IVTV_REG_SIZE		0x00010000
+
+/* Maximum ivtv driver instances. Some people have a huge number of
+   capture cards, so set this to a high value. */
+#define IVTV_MAX_CARDS 32
+
+#define IVTV_ENC_STREAM_TYPE_MPG  0
+#define IVTV_ENC_STREAM_TYPE_YUV  1
+#define IVTV_ENC_STREAM_TYPE_VBI  2
+#define IVTV_ENC_STREAM_TYPE_PCM  3
+#define IVTV_ENC_STREAM_TYPE_RAD  4
+#define IVTV_DEC_STREAM_TYPE_MPG  5
+#define IVTV_DEC_STREAM_TYPE_VBI  6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV  8
+#define IVTV_MAX_STREAMS	  9
+
+#define IVTV_DMA_SG_OSD_ENT	(2883584/PAGE_SIZE)	/* sg entities */
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER 	(0x0000)
+#define IVTV_REG_DMASTATUS 	(0x0004)
+#define IVTV_REG_DECDMAADDR 	(0x0008)
+#define IVTV_REG_ENCDMAADDR 	(0x000c)
+#define IVTV_REG_DMACONTROL 	(0x0010)
+#define IVTV_REG_IRQSTATUS 	(0x0040)
+#define IVTV_REG_IRQMASK 	(0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH 	(0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE 	(0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH 	(0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE 	(0x08FC)
+#define IVTV_REG_VDM 			(0x2800)
+#define IVTV_REG_AO 			(0x2D00)
+#define IVTV_REG_BYTEFLUSH 		(0x2D24)
+#define IVTV_REG_SPU 			(0x9050)
+#define IVTV_REG_HW_BLOCKS 		(0x9054)
+#define IVTV_REG_VPU 			(0x9058)
+#define IVTV_REG_APU 			(0xA064)
+
+/* Other registers */
+#define IVTV_REG_DEC_LINE_FIELD		(0x28C0)
+
+/* debugging */
+extern int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+extern int ivtv_fw_debug;
+#endif
+
+#define IVTV_DBGFLG_WARN    (1 << 0)
+#define IVTV_DBGFLG_INFO    (1 << 1)
+#define IVTV_DBGFLG_MB      (1 << 2)
+#define IVTV_DBGFLG_IOCTL   (1 << 3)
+#define IVTV_DBGFLG_FILE    (1 << 4)
+#define IVTV_DBGFLG_DMA     (1 << 5)
+#define IVTV_DBGFLG_IRQ     (1 << 6)
+#define IVTV_DBGFLG_DEC     (1 << 7)
+#define IVTV_DBGFLG_YUV     (1 << 8)
+#define IVTV_DBGFLG_I2C     (1 << 9)
+/* Flag to turn on high volume debugging */
+#define IVTV_DBGFLG_HIGHVOL (1 << 10)
+
+#define IVTV_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & ivtv_debug) \
+			v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args);	\
+	} while (0)
+#define IVTV_DEBUG_WARN(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_WARN,  "warn",  fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_INFO,  "info",  fmt , ## args)
+#define IVTV_DEBUG_MB(fmt, args...)    IVTV_DEBUG(IVTV_DBGFLG_MB,    "mb",    fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DMA,   "dma",   fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_FILE(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_FILE,  "file",  fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_I2C,   "i2c",   fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_IRQ,   "irq",   fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DEC,   "dec",   fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_YUV,   "yuv",   fmt , ## args)
+
+#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+	do { \
+		if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) 	\
+			v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args);	\
+	} while (0)
+#define IVTV_DEBUG_HI_WARN(fmt, args...)  IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN,  "warn",  fmt , ## args)
+#define IVTV_DEBUG_HI_INFO(fmt, args...)  IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO,  "info",  fmt , ## args)
+#define IVTV_DEBUG_HI_MB(fmt, args...)    IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_MB,    "mb",    fmt , ## args)
+#define IVTV_DEBUG_HI_DMA(fmt, args...)   IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA,   "dma",   fmt , ## args)
+#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_HI_FILE(fmt, args...)  IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_FILE,  "file",  fmt , ## args)
+#define IVTV_DEBUG_HI_I2C(fmt, args...)   IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C,   "i2c",   fmt , ## args)
+#define IVTV_DEBUG_HI_IRQ(fmt, args...)   IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ,   "irq",   fmt , ## args)
+#define IVTV_DEBUG_HI_DEC(fmt, args...)   IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC,   "dec",   fmt , ## args)
+#define IVTV_DEBUG_HI_YUV(fmt, args...)   IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV,   "yuv",   fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...)      v4l2_err(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_WARN(fmt, args...)     v4l2_warn(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_INFO(fmt, args...)     v4l2_info(&itv->v4l2_dev, fmt , ## args)
+
+/* output modes (cx23415 only) */
+#define OUT_NONE        0
+#define OUT_MPG         1
+#define OUT_YUV         2
+#define OUT_UDMA_YUV    3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+/* Default I2C SCL period in microseconds */
+#define IVTV_DEFAULT_I2C_CLOCK_PERIOD	20
+
+struct ivtv_options {
+	int kilobytes[IVTV_MAX_STREAMS];        /* size in kilobytes of each stream */
+	int cardtype;				/* force card type on load */
+	int tuner;				/* set tuner on load */
+	int radio;				/* enable/disable radio */
+	int newi2c;				/* new I2C algorithm */
+	int i2c_clock_period;			/* period of SCL for I2C bus */
+};
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+	u32 flags;
+	u32 cmd;
+	u32 retval;
+	u32 timeout;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+	unsigned long last_jiffies;		/* when last command was issued */
+	u32 data[CX2341X_MBOX_MAX_DATA];	/* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+	volatile struct ivtv_mailbox __iomem *mbox;
+	/* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+	   If the bit is set, then the corresponding mailbox is in use by the driver. */
+	unsigned long busy;
+	u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP  (1 << 0)	/* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING	0	/* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI	1       /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA	2 	/* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED 	3	/* this stream is claimed */
+#define IVTV_F_S_STREAMING      4	/* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE	5	/* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH	6	/* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF	7	/* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO        8	/* this stream is used read/written by an application */
+
+#define IVTV_F_S_PIO_PENDING	9	/* this stream has pending PIO */
+#define IVTV_F_S_PIO_HAS_VBI	1       /* the current PIO request also requests VBI data */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA		   0 	/* DMA in progress */
+#define IVTV_F_I_UDMA		   1 	/* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING	   2 	/* UDMA pending */
+#define IVTV_F_I_SPEED_CHANGE	   3 	/* a speed change is in progress */
+#define IVTV_F_I_EOS		   4 	/* end of encoder stream reached */
+#define IVTV_F_I_RADIO_USER	   5 	/* the radio tuner is selected */
+#define IVTV_F_I_DIG_RST	   6 	/* reset digitizer */
+#define IVTV_F_I_DEC_YUV	   7 	/* YUV instead of MPG is being decoded */
+#define IVTV_F_I_UPDATE_CC	   9  	/* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS	   10 	/* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS	   11 	/* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV	   12 	/* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED	   13 	/* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14 	/* last_dec_timing is valid */
+#define IVTV_F_I_HAVE_WORK  	   15	/* used in the interrupt handler: there is work to be done */
+#define IVTV_F_I_WORK_HANDLER_VBI  16	/* there is work to be done for VBI */
+#define IVTV_F_I_WORK_HANDLER_YUV  17	/* there is work to be done for YUV */
+#define IVTV_F_I_WORK_HANDLER_PIO  18	/* there is work to be done for PIO */
+#define IVTV_F_I_PIO		   19	/* PIO in progress */
+#define IVTV_F_I_DEC_PAUSED	   20 	/* the decoder is paused */
+#define IVTV_F_I_INITED		   21 	/* set after first open */
+#define IVTV_F_I_FAILED		   22 	/* set if first open failed */
+#define IVTV_F_I_WORK_HANDLER_PCM  23	/* there is work to be done for PCM */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED	   28	/* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC	   29 	/* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD    30 	/* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED  31 	/* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_sg_element {
+	__le32 src;
+	__le32 dst;
+	__le32 size;
+};
+
+struct ivtv_sg_host_element {
+	u32 src;
+	u32 dst;
+	u32 size;
+};
+
+struct ivtv_user_dma {
+	struct mutex lock;
+	int page_count;
+	struct page *map[IVTV_DMA_SG_OSD_ENT];
+	/* Needed when dealing with highmem userspace buffers */
+	struct page *bouncemap[IVTV_DMA_SG_OSD_ENT];
+
+	/* Base Dev SG Array for cx23415/6 */
+	struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
+	dma_addr_t SG_handle;
+	int SG_length;
+
+	/* SG List of Buffers */
+	struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+	unsigned long uaddr;
+	unsigned long first;
+	unsigned long last;
+	unsigned int offset;
+	unsigned int tail;
+	int page_count;
+};
+
+struct ivtv_buffer {
+	struct list_head list;
+	dma_addr_t dma_handle;
+	unsigned short b_flags;
+	unsigned short dma_xfer_cnt;
+	char *buf;
+	u32 bytesused;
+	u32 readpos;
+};
+
+struct ivtv_queue {
+	struct list_head list;          /* the list of buffers in this queue */
+	u32 buffers;                    /* number of buffers in this queue */
+	u32 length;                     /* total number of bytes of available buffer space */
+	u32 bytesused;                  /* total number of bytes used in this queue */
+};
+
+struct ivtv;				/* forward reference */
+
+struct ivtv_stream {
+	/* These first four fields are always set, even if the stream
+	   is not actually created. */
+	struct video_device *vdev;	/* NULL when stream not created */
+	struct ivtv *itv; 		/* for ease of use */
+	const char *name;		/* name of the stream */
+	int type;			/* stream type */
+	u32 caps;			/* V4L2 capabilities */
+
+	struct v4l2_fh *fh;		/* pointer to the streaming filehandle */
+	spinlock_t qlock; 		/* locks access to the queues */
+	unsigned long s_flags;		/* status flags, see above */
+	int dma;			/* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */
+	u32 pending_offset;
+	u32 pending_backup;
+	u64 pending_pts;
+
+	u32 dma_offset;
+	u32 dma_backup;
+	u64 dma_pts;
+
+	int subtype;
+	wait_queue_head_t waitq;
+	u32 dma_last_offset;
+
+	/* Buffer Stats */
+	u32 buffers;
+	u32 buf_size;
+	u32 buffers_stolen;
+
+	/* Buffer Queues */
+	struct ivtv_queue q_free;	/* free buffers */
+	struct ivtv_queue q_full;	/* full buffers */
+	struct ivtv_queue q_io;		/* waiting for I/O */
+	struct ivtv_queue q_dma;	/* waiting for DMA */
+	struct ivtv_queue q_predma;	/* waiting for DMA */
+
+	/* DMA xfer counter, buffers belonging to the same DMA
+	   xfer will have the same dma_xfer_cnt. */
+	u16 dma_xfer_cnt;
+
+	/* Base Dev SG Array for cx23415/6 */
+	struct ivtv_sg_host_element *sg_pending;
+	struct ivtv_sg_host_element *sg_processing;
+	struct ivtv_sg_element *sg_dma;
+	dma_addr_t sg_handle;
+	int sg_pending_size;
+	int sg_processing_size;
+	int sg_processed;
+
+	/* SG List of Buffers */
+	struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+	struct v4l2_fh fh;
+	int type;                       /* stream type */
+	int yuv_frames;                 /* 1: started OUT_UDMA_YUV output mode */
+	struct ivtv *itv;
+};
+
+static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh)
+{
+	return container_of(fh, struct ivtv_open_id, fh);
+}
+
+struct yuv_frame_info
+{
+	u32 update;
+	s32 src_x;
+	s32 src_y;
+	u32 src_w;
+	u32 src_h;
+	s32 dst_x;
+	s32 dst_y;
+	u32 dst_w;
+	u32 dst_h;
+	s32 pan_x;
+	s32 pan_y;
+	u32 vis_w;
+	u32 vis_h;
+	u32 interlaced_y;
+	u32 interlaced_uv;
+	s32 tru_x;
+	u32 tru_w;
+	u32 tru_h;
+	u32 offset_y;
+	s32 lace_mode;
+	u32 sync_field;
+	u32 delay;
+	u32 interlaced;
+};
+
+#define IVTV_YUV_MODE_INTERLACED	0x00
+#define IVTV_YUV_MODE_PROGRESSIVE	0x01
+#define IVTV_YUV_MODE_AUTO		0x02
+#define IVTV_YUV_MODE_MASK		0x03
+
+#define IVTV_YUV_SYNC_EVEN		0x00
+#define IVTV_YUV_SYNC_ODD		0x04
+#define IVTV_YUV_SYNC_MASK		0x04
+
+#define IVTV_YUV_BUFFERS 8
+
+struct yuv_playback_info
+{
+	u32 reg_2834;
+	u32 reg_2838;
+	u32 reg_283c;
+	u32 reg_2840;
+	u32 reg_2844;
+	u32 reg_2848;
+	u32 reg_2854;
+	u32 reg_285c;
+	u32 reg_2864;
+
+	u32 reg_2870;
+	u32 reg_2874;
+	u32 reg_2890;
+	u32 reg_2898;
+	u32 reg_289c;
+
+	u32 reg_2918;
+	u32 reg_291c;
+	u32 reg_2920;
+	u32 reg_2924;
+	u32 reg_2928;
+	u32 reg_292c;
+	u32 reg_2930;
+
+	u32 reg_2934;
+
+	u32 reg_2938;
+	u32 reg_293c;
+	u32 reg_2940;
+	u32 reg_2944;
+	u32 reg_2948;
+	u32 reg_294c;
+	u32 reg_2950;
+	u32 reg_2954;
+	u32 reg_2958;
+	u32 reg_295c;
+	u32 reg_2960;
+	u32 reg_2964;
+	u32 reg_2968;
+	u32 reg_296c;
+
+	u32 reg_2970;
+
+	int v_filter_1;
+	int v_filter_2;
+	int h_filter;
+
+	u8 track_osd; /* Should yuv output track the OSD size & position */
+
+	u32 osd_x_offset;
+	u32 osd_y_offset;
+
+	u32 osd_x_pan;
+	u32 osd_y_pan;
+
+	u32 osd_vis_w;
+	u32 osd_vis_h;
+
+	u32 osd_full_w;
+	u32 osd_full_h;
+
+	int decode_height;
+
+	int lace_mode;
+	int lace_threshold;
+	int lace_sync_field;
+
+	atomic_t next_dma_frame;
+	atomic_t next_fill_frame;
+
+	u32 yuv_forced_update;
+	int update_frame;
+
+	u8 fields_lapsed;   /* Counter used when delaying a frame */
+
+	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;
+
+	u8 running; /* Have any frames been displayed */
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_cc {
+	u8 odd[2];	/* two-byte payload of odd field */
+	u8 even[2];	/* two-byte payload of even field */;
+};
+
+struct vbi_vps {
+	u8 data[5];	/* five-byte VPS payload */
+};
+
+struct vbi_info {
+	/* VBI general data, does not change during streaming */
+
+	u32 raw_decoder_line_size;              /* raw VBI line size from digitizer */
+	u8 raw_decoder_sav_odd_field;           /* raw VBI Start Active Video digitizer code of odd field */
+	u8 raw_decoder_sav_even_field;          /* raw VBI Start Active Video digitizer code of even field */
+	u32 sliced_decoder_line_size;           /* sliced VBI line size from digitizer */
+	u8 sliced_decoder_sav_odd_field;        /* sliced VBI Start Active Video digitizer code of odd field */
+	u8 sliced_decoder_sav_even_field;       /* sliced VBI Start Active Video digitizer code of even field */
+
+	u32 start[2];				/* start of first VBI line in the odd/even fields */
+	u32 count;				/* number of VBI lines per field */
+	u32 raw_size;				/* size of raw VBI line from the digitizer */
+	u32 sliced_size;			/* size of sliced VBI line from the digitizer */
+
+	u32 dec_start;				/* start in decoder memory of VBI re-insertion buffers */
+	u32 enc_start;				/* start in encoder memory of VBI capture buffers */
+	u32 enc_size;				/* size of VBI capture area */
+	int fpi;				/* number of VBI frames per interrupt */
+
+	struct v4l2_format in;			/* current VBI capture format */
+	struct v4l2_sliced_vbi_format *sliced_in; /* convenience pointer to sliced struct in vbi.in union */
+	int insert_mpeg;			/* if non-zero, then embed VBI data in MPEG stream */
+
+	/* Raw VBI compatibility hack */
+
+	u32 frame; 				/* frame counter hack needed for backwards compatibility
+						   of old VBI software */
+
+	/* Sliced VBI output data */
+
+	struct vbi_cc cc_payload[256];		/* sliced VBI CC payload array: it is an array to
+						   prevent dropping CC data if they couldn't be
+						   processed fast enough */
+	int cc_payload_idx;			/* index in cc_payload */
+	u8 cc_missing_cnt;			/* counts number of frames without CC for passthrough mode */
+	int wss_payload;			/* sliced VBI WSS payload */
+	u8 wss_missing_cnt;			/* counts number of frames without WSS for passthrough mode */
+	struct vbi_vps vps_payload;		/* sliced VBI VPS payload */
+
+	/* Sliced VBI capture data */
+
+	struct v4l2_sliced_vbi_data sliced_data[36];	/* sliced VBI storage for VBI encoder stream */
+	struct v4l2_sliced_vbi_data sliced_dec_data[36];/* sliced VBI storage for VBI decoder stream */
+
+	/* VBI Embedding data */
+
+	/* Buffer for VBI data inserted into MPEG stream.
+	   The first byte is a dummy byte that's never used.
+	   The next 16 bytes contain the MPEG header for the VBI data,
+	   the remainder is the actual VBI data.
+	   The max size accepted by the MPEG VBI reinsertion turns out
+	   to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+	   where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+	   a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+	   However, it seems that the data must be 1K aligned, so we have to
+	   pad the data until the 1 or 2 K boundary.
+
+	   This pointer array will allocate 2049 bytes to store each VBI frame. */
+	u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+	u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+	struct ivtv_buffer sliced_mpeg_buf;	/* temporary buffer holding data from sliced_mpeg_data */
+	u32 inserted_frame;			/* index in sliced_mpeg_size of next sliced data
+						   to be inserted in the MPEG stream */
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+	/* General fixed card data */
+	struct pci_dev *pdev;		/* 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 */
+	v4l2_std_id tuner_std;		/* the norm of the card's tuner (fixed) */
+	struct v4l2_subdev *sd_video;	/* controlling video decoder subdev */
+	struct v4l2_subdev *sd_audio;	/* controlling audio subdev */
+	struct v4l2_subdev *sd_muxer;	/* controlling audio muxer subdev */
+	resource_size_t base_addr;      /* PCI resource base address */
+	volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
+	volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
+	volatile void __iomem *reg_mem; /* pointer to mapped registers */
+	struct ivtv_options options; 	/* user options */
+
+	struct v4l2_device v4l2_dev;
+	struct cx2341x_handler cxhdl;
+	struct {
+		/* PTS/Frame count control cluster */
+		struct v4l2_ctrl *ctrl_pts;
+		struct v4l2_ctrl *ctrl_frame;
+	};
+	struct {
+		/* Audio Playback control cluster */
+		struct v4l2_ctrl *ctrl_audio_playback;
+		struct v4l2_ctrl *ctrl_audio_multilingual_playback;
+	};
+	struct v4l2_ctrl_handler hdl_gpio;
+	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
+	u16 instance;
+
+	/* High-level state info */
+	unsigned long i_flags;          /* global ivtv flags */
+	u8 is_50hz;                     /* 1 if the current capture standard is 50 Hz */
+	u8 is_60hz                      /* 1 if the current capture standard is 60 Hz */;
+	u8 is_out_50hz                  /* 1 if the current TV output standard is 50 Hz */;
+	u8 is_out_60hz                  /* 1 if the current TV output standard is 60 Hz */;
+	int output_mode;                /* decoder output mode: NONE, MPG, YUV, UDMA YUV, passthrough */
+	u32 audio_input;                /* current audio input */
+	u32 active_input;               /* current video input */
+	u32 active_output;              /* current video output */
+	v4l2_std_id std;                /* current capture TV standard */
+	v4l2_std_id std_out;            /* current TV output standard */
+	u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
+	u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
+
+	/* Locking */
+	spinlock_t lock;                /* lock access to this struct */
+	struct mutex serialize_lock;    /* mutex used to serialize open/close/start/stop/ioctl operations */
+
+	/* Streams */
+	int stream_buf_size[IVTV_MAX_STREAMS];          /* stream buffer size */
+	struct ivtv_stream streams[IVTV_MAX_STREAMS]; 	/* stream data */
+	atomic_t capturing;		/* count number of active capture streams */
+	atomic_t decoding;		/* count number of active decoding streams */
+
+	/* ALSA interface for PCM capture stream */
+	struct snd_ivtv_card *alsa;
+	void (*pcm_announce_callback)(struct snd_ivtv_card *card, u8 *pcm_data,
+				      size_t num_bytes);
+
+	/* Used for ivtv-alsa module loading */
+	struct work_struct request_module_wk;
+
+	/* Interrupts & DMA */
+	u32 irqmask;                    /* active interrupts */
+	u32 irq_rr_idx;                 /* round-robin stream index */
+	struct kthread_worker irq_worker;		/* kthread worker for PIO/YUV/VBI actions */
+	struct task_struct *irq_worker_task;		/* task for irq_worker */
+	struct kthread_work irq_work;	/* kthread work entry */
+	spinlock_t dma_reg_lock;        /* lock access to DMA engine registers */
+	int cur_dma_stream;		/* index of current stream doing DMA (-1 if none) */
+	int cur_pio_stream;		/* index of current stream doing PIO (-1 if none) */
+	u32 dma_data_req_offset;        /* store offset in decoder memory of current DMA request */
+	u32 dma_data_req_size;          /* store size of current DMA request */
+	int dma_retries;                /* current DMA retry attempt */
+	struct ivtv_user_dma udma;      /* user based DMA for OSD */
+	struct timer_list dma_timer;    /* timer used to catch unfinished DMAs */
+	u32 last_vsync_field;           /* last seen vsync field */
+	wait_queue_head_t dma_waitq;    /* wake up when the current DMA is finished */
+	wait_queue_head_t eos_waitq;    /* wake up when EOS arrives */
+	wait_queue_head_t event_waitq;  /* wake up when the next decoder event arrives */
+	wait_queue_head_t vsync_waitq;  /* wake up when the next decoder vsync arrives */
+
+
+	/* Mailbox */
+	struct ivtv_mailbox_data enc_mbox;              /* encoder mailboxes */
+	struct ivtv_mailbox_data dec_mbox;              /* decoder mailboxes */
+	struct ivtv_api_cache api_cache[256]; 		/* cached API commands */
+
+
+	/* I2C */
+	struct i2c_adapter i2c_adap;
+	struct i2c_algo_bit_data i2c_algo;
+	struct i2c_client i2c_client;
+	int i2c_state;                  /* i2c bit state */
+	struct mutex i2c_bus_lock;      /* lock i2c bus */
+
+	struct IR_i2c_init_data ir_i2c_init_data;
+
+	/* Program Index information */
+	u32 pgm_info_offset;            /* start of pgm info in encoder memory */
+	u32 pgm_info_num;               /* number of elements in the pgm cyclic buffer in encoder memory */
+	u32 pgm_info_write_idx;         /* last index written by the card that was transferred to pgm_info[] */
+	u32 pgm_info_read_idx;          /* last index in pgm_info read by the application */
+	struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; /* filled from the pgm cyclic buffer on the card */
+
+
+	/* Miscellaneous */
+	u32 open_id;			/* incremented each time an open occurs, is >= 1 */
+	int search_pack_header;         /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */
+	int speed;                      /* current playback speed setting */
+	u8 speed_mute_audio;            /* 1 if audio should be muted when fast forward */
+	u64 mpg_data_received;          /* number of bytes received from the MPEG stream */
+	u64 vbi_data_inserted;          /* number of VBI bytes inserted into the MPEG stream */
+	u32 last_dec_timing[3];         /* cache last retrieved pts/scr/frame values */
+	unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */
+	u32 dualwatch_stereo_mode;      /* current detected dualwatch stereo mode */
+
+
+	/* VBI state info */
+	struct vbi_info vbi;            /* VBI-specific data */
+
+
+	/* YUV playback */
+	struct yuv_playback_info yuv_info;              /* YUV playback data */
+
+
+	/* OSD support */
+	unsigned long osd_video_pbase;
+	int osd_global_alpha_state;     /* 1 = global alpha is on */
+	int osd_local_alpha_state;      /* 1 = local alpha is on */
+	int osd_chroma_key_state;       /* 1 = chroma-keying is on */
+	u8  osd_global_alpha;           /* current global alpha */
+	u32 osd_chroma_key;             /* current chroma key */
+	struct v4l2_rect osd_rect;      /* current OSD position and size */
+	struct v4l2_rect main_rect;     /* current Main window position and size */
+	struct osd_info *osd_info;      /* ivtvfb private OSD info */
+	void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */
+};
+
+static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct ivtv, v4l2_dev);
+}
+
+/* ivtv extensions to be loaded */
+extern int (*ivtv_ext_init)(struct ivtv *);
+
+/* Globals */
+extern int ivtv_first_minor;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_msleep_timeout(unsigned int msecs, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, init cx25840, etc. */
+int ivtv_init_on_first_open(struct ivtv *itv);
+
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int ivtv_raw_vbi(const struct ivtv *itv)
+{
+	return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
+/* This is a PCI post thing, where if the pci register is not read, then
+   the write doesn't always take effect right away. By reading back the
+   register any pending PCI writes will be performed (in order), and so
+   you can be sure that the writes are guaranteed to be done.
+
+   Rarely needed, only in some timing sensitive cases.
+   Apparently if this is not done some motherboards seem
+   to kill the firmware and get into the broken state until computer is
+   rebooted. */
+#define write_sync(val, reg) \
+	do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+	do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+	do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+	do { write_dec(val, addr); read_dec(addr); } while (0)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+   match them all). Ignore any errors. */
+#define ivtv_call_hw(itv, hw, o, f, args...) 				\
+	do {								\
+		struct v4l2_subdev *__sd;				\
+		__v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd,	\
+			!(hw) || (__sd->grp_id & (hw)), o, f , ##args);	\
+	} while (0)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+   match them all). If the callback returns an error other than 0 or
+   -ENOIOCTLCMD, then return with that error code. */
+#define ivtv_call_hw_err(itv, hw, o, f, args...)			\
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd,	\
+		!(hw) || (__sd->grp_id & (hw)), o, f , ##args);		\
+})
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c
new file mode 100644
index 000000000000..9caffd8aa995
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-fileops.c
@@ -0,0 +1,1073 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+#include <media/saa7115.h>
+
+/* This function tries to claim the stream for a specific file descriptor.
+   If no one else is using this stream then the stream is claimed and
+   associated VBI streams are also automatically claimed.
+   Possible error returns: -EBUSY if someone else has claimed
+   the stream or 0 on success. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[type];
+	struct ivtv_stream *s_vbi;
+	int vbi_type;
+
+	if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		/* someone already claimed this stream */
+		if (s->fh == &id->fh) {
+			/* yes, this file descriptor did. So that's OK. */
+			return 0;
+		}
+		if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+					 type == IVTV_ENC_STREAM_TYPE_VBI)) {
+			/* VBI is handled already internally, now also assign
+			   the file descriptor to this stream for external
+			   reading of the stream. */
+			s->fh = &id->fh;
+			IVTV_DEBUG_INFO("Start Read VBI\n");
+			return 0;
+		}
+		/* someone else is using this stream already */
+		IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+		return -EBUSY;
+	}
+	s->fh = &id->fh;
+	if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+		/* Enable reinsertion interrupt */
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	}
+
+	/* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+	   IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+	   (provided VBI insertion is on and sliced VBI is selected), for all
+	   other streams we're done */
+	if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+		vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+	} else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+		   itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) {
+		vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+	} else {
+		return 0;
+	}
+	s_vbi = &itv->streams[vbi_type];
+
+	if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+		/* Enable reinsertion interrupt */
+		if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+			ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	}
+	/* mark that it is used internally */
+	set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+	return 0;
+}
+EXPORT_SYMBOL(ivtv_claim_stream);
+
+/* This function releases a previously claimed stream. It will take into
+   account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi;
+
+	s->fh = NULL;
+	if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+		test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+		/* this stream is still in use internally */
+		return;
+	}
+	if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+		return;
+	}
+
+	ivtv_flush_queues(s);
+
+	/* disable reinsertion interrupt */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+	/* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+	   IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+	   for all other streams we're done */
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+		s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+	else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+		s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	else
+		return;
+
+	/* clear internal use flag */
+	if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+		/* was already cleared */
+		return;
+	}
+	if (s_vbi->fh) {
+		/* VBI stream still claimed by a file descriptor */
+		return;
+	}
+	/* disable reinsertion interrupt */
+	if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+	clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+	ivtv_flush_queues(s_vbi);
+}
+EXPORT_SYMBOL(ivtv_release_stream);
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+	struct v4l2_tuner vt;
+	u32 new_stereo_mode;
+	const u32 dual = 0x02;
+
+	new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+	memset(&vt, 0, sizeof(vt));
+	ivtv_call_all(itv, tuner, g_tuner, &vt);
+	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+		new_stereo_mode = dual;
+
+	if (new_stereo_mode == itv->dualwatch_stereo_mode)
+		return;
+
+	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+			   itv->dualwatch_stereo_mode, new_stereo_mode);
+	if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+		IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+	u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+	int cnt;
+	int i = 0;
+
+	if (wr_idx >= itv->pgm_info_num) {
+		IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+		return;
+	}
+	cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+	while (i < cnt) {
+		int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+		struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+		u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+		const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1,
+			V4L2_ENC_IDX_FRAME_B, -1, -1, -1 };
+					// 1=I, 2=P, 4=B
+
+		e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+		if (e->offset > itv->mpg_data_received) {
+			break;
+		}
+		e->offset += itv->vbi_data_inserted;
+		e->length = read_enc(addr);
+		e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+		e->flags = mapping[read_enc(addr + 12) & 7];
+		i++;
+	}
+	itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	struct ivtv_buffer *buf;
+	DEFINE_WAIT(wait);
+
+	*err = 0;
+	while (1) {
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+			/* Process pending program info updates and pending VBI data */
+			ivtv_update_pgm_info(itv);
+
+			if (time_after(jiffies,
+				       itv->dualwatch_jiffies +
+				       msecs_to_jiffies(1000))) {
+				itv->dualwatch_jiffies = jiffies;
+				ivtv_dualwatch(itv);
+			}
+
+			if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+			    !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+				while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+					/* byteswap and process VBI data */
+					ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+					ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+				}
+			}
+			buf = &itv->vbi.sliced_mpeg_buf;
+			if (buf->readpos != buf->bytesused) {
+				return buf;
+			}
+		}
+
+		/* do we have leftover data? */
+		buf = ivtv_dequeue(s, &s->q_io);
+		if (buf)
+			return buf;
+
+		/* do we have new data? */
+		buf = ivtv_dequeue(s, &s->q_full);
+		if (buf) {
+			if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
+				return buf;
+			buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
+			if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+				/* byteswap MPG data */
+				ivtv_buf_swap(buf);
+			else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+				/* byteswap and process VBI data */
+				ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+			}
+			return buf;
+		}
+
+		/* return if end of stream */
+		if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+			IVTV_DEBUG_INFO("EOS %s\n", s->name);
+			return NULL;
+		}
+
+		/* return if file was opened with O_NONBLOCK */
+		if (non_block) {
+			*err = -EAGAIN;
+			return NULL;
+		}
+
+		/* wait for more data to arrive */
+		mutex_unlock(&itv->serialize_lock);
+		prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+		/* New buffers might have become available before we were added to the waitqueue */
+		if (!s->q_full.buffers)
+			schedule();
+		finish_wait(&s->waitq, &wait);
+		mutex_lock(&itv->serialize_lock);
+		if (signal_pending(current)) {
+			/* return if a signal was received */
+			IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+			*err = -EINTR;
+			return NULL;
+		}
+	}
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+	int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+	itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+	itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+	itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+		char __user *ubuf, size_t ucount)
+{
+	struct ivtv *itv = s->itv;
+	size_t len = buf->bytesused - buf->readpos;
+
+	if (len > ucount) len = ucount;
+	if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) {
+		const char *start = buf->buf + buf->readpos;
+		const char *p = start + 1;
+		const u8 *q;
+		u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+		int stuffing, i;
+
+		while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+			p = q + 1;
+			if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+			    q[1] != 0 || q[2] != 1 || q[3] != ch) {
+				continue;
+			}
+			if (!itv->search_pack_header) {
+				if ((q[6] & 0xc0) != 0x80)
+					continue;
+				if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+				    ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+					ch = 0xba;
+					itv->search_pack_header = 1;
+					p = q + 9;
+				}
+				continue;
+			}
+			stuffing = q[13] & 7;
+			/* all stuffing bytes must be 0xff */
+			for (i = 0; i < stuffing; i++)
+				if (q[14 + i] != 0xff)
+					break;
+			if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+					q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+					q[16 + stuffing] == 1) {
+				itv->search_pack_header = 0;
+				len = (char *)q - start;
+				ivtv_setup_sliced_vbi_buf(itv);
+				break;
+			}
+		}
+	}
+	if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+		IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+		return -EFAULT;
+	}
+	/*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+			buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+			buf == &itv->vbi.sliced_mpeg_buf); */
+	buf->readpos += len;
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+		itv->mpg_data_received += len;
+	return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+	struct ivtv *itv = s->itv;
+	size_t tot_written = 0;
+	int single_frame = 0;
+
+	if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) {
+		/* shouldn't happen */
+		IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+		return -EIO;
+	}
+
+	/* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+	   arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+	    (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv)))
+		single_frame = 1;
+
+	for (;;) {
+		struct ivtv_buffer *buf;
+		int rc;
+
+		buf = ivtv_get_buffer(s, non_block, &rc);
+		/* if there is no data available... */
+		if (buf == NULL) {
+			/* if we got data, then return that regardless */
+			if (tot_written)
+				break;
+			/* EOS condition */
+			if (rc == 0) {
+				clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+				clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+				ivtv_release_stream(s);
+			}
+			/* set errno */
+			return rc;
+		}
+		rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+		if (buf != &itv->vbi.sliced_mpeg_buf) {
+			ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+		}
+		else if (buf->readpos == buf->bytesused) {
+			int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+			itv->vbi.sliced_mpeg_size[idx] = 0;
+			itv->vbi.inserted_frame++;
+			itv->vbi_data_inserted += buf->bytesused;
+		}
+		if (rc < 0)
+			return rc;
+		tot_written += rc;
+
+		if (tot_written == tot_count || single_frame)
+			break;
+	}
+	return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+			loff_t *pos, int non_block)
+{
+	ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+	struct ivtv *itv = s->itv;
+
+	IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+	if (rc > 0)
+		pos += rc;
+	return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	struct ivtv_stream *s_vbi;
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+	    s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+	    s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+	    s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+		/* you cannot read from these stream types. */
+		return -EINVAL;
+	}
+
+	/* Try to claim this stream. */
+	if (ivtv_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* This stream does not need to start capturing */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* If capture is already in progress, then we also have to
+	   do nothing extra. */
+	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* Start VBI capture if required */
+	s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+	    !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+		/* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+		   automatically when the MPG stream is claimed.
+		   We only need to start the VBI capturing. */
+		if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+			IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+			/* Failure, clean up and return an error */
+			clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+			clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+			/* also releases the associated VBI stream */
+			ivtv_release_stream(s);
+			return -EIO;
+		}
+		IVTV_DEBUG_INFO("VBI insertion started\n");
+	}
+
+	/* Tell the card to start capturing */
+	if (!ivtv_start_v4l2_encode_stream(s)) {
+		/* We're done */
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		/* Resume a possibly paused encoder */
+		if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+			ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+		return 0;
+	}
+
+	/* failure, clean up */
+	IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+	/* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+	   automatically when the MPG stream is released.
+	   We only need to stop the VBI capturing. */
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+	    test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+		ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+		clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+	}
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+	ivtv_release_stream(s);
+	return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+	struct ivtv_open_id *id = fh2id(filp->private_data);
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	ssize_t rc;
+
+	IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+	if (mutex_lock_interruptible(&itv->serialize_lock))
+		return -ERESTARTSYS;
+	rc = ivtv_start_capture(id);
+	if (!rc)
+		rc = ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+	mutex_unlock(&itv->serialize_lock);
+	return rc;
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int rc;
+
+	if (atomic_read(&itv->decoding) == 0) {
+		if (ivtv_claim_stream(id, s->type)) {
+			/* someone else is using this stream already */
+			IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+			return -EBUSY;
+		}
+		rc = ivtv_start_v4l2_decode_stream(s, 0);
+		if (rc < 0) {
+			if (rc == -EAGAIN)
+				rc = ivtv_start_v4l2_decode_stream(s, 0);
+			if (rc < 0)
+				return rc;
+		}
+	}
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+		return ivtv_set_speed(itv, speed);
+	return 0;
+}
+
+static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+	struct ivtv_open_id *id = fh2id(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;
+	int mode;
+	int rc;
+	DEFINE_WAIT(wait);
+
+	IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name);
+
+	if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+	    s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+	    s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+		/* not decoder streams */
+		return -EINVAL;
+
+	/* Try to claim this stream */
+	if (ivtv_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* This stream does not need to start any decoding */
+	if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+		int elems = count / sizeof(struct v4l2_sliced_vbi_data);
+
+		set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return ivtv_write_vbi_from_user(itv,
+		   (const struct v4l2_sliced_vbi_data __user *)user_buf, elems);
+	}
+
+	mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+	if (ivtv_set_output_mode(itv, mode) != mode) {
+	    ivtv_release_stream(s);
+	    return -EBUSY;
+	}
+	ivtv_queue_init(&q);
+	set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+	/* Start decoder (returns 0 if already started) */
+	rc = ivtv_start_decoding(id, itv->speed);
+	if (rc) {
+		IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+		/* failure, clean up */
+		clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+		clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		return rc;
+	}
+
+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) {
+			rc = ivtv_yuv_udma_stream_frame(itv, (void __user *)user_buf);
+
+			if (rc < 0)
+				return rc;
+
+			bytes_written += itv->dma_data_req_size;
+			user_buf += itv->dma_data_req_size;
+			count -= itv->dma_data_req_size;
+		}
+		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)))
+			ivtv_enqueue(s, buf, &q);
+		while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+			ivtv_enqueue(s, buf, &q);
+		}
+		if (q.buffers)
+			break;
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		mutex_unlock(&itv->serialize_lock);
+		prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+		/* New buffers might have become free before we were added to the waitqueue */
+		if (!s->q_free.buffers)
+			schedule();
+		finish_wait(&s->waitq, &wait);
+		mutex_lock(&itv->serialize_lock);
+		if (signal_pending(current)) {
+			IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+			return -EINTR;
+		}
+	}
+
+	/* copy user data into buffers */
+	while ((buf = ivtv_dequeue(s, &q))) {
+		/* 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;
+		}
+		user_buf += rc;
+		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);
+			break;
+		}
+		/* Byteswap MPEG buffer */
+		if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+			ivtv_buf_swap(buf);
+		ivtv_enqueue(s, buf, &s->q_full);
+	}
+
+	if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+		if (s->q_full.length >= itv->dma_data_req_size) {
+			int got_sig;
+
+			if (mode == OUT_YUV)
+				ivtv_yuv_setup_stream_frame(itv);
+
+			mutex_unlock(&itv->serialize_lock);
+			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)) {
+				schedule();
+			}
+			finish_wait(&itv->dma_waitq, &wait);
+			mutex_lock(&itv->serialize_lock);
+			if (got_sig) {
+				IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+				return -EINTR;
+			}
+
+			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, 1);
+		}
+	}
+	/* more user data is available, wait until buffers become free
+	   to transfer the rest. */
+	if (count && !(filp->f_flags & O_NONBLOCK))
+		goto retry;
+	IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+	return bytes_written;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+	struct ivtv_open_id *id = fh2id(filp->private_data);
+	struct ivtv *itv = id->itv;
+	ssize_t res;
+
+	if (mutex_lock_interruptible(&itv->serialize_lock))
+		return -ERESTARTSYS;
+	res = ivtv_write(filp, user_buf, count, pos);
+	mutex_unlock(&itv->serialize_lock);
+	return res;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+	struct ivtv_open_id *id = fh2id(filp->private_data);
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int res = 0;
+
+	/* add stream's waitq to the poll list */
+	IVTV_DEBUG_HI_FILE("Decoder poll\n");
+
+	/* If there are subscribed events, then only use the new event
+	   API instead of the old video.h based API. */
+	if (!list_empty(&id->fh.subscribed)) {
+		poll_wait(filp, &id->fh.wait, wait);
+		/* Turn off the old-style vsync events */
+		clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+		if (v4l2_event_pending(&id->fh))
+			res = POLLPRI;
+	} else {
+		/* This is the old-style API which is here only for backwards
+		   compatibility. */
+		poll_wait(filp, &s->waitq, wait);
+		set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+		if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+		    test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+			res = POLLPRI;
+	}
+
+	/* Allow write if buffers are available for writing */
+	if (s->q_free.buffers)
+		res |= POLLOUT | POLLWRNORM;
+	return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+	unsigned long req_events = poll_requested_events(wait);
+	struct ivtv_open_id *id = fh2id(filp->private_data);
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+	unsigned res = 0;
+
+	/* Start a capture if there is none */
+	if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) &&
+			s->type != IVTV_ENC_STREAM_TYPE_RAD &&
+			(req_events & (POLLIN | POLLRDNORM))) {
+		int rc;
+
+		mutex_lock(&itv->serialize_lock);
+		rc = ivtv_start_capture(id);
+		mutex_unlock(&itv->serialize_lock);
+		if (rc) {
+			IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+					s->name, rc);
+			return POLLERR;
+		}
+		IVTV_DEBUG_FILE("Encoder poll started capture\n");
+	}
+
+	/* add stream's waitq to the poll list */
+	IVTV_DEBUG_HI_FILE("Encoder poll\n");
+	poll_wait(filp, &s->waitq, wait);
+	if (v4l2_event_pending(&id->fh))
+		res |= POLLPRI;
+	else
+		poll_wait(filp, &id->fh.wait, wait);
+
+	if (s->q_full.length || s->q_io.length)
+		return res | POLLIN | POLLRDNORM;
+	if (eof)
+		return res | POLLHUP;
+	return res;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+	/* 'Unclaim' this stream */
+
+	/* Stop capturing */
+	if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+		IVTV_DEBUG_INFO("close stopping capture\n");
+		/* Special case: a running VBI capture for VBI insertion
+		   in the mpeg stream. Need to stop that too. */
+		if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+		    test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+		    !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+			IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+			ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+		}
+		if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+		     id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+		    test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+			/* Also used internally, don't stop capturing */
+			s->fh = NULL;
+		}
+		else {
+			ivtv_stop_v4l2_encode_stream(s, gop_end);
+		}
+	}
+	if (!gop_end) {
+		clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+		clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+		ivtv_release_stream(s);
+	}
+}
+
+static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+	if (id->type == IVTV_DEC_STREAM_TYPE_YUV &&
+		test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+		/* Restore registers we've changed & clean up any mess */
+		ivtv_yuv_close(itv);
+	}
+
+	/* Stop decoding */
+	if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+		IVTV_DEBUG_INFO("close stopping decode\n");
+
+		ivtv_stop_v4l2_decode_stream(s, flags, pts);
+		itv->output_mode = OUT_NONE;
+	}
+	clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames)
+		itv->output_mode = OUT_NONE;
+
+	itv->speed = 0;
+	clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+	ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct file *filp)
+{
+	struct v4l2_fh *fh = filp->private_data;
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	IVTV_DEBUG_FILE("close %s\n", s->name);
+
+	mutex_lock(&itv->serialize_lock);
+
+	/* Stop radio */
+	if (id->type == IVTV_ENC_STREAM_TYPE_RAD &&
+			v4l2_fh_is_singular_file(filp)) {
+		/* Closing radio device, return to TV mode */
+		ivtv_mute(itv);
+		/* Mark that the radio is no longer in use */
+		clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+		/* Switch tuner to TV */
+		ivtv_call_all(itv, core, s_std, itv->std);
+		/* Select correct audio input (i.e. TV tuner or Line in) */
+		ivtv_audio_set_io(itv);
+		if (itv->hw_flags & IVTV_HW_SAA711X) {
+			ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+					SAA7115_FREQ_32_11_MHZ, 0);
+		}
+		if (atomic_read(&itv->capturing) > 0) {
+			/* Undo video mute */
+			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+					v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+					(v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+		}
+		/* Done! Unmute and continue. */
+		ivtv_unmute(itv);
+	}
+
+	v4l2_fh_del(fh);
+	v4l2_fh_exit(fh);
+
+	/* Easy case first: this stream was never claimed by us */
+	if (s->fh != &id->fh)
+		goto close_done;
+
+	/* 'Unclaim' this stream */
+
+	if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+		struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
+
+		ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+
+		/* If all output streams are closed, and if the user doesn't have
+		   IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
+		if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) {
+			/* disable CC on TV-out */
+			ivtv_disable_cc(itv);
+		}
+	} else {
+		ivtv_stop_capture(id, 0);
+	}
+close_done:
+	kfree(id);
+	mutex_unlock(&itv->serialize_lock);
+	return 0;
+}
+
+static int ivtv_open(struct file *filp)
+{
+	struct video_device *vdev = video_devdata(filp);
+	struct ivtv_stream *s = video_get_drvdata(vdev);
+	struct ivtv *itv = s->itv;
+	struct ivtv_open_id *item;
+	int res = 0;
+
+	IVTV_DEBUG_FILE("open %s\n", s->name);
+
+	if (ivtv_init_on_first_open(itv)) {
+		IVTV_ERR("Failed to initialize on device %s\n",
+			 video_device_node_name(vdev));
+		return -ENXIO;
+	}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/* Unless ivtv_fw_debug is set, error out if firmware dead. */
+	if (ivtv_fw_debug) {
+		IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
+			  video_device_node_name(vdev));
+		IVTV_WARN("Selected firmware errors will be ignored\n");
+	} else {
+#else
+	if (1) {
+#endif
+		res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+		if (res == -EAGAIN)
+			res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+		if (res < 0)
+			return -EIO;
+	}
+
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
+		test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+		return -EBUSY;
+
+	if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+		test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+		return -EBUSY;
+
+	if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+		if (read_reg(0x82c) == 0) {
+			IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+			/* return -ENODEV; */
+		}
+		ivtv_udma_alloc(itv);
+	}
+
+	/* Allocate memory */
+	item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+	if (NULL == item) {
+		IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+		return -ENOMEM;
+	}
+	v4l2_fh_init(&item->fh, s->vdev);
+	item->itv = itv;
+	item->type = s->type;
+
+	filp->private_data = &item->fh;
+	v4l2_fh_add(&item->fh);
+
+	if (item->type == IVTV_ENC_STREAM_TYPE_RAD &&
+			v4l2_fh_is_singular_file(filp)) {
+		if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+			if (atomic_read(&itv->capturing) > 0) {
+				/* switching to radio while capture is
+				   in progress is not polite */
+				v4l2_fh_del(&item->fh);
+				v4l2_fh_exit(&item->fh);
+				kfree(item);
+				return -EBUSY;
+			}
+		}
+		/* Mark that the radio is being used. */
+		set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+		/* We have the radio */
+		ivtv_mute(itv);
+		/* Switch tuner to radio */
+		ivtv_call_all(itv, tuner, s_radio);
+		/* Select the correct audio input (i.e. radio tuner) */
+		ivtv_audio_set_io(itv);
+		if (itv->hw_flags & IVTV_HW_SAA711X) {
+			ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+				SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL);
+		}
+		/* Done! Unmute and continue. */
+		ivtv_unmute(itv);
+	}
+
+	/* YUV or MPG Decoding Mode? */
+	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) {
+		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;
+}
+
+int ivtv_v4l2_open(struct file *filp)
+{
+	struct video_device *vdev = video_devdata(filp);
+	int res;
+
+	if (mutex_lock_interruptible(vdev->lock))
+		return -ERESTARTSYS;
+	res = ivtv_open(filp);
+	mutex_unlock(vdev->lock);
+	return res;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+	if (atomic_read(&itv->capturing))
+		ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+	IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+	if (atomic_read(&itv->capturing)) {
+		ivtv_msleep_timeout(100, 0);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+		ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+	}
+	IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.h b/drivers/media/pci/ivtv/ivtv-fileops.h
new file mode 100644
index 000000000000..5e08800772ca
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-fileops.h
@@ -0,0 +1,44 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_FILEOPS_H
+#define IVTV_FILEOPS_H
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		      loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+		       loff_t * pos);
+int ivtv_v4l2_close(struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+/* Shared with ivtv-alsa module */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type);
+void ivtv_release_stream(struct ivtv_stream *s);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c
new file mode 100644
index 000000000000..6ec7705af555
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-firmware.c
@@ -0,0 +1,402 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include <linux/firmware.h>
+#include <media/saa7127.h>
+
+#define IVTV_MASK_SPU_ENABLE 		0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 		0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 		0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 		0x00000000
+#define IVTV_CMD_AO_STOP 		0x00000005
+#define IVTV_CMD_APU_PING 		0x00000000
+#define IVTV_CMD_VPU_STOP15 		0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 		0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 		0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 		0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 	0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 	0x80000640
+#define IVTV_SDRAM_SLEEPTIME 		600
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME 	"v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE 	(152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE 		(376836)
+#define IVTV_FW_DEC_SIZE 		(256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+	const struct firmware *fw = NULL;
+	int retries = 3;
+
+retry:
+	if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) {
+		int i;
+		volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+		const u32 *src = (const u32 *)fw->data;
+
+		if (fw->size != size) {
+			/* Due to race conditions in firmware loading (esp. with udev <0.95)
+			   the wrong file was sometimes loaded. So we check filesizes to
+			   see if at least the right-sized file was loaded. If not, then we
+			   retry. */
+			IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+			release_firmware(fw);
+			retries--;
+			goto retry;
+		}
+		for (i = 0; i < fw->size; i += 4) {
+			/* no need for endianness conversion on the ppc */
+			__raw_writel(*src, dst);
+			dst++;
+			src++;
+		}
+		IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size);
+		release_firmware(fw);
+		return size;
+	}
+	IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size);
+	IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+	return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+	IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+	if (itv->has_cx23415 && itv->dec_mbox.mbox)
+		ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+	if (itv->enc_mbox.mbox)
+		ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+	ivtv_msleep_timeout(10, 0);
+	itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+	IVTV_DEBUG_INFO("Stopping VDM\n");
+	write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+	IVTV_DEBUG_INFO("Stopping AO\n");
+	write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+	IVTV_DEBUG_INFO("pinging (?) APU\n");
+	write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+	IVTV_DEBUG_INFO("Stopping VPU\n");
+	if (!itv->has_cx23415)
+		write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+	else
+		write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+	IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+	write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+	IVTV_DEBUG_INFO("Stopping SPU\n");
+	write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+	ivtv_msleep_timeout(10, 0);
+
+	IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+	write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+	IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+	write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+	if (itv->has_cx23415) {
+		IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+		write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+		IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+		write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+	}
+
+	IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME);
+	ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	/* Encoder */
+	ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+	IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+	if (data[0] != 0x02060039)
+		IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+	if (itv->has_cx23415) {
+		/* Decoder */
+		ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+		IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+	}
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+	IVTV_DEBUG_INFO("Loading encoder image\n");
+	if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+		   itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+		IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+		return -3;
+	}
+	if (!itv->has_cx23415)
+		return 0;
+
+	IVTV_DEBUG_INFO("Loading decoder image\n");
+	if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+		   itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+		IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+		return -1;
+	}
+	return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+	int i;
+
+	/* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte
+	   address boundary */
+	for (i = 0; i < size; i += 0x100) {
+		if (readl(mem + i)      == 0x12345678 &&
+		    readl(mem + i + 4)  == 0x34567812 &&
+		    readl(mem + i + 8)  == 0x56781234 &&
+		    readl(mem + i + 12) == 0x78123456) {
+			return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+		}
+	}
+	return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+	int err;
+
+	ivtv_halt_firmware(itv);
+
+	/* load firmware */
+	err = ivtv_firmware_copy(itv);
+	if (err) {
+		IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+		return err;
+	}
+
+	/* start firmware */
+	write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+	ivtv_msleep_timeout(100, 0);
+	if (itv->has_cx23415)
+		write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+	else
+		write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+	ivtv_msleep_timeout(100, 0);
+
+	/* find mailboxes and ping firmware */
+	itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+	if (itv->enc_mbox.mbox == NULL)
+		IVTV_ERR("Encoder mailbox not found\n");
+	else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+		IVTV_ERR("Encoder firmware dead!\n");
+		itv->enc_mbox.mbox = NULL;
+	}
+	if (itv->enc_mbox.mbox == NULL)
+		return -ENODEV;
+
+	if (!itv->has_cx23415)
+		return 0;
+
+	itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+	if (itv->dec_mbox.mbox == NULL) {
+		IVTV_ERR("Decoder mailbox not found\n");
+	} else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+		IVTV_ERR("Decoder firmware dead!\n");
+		itv->dec_mbox.mbox = NULL;
+	} else {
+		/* Firmware okay, so check yuv output filter table */
+		ivtv_yuv_filter_check(itv);
+	}
+	return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	long readbytes;
+	volatile u8 __iomem *mem_offset;
+
+	data[0] = 0;
+	data[1] = itv->cxhdl.width;	/* YUV source width */
+	data[2] = itv->cxhdl.height;
+	data[3] = itv->cxhdl.audio_properties;	/* Audio settings to use,
+							   bitmap. see docs. */
+	if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+		IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+		return;
+	}
+
+	if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+		IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+		return;
+	}
+	ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+	mem_offset = itv->dec_mem + data[1];
+
+	if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+		mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+		IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+				IVTV_DECODE_INIT_MPEG_FILENAME);
+	} else {
+		ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+		ivtv_msleep_timeout(100, 0);
+	}
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
+
+/* Try to restart the card & restore previous settings */
+int ivtv_firmware_restart(struct ivtv *itv)
+{
+	int rc = 0;
+	v4l2_std_id std;
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+		/* Display test image during restart */
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+		    SAA7127_INPUT_TYPE_TEST_IMAGE,
+		    itv->card->video_outputs[itv->active_output].video_output,
+		    0);
+
+	mutex_lock(&itv->udma.lock);
+
+	rc = ivtv_firmware_init(itv);
+	if (rc) {
+		mutex_unlock(&itv->udma.lock);
+		return rc;
+	}
+
+	/* Allow settings to reload */
+	ivtv_mailbox_cache_invalidate(itv);
+
+	/* Restore encoder video standard */
+	std = itv->std;
+	itv->std = 0;
+	ivtv_s_std_enc(itv, &std);
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		ivtv_init_mpeg_decoder(itv);
+
+		/* Restore decoder video standard */
+		std = itv->std_out;
+		itv->std_out = 0;
+		ivtv_s_std_dec(itv, &std);
+
+		/* Restore framebuffer if active */
+		if (itv->ivtvfb_restore)
+			itv->ivtvfb_restore(itv);
+
+		/* Restore alpha settings */
+		ivtv_set_osd_alpha(itv);
+
+		/* Restore normal output */
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+		    SAA7127_INPUT_TYPE_NORMAL,
+		    itv->card->video_outputs[itv->active_output].video_output,
+		    0);
+	}
+
+	mutex_unlock(&itv->udma.lock);
+	return rc;
+}
+
+/* Check firmware running state. The checks fall through
+   allowing multiple failures to be logged. */
+int ivtv_firmware_check(struct ivtv *itv, char *where)
+{
+	int res = 0;
+
+	/* Check encoder is still running */
+	if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) {
+		IVTV_WARN("Encoder has died : %s\n", where);
+		res = -1;
+	}
+
+	/* Also check audio. Only check if not in use & encoder is okay */
+	if (!res && !atomic_read(&itv->capturing) &&
+	    (!atomic_read(&itv->decoding) ||
+	     (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV,
+							     &itv->i_flags)))) {
+
+		if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) {
+			IVTV_WARN("Audio has died (Encoder OK) : %s\n", where);
+			res = -2;
+		}
+	}
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		/* Second audio check. Skip if audio already failed */
+		if (res != -2 && read_dec(0x100) != read_dec(0x104)) {
+			/* Wait & try again to be certain. */
+			ivtv_msleep_timeout(14, 0);
+			if (read_dec(0x100) != read_dec(0x104)) {
+				IVTV_WARN("Audio has died (Decoder) : %s\n",
+					  where);
+				res = -1;
+			}
+		}
+
+		/* Check decoder is still running */
+		if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) {
+			IVTV_WARN("Decoder has died : %s\n", where);
+			res = -1;
+		}
+	}
+
+	/* If something failed & currently idle, try to reload */
+	if (res && !atomic_read(&itv->capturing) &&
+						!atomic_read(&itv->decoding)) {
+		IVTV_INFO("Detected in %s that firmware had failed - "
+			  "Reloading\n", where);
+		res = ivtv_firmware_restart(itv);
+		/*
+		 * Even if restarted ok, still signal a problem had occurred.
+		 * The caller can come through this function again to check
+		 * if things are really ok after the restart.
+		 */
+		if (!res) {
+			IVTV_INFO("Firmware restart okay\n");
+			res = -EAGAIN;
+		} else {
+			IVTV_INFO("Firmware restart failed\n");
+		}
+	} else if (res) {
+		res = -EIO;
+	}
+
+	return res;
+}
+
+MODULE_FIRMWARE(CX2341X_FIRM_ENC_FILENAME);
+MODULE_FIRMWARE(CX2341X_FIRM_DEC_FILENAME);
+MODULE_FIRMWARE(IVTV_DECODE_INIT_MPEG_FILENAME);
diff --git a/drivers/media/pci/ivtv/ivtv-firmware.h b/drivers/media/pci/ivtv/ivtv-firmware.h
new file mode 100644
index 000000000000..52bb4e5598fd
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-firmware.h
@@ -0,0 +1,31 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_FIRMWARE_H
+#define IVTV_FIRMWARE_H
+
+int ivtv_firmware_init(struct ivtv *itv);
+void ivtv_firmware_versions(struct ivtv *itv);
+void ivtv_halt_firmware(struct ivtv *itv);
+void ivtv_init_mpeg_decoder(struct ivtv *itv);
+int ivtv_firmware_check(struct ivtv *itv, char *where);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c
new file mode 100644
index 000000000000..8f0d07789053
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-gpio.c
@@ -0,0 +1,374 @@
+/*
+    gpio functions.
+    Merging GPIO support into driver:
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "tuner-xc2028.h"
+#include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * GPIO assignment of Yuan MPG600/MPG160
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT         IN1 IN0                                       AM3 AM2 AM1 AM0
+ *  INPUT                   DM1         DM0
+ *
+ *   IN* : Input selection
+ *          IN1 IN0
+ *           1   1  N/A
+ *           1   0  Line
+ *           0   1  N/A
+ *           0   0  Tuner
+ *
+ *   AM* : Audio Mode
+ *          AM3  0: Normal        1: Mixed(Sub+Main channel)
+ *          AM2  0: Subchannel    1: Main channel
+ *          AM1  0: Stereo        1: Mono
+ *          AM0  0: Normal        1: Mute
+ *
+ *   DM* : Detected tuner audio Mode
+ *          DM1  0: Stereo        1: Mono
+ *          DM0  0: Multiplex     1: Normal
+ *
+ * GPIO Initial Settings
+ *           MPG600   MPG160
+ *     DIR   0x3080   0x7080
+ *  OUTPUT   0x000C   0x400C
+ *
+ *  Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
+ *  for analyzing GPIO of MPG160.
+ *
+ *****************************************************************************
+ *
+ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT IN0 AM0 IN1               AM1 AM2       IN2     BR0   BR1
+ *  INPUT
+ *
+ *   IN* : Input selection
+ *          IN0 IN1 IN2
+ *           *   1   *  Mute
+ *           0   0   0  Line-In
+ *           1   0   0  TV Tuner Audio
+ *           0   0   1  FM Audio
+ *           1   0   1  Mute
+ *
+ *   AM* : Audio Mode
+ *          AM0 AM1 AM2
+ *           0   0   0  TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
+ *           0   0   1  TV Tuner Audio: L_OUT=R_OUT=SAP   (SAP)
+ *           0   1   0  TV Tuner Audio: L_OUT=L, R_OUT=R   (stereo)
+ *           0   1   1  TV Tuner Audio: mute
+ *           1   *   *  TV Tuner Audio: L_OUT=R_OUT=(L+R)/2   (mono)
+ *
+ *   BR* : Audio Sample Rate (BR stands for bitrate for some reason)
+ *          BR0 BR1
+ *           0   0   32 kHz
+ *           0   1   44.1 kHz
+ *           1   0   48 kHz
+ *
+ *   DM* : Detected tuner audio Mode
+ *         Unknown currently
+ *
+ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
+ * AVerMedia for providing the GPIO information used to add support
+ * for the M179 cards.
+ */
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define IVTV_REG_GPIO_IN    0x9008
+#define IVTV_REG_GPIO_OUT   0x900c
+#define IVTV_REG_GPIO_DIR   0x9020
+
+void ivtv_reset_ir_gpio(struct ivtv *itv)
+{
+	int curdir, curout;
+
+	if (itv->card->type != IVTV_CARD_PVR_150)
+		return;
+	IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
+	curout = read_reg(IVTV_REG_GPIO_OUT);
+	curdir = read_reg(IVTV_REG_GPIO_DIR);
+	curdir |= 0x80;
+	write_reg(curdir, IVTV_REG_GPIO_DIR);
+	curout = (curout & ~0xF) | 1;
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	/* We could use something else for smaller time */
+	schedule_timeout_interruptible(msecs_to_jiffies(1));
+	curout |= 2;
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	curdir &= ~0x80;
+	write_reg(curdir, IVTV_REG_GPIO_DIR);
+}
+
+/* Xceive tuner reset function */
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+{
+	struct i2c_algo_bit_data *algo = dev;
+	struct ivtv *itv = algo->data;
+	u32 curout;
+
+	if (cmd != XC2028_TUNER_RESET)
+		return 0;
+	IVTV_DEBUG_INFO("Resetting tuner\n");
+	curout = read_reg(IVTV_REG_GPIO_OUT);
+	curout &= ~(1 << itv->card->xceive_pin);
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	schedule_timeout_interruptible(msecs_to_jiffies(1));
+
+	curout |= 1 << itv->card->xceive_pin;
+	write_reg(curout, IVTV_REG_GPIO_OUT);
+	schedule_timeout_interruptible(msecs_to_jiffies(1));
+	return 0;
+}
+
+static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ivtv, sd_gpio);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
+
+static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	mask = itv->card->gpio_audio_freq.mask;
+	switch (freq) {
+	case 32000:
+		data = itv->card->gpio_audio_freq.f32000;
+		break;
+	case 44100:
+		data = itv->card->gpio_audio_freq.f44100;
+		break;
+	case 48000:
+	default:
+		data = itv->card->gpio_audio_freq.f48000;
+		break;
+	}
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
+
+static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask;
+
+	mask = itv->card->gpio_audio_detect.mask;
+	if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+		vt->rxsubchans = V4L2_TUNER_SUB_STEREO |
+			V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+	else
+		vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+	return 0;
+}
+
+static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	mask = itv->card->gpio_audio_mode.mask;
+	switch (vt->audmode) {
+	case V4L2_TUNER_MODE_LANG1:
+		data = itv->card->gpio_audio_mode.lang1;
+		break;
+	case V4L2_TUNER_MODE_LANG2:
+		data = itv->card->gpio_audio_mode.lang2;
+		break;
+	case V4L2_TUNER_MODE_MONO:
+		data = itv->card->gpio_audio_mode.mono;
+		break;
+	case V4L2_TUNER_MODE_STEREO:
+	case V4L2_TUNER_MODE_LANG1_LANG2:
+	default:
+		data = itv->card->gpio_audio_mode.stereo;
+		break;
+	}
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
+
+static int subdev_s_radio(struct v4l2_subdev *sd)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	mask = itv->card->gpio_audio_input.mask;
+	data = itv->card->gpio_audio_input.radio;
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
+
+static int subdev_s_audio_routing(struct v4l2_subdev *sd,
+				  u32 input, u32 output, u32 config)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	if (input > 2)
+		return -EINVAL;
+	mask = itv->card->gpio_audio_input.mask;
+	switch (input) {
+	case 0:
+		data = itv->card->gpio_audio_input.tuner;
+		break;
+	case 1:
+		data = itv->card->gpio_audio_input.linein;
+		break;
+	case 2:
+	default:
+		data = itv->card->gpio_audio_input.radio;
+		break;
+	}
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
+
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		mask = itv->card->gpio_audio_mute.mask;
+		data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+		if (mask)
+			write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+					(data & mask), IVTV_REG_GPIO_OUT);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+
+static int subdev_log_status(struct v4l2_subdev *sd)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+
+	IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+			read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
+			read_reg(IVTV_REG_GPIO_IN));
+	v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
+	return 0;
+}
+
+static int subdev_s_video_routing(struct v4l2_subdev *sd,
+				  u32 input, u32 output, u32 config)
+{
+	struct ivtv *itv = sd_to_ivtv(sd);
+	u16 mask, data;
+
+	if (input > 2) /* 0:Tuner 1:Composite 2:S-Video */
+		return -EINVAL;
+	mask = itv->card->gpio_video_input.mask;
+	if (input == 0)
+		data = itv->card->gpio_video_input.tuner;
+	else if (input == 1)
+		data = itv->card->gpio_video_input.composite;
+	else
+		data = itv->card->gpio_video_input.svideo;
+	if (mask)
+		write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+	.s_ctrl = subdev_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops subdev_core_ops = {
+	.log_status = subdev_log_status,
+	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+	.g_ctrl = v4l2_subdev_g_ctrl,
+	.s_ctrl = v4l2_subdev_s_ctrl,
+	.queryctrl = v4l2_subdev_queryctrl,
+	.querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
+	.s_radio = subdev_s_radio,
+	.g_tuner = subdev_g_tuner,
+	.s_tuner = subdev_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops subdev_audio_ops = {
+	.s_clock_freq = subdev_s_clock_freq,
+	.s_routing = subdev_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops subdev_video_ops = {
+	.s_routing = subdev_s_video_routing,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+	.core = &subdev_core_ops,
+	.tuner = &subdev_tuner_ops,
+	.audio = &subdev_audio_ops,
+	.video = &subdev_video_ops,
+};
+
+int ivtv_gpio_init(struct ivtv *itv)
+{
+	u16 pin = 0;
+
+	if (itv->card->xceive_pin)
+		pin = 1 << itv->card->xceive_pin;
+
+	if ((itv->card->gpio_init.direction | pin) == 0)
+		return 0;
+
+	IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+		   read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+	/* init output data then direction */
+	write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+	write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+	v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
+	snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
+	itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+	v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+	v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	if (itv->hdl_gpio.error)
+		return itv->hdl_gpio.error;
+	itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+	v4l2_ctrl_handler_setup(&itv->hdl_gpio);
+	return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
+}
diff --git a/drivers/media/pci/ivtv/ivtv-gpio.h b/drivers/media/pci/ivtv/ivtv-gpio.h
new file mode 100644
index 000000000000..0b5d19c8ecb4
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-gpio.h
@@ -0,0 +1,29 @@
+/*
+    gpio functions.
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_GPIO_H
+#define IVTV_GPIO_H
+
+/* GPIO stuff */
+int ivtv_gpio_init(struct ivtv *itv);
+void ivtv_reset_ir_gpio(struct ivtv *itv);
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c
new file mode 100644
index 000000000000..d47f41a0ef66
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-i2c.c
@@ -0,0 +1,760 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+    This file includes an i2c implementation that was reverse engineered
+    from the Hauppauge windows driver.  Older ivtv versions used i2c-algo-bit,
+    which whilst fine under most circumstances, had trouble with the Zilog
+    CPU on the PVR-150 which handles IR functions (occasional inability to
+    communicate with the chip until it was reset) and also with the i2c
+    bus being completely unreachable when multiple PVR cards were present.
+
+    The implementation is very similar to i2c-algo-bit, but there are enough
+    subtle differences that the two are hard to merge.  The general strategy
+    employed by i2c-algo-bit is to use udelay() to implement the timing
+    when putting out bits on the scl/sda lines.  The general strategy taken
+    here is to poll the lines for state changes (see ivtv_waitscl and
+    ivtv_waitsda).  In addition there are small delays at various locations
+    which poll the SCL line 5 times (ivtv_scldelay).  I would guess that
+    since this is memory mapped I/O that the length of those delays is tied
+    to the PCI bus clock.  There is some extra code to do with recovery
+    and retries.  Since it is not known what causes the actual i2c problems
+    in the first place, the only goal if one was to attempt to use
+    i2c-algo-bit would be to try to make it follow the same code path.
+    This would be a lot of work, and I'm also not convinced that it would
+    provide a generic benefit to i2c-algo-bit.  Therefore consider this
+    an engineering solution -- not pretty, but it works.
+
+    Some more general comments about what we are doing:
+
+    The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
+    lines.  To communicate on the bus (as a master, we don't act as a slave),
+    we first initiate a start condition (ivtv_start).  We then write the
+    address of the device that we want to communicate with, along with a flag
+    that indicates whether this is a read or a write.  The slave then issues
+    an ACK signal (ivtv_ack), which tells us that it is ready for reading /
+    writing.  We then proceed with reading or writing (ivtv_read/ivtv_write),
+    and finally issue a stop condition (ivtv_stop) to make the bus available
+    to other masters.
+
+    There is an additional form of transaction where a write may be
+    immediately followed by a read.  In this case, there is no intervening
+    stop condition.  (Only the msp3400 chip uses this method of data transfer).
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-i2c.h"
+#include <media/cx25840.h>
+
+/* i2c implementation for cx23415/6 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ */
+/* i2c stuff */
+#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
+#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
+#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
+#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
+
+#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
+#define IVTV_SAA717x_I2C_ADDR 		0x21
+#define IVTV_MSP3400_I2C_ADDR 		0x40
+#define IVTV_HAUPPAUGE_I2C_ADDR 	0x50
+#define IVTV_WM8739_I2C_ADDR 		0x1a
+#define IVTV_WM8775_I2C_ADDR		0x1b
+#define IVTV_TEA5767_I2C_ADDR		0x60
+#define IVTV_UPD64031A_I2C_ADDR 	0x12
+#define IVTV_UPD64083_I2C_ADDR 		0x5c
+#define IVTV_VP27SMPX_I2C_ADDR      	0x5b
+#define IVTV_M52790_I2C_ADDR      	0x48
+#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR	0x40
+#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 	0x1a
+#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 	0x18
+#define IVTV_Z8F0811_IR_TX_I2C_ADDR	0x70
+#define IVTV_Z8F0811_IR_RX_I2C_ADDR	0x71
+#define IVTV_ADAPTEC_IR_ADDR		0x6b
+
+/* 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 */
+	IVTV_AVERMEDIA_IR_RX_I2C_ADDR,	/* IVTV_HW_I2C_IR_RX_AVER */
+	IVTV_HAUP_EXT_IR_RX_I2C_ADDR,	/* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+	IVTV_HAUP_INT_IR_RX_I2C_ADDR,	/* IVTV_HW_I2C_IR_RX_HAUP_INT */
+	IVTV_Z8F0811_IR_TX_I2C_ADDR,	/* IVTV_HW_Z8F0811_IR_TX_HAUP */
+	IVTV_Z8F0811_IR_RX_I2C_ADDR,	/* IVTV_HW_Z8F0811_IR_RX_HAUP */
+	IVTV_ADAPTEC_IR_ADDR,		/* IVTV_HW_I2C_IR_RX_ADAPTEC */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const char * const hw_devicenames[] = {
+	"cx25840",
+	"saa7115",
+	"saa7127_auto",	/* saa7127 or saa7129 */
+	"msp3400",
+	"tuner",
+	"wm8775",
+	"cs53l32a",
+	"tveeprom",
+	"saa7114",
+	"upd64031a",
+	"upd64083",
+	"saa717x",
+	"wm8739",
+	"vp27smpx",
+	"m52790",
+	"gpio",
+	"ir_video",		/* IVTV_HW_I2C_IR_RX_AVER */
+	"ir_video",		/* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+	"ir_video",		/* IVTV_HW_I2C_IR_RX_HAUP_INT */
+	"ir_tx_z8f0811_haup",	/* IVTV_HW_Z8F0811_IR_TX_HAUP */
+	"ir_rx_z8f0811_haup",	/* IVTV_HW_Z8F0811_IR_RX_HAUP */
+	"ir_video",		/* IVTV_HW_I2C_IR_RX_ADAPTEC */
+};
+
+static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char keybuf[4];
+
+	keybuf[0] = 0x00;
+	i2c_master_send(ir->c, keybuf, 1);
+	/* poll IR chip */
+	if (i2c_master_recv(ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
+		return 0;
+	}
+
+	/* key pressed ? */
+	if (keybuf[2] == 0xff)
+		return 0;
+
+	/* remove repeat bit */
+	keybuf[2] &= 0x7f;
+	keybuf[3] |= 0x80;
+
+	*ir_key = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24;
+	*ir_raw = *ir_key;
+
+	return 1;
+}
+
+static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adap = &itv->i2c_adap;
+	struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data;
+	unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+	/* Only allow one IR transmitter to be registered per board */
+	if (hw & IVTV_HW_IR_TX_ANY) {
+		if (itv->hw_flags & IVTV_HW_IR_TX_ANY)
+			return -1;
+		memset(&info, 0, sizeof(struct i2c_board_info));
+		strlcpy(info.type, type, I2C_NAME_SIZE);
+		return i2c_new_probed_device(adap, &info, addr_list, NULL)
+							   == NULL ? -1 : 0;
+	}
+
+	/* Only allow one IR receiver to be registered per board */
+	if (itv->hw_flags & IVTV_HW_IR_RX_ANY)
+		return -1;
+
+	/* Our default information for ir-kbd-i2c.c to use */
+	switch (hw) {
+	case IVTV_HW_I2C_IR_RX_AVER:
+		init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
+		init_data->internal_get_key_func =
+					IR_KBD_GET_KEY_AVERMEDIA_CARDBUS;
+		init_data->type = RC_TYPE_OTHER;
+		init_data->name = "AVerMedia AVerTV card";
+		break;
+	case IVTV_HW_I2C_IR_RX_HAUP_EXT:
+	case IVTV_HW_I2C_IR_RX_HAUP_INT:
+		init_data->ir_codes = RC_MAP_HAUPPAUGE;
+		init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
+		init_data->type = RC_TYPE_RC5;
+		init_data->name = itv->card_name;
+		break;
+	case IVTV_HW_Z8F0811_IR_RX_HAUP:
+		/* Default to grey remote */
+		init_data->ir_codes = RC_MAP_HAUPPAUGE;
+		init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+		init_data->type = RC_TYPE_RC5;
+		init_data->name = itv->card_name;
+		break;
+	case IVTV_HW_I2C_IR_RX_ADAPTEC:
+		init_data->get_key = get_key_adaptec;
+		init_data->name = itv->card_name;
+		/* FIXME: The protocol and RC_MAP needs to be corrected */
+		init_data->ir_codes = RC_MAP_EMPTY;
+		init_data->type = RC_TYPE_UNKNOWN;
+		break;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.platform_data = init_data;
+	strlcpy(info.type, type, I2C_NAME_SIZE);
+
+	return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+	       -1 : 0;
+}
+
+/* Instantiate the IR receiver device using probing -- undesirable */
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
+{
+	struct i2c_board_info info;
+	/*
+	 * The external IR receiver is at i2c address 0x34.
+	 * The internal IR receiver is at i2c address 0x30.
+	 *
+	 * In theory, both can be fitted, and Hauppauge suggests an external
+	 * overrides an internal.  That's why we probe 0x1a (~0x34) first. CB
+	 *
+	 * Some of these addresses we probe may collide with other i2c address
+	 * allocations, so this function must be called after all other i2c
+	 * devices we care about are registered.
+	 */
+	const unsigned short addr_list[] = {
+		0x1a,	/* Hauppauge IR external - collides with WM8739 */
+		0x18,	/* Hauppauge IR internal */
+		I2C_CLIENT_END
+	};
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+	return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL);
+}
+
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
+{
+	struct v4l2_subdev *sd;
+	struct i2c_adapter *adap = &itv->i2c_adap;
+	const char *type = hw_devicenames[idx];
+	u32 hw = 1 << idx;
+
+	if (idx >= ARRAY_SIZE(hw_addrs))
+		return -1;
+	if (hw == IVTV_HW_TUNER) {
+		/* special tuner handling */
+		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+				itv->card_i2c->radio);
+		if (sd)
+			sd->grp_id = 1 << idx;
+		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+				itv->card_i2c->demod);
+		if (sd)
+			sd->grp_id = 1 << idx;
+		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+				itv->card_i2c->tv);
+		if (sd)
+			sd->grp_id = 1 << idx;
+		return sd ? 0 : -1;
+	}
+
+	if (hw & IVTV_HW_IR_ANY)
+		return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]);
+
+	/* Is it not an I2C device or one we do not wish to register? */
+	if (!hw_addrs[idx])
+		return -1;
+
+	/* It's an I2C device other than an analog tuner or IR chip */
+	if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
+		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
+				adap, type, 0, I2C_ADDRS(hw_addrs[idx]));
+	} else if (hw == IVTV_HW_CX25840) {
+		struct cx25840_platform_data pdata;
+		struct i2c_board_info cx25840_info = {
+			.type = "cx25840",
+			.addr = hw_addrs[idx],
+			.platform_data = &pdata,
+		};
+
+		pdata.pvr150_workaround = itv->pvr150_workaround;
+		sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap,
+				&cx25840_info, NULL);
+	} else {
+		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
+				adap, type, hw_addrs[idx], NULL);
+	}
+	if (sd)
+		sd->grp_id = 1 << idx;
+	return sd ? 0 : -1;
+}
+
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
+{
+	struct v4l2_subdev *result = NULL;
+	struct v4l2_subdev *sd;
+
+	spin_lock(&itv->v4l2_dev.lock);
+	v4l2_device_for_each_subdev(sd, &itv->v4l2_dev) {
+		if (sd->grp_id == hw) {
+			result = sd;
+			break;
+		}
+	}
+	spin_unlock(&itv->v4l2_dev.lock);
+	return result;
+}
+
+/* Set the serial clock line to the desired state */
+static void ivtv_setscl(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+/* Set the serial data line to the desired state */
+static void ivtv_setsda(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+/* Read the serial clock line */
+static int ivtv_getscl(struct ivtv *itv)
+{
+	return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+/* Read the serial data line */
+static int ivtv_getsda(struct ivtv *itv)
+{
+	return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* Implement a short delay by polling the serial clock line */
+static void ivtv_scldelay(struct ivtv *itv)
+{
+	int i;
+
+	for (i = 0; i < 5; ++i)
+		ivtv_getscl(itv);
+}
+
+/* Wait for the serial clock line to become set to a specific value */
+static int ivtv_waitscl(struct ivtv *itv, int val)
+{
+	int i;
+
+	ivtv_scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (ivtv_getscl(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+/* Wait for the serial data line to become set to a specific value */
+static int ivtv_waitsda(struct ivtv *itv, int val)
+{
+	int i;
+
+	ivtv_scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (ivtv_getsda(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+/* Wait for the slave to issue an ACK */
+static int ivtv_ack(struct ivtv *itv)
+{
+	int ret = 0;
+
+	if (ivtv_getscl(itv) == 1) {
+		IVTV_DEBUG_HI_I2C("SCL was high starting an ack\n");
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setsda(itv, 1);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	if (!ivtv_waitsda(itv, 0)) {
+		IVTV_DEBUG_I2C("Slave did not ack\n");
+		ret = -EREMOTEIO;
+	}
+	ivtv_setscl(itv, 0);
+	if (!ivtv_waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* Write a single byte to the i2c bus and wait for the slave to ACK */
+static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
+{
+	int i, bit;
+
+	IVTV_DEBUG_HI_I2C("write %x\n",byte);
+	for (i = 0; i < 8; ++i, byte<<=1) {
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("Error setting SCL low\n");
+			return -EREMOTEIO;
+		}
+		bit = (byte>>7)&1;
+		ivtv_setsda(itv, bit);
+		if (!ivtv_waitsda(itv, bit)) {
+			IVTV_DEBUG_I2C("Error setting SDA\n");
+			return -EREMOTEIO;
+		}
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Slave not ready for bit\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setscl(itv, 0);
+	if (!ivtv_waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Error setting SCL low\n");
+		return -EREMOTEIO;
+	}
+	return ivtv_ack(itv);
+}
+
+/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
+   final byte) */
+static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+	int i;
+
+	*byte = 0;
+
+	ivtv_setsda(itv, 1);
+	ivtv_scldelay(itv);
+	for (i = 0; i < 8; ++i) {
+		ivtv_setscl(itv, 0);
+		ivtv_scldelay(itv);
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Error setting SCL high\n");
+			return -EREMOTEIO;
+		}
+		*byte = ((*byte)<<1)|ivtv_getsda(itv);
+	}
+	ivtv_setscl(itv, 0);
+	ivtv_scldelay(itv);
+	ivtv_setsda(itv, nack);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 0);
+	ivtv_scldelay(itv);
+	IVTV_DEBUG_HI_I2C("read %x\n",*byte);
+	return 0;
+}
+
+/* Issue a start condition on the i2c bus to alert slaves to prepare for
+   an address write */
+static int ivtv_start(struct ivtv *itv)
+{
+	int sda;
+
+	sda = ivtv_getsda(itv);
+	if (sda != 1) {
+		IVTV_DEBUG_HI_I2C("SDA was low at start\n");
+		ivtv_setsda(itv, 1);
+		if (!ivtv_waitsda(itv, 1)) {
+			IVTV_DEBUG_I2C("SDA stuck low\n");
+			return -EREMOTEIO;
+		}
+	}
+	if (ivtv_getscl(itv) != 1) {
+		ivtv_setscl(itv, 1);
+		if (!ivtv_waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("SCL stuck low at start\n");
+			return -EREMOTEIO;
+		}
+	}
+	ivtv_setsda(itv, 0);
+	ivtv_scldelay(itv);
+	return 0;
+}
+
+/* Issue a stop condition on the i2c bus to release it */
+static int ivtv_stop(struct ivtv *itv)
+{
+	int i;
+
+	if (ivtv_getscl(itv) != 0) {
+		IVTV_DEBUG_HI_I2C("SCL not low when stopping\n");
+		ivtv_setscl(itv, 0);
+		if (!ivtv_waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("SCL could not be set low\n");
+		}
+	}
+	ivtv_setsda(itv, 0);
+	ivtv_scldelay(itv);
+	ivtv_setscl(itv, 1);
+	if (!ivtv_waitscl(itv, 1)) {
+		IVTV_DEBUG_I2C("SCL could not be set high\n");
+		return -EREMOTEIO;
+	}
+	ivtv_scldelay(itv);
+	ivtv_setsda(itv, 1);
+	if (!ivtv_waitsda(itv, 1)) {
+		IVTV_DEBUG_I2C("resetting I2C\n");
+		for (i = 0; i < 16; ++i) {
+			ivtv_setscl(itv, 0);
+			ivtv_scldelay(itv);
+			ivtv_setscl(itv, 1);
+			ivtv_scldelay(itv);
+			ivtv_setsda(itv, 1);
+		}
+		ivtv_waitsda(itv, 1);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+/* Write a message to the given i2c slave.  do_stop may be 0 to prevent
+   issuing the i2c stop condition (when following with a read) */
+static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = ivtv_start(itv);
+
+		if (ret == 0) {
+			ret = ivtv_sendbyte(itv, addr<<1);
+			for (i = 0; ret == 0 && i < len; ++i)
+				ret = ivtv_sendbyte(itv, data[i]);
+		}
+		if (ret != 0 || do_stop) {
+			ivtv_stop(itv);
+		}
+	}
+	if (ret)
+		IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+	return ret;
+}
+
+/* Read data from the given i2c slave.  A stop condition is always issued. */
+static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = ivtv_start(itv);
+		if (ret == 0)
+			ret = ivtv_sendbyte(itv, (addr << 1) | 1);
+		for (i = 0; ret == 0 && i < len; ++i) {
+			ret = ivtv_readbyte(itv, &data[i], i == len - 1);
+		}
+		ivtv_stop(itv);
+	}
+	if (ret)
+		IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+	return ret;
+}
+
+/* Kernel i2c transfer implementation.  Takes a number of messages to be read
+   or written.  If a read follows a write, this will occur without an
+   intervening stop condition */
+static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
+	struct ivtv *itv = to_ivtv(v4l2_dev);
+	int retval;
+	int i;
+
+	mutex_lock(&itv->i2c_bus_lock);
+	for (i = retval = 0; retval == 0 && i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD)
+			retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+		else {
+			/* if followed by a read, don't stop */
+			int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
+
+			retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
+		}
+	}
+	mutex_unlock(&itv->i2c_bus_lock);
+	return retval ? retval : num;
+}
+
+/* Kernel i2c capabilities */
+static u32 ivtv_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+	.master_xfer   = ivtv_xfer,
+	.functionality = ivtv_functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
+	.name = "ivtv i2c driver",
+	.algo = &ivtv_algo,
+	.algo_data = NULL,			/* filled from template */
+	.owner = THIS_MODULE,
+};
+
+static void ivtv_setscl_old(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void ivtv_setsda_old(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int ivtv_getscl_old(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int ivtv_getsda_old(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+	.name = "ivtv i2c driver",
+	.algo = NULL,                   /* set by i2c-algo-bit */
+	.algo_data = NULL,              /* filled from template */
+	.owner = THIS_MODULE,
+};
+
+#define IVTV_ALGO_BIT_TIMEOUT	(2)	/* seconds */
+
+static const struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+	.setsda		= ivtv_setsda_old,
+	.setscl		= ivtv_setscl_old,
+	.getsda		= ivtv_getsda_old,
+	.getscl		= ivtv_getscl_old,
+	.udelay		= IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2,  /* microseconds */
+	.timeout	= IVTV_ALGO_BIT_TIMEOUT * HZ,         /* jiffies */
+};
+
+static struct i2c_client ivtv_i2c_client_template = {
+	.name = "ivtv internal",
+};
+
+/* init + register i2c adapter */
+int init_ivtv_i2c(struct ivtv *itv)
+{
+	int retval;
+
+	IVTV_DEBUG_I2C("i2c init\n");
+
+	/* Sanity checks for the I2C hardware arrays. They must be the
+	 * same size.
+	 */
+	if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs)) {
+		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));
+	} else {
+		memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+		       sizeof(struct i2c_adapter));
+		memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+		       sizeof(struct i2c_algo_bit_data));
+	}
+	itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2;
+	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->instance);
+	i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev);
+
+	memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+	       sizeof(struct i2c_client));
+	itv->i2c_client.adapter = &itv->i2c_adap;
+	itv->i2c_adap.dev.parent = &itv->pdev->dev;
+
+	IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+	ivtv_setscl(itv, 1);
+	ivtv_setsda(itv, 1);
+
+	if (itv->options.newi2c > 0)
+		retval = i2c_add_adapter(&itv->i2c_adap);
+	else
+		retval = i2c_bit_add_bus(&itv->i2c_adap);
+
+	return retval;
+}
+
+void exit_ivtv_i2c(struct ivtv *itv)
+{
+	IVTV_DEBUG_I2C("i2c exit\n");
+
+	i2c_del_adapter(&itv->i2c_adap);
+}
diff --git a/drivers/media/pci/ivtv/ivtv-i2c.h b/drivers/media/pci/ivtv/ivtv-i2c.h
new file mode 100644
index 000000000000..7b9ec1cfeb80
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-i2c.h
@@ -0,0 +1,32 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_I2C_H
+#define IVTV_I2C_H
+
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv);
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
+
+/* init + register i2c adapter */
+int init_ivtv_i2c(struct ivtv *itv);
+void exit_ivtv_i2c(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
new file mode 100644
index 000000000000..949ae230e119
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -0,0 +1,1927 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-event.h>
+#include <linux/dvb/audio.h>
+
+u16 ivtv_service2vbi(int type)
+{
+	switch (type) {
+		case V4L2_SLICED_TELETEXT_B:
+			return IVTV_SLICED_TYPE_TELETEXT_B;
+		case V4L2_SLICED_CAPTION_525:
+			return IVTV_SLICED_TYPE_CAPTION_525;
+		case V4L2_SLICED_WSS_625:
+			return IVTV_SLICED_TYPE_WSS_625;
+		case V4L2_SLICED_VPS:
+			return IVTV_SLICED_TYPE_VPS;
+		default:
+			return 0;
+	}
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+	return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+	       (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+	int i;
+
+	set = set & valid_set;
+	if (set == 0 || !valid_service_line(field, line, is_pal)) {
+		return 0;
+	}
+	if (!is_pal) {
+		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+			return V4L2_SLICED_CAPTION_525;
+	}
+	else {
+		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+			return V4L2_SLICED_VPS;
+		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+			return V4L2_SLICED_WSS_625;
+		if (line == 23)
+			return 0;
+	}
+	for (i = 0; i < 32; i++) {
+		if ((1 << i) & set)
+			return 1 << i;
+	}
+	return 0;
+}
+
+void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	u16 set = fmt->service_set;
+	int f, l;
+
+	fmt->service_set = 0;
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+		}
+	}
+}
+
+static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+	int f, l;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+		}
+	}
+}
+
+u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+	int f, l;
+	u16 set = 0;
+
+	for (f = 0; f < 2; f++) {
+		for (l = 0; l < 24; l++) {
+			set |= fmt->service_lines[f][l];
+		}
+	}
+	return set;
+}
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+	ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+		itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+	ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int single_step = (speed == 1 || speed == -1);
+	DEFINE_WAIT(wait);
+
+	if (speed == 0) speed = 1000;
+
+	/* No change? */
+	if (speed == itv->speed && !single_step)
+		return 0;
+
+	if (single_step && (speed < 0) == (itv->speed < 0)) {
+		/* Single step video and no need to change direction */
+		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+		itv->speed = speed;
+		return 0;
+	}
+	if (single_step)
+		/* Need to change direction */
+		speed = speed < 0 ? -1000 : 1000;
+
+	data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+	data[1] = (speed < 0);
+	data[2] = speed < 0 ? 3 : 7;
+	data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
+	data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+	data[5] = 0;
+	data[6] = 0;
+
+	if (speed == 1500 || speed == -1500) data[0] |= 1;
+	else if (speed == 2000 || speed == -2000) data[0] |= 2;
+	else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+	else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+	/* If not decoding, just change speed setting */
+	if (atomic_read(&itv->decoding) > 0) {
+		int got_sig = 0;
+
+		/* Stop all DMA and decoding activity */
+		ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+		/* Wait for any DMA to finish */
+		mutex_unlock(&itv->serialize_lock);
+		prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+		while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+			got_sig = signal_pending(current);
+			if (got_sig)
+				break;
+			got_sig = 0;
+			schedule();
+		}
+		finish_wait(&itv->dma_waitq, &wait);
+		mutex_lock(&itv->serialize_lock);
+		if (got_sig)
+			return -EINTR;
+
+		/* Change Speed safely */
+		ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+		IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+				data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+	}
+	if (single_step) {
+		speed = (speed < 0) ? -1 : 1;
+		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+	}
+	itv->speed = speed;
+	return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+	int fact = new_speed < 0 ? -1 : 1;
+	int s;
+
+	if (cur_speed == 0)
+		cur_speed = 1000;
+	if (new_speed < 0)
+		new_speed = -new_speed;
+	if (cur_speed < 0)
+		cur_speed = -cur_speed;
+
+	if (cur_speed <= new_speed) {
+		if (new_speed > 1500)
+			return fact * 2000;
+		if (new_speed > 1000)
+			return fact * 1500;
+	}
+	else {
+		if (new_speed >= 2000)
+			return fact * 2000;
+		if (new_speed >= 1500)
+			return fact * 1500;
+		if (new_speed >= 1000)
+			return fact * 1000;
+	}
+	if (new_speed == 0)
+		return 1000;
+	if (new_speed == 1 || new_speed == 1000)
+		return fact * new_speed;
+
+	s = new_speed;
+	new_speed = 1000 / new_speed;
+	if (1000 / cur_speed == new_speed)
+		new_speed += (cur_speed < s) ? -1 : 1;
+	if (new_speed > 60) return 1000 / (fact * 60);
+	return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+		struct v4l2_decoder_cmd *dc, int try)
+{
+	struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	switch (dc->cmd) {
+	case V4L2_DEC_CMD_START: {
+		dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
+		dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
+		if (dc->start.speed < 0)
+			dc->start.format = V4L2_DEC_START_FMT_GOP;
+		else
+			dc->start.format = V4L2_DEC_START_FMT_NONE;
+		if (dc->start.speed != 500 && dc->start.speed != 1500)
+			dc->flags = dc->start.speed == 1000 ? 0 :
+					V4L2_DEC_CMD_START_MUTE_AUDIO;
+		if (try) break;
+
+		itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
+		if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+			return -EBUSY;
+		if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+			/* forces ivtv_set_speed to be called */
+			itv->speed = 0;
+		}
+		return ivtv_start_decoding(id, dc->start.speed);
+	}
+
+	case V4L2_DEC_CMD_STOP:
+		dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
+		if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
+			dc->stop.pts = 0;
+		if (try) break;
+		if (atomic_read(&itv->decoding) == 0)
+			return 0;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+
+		itv->output_mode = OUT_NONE;
+		return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
+
+	case V4L2_DEC_CMD_PAUSE:
+		dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
+		if (try) break;
+		if (!atomic_read(&itv->decoding))
+			return -EPERM;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+		if (atomic_read(&itv->decoding) > 0) {
+			ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+				(dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
+			set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+		}
+		break;
+
+	case V4L2_DEC_CMD_RESUME:
+		dc->flags = 0;
+		if (try) break;
+		if (!atomic_read(&itv->decoding))
+			return -EPERM;
+		if (itv->output_mode != OUT_MPG)
+			return -EBUSY;
+		if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+			int speed = itv->speed;
+			itv->speed = 0;
+			return ivtv_start_decoding(id, speed);
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+	if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+		return -EINVAL;
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+	memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+	if (itv->is_60hz) {
+		vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+		vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+	} else {
+		vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+		vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+	}
+	vbifmt->service_set = ivtv_get_service_set(vbifmt);
+	return 0;
+}
+
+static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+	pixfmt->width = itv->cxhdl.width;
+	pixfmt->height = itv->cxhdl.height;
+	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	pixfmt->field = V4L2_FIELD_INTERLACED;
+	pixfmt->priv = 0;
+	if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+		pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+		/* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
+		pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
+		pixfmt->bytesperline = 720;
+	} else {
+		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+		pixfmt->sizeimage = 128 * 1024;
+		pixfmt->bytesperline = 0;
+	}
+	return 0;
+}
+
+static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+	vbifmt->sampling_rate = 27000000;
+	vbifmt->offset = 248;
+	vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+	vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+	vbifmt->start[0] = itv->vbi.start[0];
+	vbifmt->start[1] = itv->vbi.start[1];
+	vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;
+	vbifmt->flags = 0;
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+	return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+	if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+			V4L2_SLICED_VBI_525;
+		ivtv_expand_service_set(vbifmt, itv->is_50hz);
+		vbifmt->service_set = ivtv_get_service_set(vbifmt);
+		return 0;
+	}
+
+	v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt);
+	vbifmt->service_set = ivtv_get_service_set(vbifmt);
+	return 0;
+}
+
+static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+	pixfmt->width = itv->main_rect.width;
+	pixfmt->height = itv->main_rect.height;
+	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	pixfmt->field = V4L2_FIELD_INTERLACED;
+	pixfmt->priv = 0;
+	if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {
+		switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+		case IVTV_YUV_MODE_INTERLACED:
+			pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+				V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+			break;
+		case IVTV_YUV_MODE_PROGRESSIVE:
+			pixfmt->field = V4L2_FIELD_NONE;
+			break;
+		default:
+			pixfmt->field = V4L2_FIELD_ANY;
+			break;
+		}
+		pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+		pixfmt->bytesperline = 720;
+		pixfmt->width = itv->yuv_info.v4l2_src_w;
+		pixfmt->height = itv->yuv_info.v4l2_src_h;
+		/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+		pixfmt->sizeimage =
+			1080 * ((pixfmt->height + 31) & ~31);
+	} else {
+		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+		pixfmt->sizeimage = 128 * 1024;
+		pixfmt->bytesperline = 0;
+	}
+	return 0;
+}
+
+static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct v4l2_window *winfmt = &fmt->fmt.win;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+	winfmt->chromakey = itv->osd_chroma_key;
+	winfmt->global_alpha = itv->osd_global_alpha;
+	winfmt->field = V4L2_FIELD_INTERLACED;
+	winfmt->clips = NULL;
+	winfmt->clipcount = 0;
+	winfmt->bitmap = NULL;
+	winfmt->w.top = winfmt->w.left = 0;
+	winfmt->w.width = itv->osd_rect.width;
+	winfmt->w.height = itv->osd_rect.height;
+	return 0;
+}
+
+static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	int w = fmt->fmt.pix.width;
+	int h = fmt->fmt.pix.height;
+	int min_h = 2;
+
+	w = min(w, 720);
+	w = max(w, 2);
+	if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+		/* YUV height must be a multiple of 32 */
+		h &= ~0x1f;
+		min_h = 32;
+	}
+	h = min(h, itv->is_50hz ? 576 : 480);
+	h = max(h, min_h);
+	ivtv_g_fmt_vid_cap(file, fh, fmt);
+	fmt->fmt.pix.width = w;
+	fmt->fmt.pix.height = h;
+	return 0;
+}
+
+static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+
+	if (id->type == IVTV_DEC_STREAM_TYPE_VBI)
+		return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt);
+
+	/* set sliced VBI capture format */
+	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+	vbifmt->reserved[0] = 0;
+	vbifmt->reserved[1] = 0;
+
+	if (vbifmt->service_set)
+		ivtv_expand_service_set(vbifmt, itv->is_50hz);
+	check_service_set(vbifmt, itv->is_50hz);
+	vbifmt->service_set = ivtv_get_service_set(vbifmt);
+	return 0;
+}
+
+static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	s32 w = fmt->fmt.pix.width;
+	s32 h = fmt->fmt.pix.height;
+	int field = fmt->fmt.pix.field;
+	int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
+
+	w = min(w, 720);
+	w = max(w, 2);
+	/* Why can the height be 576 even when the output is NTSC?
+
+	   Internally the buffers of the PVR350 are always set to 720x576. The
+	   decoded video frame will always be placed in the top left corner of
+	   this buffer. For any video which is not 720x576, the buffer will
+	   then be cropped to remove the unused right and lower areas, with
+	   the remaining image being scaled by the hardware to fit the display
+	   area. The video can be scaled both up and down, so a 720x480 video
+	   can be displayed full-screen on PAL and a 720x576 video can be
+	   displayed without cropping on NTSC.
+
+	   Note that the scaling only occurs on the video stream, the osd
+	   resolution is locked to the broadcast standard and not scaled.
+
+	   Thanks to Ian Armstrong for this explanation. */
+	h = min(h, 576);
+	h = max(h, 2);
+	if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
+		fmt->fmt.pix.field = field;
+	fmt->fmt.pix.width = w;
+	fmt->fmt.pix.height = h;
+	return ret;
+}
+
+static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	u32 chromakey = fmt->fmt.win.chromakey;
+	u8 global_alpha = fmt->fmt.win.global_alpha;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+	ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
+	fmt->fmt.win.chromakey = chromakey;
+	fmt->fmt.win.global_alpha = global_alpha;
+	return 0;
+}
+
+static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
+	int w = fmt->fmt.pix.width;
+	int h = fmt->fmt.pix.height;
+
+	if (ret)
+		return ret;
+
+	if (itv->cxhdl.width == w && itv->cxhdl.height == h)
+		return 0;
+
+	if (atomic_read(&itv->capturing) > 0)
+		return -EBUSY;
+
+	itv->cxhdl.width = w;
+	itv->cxhdl.height = h;
+	if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+		fmt->fmt.pix.width /= 2;
+	mbus_fmt.width = fmt->fmt.pix.width;
+	mbus_fmt.height = h;
+	mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+	v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt);
+	return ivtv_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+		return -EBUSY;
+	itv->vbi.sliced_in->service_set = 0;
+	itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi);
+	return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+	if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
+		return ret;
+
+	check_service_set(vbifmt, itv->is_50hz);
+	if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+		return -EBUSY;
+	itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+	v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt);
+	memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+	return 0;
+}
+
+static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	int ret = ivtv_try_fmt_vid_out(file, fh, fmt);
+
+	if (ret)
+		return ret;
+
+	if (id->type != IVTV_DEC_STREAM_TYPE_YUV)
+		return 0;
+
+	/* Return now if we already have some frame data */
+	if (yi->stream_size)
+		return -EBUSY;
+
+	yi->v4l2_src_w = fmt->fmt.pix.width;
+	yi->v4l2_src_h = fmt->fmt.pix.height;
+
+	switch (fmt->fmt.pix.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);
+
+	return 0;
+}
+
+static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt);
+
+	if (ret == 0) {
+		itv->osd_chroma_key = fmt->fmt.win.chromakey;
+		itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+		ivtv_set_osd_alpha(itv);
+	}
+	return ret;
+}
+
+static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
+		if (v4l2_chip_match_host(&chip->match))
+			chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+		return 0;
+	}
+	if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+	    chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+		return -EINVAL;
+	/* TODO: is this correct? */
+	return ivtv_call_all_err(itv, core, g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+	struct v4l2_dbg_register *regs = arg;
+	volatile u8 __iomem *reg_start;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+		reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+	else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+			regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+		reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+	else if (regs->reg < IVTV_ENCODER_SIZE)
+		reg_start = itv->enc_mem;
+	else
+		return -EINVAL;
+
+	regs->size = 4;
+	if (cmd == VIDIOC_DBG_G_REGISTER)
+		regs->val = readl(regs->reg + reg_start);
+	else
+		writel(regs->val, regs->reg + reg_start);
+	return 0;
+}
+
+static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (v4l2_chip_match_host(&reg->match))
+		return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
+	/* TODO: subdev errors should not be ignored, this should become a
+	   subdev helper function. */
+	ivtv_call_all(itv, core, g_register, reg);
+	return 0;
+}
+
+static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (v4l2_chip_match_host(&reg->match))
+		return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
+	/* TODO: subdev errors should not be ignored, this should become a
+	   subdev helper function. */
+	ivtv_call_all(itv, core, s_register, reg);
+	return 0;
+}
+#endif
+
+static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
+{
+	struct ivtv_open_id *id = fh2id(file->private_data);
+	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[id->type];
+
+	strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+	strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+	snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
+	vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
+	vcap->device_caps = s->caps;
+	return 0;
+}
+
+static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	vin->index = itv->audio_input;
+	return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (vout->index >= itv->nof_audio_inputs)
+		return -EINVAL;
+
+	itv->audio_input = vout->index;
+	ivtv_audio_set_io(itv);
+
+	return 0;
+}
+
+static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	/* set it to defaults from our table */
+	return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	vin->index = 0;
+	return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (itv->card->video_outputs == NULL || vout->index != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	/* set it to defaults from our table */
+	return ivtv_get_input(itv, vin->index, vin);
+}
+
+static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	return ivtv_get_output(itv, vout->index, vout);
+}
+
+static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	int streamtype;
+
+	streamtype = id->type;
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return -EINVAL;
+	cropcap->bounds.top = cropcap->bounds.left = 0;
+	cropcap->bounds.width = 720;
+	if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		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) {
+		if (yi->track_osd) {
+			cropcap->bounds.width = yi->osd_full_w;
+			cropcap->bounds.height = yi->osd_full_h;
+		} else {
+			cropcap->bounds.width = 720;
+			cropcap->bounds.height =
+					itv->is_out_50hz ? 576 : 480;
+		}
+		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;
+		cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+	}
+	cropcap->defrect = cropcap->bounds;
+	return 0;
+}
+
+static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	int streamtype;
+
+	streamtype = id->type;
+
+	if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+		if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+			yi->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;
+	}
+	return -EINVAL;
+}
+
+static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	int streamtype;
+
+	streamtype = id->type;
+
+	if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+		if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+			crop->c = yi->main_rect;
+		else
+			crop->c = itv->main_rect;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+	static const struct v4l2_fmtdesc hm12 = {
+		0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+		"HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+		{ 0, 0, 0, 0 }
+	};
+	static const struct v4l2_fmtdesc mpeg = {
+		0, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+		"MPEG", V4L2_PIX_FMT_MPEG,
+		{ 0, 0, 0, 0 }
+	};
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+	if (fmt->index)
+		return -EINVAL;
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+		*fmt = mpeg;
+	else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+		*fmt = hm12;
+	else
+		return -EINVAL;
+	return 0;
+}
+
+static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+	static const struct v4l2_fmtdesc hm12 = {
+		0, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0,
+		"HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+		{ 0, 0, 0, 0 }
+	};
+	static const struct v4l2_fmtdesc mpeg = {
+		0, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FMT_FLAG_COMPRESSED,
+		"MPEG", V4L2_PIX_FMT_MPEG,
+		{ 0, 0, 0, 0 }
+	};
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+	if (fmt->index)
+		return -EINVAL;
+	if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+		*fmt = mpeg;
+	else if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+		*fmt = hm12;
+	else
+		return -EINVAL;
+	return 0;
+}
+
+static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	*i = itv->active_input;
+
+	return 0;
+}
+
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	v4l2_std_id std;
+	int i;
+
+	if (inp < 0 || inp >= itv->nof_inputs)
+		return -EINVAL;
+
+	if (inp == itv->active_input) {
+		IVTV_DEBUG_INFO("Input unchanged\n");
+		return 0;
+	}
+
+	if (atomic_read(&itv->capturing) > 0) {
+		return -EBUSY;
+	}
+
+	IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+			itv->active_input, inp);
+
+	itv->active_input = inp;
+	/* Set the audio input to whatever is appropriate for the
+	   input type. */
+	itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+	if (itv->card->video_inputs[inp].video_type == IVTV_CARD_INPUT_VID_TUNER)
+		std = itv->tuner_std;
+	else
+		std = V4L2_STD_ALL;
+	for (i = 0; i <= IVTV_ENC_STREAM_TYPE_VBI; i++)
+		itv->streams[i].vdev->tvnorms = std;
+
+	/* prevent others from messing with the streams until
+	   we're finished changing inputs. */
+	ivtv_mute(itv);
+	ivtv_video_set_io(itv);
+	ivtv_audio_set_io(itv);
+	ivtv_unmute(itv);
+
+	return 0;
+}
+
+static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	*i = itv->active_output;
+
+	return 0;
+}
+
+static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (outp >= itv->card->nof_outputs)
+		return -EINVAL;
+
+	if (outp == itv->active_output) {
+		IVTV_DEBUG_INFO("Output unchanged\n");
+		return 0;
+	}
+	IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+		   itv->active_output, outp);
+
+	itv->active_output = outp;
+	ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+			SAA7127_INPUT_TYPE_NORMAL,
+			itv->card->video_outputs[outp].video_output, 0);
+
+	return 0;
+}
+
+static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+	if (s->vdev->vfl_dir)
+		return -ENOTTY;
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	ivtv_call_all(itv, tuner, g_frequency, vf);
+	return 0;
+}
+
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+	if (s->vdev->vfl_dir)
+		return -ENOTTY;
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	ivtv_mute(itv);
+	IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+	ivtv_call_all(itv, tuner, s_frequency, vf);
+	ivtv_unmute(itv);
+	return 0;
+}
+
+static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	*std = itv->std;
+	return 0;
+}
+
+void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std)
+{
+	itv->std = *std;
+	itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+	itv->is_50hz = !itv->is_60hz;
+	cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+	itv->cxhdl.width = 720;
+	itv->cxhdl.height = itv->is_50hz ? 576 : 480;
+	itv->vbi.count = itv->is_50hz ? 18 : 12;
+	itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+	itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+
+	if (itv->hw_flags & IVTV_HW_CX25840)
+		itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+
+	/* Tuner */
+	ivtv_call_all(itv, core, s_std, itv->std);
+}
+
+void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std)
+{
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	DEFINE_WAIT(wait);
+	int f;
+
+	/* set display standard */
+	itv->std_out = *std;
+	itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+	itv->is_out_50hz = !itv->is_out_60hz;
+	ivtv_call_all(itv, video, s_std_output, itv->std_out);
+
+	/*
+	 * The next firmware call is time sensitive. Time it to
+	 * avoid risk of a hard lock, by trying to ensure the call
+	 * happens within the first 100 lines of the top field.
+	 * Make 4 attempts to sync to the decoder before giving up.
+	 */
+	mutex_unlock(&itv->serialize_lock);
+	for (f = 0; f < 4; f++) {
+		prepare_to_wait(&itv->vsync_waitq, &wait,
+				TASK_UNINTERRUPTIBLE);
+		if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100)
+			break;
+		schedule_timeout(msecs_to_jiffies(25));
+	}
+	finish_wait(&itv->vsync_waitq, &wait);
+	mutex_lock(&itv->serialize_lock);
+
+	if (f == 4)
+		IVTV_WARN("Mode change failed to sync to decoder\n");
+
+	ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+	itv->main_rect.left = 0;
+	itv->main_rect.top = 0;
+	itv->main_rect.width = 720;
+	itv->main_rect.height = itv->is_out_50hz ? 576 : 480;
+	ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+		720, itv->main_rect.height, 0, 0);
+	yi->main_rect = itv->main_rect;
+	if (!itv->osd_info) {
+		yi->osd_full_w = 720;
+		yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
+	}
+}
+
+int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if ((*std & V4L2_STD_ALL) == 0)
+		return -EINVAL;
+
+	if (*std == itv->std)
+		return 0;
+
+	if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+	    atomic_read(&itv->capturing) > 0 ||
+	    atomic_read(&itv->decoding) > 0) {
+		/* Switching standard would mess with already running
+		   streams, prevent that by returning EBUSY. */
+		return -EBUSY;
+	}
+
+	IVTV_DEBUG_INFO("Switching standard to %llx.\n",
+		(unsigned long long)itv->std);
+
+	ivtv_s_std_enc(itv, std);
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+		ivtv_s_std_dec(itv, std);
+
+	return 0;
+}
+
+static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+
+	if (vt->index != 0)
+		return -EINVAL;
+
+	ivtv_call_all(itv, tuner, s_tuner, vt);
+
+	return 0;
+}
+
+static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (vt->index != 0)
+		return -EINVAL;
+
+	ivtv_call_all(itv, tuner, g_tuner, vt);
+
+	if (vt->type == V4L2_TUNER_RADIO)
+		strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
+	else
+		strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
+	return 0;
+}
+
+static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+	int f, l;
+
+	if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+		for (f = 0; f < 2; f++) {
+			for (l = 0; l < 24; l++) {
+				if (valid_service_line(f, l, itv->is_50hz))
+					cap->service_lines[f][l] = set;
+			}
+		}
+	} else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+		if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+			return -EINVAL;
+		if (itv->is_60hz) {
+			cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+			cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+		} else {
+			cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+			cap->service_lines[0][16] = V4L2_SLICED_VPS;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	set = 0;
+	for (f = 0; f < 2; f++)
+		for (l = 0; l < 24; l++)
+			set |= cap->service_lines[f][l];
+	cap->service_set = set;
+	return 0;
+}
+
+static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	struct v4l2_enc_idx_entry *e = idx->entry;
+	int entries;
+	int i;
+
+	entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+				IVTV_MAX_PGM_INDEX;
+	if (entries > V4L2_ENC_IDX_ENTRIES)
+		entries = V4L2_ENC_IDX_ENTRIES;
+	idx->entries = 0;
+	idx->entries_cap = IVTV_MAX_PGM_INDEX;
+	if (!atomic_read(&itv->capturing))
+		return 0;
+	for (i = 0; i < entries; i++) {
+		*e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+		if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
+			idx->entries++;
+			e++;
+		}
+	}
+	itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+	return 0;
+}
+
+static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+
+
+	switch (enc->cmd) {
+	case V4L2_ENC_CMD_START:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+		enc->flags = 0;
+		return ivtv_start_capture(id);
+
+	case V4L2_ENC_CMD_STOP:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+		ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+		return 0;
+
+	case V4L2_ENC_CMD_PAUSE:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+		enc->flags = 0;
+
+		if (!atomic_read(&itv->capturing))
+			return -EPERM;
+		if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+			return 0;
+
+		ivtv_mute(itv);
+		ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+		break;
+
+	case V4L2_ENC_CMD_RESUME:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+		enc->flags = 0;
+
+		if (!atomic_read(&itv->capturing))
+			return -EPERM;
+
+		if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+			return 0;
+
+		ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+		ivtv_unmute(itv);
+		break;
+	default:
+		IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	switch (enc->cmd) {
+	case V4L2_ENC_CMD_START:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+		enc->flags = 0;
+		return 0;
+
+	case V4L2_ENC_CMD_STOP:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+		return 0;
+
+	case V4L2_ENC_CMD_PAUSE:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+		enc->flags = 0;
+		return 0;
+
+	case V4L2_ENC_CMD_RESUME:
+		IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+		enc->flags = 0;
+		return 0;
+	default:
+		IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+		return -EINVAL;
+	}
+}
+
+static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct yuv_playback_info *yi = &itv->yuv_info;
+
+	int pixfmt;
+	static u32 pixel_format[16] = {
+		V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
+		V4L2_PIX_FMT_RGB565,
+		V4L2_PIX_FMT_RGB555,
+		V4L2_PIX_FMT_RGB444,
+		V4L2_PIX_FMT_RGB32,
+		0,
+		0,
+		0,
+		V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
+		V4L2_PIX_FMT_YUV565,
+		V4L2_PIX_FMT_YUV555,
+		V4L2_PIX_FMT_YUV444,
+		V4L2_PIX_FMT_YUV32,
+		0,
+		0,
+		0,
+	};
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -EINVAL;
+	if (!itv->osd_video_pbase)
+		return -EINVAL;
+
+	fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+		V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+	ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+	data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+	pixfmt = (data[0] >> 3) & 0xf;
+
+	fb->fmt.pixelformat = pixel_format[pixfmt];
+	fb->fmt.width = itv->osd_rect.width;
+	fb->fmt.height = itv->osd_rect.height;
+	fb->fmt.field = V4L2_FIELD_INTERLACED;
+	fb->fmt.bytesperline = fb->fmt.width;
+	fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+	fb->fmt.field = V4L2_FIELD_INTERLACED;
+	fb->fmt.priv = 0;
+	if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8)
+		fb->fmt.bytesperline *= 2;
+	if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
+	    fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32)
+		fb->fmt.bytesperline *= 2;
+	fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height;
+	fb->base = (void *)itv->osd_video_pbase;
+	fb->flags = 0;
+
+	if (itv->osd_chroma_key_state)
+		fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+	if (itv->osd_global_alpha_state)
+		fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+
+	if (yi->track_osd)
+		fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
+
+	pixfmt &= 7;
+
+	/* no local alpha for RGB565 or unknown formats */
+	if (pixfmt == 1 || pixfmt > 4)
+		return 0;
+
+	/* 16-bit formats have inverted local alpha */
+	if (pixfmt == 2 || pixfmt == 3)
+		fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+	else
+		fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
+
+	if (itv->osd_local_alpha_state) {
+		/* 16-bit formats have inverted local alpha */
+		if (pixfmt == 2 || pixfmt == 3)
+			fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+		else
+			fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+	}
+
+	return 0;
+}
+
+static int ivtv_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -EINVAL;
+	if (!itv->osd_video_pbase)
+		return -EINVAL;
+
+	itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+	itv->osd_local_alpha_state =
+		(fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
+	itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+	ivtv_set_osd_alpha(itv);
+	yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+	return 0;
+}
+
+static int ivtv_overlay(struct file *file, void *fh, unsigned int on)
+{
+	struct ivtv_open_id *id = fh2id(fh);
+	struct ivtv *itv = id->itv;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -EINVAL;
+
+	ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
+
+	return 0;
+}
+
+static int ivtv_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_VSYNC:
+	case V4L2_EVENT_EOS:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	case V4L2_EVENT_CTRL:
+		return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ivtv_log_status(struct file *file, void *fh)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+	struct v4l2_input vidin;
+	struct v4l2_audio audin;
+	int i;
+
+	IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
+	if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+		struct tveeprom tv;
+
+		ivtv_read_eeprom(itv, &tv);
+	}
+	ivtv_call_all(itv, core, log_status);
+	ivtv_get_input(itv, itv->active_input, &vidin);
+	ivtv_get_audio_input(itv, itv->audio_input, &audin);
+	IVTV_INFO("Video Input:  %s\n", vidin.name);
+	IVTV_INFO("Audio Input:  %s%s\n", audin.name,
+		(itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
+	if (has_output) {
+		struct v4l2_output vidout;
+		struct v4l2_audioout audout;
+		int mode = itv->output_mode;
+		static const char * const output_modes[5] = {
+			"None",
+			"MPEG Streaming",
+			"YUV Streaming",
+			"YUV Frames",
+			"Passthrough",
+		};
+		static const char * const alpha_mode[4] = {
+			"None",
+			"Global",
+			"Local",
+			"Global and Local"
+		};
+		static const char * const pixel_format[16] = {
+			"ARGB Indexed",
+			"RGB 5:6:5",
+			"ARGB 1:5:5:5",
+			"ARGB 1:4:4:4",
+			"ARGB 8:8:8:8",
+			"5",
+			"6",
+			"7",
+			"AYUV Indexed",
+			"YUV 5:6:5",
+			"AYUV 1:5:5:5",
+			"AYUV 1:4:4:4",
+			"AYUV 8:8:8:8",
+			"13",
+			"14",
+			"15",
+		};
+
+		ivtv_get_output(itv, itv->active_output, &vidout);
+		ivtv_get_audio_output(itv, 0, &audout);
+		IVTV_INFO("Video Output: %s\n", vidout.name);
+		if (mode < 0 || mode > OUT_PASSTHROUGH)
+			mode = OUT_NONE;
+		IVTV_INFO("Output Mode:  %s\n", output_modes[mode]);
+		ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+		data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+		IVTV_INFO("Overlay:      %s, Alpha: %s, Pixel Format: %s\n",
+			data[0] & 1 ? "On" : "Off",
+			alpha_mode[(data[0] >> 1) & 0x3],
+			pixel_format[(data[0] >> 3) & 0xf]);
+	}
+	IVTV_INFO("Tuner:  %s\n",
+		test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+	v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
+	IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
+	for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+		struct ivtv_stream *s = &itv->streams[i];
+
+		if (s->vdev == NULL || s->buffers == 0)
+			continue;
+		IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+				(s->buffers - s->q_free.buffers) * 100 / s->buffers,
+				(s->buffers * s->buf_size) / 1024, s->buffers);
+	}
+
+	IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+			(long long)itv->mpg_data_received,
+			(long long)itv->vbi_data_inserted);
+	return 0;
+}
+
+static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+	struct ivtv_open_id *id = fh2id(file->private_data);
+	struct ivtv *itv = id->itv;
+
+	IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
+	return ivtv_video_command(itv, id, dec, false);
+}
+
+static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+	struct ivtv_open_id *id = fh2id(file->private_data);
+	struct ivtv *itv = id->itv;
+
+	IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
+	return ivtv_video_command(itv, id, dec, true);
+}
+
+static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+	struct ivtv_open_id *id = fh2id(filp->private_data);
+	struct ivtv *itv = id->itv;
+	int nonblocking = filp->f_flags & O_NONBLOCK;
+	struct ivtv_stream *s = &itv->streams[id->type];
+	unsigned long iarg = (unsigned long)arg;
+
+	switch (cmd) {
+	case IVTV_IOC_DMA_FRAME: {
+		struct ivtv_dma_frame *args = arg;
+
+		IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+			return 0;
+		if (ivtv_start_decoding(id, id->type)) {
+			return -EBUSY;
+		}
+		if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+			ivtv_release_stream(s);
+			return -EBUSY;
+		}
+		/* Mark that this file handle started the UDMA_YUV mode */
+		id->yuv_frames = 1;
+		if (args->y_source == NULL)
+			return 0;
+		return ivtv_yuv_prep_frame(itv, args);
+	}
+
+	case IVTV_IOC_PASSTHROUGH_MODE:
+		IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		return ivtv_passthrough_mode(itv, *(int *)arg != 0);
+
+	case VIDEO_GET_PTS: {
+		s64 *pts = arg;
+		s64 frame;
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+		if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+			*pts = s->dma_pts;
+			break;
+		}
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		return ivtv_g_pts_frame(itv, pts, &frame);
+	}
+
+	case VIDEO_GET_FRAME_COUNT: {
+		s64 *frame = arg;
+		s64 pts;
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+		if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+			*frame = 0;
+			break;
+		}
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		return ivtv_g_pts_frame(itv, &pts, frame);
+	}
+
+	case VIDEO_PLAY: {
+		struct v4l2_decoder_cmd dc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+		memset(&dc, 0, sizeof(dc));
+		dc.cmd = V4L2_DEC_CMD_START;
+		return ivtv_video_command(itv, id, &dc, 0);
+	}
+
+	case VIDEO_STOP: {
+		struct v4l2_decoder_cmd dc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+		memset(&dc, 0, sizeof(dc));
+		dc.cmd = V4L2_DEC_CMD_STOP;
+		dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
+		return ivtv_video_command(itv, id, &dc, 0);
+	}
+
+	case VIDEO_FREEZE: {
+		struct v4l2_decoder_cmd dc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+		memset(&dc, 0, sizeof(dc));
+		dc.cmd = V4L2_DEC_CMD_PAUSE;
+		return ivtv_video_command(itv, id, &dc, 0);
+	}
+
+	case VIDEO_CONTINUE: {
+		struct v4l2_decoder_cmd dc;
+
+		IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+		memset(&dc, 0, sizeof(dc));
+		dc.cmd = V4L2_DEC_CMD_RESUME;
+		return ivtv_video_command(itv, id, &dc, 0);
+	}
+
+	case VIDEO_COMMAND:
+	case VIDEO_TRY_COMMAND: {
+		/* Note: struct v4l2_decoder_cmd has the same layout as
+		   struct video_command */
+		struct v4l2_decoder_cmd *dc = arg;
+		int try = (cmd == VIDEO_TRY_COMMAND);
+
+		if (try)
+			IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
+		else
+			IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
+		return ivtv_video_command(itv, id, dc, try);
+	}
+
+	case VIDEO_GET_EVENT: {
+		struct video_event *ev = arg;
+		DEFINE_WAIT(wait);
+
+		IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		memset(ev, 0, sizeof(*ev));
+		set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+		while (1) {
+			if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+				ev->type = VIDEO_EVENT_DECODER_STOPPED;
+			else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+				ev->type = VIDEO_EVENT_VSYNC;
+				ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+					VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
+				if (itv->output_mode == OUT_UDMA_YUV &&
+					(itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
+								IVTV_YUV_MODE_PROGRESSIVE) {
+					ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
+				}
+			}
+			if (ev->type)
+				return 0;
+			if (nonblocking)
+				return -EAGAIN;
+			/* Wait for event. Note that serialize_lock is locked,
+			   so to allow other processes to access the driver while
+			   we are waiting unlock first and later lock again. */
+			mutex_unlock(&itv->serialize_lock);
+			prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+			if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) &&
+			    !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags))
+				schedule();
+			finish_wait(&itv->event_waitq, &wait);
+			mutex_lock(&itv->serialize_lock);
+			if (signal_pending(current)) {
+				/* return if a signal was received */
+				IVTV_DEBUG_INFO("User stopped wait for event\n");
+				return -EINTR;
+			}
+		}
+		break;
+	}
+
+	case VIDEO_SELECT_SOURCE:
+		IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+		if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+			return -EINVAL;
+		return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX);
+
+	case AUDIO_SET_MUTE:
+		IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+		itv->speed_mute_audio = iarg;
+		return 0;
+
+	case AUDIO_CHANNEL_SELECT:
+		IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+		if (iarg > AUDIO_STEREO_SWAPPED)
+			return -EINVAL;
+		return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1);
+
+	case AUDIO_BILINGUAL_CHANNEL_SELECT:
+		IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+		if (iarg > AUDIO_STEREO_SWAPPED)
+			return -EINVAL;
+		return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1);
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static long ivtv_default(struct file *file, void *fh, bool valid_prio,
+			 int cmd, void *arg)
+{
+	struct ivtv *itv = fh2id(fh)->itv;
+
+	if (!valid_prio) {
+		switch (cmd) {
+		case IVTV_IOC_PASSTHROUGH_MODE:
+		case VIDEO_PLAY:
+		case VIDEO_STOP:
+		case VIDEO_FREEZE:
+		case VIDEO_CONTINUE:
+		case VIDEO_COMMAND:
+		case VIDEO_SELECT_SOURCE:
+		case AUDIO_SET_MUTE:
+		case AUDIO_CHANNEL_SELECT:
+		case AUDIO_BILINGUAL_CHANNEL_SELECT:
+			return -EBUSY;
+		}
+	}
+
+	switch (cmd) {
+	case VIDIOC_INT_RESET: {
+		u32 val = *(u32 *)arg;
+
+		if ((val == 0 && itv->options.newi2c) || (val & 0x01))
+			ivtv_reset_ir_gpio(itv);
+		if (val & 0x02)
+			v4l2_subdev_call(itv->sd_video, core, reset, 0);
+		break;
+	}
+
+	case IVTV_IOC_DMA_FRAME:
+	case IVTV_IOC_PASSTHROUGH_MODE:
+	case VIDEO_GET_PTS:
+	case VIDEO_GET_FRAME_COUNT:
+	case VIDEO_GET_EVENT:
+	case VIDEO_PLAY:
+	case VIDEO_STOP:
+	case VIDEO_FREEZE:
+	case VIDEO_CONTINUE:
+	case VIDEO_COMMAND:
+	case VIDEO_TRY_COMMAND:
+	case VIDEO_SELECT_SOURCE:
+	case AUDIO_SET_MUTE:
+	case AUDIO_CHANNEL_SELECT:
+	case AUDIO_BILINGUAL_CHANNEL_SELECT:
+		return ivtv_decoder_ioctls(file, cmd, (void *)arg);
+
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
+	.vidioc_querycap    		    = ivtv_querycap,
+	.vidioc_s_audio     		    = ivtv_s_audio,
+	.vidioc_g_audio     		    = ivtv_g_audio,
+	.vidioc_enumaudio   		    = ivtv_enumaudio,
+	.vidioc_s_audout     		    = ivtv_s_audout,
+	.vidioc_g_audout     		    = ivtv_g_audout,
+	.vidioc_enum_input   		    = ivtv_enum_input,
+	.vidioc_enum_output   		    = ivtv_enum_output,
+	.vidioc_enumaudout   		    = ivtv_enumaudout,
+	.vidioc_cropcap       		    = ivtv_cropcap,
+	.vidioc_s_crop       		    = ivtv_s_crop,
+	.vidioc_g_crop       		    = ivtv_g_crop,
+	.vidioc_g_input      		    = ivtv_g_input,
+	.vidioc_s_input      		    = ivtv_s_input,
+	.vidioc_g_output     		    = ivtv_g_output,
+	.vidioc_s_output     		    = ivtv_s_output,
+	.vidioc_g_frequency 		    = ivtv_g_frequency,
+	.vidioc_s_frequency  		    = ivtv_s_frequency,
+	.vidioc_s_tuner      		    = ivtv_s_tuner,
+	.vidioc_g_tuner      		    = ivtv_g_tuner,
+	.vidioc_g_enc_index 		    = ivtv_g_enc_index,
+	.vidioc_g_fbuf			    = ivtv_g_fbuf,
+	.vidioc_s_fbuf			    = ivtv_s_fbuf,
+	.vidioc_g_std 			    = ivtv_g_std,
+	.vidioc_s_std 			    = ivtv_s_std,
+	.vidioc_overlay			    = ivtv_overlay,
+	.vidioc_log_status		    = ivtv_log_status,
+	.vidioc_enum_fmt_vid_cap 	    = ivtv_enum_fmt_vid_cap,
+	.vidioc_encoder_cmd  		    = ivtv_encoder_cmd,
+	.vidioc_try_encoder_cmd 	    = ivtv_try_encoder_cmd,
+	.vidioc_decoder_cmd		    = ivtv_decoder_cmd,
+	.vidioc_try_decoder_cmd		    = ivtv_try_decoder_cmd,
+	.vidioc_enum_fmt_vid_out 	    = ivtv_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_cap 		    = ivtv_g_fmt_vid_cap,
+	.vidioc_g_fmt_vbi_cap		    = ivtv_g_fmt_vbi_cap,
+	.vidioc_g_fmt_sliced_vbi_cap        = ivtv_g_fmt_sliced_vbi_cap,
+	.vidioc_g_fmt_vid_out               = ivtv_g_fmt_vid_out,
+	.vidioc_g_fmt_vid_out_overlay       = ivtv_g_fmt_vid_out_overlay,
+	.vidioc_g_fmt_sliced_vbi_out        = ivtv_g_fmt_sliced_vbi_out,
+	.vidioc_s_fmt_vid_cap  		    = ivtv_s_fmt_vid_cap,
+	.vidioc_s_fmt_vbi_cap 		    = ivtv_s_fmt_vbi_cap,
+	.vidioc_s_fmt_sliced_vbi_cap        = ivtv_s_fmt_sliced_vbi_cap,
+	.vidioc_s_fmt_vid_out               = ivtv_s_fmt_vid_out,
+	.vidioc_s_fmt_vid_out_overlay       = ivtv_s_fmt_vid_out_overlay,
+	.vidioc_s_fmt_sliced_vbi_out        = ivtv_s_fmt_sliced_vbi_out,
+	.vidioc_try_fmt_vid_cap  	    = ivtv_try_fmt_vid_cap,
+	.vidioc_try_fmt_vbi_cap		    = ivtv_try_fmt_vbi_cap,
+	.vidioc_try_fmt_sliced_vbi_cap      = ivtv_try_fmt_sliced_vbi_cap,
+	.vidioc_try_fmt_vid_out 	    = ivtv_try_fmt_vid_out,
+	.vidioc_try_fmt_vid_out_overlay     = ivtv_try_fmt_vid_out_overlay,
+	.vidioc_try_fmt_sliced_vbi_out 	    = ivtv_try_fmt_sliced_vbi_out,
+	.vidioc_g_sliced_vbi_cap 	    = ivtv_g_sliced_vbi_cap,
+	.vidioc_g_chip_ident 		    = ivtv_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register 		    = ivtv_g_register,
+	.vidioc_s_register 		    = ivtv_s_register,
+#endif
+	.vidioc_default 		    = ivtv_default,
+	.vidioc_subscribe_event 	    = ivtv_subscribe_event,
+	.vidioc_unsubscribe_event 	    = v4l2_event_unsubscribe,
+};
+
+void ivtv_set_funcs(struct video_device *vdev)
+{
+	vdev->ioctl_ops = &ivtv_ioctl_ops;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.h b/drivers/media/pci/ivtv/ivtv-ioctl.h
new file mode 100644
index 000000000000..7c553d16579b
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.h
@@ -0,0 +1,35 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_IOCTL_H
+#define IVTV_IOCTL_H
+
+u16 ivtv_service2vbi(int type);
+void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+void ivtv_set_osd_alpha(struct ivtv *itv);
+int ivtv_set_speed(struct ivtv *itv, int speed);
+void ivtv_set_funcs(struct video_device *vdev);
+void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std);
+void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std);
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c
new file mode 100644
index 000000000000..19a7c9b990a3
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-irq.c
@@ -0,0 +1,1088 @@
+/* interrupt handling
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+#include "ivtv-yuv.h"
+#include <media/v4l2-event.h>
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+	IVTV_ENC_STREAM_TYPE_MPG,
+	IVTV_ENC_STREAM_TYPE_YUV,
+	IVTV_ENC_STREAM_TYPE_PCM,
+	IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+static void ivtv_pcm_work_handler(struct ivtv *itv)
+{
+	struct ivtv_stream *s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+	struct ivtv_buffer *buf;
+
+	/* Pass the PCM data to ivtv-alsa */
+
+	while (1) {
+		/*
+		 * Users should not be using both the ALSA and V4L2 PCM audio
+		 * capture interfaces at the same time.  If the user is doing
+		 * this, there maybe a buffer in q_io to grab, use, and put
+		 * back in rotation.
+		 */
+		buf = ivtv_dequeue(s, &s->q_io);
+		if (buf == NULL)
+			buf = ivtv_dequeue(s, &s->q_full);
+		if (buf == NULL)
+			break;
+
+		if (buf->readpos < buf->bytesused)
+			itv->pcm_announce_callback(itv->alsa,
+				(u8 *)(buf->buf + buf->readpos),
+				(size_t)(buf->bytesused - buf->readpos));
+
+		ivtv_enqueue(s, buf, &s->q_free);
+	}
+}
+
+static void ivtv_pio_work_handler(struct ivtv *itv)
+{
+	struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
+	struct ivtv_buffer *buf;
+	int i = 0;
+
+	IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
+	if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
+			s->vdev == NULL || !ivtv_use_pio(s)) {
+		itv->cur_pio_stream = -1;
+		/* trigger PIO complete user interrupt */
+		write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+		return;
+	}
+	IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name);
+	list_for_each_entry(buf, &s->q_dma.list, list) {
+		u32 size = s->sg_processing[i].size & 0x3ffff;
+
+		/* Copy the data from the card to the buffer */
+		if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+			memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size);
+		}
+		else {
+			memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size);
+		}
+		i++;
+		if (i == s->sg_processing_size)
+			break;
+	}
+	write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+}
+
+void ivtv_irq_work_handler(struct kthread_work *work)
+{
+	struct ivtv *itv = container_of(work, struct ivtv, irq_work);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
+		ivtv_pio_work_handler(itv);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
+		ivtv_vbi_work_handler(itv);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
+		ivtv_yuv_work_handler(itv);
+
+	if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags))
+		ivtv_pcm_work_handler(itv);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+   actually copy the data from the card to the buffers in case a PIO transfer is
+   required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_buffer *buf;
+	u32 bytes_needed = 0;
+	u32 offset, size;
+	u32 UVoffset = 0, UVsize = 0;
+	int skip_bufs = s->q_predma.buffers;
+	int idx = s->sg_pending_size;
+	int rc;
+
+	/* sanity checks */
+	if (s->vdev == NULL) {
+		IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+		return -1;
+	}
+	if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+		IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+		return -1;
+	}
+
+	/* determine offset, size and PTS for the various streams */
+	switch (s->type) {
+		case IVTV_ENC_STREAM_TYPE_MPG:
+			offset = data[1];
+			size = data[2];
+			s->pending_pts = 0;
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_YUV:
+			offset = data[1];
+			size = data[2];
+			UVoffset = data[3];
+			UVsize = data[4];
+			s->pending_pts = ((u64) data[5] << 32) | data[6];
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_PCM:
+			offset = data[1] + 12;
+			size = data[2] - 12;
+			s->pending_pts = read_dec(offset - 8) |
+				((u64)(read_dec(offset - 12)) << 32);
+			if (itv->has_cx23415)
+				offset += IVTV_DECODER_OFFSET;
+			break;
+
+		case IVTV_ENC_STREAM_TYPE_VBI:
+			size = itv->vbi.enc_size * itv->vbi.fpi;
+			offset = read_enc(itv->vbi.enc_start - 4) + 12;
+			if (offset == 12) {
+				IVTV_DEBUG_INFO("VBI offset == 0\n");
+				return -1;
+			}
+			s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+			break;
+
+		case IVTV_DEC_STREAM_TYPE_VBI:
+			size = read_dec(itv->vbi.dec_start + 4) + 8;
+			offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+			s->pending_pts = 0;
+			offset += IVTV_DECODER_OFFSET;
+			break;
+		default:
+			/* shouldn't happen */
+			return -1;
+	}
+
+	/* if this is the start of the DMA then fill in the magic cookie */
+	if (s->sg_pending_size == 0 && ivtv_use_dma(s)) {
+		if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+		    s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+			s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+			write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+		}
+		else {
+			s->pending_backup = read_enc(offset);
+			write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+		}
+		s->pending_offset = offset;
+	}
+
+	bytes_needed = size;
+	if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+		/* The size for the Y samples needs to be rounded upwards to a
+		   multiple of the buf_size. The UV samples then start in the
+		   next buffer. */
+		bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+		bytes_needed += UVsize;
+	}
+
+	IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+		ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+	rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+	if (rc < 0) { /* Insufficient buffers */
+		IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+				bytes_needed, s->name);
+		return -1;
+	}
+	if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) {
+		IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+		IVTV_WARN("Cause: the application is not reading fast enough.\n");
+	}
+	s->buffers_stolen = rc;
+
+	/* got the buffers, now fill in sg_pending */
+	buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+	memset(buf->buf, 0, 128);
+	list_for_each_entry(buf, &s->q_predma.list, list) {
+		if (skip_bufs-- > 0)
+			continue;
+		s->sg_pending[idx].dst = buf->dma_handle;
+		s->sg_pending[idx].src = offset;
+		s->sg_pending[idx].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;
+		size -= buf->bytesused;
+		offset += s->buf_size;
+
+		/* Sync SG buffers */
+		ivtv_buf_sync_for_device(s, buf);
+
+		if (size == 0) {	/* YUV */
+			/* process the UV section */
+			offset = UVoffset;
+			size = UVsize;
+		}
+		idx++;
+	}
+	s->sg_pending_size = idx;
+	return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_buffer *buf = NULL;
+	struct list_head *p;
+	u32 offset;
+	__le32 *u32buf;
+	int x = 0;
+
+	IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+			s->name, s->dma_offset);
+	list_for_each(p, &s->q_dma.list) {
+		buf = list_entry(p, struct ivtv_buffer, list);
+		u32buf = (__le32 *)buf->buf;
+
+		/* Sync Buffer */
+		ivtv_buf_sync_for_cpu(s, buf);
+
+		if (x == 0 && ivtv_use_dma(s)) {
+			offset = s->dma_last_offset;
+			if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+			{
+				for (offset = 0; offset < 64; offset++) {
+					if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+						break;
+					}
+				}
+				offset *= 4;
+				if (offset == 256) {
+					IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+					offset = s->dma_last_offset;
+				}
+				if (s->dma_last_offset != offset)
+					IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+				s->dma_last_offset = offset;
+			}
+			if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+						s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+				write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+			}
+			else {
+				write_enc_sync(0, s->dma_offset);
+			}
+			if (offset) {
+				buf->bytesused -= offset;
+				memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+			}
+			*u32buf = cpu_to_le32(s->dma_backup);
+		}
+		x++;
+		/* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+		    s->type == IVTV_ENC_STREAM_TYPE_VBI)
+			buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP;
+	}
+	if (buf)
+		buf->bytesused += s->dma_last_offset;
+	if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		list_for_each_entry(buf, &s->q_dma.list, list) {
+			/* Parse and Groom VBI Data */
+			s->q_dma.bytesused -= buf->bytesused;
+			ivtv_process_vbi_data(itv, buf, 0, s->type);
+			s->q_dma.bytesused += buf->bytesused;
+		}
+		if (s->fh == NULL) {
+			ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+			return;
+		}
+	}
+
+	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_PCM &&
+	    itv->pcm_announce_callback != NULL) {
+		/*
+		 * Set up the work handler to pass the data to ivtv-alsa.
+		 *
+		 * We just use q_full and let the work handler race with users
+		 * making ivtv-fileops.c calls on the PCM device node.
+		 *
+		 * Users should not be using both the ALSA and V4L2 PCM audio
+		 * capture interfaces at the same time.  If the user does this,
+		 * fragments of data will just go out each interface as they
+		 * race for PCM data.
+		 */
+		set_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags);
+		set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+	}
+
+	if (s->fh)
+		wake_up(&s->waitq);
+}
+
+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 = 720 * ((f->src_h + 31) & ~31);
+	u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+	int y_done = 0;
+	int bytes_written = 0;
+	unsigned long flags = 0;
+	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 + 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;
+		}
+		bytes_written += buf->bytesused;
+
+		/* Sync SG buffers */
+		ivtv_buf_sync_for_device(s, buf);
+		idx++;
+	}
+	s->sg_pending_size = idx;
+
+	/* Sync Hardware SG List of buffers */
+	ivtv_stream_sync_for_device(s);
+	if (lock)
+		spin_lock_irqsave(&itv->dma_reg_lock, flags);
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+		ivtv_dma_dec_start(s);
+	}
+	else {
+		set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+	}
+	if (lock)
+		spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+	s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+	s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+	s->sg_processed++;
+	/* Sync Hardware SG List of buffers */
+	ivtv_stream_sync_for_device(s);
+	write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+	add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+	s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+	s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+	s->sg_processed++;
+	/* Sync Hardware SG List of buffers */
+	ivtv_stream_sync_for_device(s);
+	write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+	add_timer(&itv->dma_timer);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+	int i;
+
+	IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
+
+	if (s->q_predma.bytesused)
+		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+
+	if (ivtv_use_dma(s))
+		s->sg_pending[s->sg_pending_size - 1].size += 256;
+
+	/* If this is an MPEG stream, and VBI data is also pending, then append the
+	   VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+	   VBI DMA is a second class citizen compared to MPEG and mixing them together
+	   will confuse the firmware (the end of a VBI DMA is seen as the end of a
+	   MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+	   sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+	   use. This way no conflicts occur. */
+	clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&
+			s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {
+		ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+		if (ivtv_use_dma(s_vbi))
+			s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;
+		for (i = 0; i < s_vbi->sg_pending_size; i++) {
+			s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];
+		}
+		s_vbi->dma_offset = s_vbi->pending_offset;
+		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_vbi->name);
+	}
+
+	s->dma_xfer_cnt++;
+	memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+	s->sg_processing_size = s->sg_pending_size;
+	s->sg_pending_size = 0;
+	s->sg_processed = 0;
+	s->dma_offset = s->pending_offset;
+	s->dma_backup = s->pending_backup;
+	s->dma_pts = s->pending_pts;
+
+	if (ivtv_use_pio(s)) {
+		set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
+		set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+		set_bit(IVTV_F_I_PIO, &itv->i_flags);
+		itv->cur_pio_stream = s->type;
+	}
+	else {
+		itv->dma_retries = 0;
+		ivtv_dma_enc_start_xfer(s);
+		set_bit(IVTV_F_I_DMA, &itv->i_flags);
+		itv->cur_dma_stream = s->type;
+	}
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	if (s->q_predma.bytesused)
+		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+	s->dma_xfer_cnt++;
+	memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+	s->sg_processing_size = s->sg_pending_size;
+	s->sg_pending_size = 0;
+	s->sg_processed = 0;
+
+	IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);
+	itv->dma_retries = 0;
+	ivtv_dma_dec_start_xfer(s);
+	set_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = s->type;
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+	struct ivtv_stream *s = NULL;
+	struct ivtv_buffer *buf;
+	int hw_stream_type = 0;
+
+	IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");
+
+	del_timer(&itv->dma_timer);
+
+	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)
+		return;
+
+	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+		s = &itv->streams[itv->cur_dma_stream];
+		ivtv_stream_sync_for_cpu(s);
+
+		if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+			IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",
+					read_reg(IVTV_REG_DMASTATUS),
+					s->sg_processed, s->sg_processing_size, itv->dma_retries);
+			write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+			if (itv->dma_retries == 3) {
+				/* Too many retries, give up on this frame */
+				itv->dma_retries = 0;
+				s->sg_processed = s->sg_processing_size;
+			}
+			else {
+				/* Retry, starting with the first xfer segment.
+				   Just retrying the current segment is not sufficient. */
+				s->sg_processed = 0;
+				itv->dma_retries++;
+			}
+		}
+		if (s->sg_processed < s->sg_processing_size) {
+			/* DMA next buffer */
+			ivtv_dma_dec_start_xfer(s);
+			return;
+		}
+		if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+			hw_stream_type = 2;
+		IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+		/* For some reason must kick the firmware, like PIO mode,
+		   I think this tells the firmware we are done and the size
+		   of the xfer so it can calculate what we need next.
+		   I think we can do this part ourselves but would have to
+		   fully calculate xfer info ourselves and not use interrupts
+		 */
+		ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+				hw_stream_type);
+
+		/* Free last DMA call */
+		while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+			ivtv_buf_sync_for_cpu(s, buf);
+			ivtv_enqueue(s, buf, &s->q_free);
+		}
+		wake_up(&s->waitq);
+	}
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+	IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
+
+	del_timer(&itv->dma_timer);
+
+	if (itv->cur_dma_stream < 0)
+		return;
+
+	s = &itv->streams[itv->cur_dma_stream];
+	ivtv_stream_sync_for_cpu(s);
+
+	if (data[0] & 0x18) {
+		IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],
+			s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);
+		write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+		if (itv->dma_retries == 3) {
+			/* Too many retries, give up on this frame */
+			itv->dma_retries = 0;
+			s->sg_processed = s->sg_processing_size;
+		}
+		else {
+			/* Retry, starting with the first xfer segment.
+			   Just retrying the current segment is not sufficient. */
+			s->sg_processed = 0;
+			itv->dma_retries++;
+		}
+	}
+	if (s->sg_processed < s->sg_processing_size) {
+		/* DMA next buffer */
+		ivtv_dma_enc_start_xfer(s);
+		return;
+	}
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	dma_post(s);
+	if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+		s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+		dma_post(s);
+	}
+	s->sg_processing_size = 0;
+	s->sg_processed = 0;
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
+{
+	struct ivtv_stream *s;
+
+	if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
+		itv->cur_pio_stream = -1;
+		return;
+	}
+	s = &itv->streams[itv->cur_pio_stream];
+	IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
+	clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+	itv->cur_pio_stream = -1;
+	dma_post(s);
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
+	else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
+	else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
+		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
+	clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+	if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+		s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+		dma_post(s);
+	}
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	u32 status;
+
+	del_timer(&itv->dma_timer);
+
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+	status = read_reg(IVTV_REG_DMASTATUS);
+	IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+				status, itv->cur_dma_stream);
+	/*
+	 * We do *not* write back to the IVTV_REG_DMASTATUS register to
+	 * clear the error status, if either the encoder write (0x02) or
+	 * decoder read (0x01) bus master DMA operation do not indicate
+	 * completed.  We can race with the DMA engine, which may have
+	 * transitioned to completed status *after* we read the register.
+	 * Setting a IVTV_REG_DMASTATUS flag back to "busy" status, after the
+	 * DMA engine has completed, will cause the DMA engine to stop working.
+	 */
+	status &= 0x3;
+	if (status == 0x3)
+		write_reg(status, IVTV_REG_DMASTATUS);
+
+	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+	    itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+		struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+		if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+			/* retry */
+			/*
+			 * FIXME - handle cases of DMA error similar to
+			 * encoder below, except conditioned on status & 0x1
+			 */
+			ivtv_dma_dec_start(s);
+			return;
+		} else {
+			if ((status & 0x2) == 0) {
+				/*
+				 * CX2341x Bus Master DMA write is ongoing.
+				 * Reset the timer and let it complete.
+				 */
+				itv->dma_timer.expires =
+						jiffies + msecs_to_jiffies(600);
+				add_timer(&itv->dma_timer);
+				return;
+			}
+
+			if (itv->dma_retries < 3) {
+				/*
+				 * CX2341x Bus Master DMA write has ended.
+				 * Retry the write, starting with the first
+				 * xfer segment. Just retrying the current
+				 * segment is not sufficient.
+				 */
+				s->sg_processed = 0;
+				itv->dma_retries++;
+				ivtv_dma_enc_start_xfer(s);
+				return;
+			}
+			/* Too many retries, give up on this one */
+		}
+
+	}
+	if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+		ivtv_udma_start(itv);
+		return;
+	}
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	/* Get DMA destination and size arguments from card */
+	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data);
+	IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+	if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+		IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+				data[0], data[1], data[2]);
+		return;
+	}
+	s = &itv->streams[ivtv_stream_map[data[0]]];
+	if (!stream_enc_dma_append(s, data)) {
+		set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+	}
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n");
+	s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+	if (!stream_enc_dma_append(s, data))
+		set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+}
+
+static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+	IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n");
+	if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+			!stream_enc_dma_append(s, data)) {
+		set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
+	}
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv_stream *s;
+
+	/* YUV or MPG */
+
+	if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+		ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+		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 {
+		ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data);
+		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];
+	}
+	IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+		       itv->dma_data_req_offset, itv->dma_data_req_size);
+	if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+		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);
+	}
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+	/* The vsync interrupt is unusual in that it won't clear until
+	 * the end of the first line for the current field, at which
+	 * point it clears itself. This can result in repeated vsync
+	 * interrupts, or a missed vsync. Read some of the registers
+	 * to determine the line being displayed and ensure we handle
+	 * one vsync per frame.
+	 */
+	unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	int last_dma_frame = atomic_read(&yi->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 ^ 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 (!(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) % IVTV_YUV_BUFFERS;
+				atomic_set(&yi->next_dma_frame, next_dma_frame);
+				yi->fields_lapsed = -1;
+				yi->running = 1;
+			}
+		}
+	}
+	if (frame != (itv->last_vsync_field & 1)) {
+		static const struct v4l2_event evtop = {
+			.type = V4L2_EVENT_VSYNC,
+			.u.vsync.field = V4L2_FIELD_TOP,
+		};
+		static const struct v4l2_event evbottom = {
+			.type = V4L2_EVENT_VSYNC,
+			.u.vsync.field = V4L2_FIELD_BOTTOM,
+		};
+		struct ivtv_stream *s = ivtv_get_output_stream(itv);
+
+		itv->last_vsync_field += 1;
+		if (frame == 0) {
+			clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+			clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+		}
+		else {
+			set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+		}
+		if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+			set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+			wake_up(&itv->event_waitq);
+			if (s)
+				wake_up(&s->waitq);
+		}
+		if (s && s->vdev)
+			v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom);
+		wake_up(&itv->vsync_waitq);
+
+		/* Send VBI to saa7127 */
+		if (frame && (itv->output_mode == OUT_PASSTHROUGH ||
+			test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) ||
+			test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) ||
+			test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) {
+			set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
+			set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+		}
+
+		/* Check if we need to update the yuv registers */
+		if (yi->running && (yi->yuv_forced_update || f->update)) {
+			if (!f->update) {
+				last_dma_frame =
+					(u8)(atomic_read(&yi->next_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);
+			}
+		}
+
+		yi->fields_lapsed++;
+	}
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+	struct ivtv *itv = (struct ivtv *)dev_id;
+	u32 combo;
+	u32 stat;
+	int i;
+	u8 vsync_force = 0;
+
+	spin_lock(&itv->dma_reg_lock);
+	/* get contents of irq status register */
+	stat = read_reg(IVTV_REG_IRQSTATUS);
+
+	combo = ~itv->irqmask & stat;
+
+	/* Clear out IRQ */
+	if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+	if (0 == combo) {
+		/* The vsync interrupt is unusual and clears itself. If we
+		 * took too long, we may have missed it. Do some checks
+		 */
+		if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+			/* vsync is enabled, see if we're in a new field */
+			if ((itv->last_vsync_field & 1) !=
+			    (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) {
+				/* New field, looks like we missed it */
+				IVTV_DEBUG_YUV("VSync interrupt missed %d\n",
+				       read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16);
+				vsync_force = 1;
+			}
+		}
+
+		if (!vsync_force) {
+			/* No Vsync expected, wasn't for us */
+			spin_unlock(&itv->dma_reg_lock);
+			return IRQ_NONE;
+		}
+	}
+
+	/* Exclude interrupts noted below from the output, otherwise the log is flooded with
+	   these messages */
+	if (combo & ~0xff6d0400)
+		IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+	if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+		IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n");
+	}
+
+	if (combo & IVTV_IRQ_DMA_READ) {
+		ivtv_irq_dma_read(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+		ivtv_irq_enc_dma_complete(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
+		ivtv_irq_enc_pio_complete(itv);
+	}
+
+	if (combo & IVTV_IRQ_DMA_ERR) {
+		ivtv_irq_dma_err(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_START_CAP) {
+		ivtv_irq_enc_start_cap(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+		ivtv_irq_enc_vbi_cap(itv);
+	}
+
+	if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+		ivtv_irq_dec_vbi_reinsert(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_EOS) {
+		IVTV_DEBUG_IRQ("ENC EOS\n");
+		set_bit(IVTV_F_I_EOS, &itv->i_flags);
+		wake_up(&itv->eos_waitq);
+	}
+
+	if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+		ivtv_irq_dec_data_req(itv);
+	}
+
+	/* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+	if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+		ivtv_irq_vsync(itv);
+	}
+
+	if (combo & IVTV_IRQ_ENC_VIM_RST) {
+		IVTV_DEBUG_IRQ("VIM RST\n");
+		/*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+	}
+
+	if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+		IVTV_DEBUG_INFO("Stereo mode changed\n");
+	}
+
+	if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+		itv->irq_rr_idx++;
+		for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+			int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+			struct ivtv_stream *s = &itv->streams[idx];
+
+			if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+				continue;
+			if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+				ivtv_dma_dec_start(s);
+			else
+				ivtv_dma_enc_start(s);
+			break;
+		}
+
+		if (i == IVTV_MAX_STREAMS &&
+		    test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+			ivtv_udma_start(itv);
+	}
+
+	if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
+		itv->irq_rr_idx++;
+		for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+			int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+			struct ivtv_stream *s = &itv->streams[idx];
+
+			if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
+				continue;
+			if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
+				ivtv_dma_enc_start(s);
+			break;
+		}
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
+		queue_kthread_work(&itv->irq_worker, &itv->irq_work);
+	}
+
+	spin_unlock(&itv->dma_reg_lock);
+
+	/* If we've just handled a 'forced' vsync, it's safest to say it
+	 * wasn't ours. Another device may have triggered it at just
+	 * the right time.
+	 */
+	return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+	struct ivtv *itv = (struct ivtv *)arg;
+
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+		return;
+	IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+	write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+	itv->cur_dma_stream = -1;
+	wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/pci/ivtv/ivtv-irq.h b/drivers/media/pci/ivtv/ivtv-irq.h
new file mode 100644
index 000000000000..1e84433737cc
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-irq.h
@@ -0,0 +1,53 @@
+/*
+    interrupt handling
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_IRQ_H
+#define IVTV_IRQ_H
+
+#define IVTV_IRQ_ENC_START_CAP		(0x1 << 31)
+#define IVTV_IRQ_ENC_EOS		(0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP		(0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST		(0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE	(0x1 << 27)
+#define IVTV_IRQ_ENC_PIO_COMPLETE	(0x1 << 25)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG	(0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ		(0x1 << 22)
+#define IVTV_IRQ_DEC_DMA_COMPLETE	(0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT	(0x1 << 19)
+#define IVTV_IRQ_DMA_ERR		(0x1 << 18)
+#define IVTV_IRQ_DMA_WRITE		(0x1 << 17)
+#define IVTV_IRQ_DMA_READ		(0x1 << 16)
+#define IVTV_IRQ_DEC_VSYNC		(0x1 << 10)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|\
+		IVTV_IRQ_DMA_READ|IVTV_IRQ_ENC_PIO_COMPLETE)
+
+#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
+#define IVTV_IRQ_MASK_DECODE  (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
+
+void ivtv_irq_work_handler(struct kthread_work *work);
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
+void ivtv_unfinished_dma(unsigned long arg);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-mailbox.c b/drivers/media/pci/ivtv/ivtv-mailbox.c
new file mode 100644
index 000000000000..e3ce96763785
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-mailbox.c
@@ -0,0 +1,387 @@
+/*
+    mailbox functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE   0x00000002
+#define IVTV_MBOX_DRIVER_BUSY   0x00000001
+#define IVTV_MBOX_FREE 		0x00000000
+
+/* Firmware mailbox standard timeout */
+#define IVTV_API_STD_TIMEOUT 	0x02000000
+
+#define API_CACHE 	 (1 << 0) 	/* Allow the command to be stored in the cache */
+#define API_RESULT	 (1 << 1) 	/* Allow 1 second for this cmd to end */
+#define API_FAST_RESULT	 (3 << 1)	/* Allow 0.1 second for this cmd to end */
+#define API_DMA 	 (1 << 3)	/* DMA mailbox, has special handling */
+#define API_HIGH_VOL 	 (1 << 5)	/* High volume command (i.e. called during encoding or decoding) */
+#define API_NO_WAIT_MB 	 (1 << 4)	/* Command may not wait for a free mailbox */
+#define API_NO_WAIT_RES	 (1 << 5)	/* Command may not wait for the result */
+#define API_NO_POLL	 (1 << 6)	/* Avoid pointless polling */
+
+struct ivtv_api_info {
+	int flags;		/* Flags, see above */
+	const char *name; 	/* The name of the command */
+};
+
+#define API_ENTRY(x, f) [x] = { (f), #x }
+
+static const struct ivtv_api_info api_info[256] = {
+	/* MPEG encoder API */
+	API_ENTRY(CX2341X_ENC_PING_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_START_CAPTURE, 		API_RESULT | API_NO_POLL),
+	API_ENTRY(CX2341X_ENC_STOP_CAPTURE, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_PCR_ID, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_BIT_RATE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_VBI_LINE, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_HALT_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_GET_VERSION, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_GET_SEQ_END, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, 	API_DMA | API_HIGH_VOL),
+	API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, 	API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_REFRESH_INPUT, 		API_NO_WAIT_MB | API_HIGH_VOL),
+	API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, 	API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, 	API_CACHE),
+	API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, 		API_CACHE),
+	API_ENTRY(CX2341X_ENC_MUTE_VIDEO, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_MUTE_AUDIO, 		API_RESULT),
+	API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE,	API_FAST_RESULT),
+	API_ENTRY(CX2341X_ENC_MISC, 			API_FAST_RESULT),
+	/* Obsolete PULLDOWN API command */
+	API_ENTRY(0xb1, 				API_CACHE),
+
+	/* MPEG decoder API */
+	API_ENTRY(CX2341X_DEC_PING_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_START_PLAYBACK, 		API_RESULT | API_NO_POLL),
+	API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, 	API_RESULT),
+	API_ENTRY(CX2341X_DEC_STEP_VIDEO, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_XFER_INFO, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, 	API_DMA | API_HIGH_VOL),
+	API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_HALT_FW, 			API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_STANDARD, 		API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_VERSION, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, 		API_RESULT /*| API_NO_WAIT_RES*/),
+	API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, 		API_CACHE),
+	API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, 	API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, 	API_CACHE),
+	API_ENTRY(CX2341X_DEC_EXTRACT_VBI, 		API_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, 	API_CACHE),
+
+	/* OSD API */
+	API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_STATE, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_STATE, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, 		API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_BLT_COPY, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_BLT_FILL, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_BLT_TEXT, 		API_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 	API_CACHE),
+	API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, 		API_CACHE),
+	API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, 	API_FAST_RESULT),
+	API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, 	API_CACHE)
+};
+
+static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
+{
+	u32 flags = readl(&mbdata->mbox[mb].flags);
+	int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
+
+	/* if the mailbox is free, then try to claim it */
+	if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
+		write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
+		return 1;
+	}
+	return 0;
+}
+
+/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
+   attempted here. */
+static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
+{
+	unsigned long then = jiffies;
+	int i, mb;
+	int max_mbox = mbdata->max_mbox;
+	int retries = 100;
+
+	/* All slow commands use the same mailbox, serializing them and also
+	   leaving the other mailbox free for simple fast commands. */
+	if ((flags & API_FAST_RESULT) == API_RESULT)
+		max_mbox = 1;
+
+	/* find free non-DMA mailbox */
+	for (i = 0; i < retries; i++) {
+		for (mb = 1; mb <= max_mbox; mb++)
+			if (try_mailbox(itv, mbdata, mb))
+				return mb;
+
+		/* Sleep before a retry, if not atomic */
+		if (!(flags & API_NO_WAIT_MB)) {
+			if (time_after(jiffies,
+				       then + msecs_to_jiffies(10*retries)))
+			       break;
+			ivtv_msleep_timeout(10, 0);
+		}
+	}
+	return -ENODEV;
+}
+
+static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
+{
+	int i;
+
+	write_sync(cmd, &mbox->cmd);
+	write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+		write_sync(data[i], &mbox->data[i]);
+
+	write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
+}
+
+static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
+{
+	int i;
+
+	for (i = 0; i <= mbdata->max_mbox; i++) {
+		IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
+			i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
+		write_sync(0, &mbdata->mbox[i].flags);
+		clear_bit(i, &mbdata->busy);
+	}
+}
+
+static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+	struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
+	volatile struct ivtv_mailbox __iomem *mbox;
+	int api_timeout = msecs_to_jiffies(1000);
+	int flags, mb, i;
+	unsigned long then;
+
+	/* sanity checks */
+	if (NULL == mbdata) {
+		IVTV_ERR("No mailbox allocated\n");
+		return -ENODEV;
+	}
+	if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
+	    cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
+		IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args);
+		return -EINVAL;
+	}
+
+	if (api_info[cmd].flags & API_HIGH_VOL) {
+	    IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name);
+	}
+	else {
+	    IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name);
+	}
+
+	/* clear possibly uninitialized part of data array */
+	for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
+		data[i] = 0;
+
+	/* If this command was issued within the last 30 minutes and with identical
+	   data, then just return 0 as there is no need to issue this command again.
+	   Just an optimization to prevent unnecessary use of mailboxes. */
+	if (itv->api_cache[cmd].last_jiffies &&
+	    time_before(jiffies,
+			itv->api_cache[cmd].last_jiffies +
+			msecs_to_jiffies(1800000)) &&
+	    !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
+		itv->api_cache[cmd].last_jiffies = jiffies;
+		return 0;
+	}
+
+	flags = api_info[cmd].flags;
+
+	if (flags & API_DMA) {
+		for (i = 0; i < 100; i++) {
+			mb = i % (mbdata->max_mbox + 1);
+			if (try_mailbox(itv, mbdata, mb)) {
+				write_mailbox(&mbdata->mbox[mb], cmd, args, data);
+				clear_bit(mb, &mbdata->busy);
+				return 0;
+			}
+			IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
+					api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
+		}
+		IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
+		clear_all_mailboxes(itv, mbdata);
+		return -EBUSY;
+	}
+
+	if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
+		api_timeout = msecs_to_jiffies(100);
+
+	mb = get_mailbox(itv, mbdata, flags);
+	if (mb < 0) {
+		IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
+		clear_all_mailboxes(itv, mbdata);
+		return -EBUSY;
+	}
+	mbox = &mbdata->mbox[mb];
+	write_mailbox(mbox, cmd, args, data);
+	if (flags & API_CACHE) {
+		memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
+		itv->api_cache[cmd].last_jiffies = jiffies;
+	}
+	if ((flags & API_RESULT) == 0) {
+		clear_bit(mb, &mbdata->busy);
+		return 0;
+	}
+
+	/* Get results */
+	then = jiffies;
+
+	if (!(flags & API_NO_POLL)) {
+		/* First try to poll, then switch to delays */
+		for (i = 0; i < 100; i++) {
+			if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)
+				break;
+		}
+	}
+	while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
+		if (time_after(jiffies, then + api_timeout)) {
+			IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
+			/* reset the mailbox, but it is likely too late already */
+			write_sync(0, &mbox->flags);
+			clear_bit(mb, &mbdata->busy);
+			return -EIO;
+		}
+		if (flags & API_NO_WAIT_RES)
+			mdelay(1);
+		else
+			ivtv_msleep_timeout(1, 0);
+	}
+	if (time_after(jiffies, then + msecs_to_jiffies(100)))
+		IVTV_DEBUG_WARN("%s took %u jiffies\n",
+				api_info[cmd].name,
+				jiffies_to_msecs(jiffies - then));
+
+	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+		data[i] = readl(&mbox->data[i]);
+	write_sync(0, &mbox->flags);
+	clear_bit(mb, &mbdata->busy);
+	return 0;
+}
+
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+	int res = ivtv_api_call(itv, cmd, args, data);
+
+	/* Allow a single retry, probably already too late though.
+	   If there is no free mailbox then that is usually an indication
+	   of a more serious problem. */
+	return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
+}
+
+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);
+}
+
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
+{
+	va_list ap;
+	int i;
+
+	va_start(ap, args);
+	for (i = 0; i < args; i++) {
+		data[i] = va_arg(ap, u32);
+	}
+	va_end(ap);
+	return ivtv_api(itv, cmd, args, data);
+}
+
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list ap;
+	int i;
+
+	va_start(ap, args);
+	for (i = 0; i < args; i++) {
+		data[i] = va_arg(ap, u32);
+	}
+	va_end(ap);
+	return ivtv_api(itv, cmd, args, data);
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+		       int argc, u32 data[])
+{
+	volatile u32 __iomem *p = mbdata->mbox[mb].data;
+	int i;
+	for (i = 0; i < argc; i++, p++)
+		data[i] = readl(p);
+}
+
+/* Wipe api cache */
+void ivtv_mailbox_cache_invalidate(struct ivtv *itv)
+{
+	int i;
+	for (i = 0; i < 256; i++)
+		itv->api_cache[i].last_jiffies = 0;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-mailbox.h b/drivers/media/pci/ivtv/ivtv-mailbox.h
new file mode 100644
index 000000000000..2c834d2cb56f
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-mailbox.h
@@ -0,0 +1,35 @@
+/*
+    mailbox functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_MAILBOX_H
+#define IVTV_MAILBOX_H
+
+#define IVTV_MBOX_DMA_END         8
+#define IVTV_MBOX_DMA             9
+
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+		       int argc, 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, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
+void ivtv_mailbox_cache_invalidate(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-queue.c b/drivers/media/pci/ivtv/ivtv-queue.c
new file mode 100644
index 000000000000..7fde36e6d227
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-queue.c
@@ -0,0 +1,297 @@
+/*
+    buffer queues.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
+{
+	if (s->buf_size - buf->bytesused < copybytes)
+		copybytes = s->buf_size - buf->bytesused;
+	if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
+		return -EFAULT;
+	}
+	buf->bytesused += copybytes;
+	return copybytes;
+}
+
+void ivtv_buf_swap(struct ivtv_buffer *buf)
+{
+	int i;
+
+	for (i = 0; i < buf->bytesused; i += 4)
+		swab32s((u32 *)(buf->buf + i));
+}
+
+void ivtv_queue_init(struct ivtv_queue *q)
+{
+	INIT_LIST_HEAD(&q->list);
+	q->buffers = 0;
+	q->length = 0;
+	q->bytesused = 0;
+}
+
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
+{
+	unsigned long flags;
+
+	/* clear the buffer if it is going to be enqueued to the free queue */
+	if (q == &s->q_free) {
+		buf->bytesused = 0;
+		buf->readpos = 0;
+		buf->b_flags = 0;
+		buf->dma_xfer_cnt = 0;
+	}
+	spin_lock_irqsave(&s->qlock, flags);
+	list_add_tail(&buf->list, &q->list);
+	q->buffers++;
+	q->length += s->buf_size;
+	q->bytesused += buf->bytesused - buf->readpos;
+	spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
+{
+	struct ivtv_buffer *buf = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->qlock, flags);
+	if (!list_empty(&q->list)) {
+		buf = list_entry(q->list.next, struct ivtv_buffer, list);
+		list_del_init(q->list.next);
+		q->buffers--;
+		q->length -= s->buf_size;
+		q->bytesused -= buf->bytesused - buf->readpos;
+	}
+	spin_unlock_irqrestore(&s->qlock, flags);
+	return buf;
+}
+
+static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
+		struct ivtv_queue *to, int clear)
+{
+	struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
+
+	list_move_tail(from->list.next, &to->list);
+	from->buffers--;
+	from->length -= s->buf_size;
+	from->bytesused -= buf->bytesused - buf->readpos;
+	/* special handling for q_free */
+	if (clear)
+		buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
+	to->buffers++;
+	to->length += s->buf_size;
+	to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+   If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+   If 'steal' != NULL, then buffers may also taken from that queue if
+   needed, but only if 'from' is the free queue.
+
+   The buffer is automatically cleared if it goes to the free queue. It is
+   also cleared if buffers need to be taken from the 'steal' queue and
+   the 'from' queue is the free queue.
+
+   When 'from' is q_free, then needed_bytes is compared to the total
+   available buffer length, otherwise needed_bytes is compared to the
+   bytesused value. For the 'steal' queue the total available buffer
+   length is always used.
+
+   -ENOMEM is returned if the buffers could not be obtained, 0 if all
+   buffers where obtained from the 'from' list and if non-zero then
+   the number of stolen buffers is returned. */
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+		    struct ivtv_queue *to, int needed_bytes)
+{
+	unsigned long flags;
+	int rc = 0;
+	int from_free = from == &s->q_free;
+	int to_free = to == &s->q_free;
+	int bytes_available, bytes_steal;
+
+	spin_lock_irqsave(&s->qlock, flags);
+	if (needed_bytes == 0) {
+		from_free = 1;
+		needed_bytes = from->length;
+	}
+
+	bytes_available = from_free ? from->length : from->bytesused;
+	bytes_steal = (from_free && steal) ? steal->length : 0;
+
+	if (bytes_available + bytes_steal < needed_bytes) {
+		spin_unlock_irqrestore(&s->qlock, flags);
+		return -ENOMEM;
+	}
+	while (bytes_available < needed_bytes) {
+		struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
+		u16 dma_xfer_cnt = buf->dma_xfer_cnt;
+
+		/* move buffers from the tail of the 'steal' queue to the tail of the
+		   'from' queue. Always copy all the buffers with the same dma_xfer_cnt
+		   value, this ensures that you do not end up with partial frame data
+		   if one frame is stored in multiple buffers. */
+		while (dma_xfer_cnt == buf->dma_xfer_cnt) {
+			list_move_tail(steal->list.prev, &from->list);
+			rc++;
+			steal->buffers--;
+			steal->length -= s->buf_size;
+			steal->bytesused -= buf->bytesused - buf->readpos;
+			buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
+			from->buffers++;
+			from->length += s->buf_size;
+			bytes_available += s->buf_size;
+			if (list_empty(&steal->list))
+				break;
+			buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
+		}
+	}
+	if (from_free) {
+		u32 old_length = to->length;
+
+		while (to->length - old_length < needed_bytes) {
+			ivtv_queue_move_buf(s, from, to, 1);
+		}
+	}
+	else {
+		u32 old_bytesused = to->bytesused;
+
+		while (to->bytesused - old_bytesused < needed_bytes) {
+			ivtv_queue_move_buf(s, from, to, to_free);
+		}
+	}
+	spin_unlock_irqrestore(&s->qlock, flags);
+	return rc;
+}
+
+void ivtv_flush_queues(struct ivtv_stream *s)
+{
+	ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+	ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+}
+
+int ivtv_stream_alloc(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+	int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers;
+	int i;
+
+	if (s->buffers == 0)
+		return 0;
+
+	IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
+		s->dma != PCI_DMA_NONE ? "DMA " : "",
+		s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
+
+	s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
+	if (s->sg_pending == NULL) {
+		IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name);
+		return -ENOMEM;
+	}
+	s->sg_pending_size = 0;
+
+	s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
+	if (s->sg_processing == NULL) {
+		IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name);
+		kfree(s->sg_pending);
+		s->sg_pending = NULL;
+		return -ENOMEM;
+	}
+	s->sg_processing_size = 0;
+
+	s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element),
+					GFP_KERNEL|__GFP_NOWARN);
+	if (s->sg_dma == NULL) {
+		IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);
+		kfree(s->sg_pending);
+		s->sg_pending = NULL;
+		kfree(s->sg_processing);
+		s->sg_processing = NULL;
+		return -ENOMEM;
+	}
+	if (ivtv_might_use_dma(s)) {
+		s->sg_handle = pci_map_single(itv->pdev, s->sg_dma,
+				sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+		ivtv_stream_sync_for_cpu(s);
+	}
+
+	/* allocate stream buffers. Initially all buffers are in q_free. */
+	for (i = 0; i < s->buffers; i++) {
+		struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer),
+						GFP_KERNEL|__GFP_NOWARN);
+
+		if (buf == NULL)
+			break;
+		buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN);
+		if (buf->buf == NULL) {
+			kfree(buf);
+			break;
+		}
+		INIT_LIST_HEAD(&buf->list);
+		if (ivtv_might_use_dma(s)) {
+			buf->dma_handle = pci_map_single(s->itv->pdev,
+				buf->buf, s->buf_size + 256, s->dma);
+			ivtv_buf_sync_for_cpu(s, buf);
+		}
+		ivtv_enqueue(s, buf, &s->q_free);
+	}
+	if (i == s->buffers)
+		return 0;
+	IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+	ivtv_stream_free(s);
+	return -ENOMEM;
+}
+
+void ivtv_stream_free(struct ivtv_stream *s)
+{
+	struct ivtv_buffer *buf;
+
+	/* move all buffers to q_free */
+	ivtv_flush_queues(s);
+
+	/* empty q_free */
+	while ((buf = ivtv_dequeue(s, &s->q_free))) {
+		if (ivtv_might_use_dma(s))
+			pci_unmap_single(s->itv->pdev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+		kfree(buf->buf);
+		kfree(buf);
+	}
+
+	/* Free SG Array/Lists */
+	if (s->sg_dma != NULL) {
+		if (s->sg_handle != IVTV_DMA_UNMAPPED) {
+			pci_unmap_single(s->itv->pdev, s->sg_handle,
+				 sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+			s->sg_handle = IVTV_DMA_UNMAPPED;
+		}
+		kfree(s->sg_pending);
+		kfree(s->sg_processing);
+		kfree(s->sg_dma);
+		s->sg_pending = NULL;
+		s->sg_processing = NULL;
+		s->sg_dma = NULL;
+		s->sg_pending_size = 0;
+		s->sg_processing_size = 0;
+	}
+}
diff --git a/drivers/media/pci/ivtv/ivtv-queue.h b/drivers/media/pci/ivtv/ivtv-queue.h
new file mode 100644
index 000000000000..91233839a26c
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-queue.h
@@ -0,0 +1,96 @@
+/*
+    buffer queues.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_QUEUE_H
+#define IVTV_QUEUE_H
+
+#define IVTV_DMA_UNMAPPED	((u32) -1)
+#define SLICED_VBI_PIO 0
+
+/* ivtv_buffer utility functions */
+
+static inline int ivtv_might_use_pio(struct ivtv_stream *s)
+{
+	return s->dma == PCI_DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI);
+}
+
+static inline int ivtv_use_pio(struct ivtv_stream *s)
+{
+	struct ivtv *itv = s->itv;
+
+	return s->dma == PCI_DMA_NONE ||
+	    (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+}
+
+static inline int ivtv_might_use_dma(struct ivtv_stream *s)
+{
+	return s->dma != PCI_DMA_NONE;
+}
+
+static inline int ivtv_use_dma(struct ivtv_stream *s)
+{
+	return !ivtv_use_pio(s);
+}
+
+static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+	if (ivtv_use_dma(s))
+		pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+}
+
+static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+	if (ivtv_use_dma(s))
+		pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle,
+				s->buf_size + 256, s->dma);
+}
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
+void ivtv_buf_swap(struct ivtv_buffer *buf);
+
+/* ivtv_queue utility functions */
+void ivtv_queue_init(struct ivtv_queue *q);
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+		    struct ivtv_queue *to, int needed_bytes);
+void ivtv_flush_queues(struct ivtv_stream *s);
+
+/* ivtv_stream utility functions */
+int ivtv_stream_alloc(struct ivtv_stream *s);
+void ivtv_stream_free(struct ivtv_stream *s);
+
+static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+{
+	if (ivtv_use_dma(s))
+		pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle,
+			sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+{
+	if (ivtv_use_dma(s))
+		pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle,
+			sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+}
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-routing.c b/drivers/media/pci/ivtv/ivtv-routing.c
new file mode 100644
index 000000000000..8898c569a1c9
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-routing.c
@@ -0,0 +1,119 @@
+/*
+    Audio/video-routing-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-i2c.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-routing.h"
+
+#include <media/msp3400.h>
+#include <media/m52790.h>
+#include <media/upd64031a.h>
+#include <media/upd64083.h>
+
+/* Selects the audio input and output according to the current
+   settings. */
+void ivtv_audio_set_io(struct ivtv *itv)
+{
+	const struct ivtv_card_audio_input *in;
+	u32 input, output = 0;
+
+	/* Determine which input to use */
+	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 */
+	input = in->muxer_input;
+	if (itv->card->hw_muxer & IVTV_HW_M52790)
+		output = M52790_OUT_STEREO;
+	v4l2_subdev_call(itv->sd_muxer, audio, s_routing,
+			input, output, 0);
+
+	input = in->audio_input;
+	output = 0;
+	if (itv->card->hw_audio & IVTV_HW_MSP34XX)
+		output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+	ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing,
+			input, output, 0);
+}
+
+/* Selects the video input and output according to the current
+   settings. */
+void ivtv_video_set_io(struct ivtv *itv)
+{
+	int inp = itv->active_input;
+	u32 input;
+	u32 type;
+
+	v4l2_subdev_call(itv->sd_video, video, s_routing,
+		itv->card->video_inputs[inp].video_input, 0, 0);
+
+	type = itv->card->video_inputs[inp].video_type;
+
+	if (type == IVTV_CARD_INPUT_VID_TUNER) {
+		input = 0;  /* Tuner */
+	} else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
+		input = 2;  /* S-Video */
+	} else {
+		input = 1;  /* Composite */
+	}
+
+	if (itv->card->hw_video & IVTV_HW_GPIO)
+		ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing,
+				input, 0, 0);
+
+	if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+		if (type == IVTV_CARD_INPUT_VID_TUNER ||
+		    type >= IVTV_CARD_INPUT_COMPOSITE1) {
+			/* Composite: GR on, connect to 3DYCS */
+			input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
+		} else {
+			/* S-Video: GR bypassed, turn it off */
+			input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
+		}
+		input |= itv->card->gr_config;
+
+		ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing,
+				input, 0, 0);
+	}
+
+	if (itv->card->hw_video & IVTV_HW_UPD6408X) {
+		input = UPD64083_YCS_MODE;
+		if (type > IVTV_CARD_INPUT_VID_TUNER &&
+		    type < IVTV_CARD_INPUT_COMPOSITE1) {
+			/* S-Video uses YCNR mode and internal Y-ADC, the
+			   upd64031a is not used. */
+			input |= UPD64083_YCNR_MODE;
+		}
+		else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+			/* Use upd64031a output for tuner and
+			   composite(CX23416GYC only) inputs */
+			if (type == IVTV_CARD_INPUT_VID_TUNER ||
+			    itv->card->type == IVTV_CARD_CX23416GYC) {
+				input |= UPD64083_EXT_Y_ADC;
+			}
+		}
+		ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing,
+				input, 0, 0);
+	}
+}
diff --git a/drivers/media/pci/ivtv/ivtv-routing.h b/drivers/media/pci/ivtv/ivtv-routing.h
new file mode 100644
index 000000000000..c72a9731ca01
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-routing.h
@@ -0,0 +1,27 @@
+/*
+    Audio/video-routing-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_ROUTING_H
+#define IVTV_ROUTING_H
+
+void ivtv_audio_set_io(struct ivtv *itv);
+void ivtv_video_set_io(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c
new file mode 100644
index 000000000000..70dad588a677
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-streams.c
@@ -0,0 +1,1039 @@
+/*
+    init/start/stop/exit stream functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-yuv.h"
+#include "ivtv-cards.h"
+#include "ivtv-streams.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+
+static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
+	.owner = THIS_MODULE,
+	.read = ivtv_v4l2_read,
+	.write = ivtv_v4l2_write,
+	.open = ivtv_v4l2_open,
+	.unlocked_ioctl = video_ioctl2,
+	.release = ivtv_v4l2_close,
+	.poll = ivtv_v4l2_enc_poll,
+};
+
+static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
+	.owner = THIS_MODULE,
+	.read = ivtv_v4l2_read,
+	.write = ivtv_v4l2_write,
+	.open = ivtv_v4l2_open,
+	.unlocked_ioctl = video_ioctl2,
+	.release = ivtv_v4l2_close,
+	.poll = ivtv_v4l2_dec_poll,
+};
+
+static const struct v4l2_file_operations ivtv_v4l2_radio_fops = {
+	.owner = THIS_MODULE,
+	.open = ivtv_v4l2_open,
+	.unlocked_ioctl = video_ioctl2,
+	.release = ivtv_v4l2_close,
+	.poll = ivtv_v4l2_enc_poll,
+};
+
+#define IVTV_V4L2_DEC_MPG_OFFSET  16	/* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET  24	/* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET  32	/* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET  48	/* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET   8	/* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16	/* offset from 0 to register vbi output v4l2 minors on */
+
+static struct {
+	const char *name;
+	int vfl_type;
+	int num_offset;
+	int dma, pio;
+	u32 v4l2_caps;
+	const struct v4l2_file_operations *fops;
+} ivtv_stream_info[] = {
+	{	/* IVTV_ENC_STREAM_TYPE_MPG */
+		"encoder MPG",
+		VFL_TYPE_GRABBER, 0,
+		PCI_DMA_FROMDEVICE, 0,
+		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+			V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_YUV */
+		"encoder YUV",
+		VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+		PCI_DMA_FROMDEVICE, 0,
+		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+			V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_VBI */
+		"encoder VBI",
+		VFL_TYPE_VBI, 0,
+		PCI_DMA_FROMDEVICE, 0,
+		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
+			V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_PCM */
+		"encoder PCM",
+		VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+		PCI_DMA_FROMDEVICE, 0,
+		V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_ENC_STREAM_TYPE_RAD */
+		"encoder radio",
+		VFL_TYPE_RADIO, 0,
+		PCI_DMA_NONE, 1,
+		V4L2_CAP_RADIO | V4L2_CAP_TUNER,
+		&ivtv_v4l2_radio_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_MPG */
+		"decoder MPG",
+		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+		PCI_DMA_TODEVICE, 0,
+		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_dec_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_VBI */
+		"decoder VBI",
+		VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+		PCI_DMA_NONE, 1,
+		V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_enc_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_VOUT */
+		"decoder VOUT",
+		VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+		PCI_DMA_NONE, 1,
+		V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_dec_fops
+	},
+	{	/* IVTV_DEC_STREAM_TYPE_YUV */
+		"decoder YUV",
+		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+		PCI_DMA_TODEVICE, 0,
+		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		&ivtv_v4l2_dec_fops
+	}
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+	struct ivtv_stream *s = &itv->streams[type];
+	struct video_device *vdev = s->vdev;
+
+	/* we need to keep vdev, so restore it afterwards */
+	memset(s, 0, sizeof(*s));
+	s->vdev = vdev;
+
+	/* initialize ivtv_stream fields */
+	s->itv = itv;
+	s->type = type;
+	s->name = ivtv_stream_info[type].name;
+	s->caps = ivtv_stream_info[type].v4l2_caps;
+
+	if (ivtv_stream_info[type].pio)
+		s->dma = PCI_DMA_NONE;
+	else
+		s->dma = ivtv_stream_info[type].dma;
+	s->buf_size = itv->stream_buf_size[type];
+	if (s->buf_size)
+		s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size;
+	spin_lock_init(&s->qlock);
+	init_waitqueue_head(&s->waitq);
+	s->sg_handle = IVTV_DMA_UNMAPPED;
+	ivtv_queue_init(&s->q_free);
+	ivtv_queue_init(&s->q_full);
+	ivtv_queue_init(&s->q_dma);
+	ivtv_queue_init(&s->q_predma);
+	ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_prep_dev(struct ivtv *itv, int type)
+{
+	struct ivtv_stream *s = &itv->streams[type];
+	int num_offset = ivtv_stream_info[type].num_offset;
+	int num = itv->instance + ivtv_first_minor + num_offset;
+
+	/* These four fields are always initialized. If vdev == NULL, then
+	   this stream is not in use. In that case no other fields but these
+	   four can be used. */
+	s->vdev = NULL;
+	s->itv = itv;
+	s->type = type;
+	s->name = ivtv_stream_info[type].name;
+
+	/* Check whether the radio is supported */
+	if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+		return 0;
+	if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return 0;
+
+	/* User explicitly selected 0 buffers for these streams, so don't
+	   create them. */
+	if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+	    itv->options.kilobytes[type] == 0) {
+		IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+		return 0;
+	}
+
+	ivtv_stream_init(itv, type);
+
+	/* allocate and initialize the v4l2 video device structure */
+	s->vdev = video_device_alloc();
+	if (s->vdev == NULL) {
+		IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+		return -ENOMEM;
+	}
+
+	snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
+			itv->v4l2_dev.name, s->name);
+
+	s->vdev->num = num;
+	s->vdev->v4l2_dev = &itv->v4l2_dev;
+	if (ivtv_stream_info[type].v4l2_caps &
+			(V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT))
+		s->vdev->vfl_dir = VFL_DIR_TX;
+	s->vdev->fops = ivtv_stream_info[type].fops;
+	s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
+	s->vdev->release = video_device_release;
+	s->vdev->tvnorms = V4L2_STD_ALL;
+	s->vdev->lock = &itv->serialize_lock;
+	if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+		v4l2_disable_ioctl(s->vdev, VIDIOC_S_AUDIO);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_G_AUDIO);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMAUDIO);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMINPUT);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_S_INPUT);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_G_INPUT);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_S_FREQUENCY);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_G_FREQUENCY);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_S_TUNER);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_G_TUNER);
+		v4l2_disable_ioctl(s->vdev, VIDIOC_S_STD);
+	}
+	set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags);
+	ivtv_set_funcs(s->vdev);
+	return 0;
+}
+
+/* Initialize v4l2 variables and prepare v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+	int type;
+
+	/* Setup V4L2 Devices */
+	for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+		/* Prepare device */
+		if (ivtv_prep_dev(itv, type))
+			break;
+
+		if (itv->streams[type].vdev == NULL)
+			continue;
+
+		/* Allocate Stream */
+		if (ivtv_stream_alloc(&itv->streams[type]))
+			break;
+	}
+	if (type == IVTV_MAX_STREAMS)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	ivtv_streams_cleanup(itv, 0);
+	return -ENOMEM;
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+	struct ivtv_stream *s = &itv->streams[type];
+	int vfl_type = ivtv_stream_info[type].vfl_type;
+	const char *name;
+	int num;
+
+	if (s->vdev == NULL)
+		return 0;
+
+	num = s->vdev->num;
+	/* card number + user defined offset + device offset */
+	if (type != IVTV_ENC_STREAM_TYPE_MPG) {
+		struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+
+		if (s_mpg->vdev)
+			num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
+	}
+	video_set_drvdata(s->vdev, s);
+
+	/* Register device. First try the desired minor, then any free one. */
+	if (video_register_device_no_warn(s->vdev, vfl_type, num)) {
+		IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+				s->name, num);
+		video_device_release(s->vdev);
+		s->vdev = NULL;
+		return -ENOMEM;
+	}
+	name = video_device_node_name(s->vdev);
+
+	switch (vfl_type) {
+	case VFL_TYPE_GRABBER:
+		IVTV_INFO("Registered device %s for %s (%d kB)\n",
+			name, s->name, itv->options.kilobytes[type]);
+		break;
+	case VFL_TYPE_RADIO:
+		IVTV_INFO("Registered device %s for %s\n",
+			name, s->name);
+		break;
+	case VFL_TYPE_VBI:
+		if (itv->options.kilobytes[type])
+			IVTV_INFO("Registered device %s for %s (%d kB)\n",
+				name, s->name, itv->options.kilobytes[type]);
+		else
+			IVTV_INFO("Registered device %s for %s\n",
+				name, s->name);
+		break;
+	}
+	return 0;
+}
+
+/* Register v4l2 devices */
+int ivtv_streams_register(struct ivtv *itv)
+{
+	int type;
+	int err = 0;
+
+	/* Register V4L2 devices */
+	for (type = 0; type < IVTV_MAX_STREAMS; type++)
+		err |= ivtv_reg_dev(itv, type);
+
+	if (err == 0)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	ivtv_streams_cleanup(itv, 1);
+	return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
+{
+	int type;
+
+	/* Teardown all streams */
+	for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+		struct video_device *vdev = itv->streams[type].vdev;
+
+		itv->streams[type].vdev = NULL;
+		if (vdev == NULL)
+			continue;
+
+		ivtv_stream_free(&itv->streams[type]);
+		/* Unregister or release device */
+		if (unregister)
+			video_unregister_device(vdev);
+		else
+			video_device_release(vdev);
+	}
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+	int raw = ivtv_raw_vbi(itv);
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int lines;
+	int i;
+
+	/* Reset VBI */
+	ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+	/* setup VBI registers */
+	if (raw)
+		v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi);
+	else
+		v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced);
+
+	/* determine number of lines and total number of VBI bytes.
+	   A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+	   The '- 1' byte is probably an unused U or V byte. Or something...
+	   A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+	   header, 42 data bytes + checksum (to be confirmed) */
+	if (raw) {
+		lines = itv->vbi.count * 2;
+	} else {
+		lines = itv->is_60hz ? 24 : 38;
+		if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+			lines += 2;
+	}
+
+	itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+	/* Note: sliced vs raw flag doesn't seem to have any effect
+	   TODO: check mode (0x02) value with older ivtv versions. */
+	data[0] = raw | 0x02 | (0xbd << 8);
+
+	/* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+	data[1] = 1;
+	/* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+	data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size);
+	/* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+	   The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+	   is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+	   code. These values for raw VBI are obtained from a driver disassembly. The sliced
+	   start/stop codes was deduced from this, but they do not appear in the driver.
+	   Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+	   However, I have no idea what these values are for. */
+	if (itv->hw_flags & IVTV_HW_CX25840) {
+		/* Setup VBI for the cx25840 digitizer */
+		if (raw) {
+			data[3] = 0x20602060;
+			data[4] = 0x30703070;
+		} else {
+			data[3] = 0xB0F0B0F0;
+			data[4] = 0xA0E0A0E0;
+		}
+		/* Lines per frame */
+		data[5] = lines;
+		/* bytes per line */
+		data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+	} else {
+		/* Setup VBI for the saa7115 digitizer */
+		if (raw) {
+			data[3] = 0x25256262;
+			data[4] = 0x387F7F7F;
+		} else {
+			data[3] = 0xABABECEC;
+			data[4] = 0xB6F1F1F1;
+		}
+		/* Lines per frame */
+		data[5] = lines;
+		/* bytes per line */
+		data[6] = itv->vbi.enc_size / lines;
+	}
+
+	IVTV_DEBUG_INFO(
+		"Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+			data[0], data[1], data[2], data[5], data[6]);
+
+	ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+	/* returns the VBI encoder memory area. */
+	itv->vbi.enc_start = data[2];
+	itv->vbi.fpi = data[0];
+	if (!itv->vbi.fpi)
+		itv->vbi.fpi = 1;
+
+	IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n",
+		itv->vbi.enc_start, data[1], itv->vbi.fpi);
+
+	/* select VBI lines.
+	   Note that the sliced argument seems to have no effect. */
+	for (i = 2; i <= 24; i++) {
+		int valid;
+
+		if (itv->is_60hz) {
+			valid = i >= 10 && i < 22;
+		} else {
+			valid = i >= 6 && i < 24;
+		}
+		ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+				valid, 0 , 0, 0);
+		ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+				valid, 0, 0, 0);
+	}
+
+	/* Remaining VBI questions:
+	   - Is it possible to select particular VBI lines only for inclusion in the MPEG
+	   stream? Currently you can only get the first X lines.
+	   - Is mixed raw and sliced VBI possible?
+	   - What's the meaning of the raw/sliced flag?
+	   - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv *itv = s->itv;
+	int captype = 0, subtype = 0;
+	int enable_passthrough = 0;
+
+	if (s->vdev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+	switch (s->type) {
+	case IVTV_ENC_STREAM_TYPE_MPG:
+		captype = 0;
+		subtype = 3;
+
+		/* Stop Passthrough */
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			ivtv_passthrough_mode(itv, 0);
+			enable_passthrough = 1;
+		}
+		itv->mpg_data_received = itv->vbi_data_inserted = 0;
+		itv->dualwatch_jiffies = jiffies;
+		itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+		itv->search_pack_header = 0;
+		break;
+
+	case IVTV_ENC_STREAM_TYPE_YUV:
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			captype = 2;
+			subtype = 11;	/* video+audio+decoder */
+			break;
+		}
+		captype = 1;
+		subtype = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_PCM:
+		captype = 1;
+		subtype = 2;
+		break;
+	case IVTV_ENC_STREAM_TYPE_VBI:
+		captype = 1;
+		subtype = 4;
+
+		itv->vbi.frame = 0;
+		itv->vbi.inserted_frame = 0;
+		memset(itv->vbi.sliced_mpeg_size,
+			0, sizeof(itv->vbi.sliced_mpeg_size));
+		break;
+	default:
+		return -EINVAL;
+	}
+	s->subtype = subtype;
+	s->buffers_stolen = 0;
+
+	/* Clear Streamoff flags in case left from last capture */
+	clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+	if (atomic_read(&itv->capturing) == 0) {
+		int digitizer;
+
+		/* Always use frame based mode. Experiments have demonstrated that byte
+		   stream based mode results in dropped frames and corruption. Not often,
+		   but occasionally. Many thanks go to Leonard Orb who spent a lot of
+		   effort and time trying to trace the cause of the drop outs. */
+		/* 1 frame per DMA */
+		/*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+		ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+		/* Stuff from Windows, we don't know what it is */
+		ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+		/* According to the docs, this should be correct. However, this is
+		   untested. I don't dare enable this without having tested it.
+		   Only very few old cards actually have this hardware combination.
+		ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+			((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+		*/
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+		ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+		/* assign placeholder */
+		ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+		if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+		    digitizer = 0xF1;
+		else if (itv->card->hw_all & IVTV_HW_SAA7114)
+		    digitizer = 0xEF;
+		else /* cx25840 */
+		    digitizer = 0x140;
+
+		ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer);
+
+		/* Setup VBI */
+		if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+			ivtv_vbi_setup(itv);
+		}
+
+		/* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+		ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+		itv->pgm_info_offset = data[0];
+		itv->pgm_info_num = data[1];
+		itv->pgm_info_write_idx = 0;
+		itv->pgm_info_read_idx = 0;
+
+		IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+				itv->pgm_info_offset, itv->pgm_info_num);
+
+		/* Setup API for Stream */
+		cx2341x_handler_setup(&itv->cxhdl);
+
+		/* mute if capturing radio */
+		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
+			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+				1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+	}
+
+	/* Vsync Setup */
+	if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+		/* event notification (on) */
+		ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+	}
+
+	if (atomic_read(&itv->capturing) == 0) {
+		/* Clear all Pending Interrupts */
+		ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+		clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+		cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
+		/* Initialize Digitizer for Capture */
+		/* Avoid tinny audio problem - ensure audio clocks are going */
+		v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
+		/* Avoid unpredictable PCI bus hang - disable video clocks */
+		v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
+		ivtv_msleep_timeout(300, 0);
+		ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+		v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
+	}
+
+	/* begin_capture */
+	if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+	{
+		IVTV_DEBUG_WARN( "Error starting capture!\n");
+		return -EINVAL;
+	}
+
+	/* Start Passthrough */
+	if (enable_passthrough) {
+		ivtv_passthrough_mode(itv, 1);
+	}
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+	else
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->capturing);
+	return 0;
+}
+EXPORT_SYMBOL(ivtv_start_v4l2_encode_stream);
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	struct ivtv *itv = s->itv;
+	int datatype;
+	u16 width;
+	u16 height;
+
+	if (s->vdev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+	width = itv->cxhdl.width;
+	height = itv->cxhdl.height;
+
+	/* set audio mode to left/stereo  for dual/stereo mode. */
+	ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+	/* set number of internal decoder buffers */
+	ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+	/* prebuffering */
+	ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+	/* extract from user packets */
+	ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+	itv->vbi.dec_start = data[0];
+
+	IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+		itv->vbi.dec_start, data[1]);
+
+	/* set decoder source settings */
+	/* Data type: 0 = mpeg from host,
+	   1 = yuv from encoder,
+	   2 = yuv_from_host */
+	switch (s->type) {
+	case IVTV_DEC_STREAM_TYPE_YUV:
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			datatype = 1;
+		} else {
+			/* Fake size to avoid switching video standard */
+			datatype = 2;
+			width = 720;
+			height = itv->is_out_50hz ? 576 : 480;
+		}
+		IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+		break;
+	case IVTV_DEC_STREAM_TYPE_MPG:
+	default:
+		datatype = 0;
+		break;
+	}
+	if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+			width, height, itv->cxhdl.audio_properties)) {
+		IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
+	}
+
+	/* Decoder sometimes dies here, so wait a moment */
+	ivtv_msleep_timeout(10, 0);
+
+	/* Known failure point for firmware, so check */
+	return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream");
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+	struct ivtv *itv = s->itv;
+	int rc;
+
+	if (s->vdev == NULL)
+		return -EINVAL;
+
+	if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+		return 0;	/* already started */
+
+	IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+	rc = ivtv_setup_v4l2_decode_stream(s);
+	if (rc < 0) {
+		clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+		return rc;
+	}
+
+	/* 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 */
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+	writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+	/* turn on notification of dual/stereo mode change */
+	ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+	/* start playback */
+	ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+	/* Let things settle before we actually start */
+	ivtv_msleep_timeout(10, 0);
+
+	/* Clear the following Interrupt mask bits for decoding */
+	ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+	IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->decoding);
+	return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+	int i;
+
+	for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+		struct ivtv_stream *s = &itv->streams[i];
+
+		if (s->vdev == NULL)
+			continue;
+		if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+			ivtv_stop_v4l2_encode_stream(s, 0);
+		}
+	}
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+	struct ivtv *itv = s->itv;
+	DECLARE_WAITQUEUE(wait, current);
+	int cap_type;
+	int stopmode;
+
+	if (s->vdev == NULL)
+		return -EINVAL;
+
+	/* This function assumes that you are allowed to stop the capture
+	   and that we are actually capturing */
+
+	IVTV_DEBUG_INFO("Stop Capture\n");
+
+	if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+		return 0;
+	if (atomic_read(&itv->capturing) == 0)
+		return 0;
+
+	switch (s->type) {
+	case IVTV_ENC_STREAM_TYPE_YUV:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_PCM:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_VBI:
+		cap_type = 1;
+		break;
+	case IVTV_ENC_STREAM_TYPE_MPG:
+	default:
+		cap_type = 0;
+		break;
+	}
+
+	/* Stop Capture Mode */
+	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+		stopmode = 0;
+	} else {
+		stopmode = 1;
+	}
+
+	/* end_capture */
+	/* when: 0 =  end of GOP  1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+	ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+	if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+		if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+			/* only run these if we're shutting down the last cap */
+			unsigned long duration;
+			unsigned long then = jiffies;
+
+			add_wait_queue(&itv->eos_waitq, &wait);
+
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			/* wait 2s for EOS interrupt */
+			while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) &&
+				time_before(jiffies,
+					    then + msecs_to_jiffies(2000))) {
+				schedule_timeout(msecs_to_jiffies(10));
+			}
+
+			/* To convert jiffies to ms, we must multiply by 1000
+			 * and divide by HZ.  To avoid runtime division, we
+			 * convert this to multiplication by 1000/HZ.
+			 * Since integer division truncates, we get the best
+			 * accuracy if we do a rounding calculation of the constant.
+			 * Think of the case where HZ is 1024.
+			 */
+			duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+			if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+				IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+				IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+			} else {
+				IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+			}
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&itv->eos_waitq, &wait);
+			set_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+		}
+
+		/* Handle any pending interrupts */
+		ivtv_msleep_timeout(100, 0);
+	}
+
+	atomic_dec(&itv->capturing);
+
+	/* Clear capture and no-read bits */
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+	if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+		ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+	if (atomic_read(&itv->capturing) > 0) {
+		return 0;
+	}
+
+	cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+	/* Set the following Interrupt mask bits for capture */
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+	del_timer(&itv->dma_timer);
+
+	/* event notification (off) */
+	if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+		/* type: 0 = refresh */
+		/* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+		ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+		ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+	}
+
+	/* Raw-passthrough is implied on start. Make sure it's stopped so
+	   the encoder will re-initialize when next started */
+	ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7);
+
+	wake_up(&s->waitq);
+
+	return 0;
+}
+EXPORT_SYMBOL(ivtv_stop_v4l2_encode_stream);
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+	static const struct v4l2_event ev = {
+		.type = V4L2_EVENT_EOS,
+	};
+	struct ivtv *itv = s->itv;
+
+	if (s->vdev == NULL)
+		return -EINVAL;
+
+	if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+		return -EINVAL;
+
+	if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+		return 0;
+
+	IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
+
+	/* Stop Decoder */
+	if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
+		u32 tmp = 0;
+
+		/* Wait until the decoder is no longer running */
+		if (pts) {
+			ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+				0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+		}
+		while (1) {
+			u32 data[CX2341X_MBOX_MAX_DATA];
+			ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+			if (s->q_full.buffers + s->q_dma.buffers == 0) {
+				if (tmp == data[3])
+					break;
+				tmp = data[3];
+			}
+			if (ivtv_msleep_timeout(100, 1))
+				break;
+		}
+	}
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
+
+	/* turn off notification of dual/stereo mode change */
+	ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+	del_timer(&itv->dma_timer);
+
+	clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+	ivtv_flush_queues(s);
+
+	/* decoder needs time to settle */
+	ivtv_msleep_timeout(40, 0);
+
+	/* decrement decoding */
+	atomic_dec(&itv->decoding);
+
+	set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+	wake_up(&itv->event_waitq);
+	v4l2_event_queue(s->vdev, &ev);
+
+	/* wake up wait queues */
+	wake_up(&s->waitq);
+
+	return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+	struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+	struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+	if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
+		return -EINVAL;
+
+	IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+	/* Prevent others from starting/stopping streams while we
+	   initiate/terminate passthrough mode */
+	if (enable) {
+		if (itv->output_mode == OUT_PASSTHROUGH) {
+			return 0;
+		}
+		if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+			return -EBUSY;
+
+		/* Fully initialize stream, and then unflag init */
+		set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+		set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+		/* Setup YUV Decoder */
+		ivtv_setup_v4l2_decode_stream(dec_stream);
+
+		/* Start Decoder */
+		ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+		atomic_inc(&itv->decoding);
+
+		/* Setup capture if not already done */
+		if (atomic_read(&itv->capturing) == 0) {
+			cx2341x_handler_setup(&itv->cxhdl);
+			cx2341x_handler_set_busy(&itv->cxhdl, 1);
+		}
+
+		/* Start Passthrough Mode */
+		ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+		atomic_inc(&itv->capturing);
+		return 0;
+	}
+
+	if (itv->output_mode != OUT_PASSTHROUGH)
+		return 0;
+
+	/* Stop Passthrough Mode */
+	ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+	ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+	atomic_dec(&itv->capturing);
+	atomic_dec(&itv->decoding);
+	clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+	clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+	itv->output_mode = OUT_NONE;
+	if (atomic_read(&itv->capturing) == 0)
+		cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+	return 0;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-streams.h b/drivers/media/pci/ivtv/ivtv-streams.h
new file mode 100644
index 000000000000..a653a5136417
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-streams.h
@@ -0,0 +1,37 @@
+/*
+    init/start/stop/exit stream functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_STREAMS_H
+#define IVTV_STREAMS_H
+
+int ivtv_streams_setup(struct ivtv *itv);
+int ivtv_streams_register(struct ivtv *itv);
+void ivtv_streams_cleanup(struct ivtv *itv, int unregister);
+
+/* Capture related */
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
+
+void ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_passthrough_mode(struct ivtv *itv, int enable);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c
new file mode 100644
index 000000000000..7338cb2d0a38
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-udma.c
@@ -0,0 +1,234 @@
+/*
+    User DMA
+
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-udma.h"
+
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
+{
+	dma_page->uaddr = first & PAGE_MASK;
+	dma_page->offset = first & ~PAGE_MASK;
+	dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
+	dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
+	dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+	dma_page->page_count = dma_page->last - dma_page->first + 1;
+	if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
+}
+
+int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
+{
+	int i, offset;
+	unsigned long flags;
+
+	if (map_offset < 0)
+		return map_offset;
+
+	offset = dma_page->offset;
+
+	/* Fill SG Array with new values */
+	for (i = 0; i < dma_page->page_count; i++) {
+		unsigned int len = (i == dma_page->page_count - 1) ?
+			dma_page->tail : PAGE_SIZE - offset;
+
+		if (PageHighMem(dma->map[map_offset])) {
+			void *src;
+
+			if (dma->bouncemap[map_offset] == NULL)
+				dma->bouncemap[map_offset] = alloc_page(GFP_KERNEL);
+			if (dma->bouncemap[map_offset] == NULL)
+				return -1;
+			local_irq_save(flags);
+			src = kmap_atomic(dma->map[map_offset]) + offset;
+			memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len);
+			kunmap_atomic(src);
+			local_irq_restore(flags);
+			sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset);
+		}
+		else {
+			sg_set_page(&dma->SGlist[map_offset], dma->map[map_offset], len, offset);
+		}
+		offset = 0;
+		map_offset++;
+	}
+	return map_offset;
+}
+
+void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
+	int i;
+	struct scatterlist *sg;
+
+	for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+		dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
+		dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
+		dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
+		buffer_offset += sg_dma_len(sg);
+
+		split -= sg_dma_len(sg);
+		if (split == 0)
+			buffer_offset = buffer_offset_2;
+	}
+}
+
+/* User DMA Buffers */
+void ivtv_udma_alloc(struct ivtv *itv)
+{
+	if (itv->udma.SG_handle == 0) {
+		/* Map DMA Page Array Buffer */
+		itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray,
+			   sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+		ivtv_udma_sync_for_cpu(itv);
+	}
+}
+
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+		       void __user *userbuf, int size_in_bytes)
+{
+	struct ivtv_dma_page_info user_dma;
+	struct ivtv_user_dma *dma = &itv->udma;
+	int i, err;
+
+	IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
+
+	/* Still in USE */
+	if (dma->SG_length || dma->page_count) {
+		IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
+			   dma->SG_length, dma->page_count);
+		return -EBUSY;
+	}
+
+	ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
+
+	if (user_dma.page_count <= 0) {
+		IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
+			   user_dma.page_count, size_in_bytes, user_dma.offset);
+		return -EINVAL;
+	}
+
+	/* Get user pages for DMA Xfer */
+	down_read(&current->mm->mmap_sem);
+	err = get_user_pages(current, current->mm,
+			user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
+	up_read(&current->mm->mmap_sem);
+
+	if (user_dma.page_count != err) {
+		IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+			   err, user_dma.page_count);
+		if (err >= 0) {
+			for (i = 0; i < err; i++)
+				put_page(dma->map[i]);
+			return -EINVAL;
+		}
+		return err;
+	}
+
+	dma->page_count = user_dma.page_count;
+
+	/* Fill SG List with new values */
+	if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) {
+		for (i = 0; i < dma->page_count; i++) {
+			put_page(dma->map[i]);
+		}
+		dma->page_count = 0;
+		return -ENOMEM;
+	}
+
+	/* Map SG List */
+	dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+	/* Fill SG Array with new values */
+	ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+
+	/* Tag SG Array with Interrupt Bit */
+	dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	ivtv_udma_sync_for_device(itv);
+	return dma->page_count;
+}
+
+void ivtv_udma_unmap(struct ivtv *itv)
+{
+	struct ivtv_user_dma *dma = &itv->udma;
+	int i;
+
+	IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
+
+	/* Nothing to free */
+	if (dma->page_count == 0)
+		return;
+
+	/* Unmap Scatterlist */
+	if (dma->SG_length) {
+		pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+		dma->SG_length = 0;
+	}
+	/* sync DMA */
+	ivtv_udma_sync_for_cpu(itv);
+
+	/* Release User Pages */
+	for (i = 0; i < dma->page_count; i++) {
+		put_page(dma->map[i]);
+	}
+	dma->page_count = 0;
+}
+
+void ivtv_udma_free(struct ivtv *itv)
+{
+	int i;
+
+	/* Unmap SG Array */
+	if (itv->udma.SG_handle) {
+		pci_unmap_single(itv->pdev, itv->udma.SG_handle,
+			 sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+	}
+
+	/* Unmap Scatterlist */
+	if (itv->udma.SG_length) {
+		pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+	}
+
+	for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) {
+		if (itv->udma.bouncemap[i])
+			__free_page(itv->udma.bouncemap[i]);
+	}
+}
+
+void ivtv_udma_start(struct ivtv *itv)
+{
+	IVTV_DEBUG_DMA("start UDMA\n");
+	write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
+	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+	set_bit(IVTV_F_I_DMA, &itv->i_flags);
+	set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+	clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+}
+
+void ivtv_udma_prepare(struct ivtv *itv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->dma_reg_lock, flags);
+	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+		ivtv_udma_start(itv);
+	else
+		set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+	spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
diff --git a/drivers/media/pci/ivtv/ivtv-udma.h b/drivers/media/pci/ivtv/ivtv-udma.h
new file mode 100644
index 000000000000..ee3c9efb5b72
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-udma.h
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2006-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_UDMA_H
+#define IVTV_UDMA_H
+
+/* User DMA functions */
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
+int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
+void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+		       void __user *userbuf, int size_in_bytes);
+void ivtv_udma_unmap(struct ivtv *itv);
+void ivtv_udma_free(struct ivtv *itv);
+void ivtv_udma_alloc(struct ivtv *itv);
+void ivtv_udma_prepare(struct ivtv *itv);
+void ivtv_udma_start(struct ivtv *itv);
+
+static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+{
+	pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle,
+		sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+{
+	pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle,
+		sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c
new file mode 100644
index 000000000000..293db806d936
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-vbi.c
@@ -0,0 +1,549 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-i2c.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-queue.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+
+static void ivtv_set_vps(struct ivtv *itv, int enabled)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	data.id = V4L2_SLICED_VPS;
+	data.field = 0;
+	data.line = enabled ? 16 : 0;
+	data.data[2] = itv->vbi.vps_payload.data[0];
+	data.data[8] = itv->vbi.vps_payload.data[1];
+	data.data[9] = itv->vbi.vps_payload.data[2];
+	data.data[10] = itv->vbi.vps_payload.data[3];
+	data.data[11] = itv->vbi.vps_payload.data[4];
+	ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	data.id = V4L2_SLICED_CAPTION_525;
+	data.field = 0;
+	data.line = (mode & 1) ? 21 : 0;
+	data.data[0] = cc->odd[0];
+	data.data[1] = cc->odd[1];
+	ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+	data.field = 1;
+	data.line = (mode & 2) ? 21 : 0;
+	data.data[0] = cc->even[0];
+	data.data[1] = cc->even[1];
+	ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
+{
+	struct v4l2_sliced_vbi_data data;
+
+	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return;
+	/* When using a 50 Hz system, always turn on the
+	   wide screen signal with 4x3 ratio as the default.
+	   Turning this signal on and off can confuse certain
+	   TVs. As far as I can tell there is no reason not to
+	   transmit this signal. */
+	if ((itv->std_out & V4L2_STD_625_50) && !enabled) {
+		enabled = 1;
+		mode = 0x08;  /* 4x3 full format */
+	}
+	data.id = V4L2_SLICED_WSS_625;
+	data.field = 0;
+	data.line = enabled ? 23 : 0;
+	data.data[0] = mode & 0xff;
+	data.data[1] = (mode >> 8) & 0xff;
+	ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static int odd_parity(u8 c)
+{
+	c ^= (c >> 4);
+	c ^= (c >> 2);
+	c ^= (c >> 1);
+
+	return c & 1;
+}
+
+static void ivtv_write_vbi_line(struct ivtv *itv,
+				const struct v4l2_sliced_vbi_data *d,
+				struct vbi_cc *cc, int *found_cc)
+{
+	struct vbi_info *vi = &itv->vbi;
+
+	if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
+		if (d->field) {
+			cc->even[0] = d->data[0];
+			cc->even[1] = d->data[1];
+		} else {
+			cc->odd[0] = d->data[0];
+			cc->odd[1] = d->data[1];
+		}
+		*found_cc = 1;
+	} else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
+		struct vbi_vps vps;
+
+		vps.data[0] = d->data[2];
+		vps.data[1] = d->data[8];
+		vps.data[2] = d->data[9];
+		vps.data[3] = d->data[10];
+		vps.data[4] = d->data[11];
+		if (memcmp(&vps, &vi->vps_payload, sizeof(vps))) {
+			vi->vps_payload = vps;
+			set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+		}
+	} else if (d->id == V4L2_SLICED_WSS_625 &&
+		   d->line == 23 && d->field == 0) {
+		int wss = d->data[0] | d->data[1] << 8;
+
+		if (vi->wss_payload != wss) {
+			vi->wss_payload = wss;
+			set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+		}
+	}
+}
+
+static void ivtv_write_vbi_cc_lines(struct ivtv *itv, const struct vbi_cc *cc)
+{
+	struct vbi_info *vi = &itv->vbi;
+
+	if (vi->cc_payload_idx < ARRAY_SIZE(vi->cc_payload)) {
+		memcpy(&vi->cc_payload[vi->cc_payload_idx], cc,
+		       sizeof(struct vbi_cc));
+		vi->cc_payload_idx++;
+		set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+	}
+}
+
+static void ivtv_write_vbi(struct ivtv *itv,
+			   const struct v4l2_sliced_vbi_data *sliced,
+			   size_t cnt)
+{
+	struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+	int found_cc = 0;
+	size_t i;
+
+	for (i = 0; i < cnt; i++)
+		ivtv_write_vbi_line(itv, sliced + i, &cc, &found_cc);
+
+	if (found_cc)
+		ivtv_write_vbi_cc_lines(itv, &cc);
+}
+
+ssize_t
+ivtv_write_vbi_from_user(struct ivtv *itv,
+			 const struct v4l2_sliced_vbi_data __user *sliced,
+			 size_t cnt)
+{
+	struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+	int found_cc = 0;
+	size_t i;
+	struct v4l2_sliced_vbi_data d;
+	ssize_t ret = cnt * sizeof(struct v4l2_sliced_vbi_data);
+
+	for (i = 0; i < cnt; i++) {
+		if (copy_from_user(&d, sliced + i,
+				   sizeof(struct v4l2_sliced_vbi_data))) {
+			ret = -EFAULT;
+			break;
+		}
+		ivtv_write_vbi_line(itv, &d, &cc, &found_cc);
+	}
+
+	if (found_cc)
+		ivtv_write_vbi_cc_lines(itv, &cc);
+
+	return ret;
+}
+
+static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+{
+	int line = 0;
+	int i;
+	u32 linemask[2] = { 0, 0 };
+	unsigned short size;
+	static const u8 mpeg_hdr_data[] = {
+		0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+		0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+		0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+		0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+	};
+	const int sd = sizeof(mpeg_hdr_data);	/* start of vbi data */
+	int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
+	u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
+
+	for (i = 0; i < lines; i++) {
+		int f, l;
+
+		if (itv->vbi.sliced_data[i].id == 0)
+			continue;
+
+		l = itv->vbi.sliced_data[i].line - 6;
+		f = itv->vbi.sliced_data[i].field;
+		if (f)
+			l += 18;
+		if (l < 32)
+			linemask[0] |= (1 << l);
+		else
+			linemask[1] |= (1 << (l - 32));
+		dst[sd + 12 + line * 43] =
+			ivtv_service2vbi(itv->vbi.sliced_data[i].id);
+		memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
+		line++;
+	}
+	memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+	if (line == 36) {
+		/* All lines are used, so there is no space for the linemask
+		   (the max size of the VBI data is 36 * 43 + 4 bytes).
+		   So in this case we use the magic number 'ITV0'. */
+		memcpy(dst + sd, "ITV0", 4);
+		memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+		size = 4 + ((43 * line + 3) & ~3);
+	} else {
+		memcpy(dst + sd, "itv0", 4);
+		cpu_to_le32s(&linemask[0]);
+		cpu_to_le32s(&linemask[1]);
+		memcpy(dst + sd + 4, &linemask[0], 8);
+		size = 12 + ((43 * line + 3) & ~3);
+	}
+	dst[4+16] = (size + 10) >> 8;
+	dst[5+16] = (size + 10) & 0xff;
+	dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+	dst[10+16] = (pts_stamp >> 22) & 0xff;
+	dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+	dst[12+16] = (pts_stamp >> 7) & 0xff;
+	dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+	itv->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
+{
+	u32 linemask[2];
+	int i, l, id2;
+	int line = 0;
+
+	if (!memcmp(p, "itv0", 4)) {
+		memcpy(linemask, p + 4, 8);
+		p += 12;
+	} else if (!memcmp(p, "ITV0", 4)) {
+		linemask[0] = 0xffffffff;
+		linemask[1] = 0xf;
+		p += 4;
+	} else {
+		/* unknown VBI data, convert to empty VBI frame */
+		linemask[0] = linemask[1] = 0;
+	}
+	for (i = 0; i < 36; i++) {
+		int err = 0;
+
+		if (i < 32 && !(linemask[0] & (1 << i)))
+			continue;
+		if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
+			continue;
+		id2 = *p & 0xf;
+		switch (id2) {
+		case IVTV_SLICED_TYPE_TELETEXT_B:
+			id2 = V4L2_SLICED_TELETEXT_B;
+			break;
+		case IVTV_SLICED_TYPE_CAPTION_525:
+			id2 = V4L2_SLICED_CAPTION_525;
+			err = !odd_parity(p[1]) || !odd_parity(p[2]);
+			break;
+		case IVTV_SLICED_TYPE_VPS:
+			id2 = V4L2_SLICED_VPS;
+			break;
+		case IVTV_SLICED_TYPE_WSS_625:
+			id2 = V4L2_SLICED_WSS_625;
+			break;
+		default:
+			id2 = 0;
+			break;
+		}
+		if (err == 0) {
+			l = (i < 18) ? i + 6 : i - 18 + 6;
+			itv->vbi.sliced_dec_data[line].line = l;
+			itv->vbi.sliced_dec_data[line].field = i >= 18;
+			itv->vbi.sliced_dec_data[line].id = id2;
+			memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
+			line++;
+		}
+		p += 43;
+	}
+	while (line < 36) {
+		itv->vbi.sliced_dec_data[line].id = 0;
+		itv->vbi.sliced_dec_data[line].line = 0;
+		itv->vbi.sliced_dec_data[line].field = 0;
+		line++;
+	}
+	return line * sizeof(itv->vbi.sliced_dec_data[0]);
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space after the
+   field.
+   Returns new compressed size. */
+static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
+{
+	u32 line_size = itv->vbi.raw_decoder_line_size;
+	u32 lines = itv->vbi.count;
+	u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
+	u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
+	u8 *q = buf;
+	u8 *p;
+	int i;
+
+	for (i = 0; i < lines; i++) {
+		p = buf + i * line_size;
+
+		/* Look for SAV code */
+		if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
+			break;
+		}
+		memcpy(q, p + 4, line_size - 4);
+		q += line_size - 4;
+	}
+	return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+   Returns new compressed size */
+static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
+{
+	u32 line_size = itv->vbi.sliced_decoder_line_size;
+	struct v4l2_decode_vbi_line vbi;
+	int i;
+	unsigned lines = 0;
+
+	/* find the first valid line */
+	for (i = 0; i < size; i++, buf++) {
+		if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+			break;
+	}
+
+	size -= i;
+	if (size < line_size) {
+		return line;
+	}
+	for (i = 0; i < size / line_size; i++) {
+		u8 *p = buf + i * line_size;
+
+		/* Look for SAV code  */
+		if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
+			continue;
+		}
+		vbi.p = p + 4;
+		v4l2_subdev_call(itv->sd_video, vbi, decode_vbi_line, &vbi);
+		if (vbi.type && !(lines & (1 << vbi.line))) {
+			lines |= 1 << vbi.line;
+			itv->vbi.sliced_data[line].id = vbi.type;
+			itv->vbi.sliced_data[line].field = vbi.is_second_field;
+			itv->vbi.sliced_data[line].line = vbi.line;
+			memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
+			line++;
+		}
+	}
+	return line;
+}
+
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+			   u64 pts_stamp, int streamtype)
+{
+	u8 *p = (u8 *) buf->buf;
+	u32 size = buf->bytesused;
+	int y;
+
+	/* Raw VBI data */
+	if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && ivtv_raw_vbi(itv)) {
+		u8 type;
+
+		ivtv_buf_swap(buf);
+
+		type = p[3];
+
+		size = buf->bytesused = compress_raw_buf(itv, p, size);
+
+		/* second field of the frame? */
+		if (type == itv->vbi.raw_decoder_sav_even_field) {
+			/* Dirty hack needed for backwards
+			   compatibility of old VBI software. */
+			p += size - 4;
+			memcpy(p, &itv->vbi.frame, 4);
+			itv->vbi.frame++;
+		}
+		return;
+	}
+
+	/* Sliced VBI data with data insertion */
+	if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
+		int lines;
+
+		ivtv_buf_swap(buf);
+
+		/* first field */
+		lines = compress_sliced_buf(itv, 0, p, size / 2,
+			itv->vbi.sliced_decoder_sav_odd_field);
+		/* second field */
+		/* experimentation shows that the second half does not always begin
+		   at the exact address. So start a bit earlier (hence 32). */
+		lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
+			itv->vbi.sliced_decoder_sav_even_field);
+		/* always return at least one empty line */
+		if (lines == 0) {
+			itv->vbi.sliced_data[0].id = 0;
+			itv->vbi.sliced_data[0].line = 0;
+			itv->vbi.sliced_data[0].field = 0;
+			lines = 1;
+		}
+		buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
+		memcpy(p, &itv->vbi.sliced_data[0], size);
+
+		if (itv->vbi.insert_mpeg) {
+			copy_vbi_data(itv, lines, pts_stamp);
+		}
+		itv->vbi.frame++;
+		return;
+	}
+
+	/* Sliced VBI re-inserted from an MPEG stream */
+	if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+		/* If the size is not 4-byte aligned, then the starting address
+		   for the swapping is also shifted. After swapping the data the
+		   real start address of the VBI data is exactly 4 bytes after the
+		   original start. It's a bit fiddly but it works like a charm.
+		   Non-4-byte alignment happens when an lseek is done on the input
+		   mpeg file to a non-4-byte aligned position. So on arrival here
+		   the VBI data is also non-4-byte aligned. */
+		int offset = size & 3;
+		int cnt;
+
+		if (offset) {
+			p += 4 - offset;
+		}
+		/* Swap Buffer */
+		for (y = 0; y < size; y += 4) {
+		       swab32s((u32 *)(p + y));
+		}
+
+		cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
+		memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
+		buf->bytesused = cnt;
+
+		ivtv_write_vbi(itv, itv->vbi.sliced_dec_data,
+			       cnt / sizeof(itv->vbi.sliced_dec_data[0]));
+		return;
+	}
+}
+
+void ivtv_disable_cc(struct ivtv *itv)
+{
+	struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+
+	clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+	ivtv_set_cc(itv, 0, &cc);
+	itv->vbi.cc_payload_idx = 0;
+}
+
+
+void ivtv_vbi_work_handler(struct ivtv *itv)
+{
+	struct vbi_info *vi = &itv->vbi;
+	struct v4l2_sliced_vbi_data data;
+	struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+
+	/* Lock */
+	if (itv->output_mode == OUT_PASSTHROUGH) {
+		if (itv->is_50hz) {
+			data.id = V4L2_SLICED_WSS_625;
+			data.field = 0;
+
+			if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+				ivtv_set_wss(itv, 1, data.data[0] & 0xf);
+				vi->wss_missing_cnt = 0;
+			} else if (vi->wss_missing_cnt == 4) {
+				ivtv_set_wss(itv, 1, 0x8);  /* 4x3 full format */
+			} else {
+				vi->wss_missing_cnt++;
+			}
+		}
+		else {
+			int mode = 0;
+
+			data.id = V4L2_SLICED_CAPTION_525;
+			data.field = 0;
+			if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+				mode |= 1;
+				cc.odd[0] = data.data[0];
+				cc.odd[1] = data.data[1];
+			}
+			data.field = 1;
+			if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+				mode |= 2;
+				cc.even[0] = data.data[0];
+				cc.even[1] = data.data[1];
+			}
+			if (mode) {
+				vi->cc_missing_cnt = 0;
+				ivtv_set_cc(itv, mode, &cc);
+			} else if (vi->cc_missing_cnt == 4) {
+				ivtv_set_cc(itv, 0, &cc);
+			} else {
+				vi->cc_missing_cnt++;
+			}
+		}
+		return;
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
+		ivtv_set_wss(itv, 1, vi->wss_payload & 0xf);
+	}
+
+	if (test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
+		if (vi->cc_payload_idx == 0) {
+			clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+			ivtv_set_cc(itv, 3, &cc);
+		}
+		while (vi->cc_payload_idx) {
+			cc = vi->cc_payload[0];
+
+			memcpy(vi->cc_payload, vi->cc_payload + 1,
+					sizeof(vi->cc_payload) - sizeof(vi->cc_payload[0]));
+			vi->cc_payload_idx--;
+			if (vi->cc_payload_idx && cc.odd[0] == 0x80 && cc.odd[1] == 0x80)
+				continue;
+
+			ivtv_set_cc(itv, 3, &cc);
+			break;
+		}
+	}
+
+	if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
+		ivtv_set_vps(itv, 1);
+	}
+}
diff --git a/drivers/media/pci/ivtv/ivtv-vbi.h b/drivers/media/pci/ivtv/ivtv-vbi.h
new file mode 100644
index 000000000000..166dd0b75d0f
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-vbi.h
@@ -0,0 +1,34 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_VBI_H
+#define IVTV_VBI_H
+
+ssize_t
+ivtv_write_vbi_from_user(struct ivtv *itv,
+			 const struct v4l2_sliced_vbi_data __user *sliced,
+			 size_t count);
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+			   u64 pts_stamp, int streamtype);
+int ivtv_used_line(struct ivtv *itv, int line, int field);
+void ivtv_disable_cc(struct ivtv *itv);
+void ivtv_set_vbi(unsigned long arg);
+void ivtv_vbi_work_handler(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-version.h b/drivers/media/pci/ivtv/ivtv-version.h
new file mode 100644
index 000000000000..a20f346fcad8
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-version.h
@@ -0,0 +1,26 @@
+/*
+    ivtv driver version information
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_VERSION_H
+#define IVTV_VERSION_H
+
+#define IVTV_DRIVER_NAME "ivtv"
+#define IVTV_VERSION "1.4.3"
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c
new file mode 100644
index 000000000000..2ad65eb29832
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-yuv.c
@@ -0,0 +1,1296 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-udma.h"
+#include "ivtv-yuv.h"
+
+/* 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_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;
+
+	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 = f->src_h + f->src_y;
+
+	if (f->offset_y)
+		y_buffer_offset += 720 * 16;
+
+	if (y_decode_height & 15)
+		y_decode_height = (y_decode_height + 16) & ~15;
+
+	if (uv_decode_height & 31)
+		uv_decode_height = (uv_decode_height + 32) & ~31;
+
+	y_size = 720 * y_decode_height;
+
+	/* 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);
+		return -EBUSY;
+	}
+
+	ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
+	ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
+
+	/* Get user pages for DMA Xfer */
+	down_read(&current->mm->mmap_sem);
+	y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
+	uv_pages = 0; /* silence gcc. value is set and consumed only if: */
+	if (y_pages == y_dma.page_count) {
+		uv_pages = get_user_pages(current, current->mm,
+					  uv_dma.uaddr, uv_dma.page_count, 0, 1,
+					  &dma->map[y_pages], NULL);
+	}
+	up_read(&current->mm->mmap_sem);
+
+	if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
+		int rc = -EFAULT;
+
+		if (y_pages == y_dma.page_count) {
+			IVTV_DEBUG_WARN
+				("failed to map uv user pages, returned %d "
+				 "expecting %d\n", uv_pages, uv_dma.page_count);
+
+			if (uv_pages >= 0) {
+				for (i = 0; i < uv_pages; i++)
+					put_page(dma->map[y_pages + i]);
+				rc = -EFAULT;
+			} else {
+				rc = uv_pages;
+			}
+		} else {
+			IVTV_DEBUG_WARN
+				("failed to map y user pages, returned %d "
+				 "expecting %d\n", y_pages, y_dma.page_count);
+		}
+		if (y_pages >= 0) {
+			for (i = 0; i < y_pages; i++)
+				put_page(dma->map[i]);
+			/*
+			 * Inherit the -EFAULT from rc's
+			 * initialization, but allow it to be
+			 * overriden by uv_pages above if it was an
+			 * actual errno.
+			 */
+		} else {
+			rc = y_pages;
+		}
+		return rc;
+	}
+
+	dma->page_count = y_pages + uv_pages;
+
+	/* Fill & map SG List */
+	if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
+		IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
+		for (i = 0; i < dma->page_count; i++) {
+			put_page(dma->map[i]);
+		}
+		dma->page_count = 0;
+		return -ENOMEM;
+	}
+	dma->SG_length = pci_map_sg(itv->pdev, 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);
+
+	/* If we've offset the y plane, ensure top area is blanked */
+	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 */
+	dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+	ivtv_udma_sync_for_device(itv);
+	return 0;
+}
+
+/* We rely on a table held in the firmware - Quick check. */
+int ivtv_yuv_filter_check(struct ivtv *itv)
+{
+	int i, y, uv;
+
+	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;
+		}
+	}
+	return 0;
+}
+
+static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
+{
+	u32 i, line;
+
+	/* If any filter is -1, then don't update it */
+	if (h_filter > -1) {
+		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);
+		}
+		IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
+	}
+
+	if (v_filter_1 > -1) {
+		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);
+		}
+		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;
+		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);
+		}
+		IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
+	}
+}
+
+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;
+	u32 reg_2870, reg_2870_base, reg_2870_offset;
+	int x_cutoff;
+	int h_filter;
+	u32 master_width;
+
+	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 = f->src_w + f->src_x;
+
+	/* Set the display width */
+	reg_2834 = f->dst_w;
+	reg_2838 = reg_2834;
+
+	/* Set the display position */
+	reg_2890 = f->dst_x;
+
+	/* Index into the image horizontally */
+	reg_2870 = 0;
+
+	/* 2870 is normally fudged to align video coords with osd coords.
+	   If running full screen, it causes an unwanted left shift
+	   Remove the fudge if we almost fill the screen.
+	   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 (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 (f->dst_w >= f->src_w)
+			reg_2870 = reg_2870 << 16 | reg_2870;
+		else
+			reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
+	}
+
+	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 = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19;
+
+	if (f->dst_w >= f->src_w) {
+		x_cutoff &= ~1;
+		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;
+		reg_2844 = master_width >> 2;
+		reg_2854 = master_width;
+		reg_285c = master_width >> 1;
+		reg_2864 = master_width >> 1;
+
+		/* We also need to factor in the scaling
+		   (src_w - dst_w) / (src_w / 4) */
+		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 (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;
+		reg_2844 = master_width >> 1;
+		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 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16;
+		reg_2874 = 0x00000012;
+	} 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;
+		reg_2844 = master_width >> 1;
+		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 - (((f->src_w * 3) - 1) / f->dst_w)) << 16;
+		reg_2874 = 0x00000001;
+	}
+
+	/* Select the horizontal filter */
+	if (f->src_w == f->dst_w) {
+		/* An exact size match uses filter 0 */
+		h_filter = 0;
+	} else {
+		/* Figure out which filter to use */
+		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 */
+		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",
+		       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",
+		       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",
+		       yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514);
+
+	write_reg(reg_2854, 0x02854);
+	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",
+		       yi->reg_285c, reg_285c, yi->reg_2864, reg_2864);
+
+	write_reg(reg_2874, 0x02874);
+	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",
+		       yi->reg_2870, reg_2870);
+
+	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 != 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 *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;
+	u32 reg_2940, reg_2944, reg_294c;
+	u32 reg_2950, reg_2954, reg_2958, reg_295c;
+	u32 reg_2960, reg_2964, reg_2968, reg_296c;
+	u32 reg_289c;
+	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
+	    ("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... */
+	IVTV_DEBUG_YUV("Scaling mode Y: %s\n",
+		       f->interlaced_y ? "Interlaced" : "Progressive");
+
+	IVTV_DEBUG_YUV("Scaling mode UV: %s\n",
+		       f->interlaced_uv ? "Interlaced" : "Progressive");
+
+	/* What is the source video being treated as... */
+	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 (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_minor_y = src_minor_uv;
+	src_major_y = src_major_uv;
+
+	if (f->offset_y)
+		src_minor_y += 16;
+
+	if (f->interlaced_y)
+		reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y);
+	else
+		reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1);
+
+	if (f->interlaced_uv)
+		reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1);
+	else
+		reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv);
+
+	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 (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;
+		reg_2940 = master_height >> 1;
+		reg_2964_base >>= 3;
+		reg_2968_base >>= 3;
+		reg_296c = 0x00000000;
+	} 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 (f->interlaced_y) {
+			reg_2964_base >>= 3;
+		} else {
+			reg_296c++;
+			reg_2964_base >>= 2;
+		}
+		if (f->interlaced_uv)
+			reg_2928 >>= 1;
+		reg_2968_base >>= 3;
+	} 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 (f->interlaced_y) {
+			reg_2964_base >>= 2;
+		} else {
+			reg_296c++;
+			reg_2964_base >>= 1;
+		}
+		if (f->interlaced_uv)
+			reg_2928 >>= 1;
+		reg_2968_base >>= 2;
+	} 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;
+		reg_2930 = master_height;
+		reg_2940 = master_height;
+		reg_2964_base >>= 1;
+		reg_2968_base >>= 2;
+		reg_296c = 0x00000102;
+	}
+
+	/* FIXME These registers change depending on scaled / unscaled output
+	   We really need to work out what they should be */
+	if (f->src_h == f->dst_h) {
+		reg_2934 = 0x00020000;
+		reg_293c = 0x00100000;
+		reg_2944 = 0x00040000;
+		reg_294c = 0x000b0000;
+	} else {
+		reg_2934 = 0x00000FF0;
+		reg_293c = 0x00000FF0;
+		reg_2944 = 0x00000FF0;
+		reg_294c = 0x00000FF0;
+	}
+
+	/* The first line to be displayed */
+	reg_2950 = 0x00010000 + src_major_y;
+	if (f->interlaced_y)
+		reg_2950 += 0x00010000;
+	reg_2954 = reg_2950 + 1;
+
+	reg_2958 = 0x00010000 + (src_major_y >> 1);
+	if (f->interlaced_uv)
+		reg_2958 += 0x00010000;
+	reg_295c = reg_2958 + 1;
+
+	if (yi->decode_height == 480)
+		reg_289c = 0x011e0017;
+	else
+		reg_289c = 0x01500017;
+
+	if (f->dst_y < 0)
+		reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1);
+	else
+		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_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 (f->src_h == f->dst_h) {
+		reg_2964 = 1;
+	} 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);
+	reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
+
+	/* Okay, we've wasted time working out the correct value,
+	   but if we use it, it fouls the the window alignment.
+	   Fudge it to what we want... */
+	reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
+	reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
+
+	/* 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) && (f->dst_h / 2 <= f->src_h))
+		reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
+
+	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 (f->src_h == f->dst_h) {
+		/* An exact size match uses filter 0/1 */
+		v_filter_1 = 0;
+		v_filter_2 = 1;
+	} else {
+		/* Figure out which filter to use */
+		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 */
+		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",
+		       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",
+		       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", 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",
+		       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",
+		       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",
+		       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",
+		       yi->reg_2918, reg_2918, yi->reg_291c, reg_291c);
+
+	write_reg(reg_296c, 0x0296c);
+	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",
+		       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",
+		       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",
+		       yi->reg_2958, reg_2958, yi->reg_295c, reg_295c);
+
+	write_reg(reg_2960, 0x02960);
+	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",
+		       yi->reg_2964, reg_2964, yi->reg_2968, reg_2968);
+
+	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 != 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 != 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 *f)
+{
+	struct yuv_frame_info *of = &itv->yuv_info.old_frame_info;
+	int osd_crop;
+	u32 osd_scale;
+	u32 yuv_update = 0;
+
+	/* Sorry, but no negative coords for src */
+	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 = 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 (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 = f->src_h - 4 * f->dst_h) > 0) {
+			/* If we reach here we'll have to force the height. */
+			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)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 = (f->src_h << 16) / f->dst_h;
+
+	if ((osd_crop = f->pan_y - f->dst_y) > 0) {
+		/* Falls off the upper edge - crop */
+		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 = f->dst_h + f->dst_y - f->vis_h) > 0) {
+		/* Falls off the lower edge - crop */
+		f->dst_h -= osd_crop;
+		f->src_h -= (osd_scale * osd_crop) >> 16;
+	}
+
+	osd_scale = (f->src_w << 16) / f->dst_w;
+
+	if ((osd_crop = f->pan_x - f->dst_x) > 0) {
+		/* Fall off the left edge - crop */
+		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 = f->dst_w + f->dst_x - f->vis_w) > 0) {
+		/* Falls off the right edge - crop */
+		f->dst_w -= osd_crop;
+		f->src_w -= (osd_scale * osd_crop) >> 16;
+	}
+
+	if (itv->yuv_info.track_osd) {
+		/* The OSD can be moved. Track to it */
+		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. */
+	f->dst_w &= ~1;
+	f->dst_x &= ~1;
+
+	f->src_w += f->src_x & 1;
+	f->src_x &= ~1;
+
+	f->src_w &= ~1;
+	f->dst_w &= ~1;
+
+	f->dst_h &= ~1;
+	f->dst_y &= ~1;
+
+	f->src_h += f->src_y & 1;
+	f->src_y &= ~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 (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 (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)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 ((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 ((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;
+	}
+
+	return yuv_update;
+}
+
+/* Update the scaling register to the requested value */
+void ivtv_yuv_work_handler(struct ivtv *itv)
+{
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	struct yuv_frame_info f;
+	int frame = yi->update_frame;
+	u32 yuv_update;
+
+	IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame);
+	f = yi->new_frame_info[frame];
+
+	if (yi->track_osd) {
+		/* Snapshot the osd pan info */
+		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;
+	} else {
+		/* Not tracking the osd, so assume full screen */
+		f.pan_x = 0;
+		f.pan_y = 0;
+		f.vis_w = 720;
+		f.vis_h = yi->decode_height;
+	}
+
+	/* Calculate the display window coordinates. Exit if nothing left */
+	if (!(yuv_update = ivtv_yuv_window_setup(itv, &f)))
+		return;
+
+	if (yuv_update & IVTV_YUV_UPDATE_INVALID) {
+		write_reg(0x01008080, 0x2898);
+	} else if (yuv_update) {
+		write_reg(0x00108080, 0x2898);
+
+		if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
+			ivtv_yuv_handle_horizontal(itv, &f);
+
+		if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
+			ivtv_yuv_handle_vertical(itv, &f);
+	}
+	yi->old_frame_info = f;
+}
+
+static void ivtv_yuv_init(struct ivtv *itv)
+{
+	struct yuv_playback_info *yi = &itv->yuv_info;
+
+	IVTV_DEBUG_YUV("ivtv_yuv_init\n");
+
+	/* Take a snapshot of the current register settings */
+	yi->reg_2834 = read_reg(0x02834);
+	yi->reg_2838 = read_reg(0x02838);
+	yi->reg_283c = read_reg(0x0283c);
+	yi->reg_2840 = read_reg(0x02840);
+	yi->reg_2844 = read_reg(0x02844);
+	yi->reg_2848 = read_reg(0x02848);
+	yi->reg_2854 = read_reg(0x02854);
+	yi->reg_285c = read_reg(0x0285c);
+	yi->reg_2864 = read_reg(0x02864);
+	yi->reg_2870 = read_reg(0x02870);
+	yi->reg_2874 = read_reg(0x02874);
+	yi->reg_2898 = read_reg(0x02898);
+	yi->reg_2890 = read_reg(0x02890);
+
+	yi->reg_289c = read_reg(0x0289c);
+	yi->reg_2918 = read_reg(0x02918);
+	yi->reg_291c = read_reg(0x0291c);
+	yi->reg_2920 = read_reg(0x02920);
+	yi->reg_2924 = read_reg(0x02924);
+	yi->reg_2928 = read_reg(0x02928);
+	yi->reg_292c = read_reg(0x0292c);
+	yi->reg_2930 = read_reg(0x02930);
+	yi->reg_2934 = read_reg(0x02934);
+	yi->reg_2938 = read_reg(0x02938);
+	yi->reg_293c = read_reg(0x0293c);
+	yi->reg_2940 = read_reg(0x02940);
+	yi->reg_2944 = read_reg(0x02944);
+	yi->reg_2948 = read_reg(0x02948);
+	yi->reg_294c = read_reg(0x0294c);
+	yi->reg_2950 = read_reg(0x02950);
+	yi->reg_2954 = read_reg(0x02954);
+	yi->reg_2958 = read_reg(0x02958);
+	yi->reg_295c = read_reg(0x0295c);
+	yi->reg_2960 = read_reg(0x02960);
+	yi->reg_2964 = read_reg(0x02964);
+	yi->reg_2968 = read_reg(0x02968);
+	yi->reg_296c = read_reg(0x0296c);
+	yi->reg_2970 = read_reg(0x02970);
+
+	yi->v_filter_1 = -1;
+	yi->v_filter_2 = -1;
+	yi->h_filter = -1;
+
+	/* Set some valid size info */
+	yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
+	yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
+
+	/* Bit 2 of reg 2878 indicates current decoder output format
+	   0 : NTSC    1 : PAL */
+	if (read_reg(0x2878) & 4)
+		yi->decode_height = 576;
+	else
+		yi->decode_height = 480;
+
+	if (!itv->osd_info) {
+		yi->osd_vis_w = 720 - yi->osd_x_offset;
+		yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
+	} else {
+		/* If no visible size set, assume full size */
+		if (!yi->osd_vis_w)
+			yi->osd_vis_w = 720 - yi->osd_x_offset;
+
+		if (!yi->osd_vis_h) {
+			yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
+		} 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 */
+			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|__GFP_NOWARN);
+	if (yi->blanking_ptr) {
+		yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+	} else {
+		yi->blanking_dmaptr = 0;
+		IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
+	}
+
+	/* Enable YUV decoder output */
+	write_reg_sync(0x01, IVTV_REG_VDM);
+
+	set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+	atomic_set(&yi->next_dma_frame, 0);
+}
+
+/* Get next available yuv buffer on PVR350 */
+static void ivtv_yuv_next_free(struct ivtv *itv)
+{
+	int draw, display;
+	struct yuv_playback_info *yi = &itv->yuv_info;
+
+	if (atomic_read(&yi->next_dma_frame) == -1)
+		ivtv_yuv_init(itv);
+
+	draw = atomic_read(&yi->next_fill_frame);
+	display = atomic_read(&yi->next_dma_frame);
+
+	if (display > draw)
+		display -= IVTV_YUV_BUFFERS;
+
+	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 */
+static 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 */
+	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 */
+	nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0;
+
+	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;
+	}
+
+	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);
+	}
+
+	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);
+}
+
+static 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);
+
+	if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
+		mutex_unlock(&itv->udma.lock);
+		return rc;
+	}
+
+	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 */
+	while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+	       test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+		/* don't interrupt if the DMA is in progress but break off
+		   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;
+		got_sig = 0;
+		schedule();
+	}
+	finish_wait(&itv->dma_waitq, &wait);
+
+	/* Unmap Last DMA Xfer */
+	ivtv_udma_unmap(itv);
+
+	if (got_sig) {
+		IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
+		mutex_unlock(&itv->udma.lock);
+		return -EINTR;
+	}
+
+	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 = NULL;
+	dma_args.uv_source = NULL;
+	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 __user *src)
+{
+	struct yuv_playback_info *yi = &itv->yuv_info;
+	struct ivtv_dma_frame dma_args;
+	int res;
+
+	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);
+	/* Wait for frame DMA. Note that serialize_lock is locked,
+	   so to allow other processes to access the driver while
+	   we are waiting unlock first and later lock again. */
+	mutex_unlock(&itv->serialize_lock);
+	res = ivtv_yuv_udma_frame(itv, &dma_args);
+	mutex_lock(&itv->serialize_lock);
+	return res;
+}
+
+/* IVTV_IOC_DMA_FRAME ioctl handler */
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+	int res;
+
+/*	IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
+	ivtv_yuv_next_free(itv);
+	ivtv_yuv_setup_frame(itv, args);
+	/* Wait for frame DMA. Note that serialize_lock is locked,
+	   so to allow other processes to access the driver while
+	   we are waiting unlock first and later lock again. */
+	mutex_unlock(&itv->serialize_lock);
+	res = ivtv_yuv_udma_frame(itv, args);
+	mutex_lock(&itv->serialize_lock);
+	return res;
+}
+
+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");
+	mutex_unlock(&itv->serialize_lock);
+	ivtv_waitq(&itv->vsync_waitq);
+	mutex_lock(&itv->serialize_lock);
+
+	yi->running = 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(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 ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) {
+		/* An exact size match uses filter 0 */
+		h_filter = 0;
+	} else {
+		/* Figure out which filter to use */
+		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. */
+		h_filter += !h_filter;
+	}
+
+	/* Now the vertical filter */
+	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 {
+		/* Figure out which filter to use */
+		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 */
+		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);
+
+	/* and clear a few registers */
+	write_reg(0, 0x02814);
+	write_reg(0, 0x0282c);
+	write_reg(0, 0x02904);
+	write_reg(0, 0x02910);
+
+	/* Release the blanking buffer */
+	if (yi->blanking_ptr) {
+		kfree(yi->blanking_ptr);
+		yi->blanking_ptr = NULL;
+		pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+	}
+
+	/* Invalidate the old dimension information */
+	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/pci/ivtv/ivtv-yuv.h b/drivers/media/pci/ivtv/ivtv-yuv.h
new file mode 100644
index 000000000000..ca5173fbf006
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-yuv.h
@@ -0,0 +1,44 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_YUV_H
+#define IVTV_YUV_H
+
+#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400	/* Offset to UV Buffer */
+
+/* Offset to filter table in firmware */
+#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
+#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
+
+#define IVTV_YUV_UPDATE_HORIZONTAL  0x01
+#define IVTV_YUV_UPDATE_VERTICAL    0x02
+#define IVTV_YUV_UPDATE_INVALID     0x04
+
+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 __user *src);
+void ivtv_yuv_frame_complete(struct ivtv *itv);
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
+void ivtv_yuv_close(struct ivtv *itv);
+void ivtv_yuv_work_handler(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
new file mode 100644
index 000000000000..05b94aa8ba32
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtvfb.c
@@ -0,0 +1,1317 @@
+/*
+    On Screen Display cx23415 Framebuffer driver
+
+    This module presents the cx23415 OSD (onscreen display) framebuffer memory
+    as a standard Linux /dev/fb style framebuffer device. The framebuffer has
+    support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp
+    mode, there is a choice of a three color depths (12, 15 or 16 bits), but no
+    local alpha. The colorspace is selectable between rgb & yuv.
+    Depending on the TV standard configured in the ivtv module at load time,
+    the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp.
+    Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL)
+    or 59.94 (NTSC)
+
+    Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com>
+
+    Derived from drivers/video/vesafb.c
+    Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+    2.6 kernel port:
+    Copyright (C) 2004 Matthias Badaire
+
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+
+    Copyright (C) 2006  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fb.h>
+#include <linux/ivtvfb.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+#include "ivtv-udma.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+
+/* card parameters */
+static int ivtvfb_card_id = -1;
+static int ivtvfb_debug = 0;
+static bool osd_laced;
+static int osd_depth;
+static int osd_upper;
+static int osd_left;
+static int osd_yres;
+static int osd_xres;
+
+module_param(ivtvfb_card_id, int, 0444);
+module_param_named(debug,ivtvfb_debug, int, 0644);
+module_param(osd_laced, bool, 0444);
+module_param(osd_depth, int, 0444);
+module_param(osd_upper, int, 0444);
+module_param(osd_left, int, 0444);
+module_param(osd_yres, int, 0444);
+module_param(osd_xres, int, 0444);
+
+MODULE_PARM_DESC(ivtvfb_card_id,
+		 "Only use framebuffer of the specified ivtv card (0-31)\n"
+		 "\t\t\tdefault -1: initialize all available framebuffers");
+
+MODULE_PARM_DESC(debug,
+		 "Debug level (bitmask). Default: errors only\n"
+		 "\t\t\t(debug = 3 gives full debugging)");
+
+/* Why upper, left, xres, yres, depth, laced ? To match terminology used
+   by fbset.
+   Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
+
+MODULE_PARM_DESC(osd_laced,
+		 "Interlaced mode\n"
+		 "\t\t\t0=off\n"
+		 "\t\t\t1=on\n"
+		 "\t\t\tdefault off");
+
+MODULE_PARM_DESC(osd_depth,
+		 "Bits per pixel - 8, 16, 32\n"
+		 "\t\t\tdefault 8");
+
+MODULE_PARM_DESC(osd_upper,
+		 "Vertical start position\n"
+		 "\t\t\tdefault 0 (Centered)");
+
+MODULE_PARM_DESC(osd_left,
+		 "Horizontal start position\n"
+		 "\t\t\tdefault 0 (Centered)");
+
+MODULE_PARM_DESC(osd_yres,
+		 "Display height\n"
+		 "\t\t\tdefault 480 (PAL)\n"
+		 "\t\t\t        400 (NTSC)");
+
+MODULE_PARM_DESC(osd_xres,
+		 "Display width\n"
+		 "\t\t\tdefault 640");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+#define IVTVFB_DBGFLG_WARN  (1 << 0)
+#define IVTVFB_DBGFLG_INFO  (1 << 1)
+
+#define IVTVFB_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & ivtvfb_debug) \
+			printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \
+	} while (0)
+#define IVTVFB_DEBUG_WARN(fmt, args...)  IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTVFB_DEBUG_INFO(fmt, args...)  IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTVFB_ERR(fmt, args...)   printk(KERN_ERR  "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_WARN(fmt, args...)  printk(KERN_WARNING  "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_INFO(fmt, args...)  printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)
+
+/* --------------------------------------------------------------------- */
+
+#define IVTV_OSD_MAX_WIDTH  720
+#define IVTV_OSD_MAX_HEIGHT 576
+
+#define IVTV_OSD_BPP_8      0x00
+#define IVTV_OSD_BPP_16_444 0x03
+#define IVTV_OSD_BPP_16_555 0x02
+#define IVTV_OSD_BPP_16_565 0x01
+#define IVTV_OSD_BPP_32     0x04
+
+struct osd_info {
+	/* Physical base address */
+	unsigned long video_pbase;
+	/* Relative base address (relative to start of decoder memory) */
+	u32 video_rbase;
+	/* Mapped base address */
+	volatile char __iomem *video_vbase;
+	/* Buffer size */
+	u32 video_buffer_size;
+
+#ifdef CONFIG_MTRR
+	/* video_base rounded down as required by hardware MTRRs */
+	unsigned long fb_start_aligned_physaddr;
+	/* video_base rounded up as required by hardware MTRRs */
+	unsigned long fb_end_aligned_physaddr;
+#endif
+
+	/* Store the buffer offset */
+	int set_osd_coords_x;
+	int set_osd_coords_y;
+
+	/* Current dimensions (NOT VISIBLE SIZE!) */
+	int display_width;
+	int display_height;
+	int display_byte_stride;
+
+	/* Current bits per pixel */
+	int bits_per_pixel;
+	int bytes_per_pixel;
+
+	/* Frame buffer stuff */
+	struct fb_info ivtvfb_info;
+	struct fb_var_screeninfo ivtvfb_defined;
+	struct fb_fix_screeninfo ivtvfb_fix;
+
+	/* Used for a warm start */
+	struct fb_var_screeninfo fbvar_cur;
+	int blank_cur;
+	u32 palette_cur[256];
+	u32 pan_cur;
+};
+
+struct ivtv_osd_coords {
+	unsigned long offset;
+	unsigned long max_offset;
+	int pixel_stride;
+	int lines;
+	int x;
+	int y;
+};
+
+/* --------------------------------------------------------------------- */
+
+/* ivtv API calls for framebuffer related support */
+
+static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
+				       u32 *fblength)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int rc;
+
+	ivtv_firmware_check(itv, "ivtvfb_get_framebuffer");
+	rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
+	*fbbase = data[0];
+	*fblength = data[1];
+	return rc;
+}
+
+static int ivtvfb_get_osd_coords(struct ivtv *itv,
+				      struct ivtv_osd_coords *osd)
+{
+	struct osd_info *oi = itv->osd_info;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+
+	ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
+
+	osd->offset = data[0] - oi->video_rbase;
+	osd->max_offset = oi->display_width * oi->display_height * 4;
+	osd->pixel_stride = data[1];
+	osd->lines = data[2];
+	osd->x = data[3];
+	osd->y = data[4];
+	return 0;
+}
+
+static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
+{
+	struct osd_info *oi = itv->osd_info;
+
+	oi->display_width = osd->pixel_stride;
+	oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
+	oi->set_osd_coords_x += osd->x;
+	oi->set_osd_coords_y = osd->y;
+
+	return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
+			osd->offset + oi->video_rbase,
+			osd->pixel_stride,
+			osd->lines, osd->x, osd->y);
+}
+
+static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
+{
+	int osd_height_limit = itv->is_out_50hz ? 576 : 480;
+
+	/* Only fail if resolution too high, otherwise fudge the start coords. */
+	if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
+		return -EINVAL;
+
+	/* Ensure we don't exceed display limits */
+	if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
+		IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
+			ivtv_window->top, ivtv_window->height);
+		ivtv_window->top = osd_height_limit - ivtv_window->height;
+	}
+
+	if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
+		IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
+			ivtv_window->left, ivtv_window->width);
+		ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
+	}
+
+	/* Set the OSD origin */
+	write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
+
+	/* How much to display */
+	write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
+
+	/* Pass this info back the yuv handler */
+	itv->yuv_info.osd_vis_w = ivtv_window->width;
+	itv->yuv_info.osd_vis_h = ivtv_window->height;
+	itv->yuv_info.osd_x_offset = ivtv_window->left;
+	itv->yuv_info.osd_y_offset = ivtv_window->top;
+
+	return 0;
+}
+
+static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
+				  unsigned long ivtv_dest_addr, void __user *userbuf,
+				  int size_in_bytes)
+{
+	DEFINE_WAIT(wait);
+	int got_sig = 0;
+
+	mutex_lock(&itv->udma.lock);
+	/* Map User DMA */
+	if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
+		mutex_unlock(&itv->udma.lock);
+		IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, "
+			       "Error with get_user_pages: %d bytes, %d pages returned\n",
+			       size_in_bytes, itv->udma.page_count);
+
+		/* get_user_pages must have failed completely */
+		return -EIO;
+	}
+
+	IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
+		       size_in_bytes, itv->udma.page_count);
+
+	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 */
+	while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+	       test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+		/* don't interrupt if the DMA is in progress but break off
+		   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;
+		got_sig = 0;
+		schedule();
+	}
+	finish_wait(&itv->dma_waitq, &wait);
+
+	/* Unmap Last DMA Xfer */
+	ivtv_udma_unmap(itv);
+	mutex_unlock(&itv->udma.lock);
+	if (got_sig) {
+		IVTV_DEBUG_INFO("User stopped OSD\n");
+		return -EINTR;
+	}
+
+	return 0;
+}
+
+static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
+			      unsigned long dest_offset, int count)
+{
+	DEFINE_WAIT(wait);
+	struct osd_info *oi = itv->osd_info;
+
+	/* Nothing to do */
+	if (count == 0) {
+		IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");
+		return -EINVAL;
+	}
+
+	/* Check Total FB Size */
+	if ((dest_offset + count) > oi->video_buffer_size) {
+		IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
+			dest_offset + count, oi->video_buffer_size);
+		return -E2BIG;
+	}
+
+	/* Not fatal, but will have undesirable results */
+	if ((unsigned long)source & 3)
+		IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
+			(unsigned long)source);
+
+	if (dest_offset & 3)
+		IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
+
+	if (count & 3)
+		IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);
+
+	/* Check Source */
+	if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
+		IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
+			(unsigned long)source);
+
+		IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
+			dest_offset, (unsigned long)source,
+			count);
+		return -EINVAL;
+	}
+
+	/* OSD Address to send DMA to */
+	dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
+
+	/* Fill Buffers */
+	return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
+}
+
+static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
+						size_t count, loff_t *ppos)
+{
+	unsigned long p = *ppos;
+	void *dst;
+	int err = 0;
+	int dma_err;
+	unsigned long total_size;
+	struct ivtv *itv = (struct ivtv *) info->par;
+	unsigned long dma_offset =
+			IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
+	unsigned long dma_size;
+	u16 lead = 0, tail = 0;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return -EPERM;
+
+	total_size = info->screen_size;
+
+	if (total_size == 0)
+		total_size = info->fix.smem_len;
+
+	if (p > total_size)
+		return -EFBIG;
+
+	if (count > total_size) {
+		err = -EFBIG;
+		count = total_size;
+	}
+
+	if (count + p > total_size) {
+		if (!err)
+			err = -ENOSPC;
+		count = total_size - p;
+	}
+
+	dst = (void __force *) (info->screen_base + p);
+
+	if (info->fbops->fb_sync)
+		info->fbops->fb_sync(info);
+
+	/* If transfer size > threshold and both src/dst
+	addresses are aligned, use DMA */
+	if (count >= 4096 &&
+	    ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
+		/* Odd address = can't DMA. Align */
+		if ((unsigned long)dst & 3) {
+			lead = 4 - ((unsigned long)dst & 3);
+			if (copy_from_user(dst, buf, lead))
+				return -EFAULT;
+			buf += lead;
+			dst += lead;
+		}
+		/* DMA resolution is 32 bits */
+		if ((count - lead) & 3)
+			tail = (count - lead) & 3;
+		/* DMA the data */
+		dma_size = count - lead - tail;
+		dma_err = ivtvfb_prep_dec_dma_to_device(itv,
+		       p + lead + dma_offset, (void __user *)buf, dma_size);
+		if (dma_err)
+			return dma_err;
+		dst += dma_size;
+		buf += dma_size;
+		/* Copy any leftover data */
+		if (tail && copy_from_user(dst, buf, tail))
+			return -EFAULT;
+	} else if (copy_from_user(dst, buf, count)) {
+		return -EFAULT;
+	}
+
+	if  (!err)
+		*ppos += count;
+
+	return (err) ? err : count;
+}
+
+static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+	DEFINE_WAIT(wait);
+	struct ivtv *itv = (struct ivtv *)info->par;
+	int rc = 0;
+
+	switch (cmd) {
+		case FBIOGET_VBLANK: {
+			struct fb_vblank vblank;
+			u32 trace;
+
+			memset(&vblank, 0, sizeof(struct fb_vblank));
+
+			vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
+					FB_VBLANK_HAVE_VSYNC;
+			trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16;
+			if (itv->is_out_50hz && trace > 312)
+				trace -= 312;
+			else if (itv->is_out_60hz && trace > 262)
+				trace -= 262;
+			if (trace == 1)
+				vblank.flags |= FB_VBLANK_VSYNCING;
+			vblank.count = itv->last_vsync_field;
+			vblank.vcount = trace;
+			vblank.hcount = 0;
+			if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
+				return -EFAULT;
+			return 0;
+		}
+
+		case FBIO_WAITFORVSYNC:
+			prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
+			if (!schedule_timeout(msecs_to_jiffies(50)))
+				rc = -ETIMEDOUT;
+			finish_wait(&itv->vsync_waitq, &wait);
+			return rc;
+
+		case IVTVFB_IOC_DMA_FRAME: {
+			struct ivtvfb_dma_frame args;
+
+			IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
+			if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
+				return -EFAULT;
+
+			return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
+		}
+
+		default:
+			IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
+			return -EINVAL;
+	}
+	return 0;
+}
+
+/* Framebuffer device handling */
+
+static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
+{
+	struct osd_info *oi = itv->osd_info;
+	struct ivtv_osd_coords ivtv_osd;
+	struct v4l2_rect ivtv_window;
+	int osd_mode = -1;
+
+	IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
+
+	/* Select color space */
+	if (var->nonstd) /* YUV */
+		write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
+	else /* RGB  */
+		write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
+
+	/* Set the color mode */
+	switch (var->bits_per_pixel) {
+		case 8:
+			osd_mode = IVTV_OSD_BPP_8;
+			break;
+		case 32:
+			osd_mode = IVTV_OSD_BPP_32;
+			break;
+		case 16:
+			switch (var->green.length) {
+			case 4:
+				osd_mode = IVTV_OSD_BPP_16_444;
+				break;
+			case 5:
+				osd_mode = IVTV_OSD_BPP_16_555;
+				break;
+			case 6:
+				osd_mode = IVTV_OSD_BPP_16_565;
+				break;
+			default:
+				IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
+			}
+			break;
+		default:
+			IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
+	}
+
+	/* Set video mode. Although rare, the display can become scrambled even
+	   if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
+	if (osd_mode != -1) {
+		ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
+		ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
+	}
+
+	oi->bits_per_pixel = var->bits_per_pixel;
+	oi->bytes_per_pixel = var->bits_per_pixel / 8;
+
+	/* Set the flicker filter */
+	switch (var->vmode & FB_VMODE_MASK) {
+		case FB_VMODE_NONINTERLACED: /* Filter on */
+			ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
+			break;
+		case FB_VMODE_INTERLACED: /* Filter off */
+			ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
+			break;
+		default:
+			IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
+	}
+
+	/* Read the current osd info */
+	ivtvfb_get_osd_coords(itv, &ivtv_osd);
+
+	/* Now set the OSD to the size we want */
+	ivtv_osd.pixel_stride = var->xres_virtual;
+	ivtv_osd.lines = var->yres_virtual;
+	ivtv_osd.x = 0;
+	ivtv_osd.y = 0;
+	ivtvfb_set_osd_coords(itv, &ivtv_osd);
+
+	/* Can't seem to find the right API combo for this.
+	   Use another function which does what we need through direct register access. */
+	ivtv_window.width = var->xres;
+	ivtv_window.height = var->yres;
+
+	/* Minimum margin cannot be 0, as X won't allow such a mode */
+	if (!var->upper_margin)
+		var->upper_margin++;
+	if (!var->left_margin)
+		var->left_margin++;
+	ivtv_window.top = var->upper_margin - 1;
+	ivtv_window.left = var->left_margin - 1;
+
+	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;
+
+	/* Keep a copy of these settings */
+	memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur));
+
+	IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
+		      var->xres, var->yres,
+		      var->xres_virtual, var->yres_virtual,
+		      var->bits_per_pixel);
+
+	IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
+		      var->left_margin, var->upper_margin);
+
+	IVTVFB_DEBUG_INFO("Display filter: %s\n",
+			(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
+	IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
+
+	return 0;
+}
+
+static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
+{
+	struct osd_info *oi = itv->osd_info;
+
+	IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
+	fix->smem_start = oi->video_pbase;
+	fix->smem_len = oi->video_buffer_size;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+	fix->ywrapstep = 0;
+	fix->line_length = oi->display_byte_stride;
+	fix->accel = FB_ACCEL_NONE;
+	return 0;
+}
+
+/* Check the requested display mode, returning -EINVAL if we can't
+   handle it. */
+
+static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
+{
+	struct osd_info *oi = itv->osd_info;
+	int osd_height_limit;
+	u32 pixclock, hlimit, vlimit;
+
+	IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
+
+	/* Set base references for mode calcs. */
+	if (itv->is_out_50hz) {
+		pixclock = 84316;
+		hlimit = 776;
+		vlimit = 591;
+		osd_height_limit = 576;
+	}
+	else {
+		pixclock = 83926;
+		hlimit = 776;
+		vlimit = 495;
+		osd_height_limit = 480;
+	}
+
+	if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
+		var->transp.offset = 24;
+		var->transp.length = 8;
+		var->red.offset = 16;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+	}
+	else if (var->bits_per_pixel == 16) {
+		/* To find out the true mode, check green length */
+		switch (var->green.length) {
+			case 4:
+				var->red.offset = 8;
+				var->red.length = 4;
+				var->green.offset = 4;
+				var->green.length = 4;
+				var->blue.offset = 0;
+				var->blue.length = 4;
+				var->transp.offset = 12;
+				var->transp.length = 1;
+				break;
+			case 5:
+				var->red.offset = 10;
+				var->red.length = 5;
+				var->green.offset = 5;
+				var->green.length = 5;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 15;
+				var->transp.length = 1;
+				break;
+			default:
+				var->red.offset = 11;
+				var->red.length = 5;
+				var->green.offset = 5;
+				var->green.length = 6;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+		}
+	}
+	else {
+		IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
+		return -EINVAL;
+	}
+
+	/* Check the resolution */
+	if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
+		IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
+				var->xres, var->yres);
+		return -EINVAL;
+	}
+
+	/* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
+	if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
+	    var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
+	    var->xres_virtual < var->xres ||
+	    var->yres_virtual < var->yres) {
+		IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
+			var->xres_virtual, var->yres_virtual);
+		return -EINVAL;
+	}
+
+	/* Some extra checks if in 8 bit mode */
+	if (var->bits_per_pixel == 8) {
+		/* Width must be a multiple of 4 */
+		if (var->xres & 3) {
+			IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
+			return -EINVAL;
+		}
+		if (var->xres_virtual & 3) {
+			IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
+			return -EINVAL;
+		}
+	}
+	else if (var->bits_per_pixel == 16) {
+		/* Width must be a multiple of 2 */
+		if (var->xres & 1) {
+			IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
+			return -EINVAL;
+		}
+		if (var->xres_virtual & 1) {
+			IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
+			return -EINVAL;
+		}
+	}
+
+	/* Now check the offsets */
+	if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
+		IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
+			var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
+		return -EINVAL;
+	}
+
+	/* Check pixel format */
+	if (var->nonstd > 1) {
+		IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
+		return -EINVAL;
+	}
+
+	/* Check video mode */
+	if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
+		((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
+		IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
+		return -EINVAL;
+	}
+
+	/* Check the left & upper margins
+	   If the margins are too large, just center the screen
+	   (enforcing margins causes too many problems) */
+
+	if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1)
+		var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
+
+	if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481))
+		var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) -
+			var->yres) / 2);
+
+	/* Maintain overall 'size' for a constant refresh rate */
+	var->right_margin = hlimit - var->left_margin - var->xres;
+	var->lower_margin = vlimit - var->upper_margin - var->yres;
+
+	/* Fixed sync times */
+	var->hsync_len = 24;
+	var->vsync_len = 2;
+
+	/* Non-interlaced / interlaced mode is used to switch the OSD filter
+	   on or off. Adjust the clock timings to maintain a constant
+	   vertical refresh rate. */
+	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
+		var->pixclock = pixclock / 2;
+	else
+		var->pixclock = pixclock;
+
+	itv->osd_rect.width = var->xres;
+	itv->osd_rect.height = var->yres;
+
+	IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
+		      var->xres, var->yres,
+		      var->xres_virtual, var->yres_virtual,
+		      var->bits_per_pixel);
+
+	IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
+		      var->left_margin, var->upper_margin);
+
+	IVTVFB_DEBUG_INFO("Display filter: %s\n",
+			(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
+	IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
+	return 0;
+}
+
+static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct ivtv *itv = (struct ivtv *) info->par;
+	IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
+	return _ivtvfb_check_var(var, itv);
+}
+
+static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	u32 osd_pan_index;
+	struct ivtv *itv = (struct ivtv *) info->par;
+
+	if (var->yoffset + info->var.yres > info->var.yres_virtual ||
+	    var->xoffset + info->var.xres > info->var.xres_virtual)
+		return -EINVAL;
+
+	osd_pan_index = var->yoffset * info->fix.line_length
+		      + var->xoffset * info->var.bits_per_pixel / 8;
+	write_reg(osd_pan_index, 0x02A0C);
+
+	/* Pass this info back the yuv handler */
+	itv->yuv_info.osd_x_pan = var->xoffset;
+	itv->yuv_info.osd_y_pan = var->yoffset;
+	/* Force update of yuv registers */
+	itv->yuv_info.yuv_forced_update = 1;
+	/* Remember this value */
+	itv->osd_info->pan_cur = osd_pan_index;
+	return 0;
+}
+
+static int ivtvfb_set_par(struct fb_info *info)
+{
+	int rc = 0;
+	struct ivtv *itv = (struct ivtv *) info->par;
+
+	IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
+
+	rc = ivtvfb_set_var(itv, &info->var);
+	ivtvfb_pan_display(&info->var, info);
+	ivtvfb_get_fix(itv, &info->fix);
+	ivtv_firmware_check(itv, "ivtvfb_set_par");
+	return rc;
+}
+
+static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+				unsigned blue, unsigned transp,
+				struct fb_info *info)
+{
+	u32 color, *palette;
+	struct ivtv *itv = (struct ivtv *)info->par;
+
+	if (regno >= info->cmap.len)
+		return -EINVAL;
+
+	color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
+	if (info->var.bits_per_pixel <= 8) {
+		write_reg(regno, 0x02a30);
+		write_reg(color, 0x02a34);
+		itv->osd_info->palette_cur[regno] = color;
+		return 0;
+	}
+	if (regno >= 16)
+		return -EINVAL;
+
+	palette = info->pseudo_palette;
+	if (info->var.bits_per_pixel == 16) {
+		switch (info->var.green.length) {
+			case 4:
+				color = ((red & 0xf000) >> 4) |
+					((green & 0xf000) >> 8) |
+					((blue & 0xf000) >> 12);
+				break;
+			case 5:
+				color = ((red & 0xf800) >> 1) |
+					((green & 0xf800) >> 6) |
+					((blue & 0xf800) >> 11);
+				break;
+			case 6:
+				color = (red & 0xf800 ) |
+					((green & 0xfc00) >> 5) |
+					((blue & 0xf800) >> 11);
+				break;
+		}
+	}
+	palette[regno] = color;
+	return 0;
+}
+
+/* We don't really support blanking. All this does is enable or
+   disable the OSD. */
+static int ivtvfb_blank(int blank_mode, struct fb_info *info)
+{
+	struct ivtv *itv = (struct ivtv *)info->par;
+
+	IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
+	switch (blank_mode) {
+	case FB_BLANK_UNBLANK:
+		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+		break;
+	case FB_BLANK_NORMAL:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_VSYNC_SUSPEND:
+		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+		break;
+	case FB_BLANK_POWERDOWN:
+		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
+		break;
+	}
+	itv->osd_info->blank_cur = blank_mode;
+	return 0;
+}
+
+static struct fb_ops ivtvfb_ops = {
+	.owner = THIS_MODULE,
+	.fb_write       = ivtvfb_write,
+	.fb_check_var   = ivtvfb_check_var,
+	.fb_set_par     = ivtvfb_set_par,
+	.fb_setcolreg   = ivtvfb_setcolreg,
+	.fb_fillrect    = cfb_fillrect,
+	.fb_copyarea    = cfb_copyarea,
+	.fb_imageblit   = cfb_imageblit,
+	.fb_cursor      = NULL,
+	.fb_ioctl       = ivtvfb_ioctl,
+	.fb_pan_display = ivtvfb_pan_display,
+	.fb_blank       = ivtvfb_blank,
+};
+
+/* Restore hardware after firmware restart */
+static void ivtvfb_restore(struct ivtv *itv)
+{
+	struct osd_info *oi = itv->osd_info;
+	int i;
+
+	ivtvfb_set_var(itv, &oi->fbvar_cur);
+	ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info);
+	for (i = 0; i < 256; i++) {
+		write_reg(i, 0x02a30);
+		write_reg(oi->palette_cur[i], 0x02a34);
+	}
+	write_reg(oi->pan_cur, 0x02a0c);
+}
+
+/* Initialization */
+
+
+/* Setup our initial video mode */
+static int ivtvfb_init_vidmode(struct ivtv *itv)
+{
+	struct osd_info *oi = itv->osd_info;
+	struct v4l2_rect start_window;
+	int max_height;
+
+	/* Color mode */
+
+	if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
+		osd_depth = 8;
+	oi->bits_per_pixel = osd_depth;
+	oi->bytes_per_pixel = oi->bits_per_pixel / 8;
+
+	/* Horizontal size & position */
+
+	if (osd_xres > 720)
+		osd_xres = 720;
+
+	/* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
+	if (osd_depth == 8)
+		osd_xres &= ~3;
+	else if (osd_depth == 16)
+		osd_xres &= ~1;
+
+	start_window.width = osd_xres ? osd_xres : 640;
+
+	/* Check horizontal start (osd_left). */
+	if (osd_left && osd_left + start_window.width > 721) {
+		IVTVFB_ERR("Invalid osd_left - assuming default\n");
+		osd_left = 0;
+	}
+
+	/* Hardware coords start at 0, user coords start at 1. */
+	osd_left--;
+
+	start_window.left = osd_left >= 0 ?
+		 osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
+
+	oi->display_byte_stride =
+			start_window.width * oi->bytes_per_pixel;
+
+	/* Vertical size & position */
+
+	max_height = itv->is_out_50hz ? 576 : 480;
+
+	if (osd_yres > max_height)
+		osd_yres = max_height;
+
+	start_window.height = osd_yres ?
+		osd_yres : itv->is_out_50hz ? 480 : 400;
+
+	/* Check vertical start (osd_upper). */
+	if (osd_upper + start_window.height > max_height + 1) {
+		IVTVFB_ERR("Invalid osd_upper - assuming default\n");
+		osd_upper = 0;
+	}
+
+	/* Hardware coords start at 0, user coords start at 1. */
+	osd_upper--;
+
+	start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
+
+	oi->display_width = start_window.width;
+	oi->display_height = start_window.height;
+
+	/* Generate a valid fb_var_screeninfo */
+
+	oi->ivtvfb_defined.xres = oi->display_width;
+	oi->ivtvfb_defined.yres = oi->display_height;
+	oi->ivtvfb_defined.xres_virtual = oi->display_width;
+	oi->ivtvfb_defined.yres_virtual = oi->display_height;
+	oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
+	oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
+	oi->ivtvfb_defined.left_margin = start_window.left + 1;
+	oi->ivtvfb_defined.upper_margin = start_window.top + 1;
+	oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
+	oi->ivtvfb_defined.nonstd = 0;
+
+	/* We've filled in the most data, let the usual mode check
+	   routine fill in the rest. */
+	_ivtvfb_check_var(&oi->ivtvfb_defined, itv);
+
+	/* Generate valid fb_fix_screeninfo */
+
+	ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
+
+	/* Generate valid fb_info */
+
+	oi->ivtvfb_info.node = -1;
+	oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
+	oi->ivtvfb_info.fbops = &ivtvfb_ops;
+	oi->ivtvfb_info.par = itv;
+	oi->ivtvfb_info.var = oi->ivtvfb_defined;
+	oi->ivtvfb_info.fix = oi->ivtvfb_fix;
+	oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
+	oi->ivtvfb_info.fbops = &ivtvfb_ops;
+
+	/* Supply some monitor specs. Bogus values will do for now */
+	oi->ivtvfb_info.monspecs.hfmin = 8000;
+	oi->ivtvfb_info.monspecs.hfmax = 70000;
+	oi->ivtvfb_info.monspecs.vfmin = 10;
+	oi->ivtvfb_info.monspecs.vfmax = 100;
+
+	/* Allocate color map */
+	if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
+		IVTVFB_ERR("abort, unable to alloc cmap\n");
+		return -ENOMEM;
+	}
+
+	/* Allocate the pseudo palette */
+	oi->ivtvfb_info.pseudo_palette =
+		kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
+
+	if (!oi->ivtvfb_info.pseudo_palette) {
+		IVTVFB_ERR("abort, unable to alloc pseudo palette\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
+
+static int ivtvfb_init_io(struct ivtv *itv)
+{
+	struct osd_info *oi = itv->osd_info;
+
+	mutex_lock(&itv->serialize_lock);
+	if (ivtv_init_on_first_open(itv)) {
+		mutex_unlock(&itv->serialize_lock);
+		IVTVFB_ERR("Failed to initialize ivtv\n");
+		return -ENXIO;
+	}
+	mutex_unlock(&itv->serialize_lock);
+
+	if (ivtvfb_get_framebuffer(itv, &oi->video_rbase,
+					&oi->video_buffer_size) < 0) {
+		IVTVFB_ERR("Firmware failed to respond\n");
+		return -EIO;
+	}
+
+	/* The osd buffer size depends on the number of video buffers allocated
+	   on the PVR350 itself. For now we'll hardcode the smallest osd buffer
+	   size to prevent any overlap. */
+	oi->video_buffer_size = 1704960;
+
+	oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
+	oi->video_vbase = itv->dec_mem + oi->video_rbase;
+
+	if (!oi->video_vbase) {
+		IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
+		     oi->video_buffer_size, oi->video_pbase);
+		return -EIO;
+	}
+
+	IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
+			oi->video_pbase, oi->video_vbase,
+			oi->video_buffer_size / 1024);
+
+#ifdef CONFIG_MTRR
+	{
+		/* Find the largest power of two that maps the whole buffer */
+		int size_shift = 31;
+
+		while (!(oi->video_buffer_size & (1 << size_shift))) {
+			size_shift--;
+		}
+		size_shift++;
+		oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
+		oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
+		oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
+		oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
+		if (mtrr_add(oi->fb_start_aligned_physaddr,
+			oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
+			     MTRR_TYPE_WRCOMB, 1) < 0) {
+			IVTVFB_INFO("disabled mttr\n");
+			oi->fb_start_aligned_physaddr = 0;
+			oi->fb_end_aligned_physaddr = 0;
+		}
+	}
+#endif
+
+	/* Blank the entire osd. */
+	memset_io(oi->video_vbase, 0, oi->video_buffer_size);
+
+	return 0;
+}
+
+/* Release any memory we've grabbed & remove mtrr entry */
+static void ivtvfb_release_buffers (struct ivtv *itv)
+{
+	struct osd_info *oi = itv->osd_info;
+
+	/* Release cmap */
+	if (oi->ivtvfb_info.cmap.len)
+		fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
+
+	/* Release pseudo palette */
+	if (oi->ivtvfb_info.pseudo_palette)
+		kfree(oi->ivtvfb_info.pseudo_palette);
+
+#ifdef CONFIG_MTRR
+	if (oi->fb_end_aligned_physaddr) {
+		mtrr_del(-1, oi->fb_start_aligned_physaddr,
+			oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
+	}
+#endif
+
+	kfree(oi);
+	itv->osd_info = NULL;
+}
+
+/* Initialize the specified card */
+
+static int ivtvfb_init_card(struct ivtv *itv)
+{
+	int rc;
+
+	if (itv->osd_info) {
+		IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
+		return -EBUSY;
+	}
+
+	itv->osd_info = kzalloc(sizeof(struct osd_info),
+					GFP_ATOMIC|__GFP_NOWARN);
+	if (itv->osd_info == NULL) {
+		IVTVFB_ERR("Failed to allocate memory for osd_info\n");
+		return -ENOMEM;
+	}
+
+	/* Find & setup the OSD buffer */
+	rc = ivtvfb_init_io(itv);
+	if (rc) {
+		ivtvfb_release_buffers(itv);
+		return rc;
+	}
+
+	/* Set the startup video mode information */
+	if ((rc = ivtvfb_init_vidmode(itv))) {
+		ivtvfb_release_buffers(itv);
+		return rc;
+	}
+
+	/* Register the framebuffer */
+	if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
+		ivtvfb_release_buffers(itv);
+		return -EINVAL;
+	}
+
+	itv->osd_video_pbase = itv->osd_info->video_pbase;
+
+	/* Set the card to the requested mode */
+	ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
+
+	/* Set color 0 to black */
+	write_reg(0, 0x02a30);
+	write_reg(0, 0x02a34);
+
+	/* Enable the osd */
+	ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
+
+	/* Enable restart */
+	itv->ivtvfb_restore = ivtvfb_restore;
+
+	/* Allocate DMA */
+	ivtv_udma_alloc(itv);
+	return 0;
+
+}
+
+static int __init ivtvfb_callback_init(struct device *dev, void *p)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+	struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		if (ivtvfb_init_card(itv) == 0) {
+			IVTVFB_INFO("Framebuffer registered on %s\n",
+					itv->v4l2_dev.name);
+			(*(int *)p)++;
+		}
+	}
+	return 0;
+}
+
+static int ivtvfb_callback_cleanup(struct device *dev, void *p)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+	struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+	struct osd_info *oi = itv->osd_info;
+
+	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+		if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+			IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
+				       itv->instance);
+			return 0;
+		}
+		IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
+		itv->ivtvfb_restore = NULL;
+		ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);
+		ivtvfb_release_buffers(itv);
+		itv->osd_video_pbase = 0;
+	}
+	return 0;
+}
+
+static int __init ivtvfb_init(void)
+{
+	struct device_driver *drv;
+	int registered = 0;
+	int err;
+
+	if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
+		printk(KERN_ERR "ivtvfb:  ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
+		     IVTV_MAX_CARDS - 1);
+		return -EINVAL;
+	}
+
+	drv = driver_find("ivtv", &pci_bus_type);
+	err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+	(void)err;	/* suppress compiler warning */
+	if (!registered) {
+		printk(KERN_ERR "ivtvfb:  no cards found\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void ivtvfb_cleanup(void)
+{
+	struct device_driver *drv;
+	int err;
+
+	printk(KERN_INFO "ivtvfb:  Unloading framebuffer module\n");
+
+	drv = driver_find("ivtv", &pci_bus_type);
+	err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+	(void)err;	/* suppress compiler warning */
+}
+
+module_init(ivtvfb_init);
+module_exit(ivtvfb_cleanup);
diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig
new file mode 100644
index 000000000000..d3cc21633b94
--- /dev/null
+++ b/drivers/media/pci/mantis/Kconfig
@@ -0,0 +1,38 @@
+config MANTIS_CORE
+	tristate "Mantis/Hopper PCI bridge based devices"
+	depends on PCI && I2C && INPUT && RC_CORE
+
+	help
+	  Support for PCI cards based on the Mantis and Hopper PCi bridge.
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_MANTIS
+	tristate "MANTIS based cards"
+	depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+	select DVB_MB86A16 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA665x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_PLL
+	help
+	  Support for PCI cards based on the Mantis PCI bridge.
+	  Say Y when you have a Mantis based DVB card and want to use it.
+
+	  If unsure say N.
+
+config DVB_HOPPER
+	tristate "HOPPER based cards"
+	depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_PLL
+	help
+	  Support for PCI cards based on the Hopper  PCI bridge.
+	  Say Y when you have a Hopper based DVB card and want to use it.
+
+	  If unsure say N
diff --git a/drivers/media/pci/mantis/Makefile b/drivers/media/pci/mantis/Makefile
new file mode 100644
index 000000000000..f715051e4453
--- /dev/null
+++ b/drivers/media/pci/mantis/Makefile
@@ -0,0 +1,28 @@
+mantis_core-objs :=	mantis_ioc.o	\
+			mantis_uart.o	\
+			mantis_dma.o	\
+			mantis_pci.o	\
+			mantis_i2c.o	\
+			mantis_dvb.o	\
+			mantis_evm.o	\
+			mantis_hif.o	\
+			mantis_ca.o	\
+			mantis_pcmcia.o	\
+			mantis_input.o
+
+mantis-objs	:=	mantis_cards.o	\
+			mantis_vp1033.o	\
+			mantis_vp1034.o	\
+			mantis_vp1041.o	\
+			mantis_vp2033.o	\
+			mantis_vp2040.o	\
+			mantis_vp3030.o
+
+hopper-objs	:=	hopper_cards.o	\
+			hopper_vp3028.o
+
+obj-$(CONFIG_MANTIS_CORE)	+= mantis_core.o
+obj-$(CONFIG_DVB_MANTIS)	+= mantis.o
+obj-$(CONFIG_DVB_HOPPER)	+= hopper.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c
new file mode 100644
index 000000000000..cc0251e01077
--- /dev/null
+++ b/drivers/media/pci/mantis/hopper_cards.c
@@ -0,0 +1,277 @@
+/*
+	Hopper PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "hopper_vp3028.h"
+#include "mantis_dma.h"
+#include "mantis_dvb.h"
+#include "mantis_uart.h"
+#include "mantis_ioc.h"
+#include "mantis_pci.h"
+#include "mantis_i2c.h"
+#include "mantis_reg.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)");
+
+#define DRIVER_NAME	"Hopper"
+
+static char *label[10] = {
+	"DMA",
+	"IRQ-0",
+	"IRQ-1",
+	"OCERR",
+	"PABRT",
+	"RIPRR",
+	"PPERR",
+	"FTRGT",
+	"RISCI",
+	"RACK"
+};
+
+static int devs;
+
+static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
+{
+	u32 stat = 0, mask = 0;
+	u32 rst_stat = 0, rst_mask = 0;
+
+	struct mantis_pci *mantis;
+	struct mantis_ca *ca;
+
+	mantis = (struct mantis_pci *) dev_id;
+	if (unlikely(mantis == NULL)) {
+		dprintk(MANTIS_ERROR, 1, "Mantis == NULL");
+		return IRQ_NONE;
+	}
+	ca = mantis->mantis_ca;
+
+	stat = mmread(MANTIS_INT_STAT);
+	mask = mmread(MANTIS_INT_MASK);
+	if (!(stat & mask))
+		return IRQ_NONE;
+
+	rst_mask  = MANTIS_GPIF_WRACK  |
+		    MANTIS_GPIF_OTHERR |
+		    MANTIS_SBUF_WSTO   |
+		    MANTIS_GPIF_EXTIRQ;
+
+	rst_stat  = mmread(MANTIS_GPIF_STATUS);
+	rst_stat &= rst_mask;
+	mmwrite(rst_stat, MANTIS_GPIF_STATUS);
+
+	mantis->mantis_int_stat = stat;
+	mantis->mantis_int_mask = mask;
+	dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask);
+	if (stat & MANTIS_INT_RISCEN) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]);
+	}
+	if (stat & MANTIS_INT_IRQ0) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]);
+		mantis->gpif_status = rst_stat;
+		wake_up(&ca->hif_write_wq);
+		schedule_work(&ca->hif_evm_work);
+	}
+	if (stat & MANTIS_INT_IRQ1) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]);
+		schedule_work(&mantis->uart_work);
+	}
+	if (stat & MANTIS_INT_OCERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]);
+	}
+	if (stat & MANTIS_INT_PABORT) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]);
+	}
+	if (stat & MANTIS_INT_RIPERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]);
+	}
+	if (stat & MANTIS_INT_PPERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]);
+	}
+	if (stat & MANTIS_INT_FTRGT) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]);
+	}
+	if (stat & MANTIS_INT_RISCI) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
+		mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+		tasklet_schedule(&mantis->tasklet);
+	}
+	if (stat & MANTIS_INT_I2CDONE) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
+		wake_up(&mantis->i2c_wq);
+	}
+	mmwrite(stat, MANTIS_INT_STAT);
+	stat &= ~(MANTIS_INT_RISCEN   | MANTIS_INT_I2CDONE |
+		  MANTIS_INT_I2CRACK  | MANTIS_INT_PCMCIA7 |
+		  MANTIS_INT_PCMCIA6  | MANTIS_INT_PCMCIA5 |
+		  MANTIS_INT_PCMCIA4  | MANTIS_INT_PCMCIA3 |
+		  MANTIS_INT_PCMCIA2  | MANTIS_INT_PCMCIA1 |
+		  MANTIS_INT_PCMCIA0  | MANTIS_INT_IRQ1	   |
+		  MANTIS_INT_IRQ0     | MANTIS_INT_OCERR   |
+		  MANTIS_INT_PABORT   | MANTIS_INT_RIPERR  |
+		  MANTIS_INT_PPERR    | MANTIS_INT_FTRGT   |
+		  MANTIS_INT_RISCI);
+
+	if (stat)
+		dprintk(MANTIS_DEBUG, 0, "<Unknown> Stat=<%02x> Mask=<%02x>", stat, mask);
+
+	dprintk(MANTIS_DEBUG, 0, "\n");
+	return IRQ_HANDLED;
+}
+
+static int __devinit hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct mantis_pci *mantis;
+	struct mantis_hwconfig *config;
+	int err = 0;
+
+	mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL);
+	if (mantis == NULL) {
+		printk(KERN_ERR "%s ERROR: Out of memory\n", __func__);
+		err = -ENOMEM;
+		goto fail0;
+	}
+
+	mantis->num		= devs;
+	mantis->verbose		= verbose;
+	mantis->pdev		= pdev;
+	config			= (struct mantis_hwconfig *) pci_id->driver_data;
+	config->irq_handler	= &hopper_irq_handler;
+	mantis->hwconfig	= config;
+
+	err = mantis_pci_init(mantis);
+	if (err) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err);
+		goto fail1;
+	}
+
+	err = mantis_stream_control(mantis, STREAM_TO_HIF);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err);
+		goto fail1;
+	}
+
+	err = mantis_i2c_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err);
+		goto fail2;
+	}
+
+	err = mantis_get_mac(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err);
+		goto fail2;
+	}
+
+	err = mantis_dma_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err);
+		goto fail3;
+	}
+
+	err = mantis_dvb_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err);
+		goto fail4;
+	}
+	devs++;
+
+	return err;
+
+fail4:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err);
+	mantis_dma_exit(mantis);
+
+fail3:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err);
+	mantis_i2c_exit(mantis);
+
+fail2:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err);
+	mantis_pci_exit(mantis);
+
+fail1:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err);
+	kfree(mantis);
+
+fail0:
+	return err;
+}
+
+static void __devexit hopper_pci_remove(struct pci_dev *pdev)
+{
+	struct mantis_pci *mantis = pci_get_drvdata(pdev);
+
+	if (mantis) {
+		mantis_dvb_exit(mantis);
+		mantis_dma_exit(mantis);
+		mantis_i2c_exit(mantis);
+		mantis_pci_exit(mantis);
+		kfree(mantis);
+	}
+	return;
+
+}
+
+static struct pci_device_id hopper_pci_table[] = {
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config),
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, hopper_pci_table);
+
+static struct pci_driver hopper_pci_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= hopper_pci_table,
+	.probe		= hopper_pci_probe,
+	.remove		= hopper_pci_remove,
+};
+
+static int __devinit hopper_init(void)
+{
+	return pci_register_driver(&hopper_pci_driver);
+}
+
+static void __devexit hopper_exit(void)
+{
+	return pci_unregister_driver(&hopper_pci_driver);
+}
+
+module_init(hopper_init);
+module_exit(hopper_exit);
+
+MODULE_DESCRIPTION("HOPPER driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c
new file mode 100644
index 000000000000..68a29f8bdf73
--- /dev/null
+++ b/drivers/media/pci/mantis/hopper_vp3028.c
@@ -0,0 +1,88 @@
+/*
+	Hopper VP-3028 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "zl10353.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "hopper_vp3028.h"
+
+struct zl10353_config hopper_vp3028_config = {
+	.demod_address	= 0x0f,
+};
+
+#define MANTIS_MODEL_NAME	"VP-3028"
+#define MANTIS_DEV_TYPE		"DVB-T"
+
+static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter	= &mantis->adapter;
+	struct mantis_hwconfig *config	= mantis->hwconfig;
+	int err = 0;
+
+	mantis_gpio_set_bits(mantis, config->reset, 0);
+	msleep(100);
+	err = mantis_frontend_power(mantis, POWER_ON);
+	msleep(100);
+	mantis_gpio_set_bits(mantis, config->reset, 1);
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		msleep(250);
+		dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)");
+		fe = dvb_attach(zl10353_attach, &hopper_vp3028_config, adapter);
+
+		if (!fe)
+			return -1;
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+	dprintk(MANTIS_ERROR, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp3028_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_188,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp3028_frontend_init,
+	.power		= GPIF_A00,
+	.reset		= GPIF_A03,
+};
diff --git a/drivers/media/pci/mantis/hopper_vp3028.h b/drivers/media/pci/mantis/hopper_vp3028.h
new file mode 100644
index 000000000000..57239498bc87
--- /dev/null
+++ b/drivers/media/pci/mantis/hopper_vp3028.h
@@ -0,0 +1,30 @@
+/*
+	Hopper VP-3028 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP3028_H
+#define __MANTIS_VP3028_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_3028_DVB_T	0x0028
+
+extern struct mantis_hwconfig vp3028_config;
+
+#endif /* __MANTIS_VP3028_H */
diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c
new file mode 100644
index 000000000000..3d7046909009
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_ca.c
@@ -0,0 +1,209 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_link.h"
+#include "mantis_hif.h"
+#include "mantis_reg.h"
+
+#include "mantis_ca.h"
+
+static int mantis_ca_read_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Read", slot);
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return mantis_hif_read_mem(ca, addr);
+}
+
+static int mantis_ca_write_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Write", slot);
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return mantis_hif_write_mem(ca, addr, data);
+}
+
+static int mantis_ca_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Read", slot);
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return mantis_hif_read_iom(ca, addr);
+}
+
+static int mantis_ca_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Write", slot);
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return mantis_hif_write_iom(ca, addr, data);
+}
+
+static int mantis_ca_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot RESET", slot);
+	udelay(500); /* Wait.. */
+	mmwrite(0xda, MANTIS_PCMCIA_RESET); /* Leading edge assert */
+	udelay(500);
+	mmwrite(0x00, MANTIS_PCMCIA_RESET); /* Trailing edge deassert */
+	msleep(1000);
+	dvb_ca_en50221_camready_irq(&ca->en50221, 0);
+
+	return 0;
+}
+
+static int mantis_ca_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot shutdown", slot);
+
+	return 0;
+}
+
+static int mantis_ts_control(struct dvb_ca_en50221 *en50221, int slot)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): TS control", slot);
+/*	mantis_set_direction(mantis, 1); */ /* Enable TS through CAM */
+
+	return 0;
+}
+
+static int mantis_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open)
+{
+	struct mantis_ca *ca = en50221->data;
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Slot(%d): Poll Slot status", slot);
+
+	if (ca->slot_state == MODULE_INSERTED) {
+		dprintk(MANTIS_DEBUG, 1, "CA Module present and ready");
+		return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+	} else {
+		dprintk(MANTIS_DEBUG, 1, "CA Module not present or not ready");
+	}
+
+	return 0;
+}
+
+int mantis_ca_init(struct mantis_pci *mantis)
+{
+	struct dvb_adapter *dvb_adapter	= &mantis->dvb_adapter;
+	struct mantis_ca *ca;
+	int ca_flags = 0, result;
+
+	dprintk(MANTIS_DEBUG, 1, "Initializing Mantis CA");
+	ca = kzalloc(sizeof(struct mantis_ca), GFP_KERNEL);
+	if (!ca) {
+		dprintk(MANTIS_ERROR, 1, "Out of memory!, exiting ..");
+		result = -ENOMEM;
+		goto err;
+	}
+
+	ca->ca_priv		= mantis;
+	mantis->mantis_ca	= ca;
+	ca_flags		= DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE;
+	/* register CA interface */
+	ca->en50221.owner		= THIS_MODULE;
+	ca->en50221.read_attribute_mem	= mantis_ca_read_attr_mem;
+	ca->en50221.write_attribute_mem	= mantis_ca_write_attr_mem;
+	ca->en50221.read_cam_control	= mantis_ca_read_cam_ctl;
+	ca->en50221.write_cam_control	= mantis_ca_write_cam_ctl;
+	ca->en50221.slot_reset		= mantis_ca_slot_reset;
+	ca->en50221.slot_shutdown	= mantis_ca_slot_shutdown;
+	ca->en50221.slot_ts_enable	= mantis_ts_control;
+	ca->en50221.poll_slot_status	= mantis_slot_status;
+	ca->en50221.data		= ca;
+
+	mutex_init(&ca->ca_lock);
+
+	init_waitqueue_head(&ca->hif_data_wq);
+	init_waitqueue_head(&ca->hif_opdone_wq);
+	init_waitqueue_head(&ca->hif_write_wq);
+
+	dprintk(MANTIS_ERROR, 1, "Registering EN50221 device");
+	result = dvb_ca_en50221_init(dvb_adapter, &ca->en50221, ca_flags, 1);
+	if (result != 0) {
+		dprintk(MANTIS_ERROR, 1, "EN50221: Initialization failed <%d>", result);
+		goto err;
+	}
+	dprintk(MANTIS_ERROR, 1, "Registered EN50221 device");
+	mantis_evmgr_init(ca);
+	return 0;
+err:
+	kfree(ca);
+	return result;
+}
+EXPORT_SYMBOL_GPL(mantis_ca_init);
+
+void mantis_ca_exit(struct mantis_pci *mantis)
+{
+	struct mantis_ca *ca = mantis->mantis_ca;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis CA exit");
+
+	mantis_evmgr_exit(ca);
+	dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device");
+	if (ca)
+		dvb_ca_en50221_release(&ca->en50221);
+
+	kfree(ca);
+}
+EXPORT_SYMBOL_GPL(mantis_ca_exit);
diff --git a/drivers/media/pci/mantis/mantis_ca.h b/drivers/media/pci/mantis/mantis_ca.h
new file mode 100644
index 000000000000..dc63e55f7eca
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_ca.h
@@ -0,0 +1,27 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_CA_H
+#define __MANTIS_CA_H
+
+extern int mantis_ca_init(struct mantis_pci *mantis);
+extern void mantis_ca_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_CA_H */
diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c
new file mode 100644
index 000000000000..0207d1f064e0
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_cards.c
@@ -0,0 +1,307 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+
+#include "mantis_vp1033.h"
+#include "mantis_vp1034.h"
+#include "mantis_vp1041.h"
+#include "mantis_vp2033.h"
+#include "mantis_vp2040.h"
+#include "mantis_vp3030.h"
+
+#include "mantis_dma.h"
+#include "mantis_ca.h"
+#include "mantis_dvb.h"
+#include "mantis_uart.h"
+#include "mantis_ioc.h"
+#include "mantis_pci.h"
+#include "mantis_i2c.h"
+#include "mantis_reg.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)");
+
+static int devs;
+
+#define DRIVER_NAME	"Mantis"
+
+static char *label[10] = {
+	"DMA",
+	"IRQ-0",
+	"IRQ-1",
+	"OCERR",
+	"PABRT",
+	"RIPRR",
+	"PPERR",
+	"FTRGT",
+	"RISCI",
+	"RACK"
+};
+
+static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
+{
+	u32 stat = 0, mask = 0;
+	u32 rst_stat = 0, rst_mask = 0;
+
+	struct mantis_pci *mantis;
+	struct mantis_ca *ca;
+
+	mantis = (struct mantis_pci *) dev_id;
+	if (unlikely(mantis == NULL)) {
+		dprintk(MANTIS_ERROR, 1, "Mantis == NULL");
+		return IRQ_NONE;
+	}
+	ca = mantis->mantis_ca;
+
+	stat = mmread(MANTIS_INT_STAT);
+	mask = mmread(MANTIS_INT_MASK);
+	if (!(stat & mask))
+		return IRQ_NONE;
+
+	rst_mask  = MANTIS_GPIF_WRACK  |
+		    MANTIS_GPIF_OTHERR |
+		    MANTIS_SBUF_WSTO   |
+		    MANTIS_GPIF_EXTIRQ;
+
+	rst_stat  = mmread(MANTIS_GPIF_STATUS);
+	rst_stat &= rst_mask;
+	mmwrite(rst_stat, MANTIS_GPIF_STATUS);
+
+	mantis->mantis_int_stat = stat;
+	mantis->mantis_int_mask = mask;
+	dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask);
+	if (stat & MANTIS_INT_RISCEN) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]);
+	}
+	if (stat & MANTIS_INT_IRQ0) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]);
+		mantis->gpif_status = rst_stat;
+		wake_up(&ca->hif_write_wq);
+		schedule_work(&ca->hif_evm_work);
+	}
+	if (stat & MANTIS_INT_IRQ1) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]);
+		schedule_work(&mantis->uart_work);
+	}
+	if (stat & MANTIS_INT_OCERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]);
+	}
+	if (stat & MANTIS_INT_PABORT) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]);
+	}
+	if (stat & MANTIS_INT_RIPERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]);
+	}
+	if (stat & MANTIS_INT_PPERR) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]);
+	}
+	if (stat & MANTIS_INT_FTRGT) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]);
+	}
+	if (stat & MANTIS_INT_RISCI) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
+		mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+		tasklet_schedule(&mantis->tasklet);
+	}
+	if (stat & MANTIS_INT_I2CDONE) {
+		dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
+		wake_up(&mantis->i2c_wq);
+	}
+	mmwrite(stat, MANTIS_INT_STAT);
+	stat &= ~(MANTIS_INT_RISCEN   | MANTIS_INT_I2CDONE |
+		  MANTIS_INT_I2CRACK  | MANTIS_INT_PCMCIA7 |
+		  MANTIS_INT_PCMCIA6  | MANTIS_INT_PCMCIA5 |
+		  MANTIS_INT_PCMCIA4  | MANTIS_INT_PCMCIA3 |
+		  MANTIS_INT_PCMCIA2  | MANTIS_INT_PCMCIA1 |
+		  MANTIS_INT_PCMCIA0  | MANTIS_INT_IRQ1	   |
+		  MANTIS_INT_IRQ0     | MANTIS_INT_OCERR   |
+		  MANTIS_INT_PABORT   | MANTIS_INT_RIPERR  |
+		  MANTIS_INT_PPERR    | MANTIS_INT_FTRGT   |
+		  MANTIS_INT_RISCI);
+
+	if (stat)
+		dprintk(MANTIS_DEBUG, 0, "<Unknown> Stat=<%02x> Mask=<%02x>", stat, mask);
+
+	dprintk(MANTIS_DEBUG, 0, "\n");
+	return IRQ_HANDLED;
+}
+
+static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct mantis_pci *mantis;
+	struct mantis_hwconfig *config;
+	int err = 0;
+
+	mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL);
+	if (mantis == NULL) {
+		printk(KERN_ERR "%s ERROR: Out of memory\n", __func__);
+		err = -ENOMEM;
+		goto fail0;
+	}
+
+	mantis->num		= devs;
+	mantis->verbose		= verbose;
+	mantis->pdev		= pdev;
+	config			= (struct mantis_hwconfig *) pci_id->driver_data;
+	config->irq_handler	= &mantis_irq_handler;
+	mantis->hwconfig	= config;
+
+	err = mantis_pci_init(mantis);
+	if (err) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err);
+		goto fail1;
+	}
+
+	err = mantis_stream_control(mantis, STREAM_TO_HIF);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err);
+		goto fail1;
+	}
+
+	err = mantis_i2c_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err);
+		goto fail2;
+	}
+
+	err = mantis_get_mac(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err);
+		goto fail2;
+	}
+
+	err = mantis_dma_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err);
+		goto fail3;
+	}
+
+	err = mantis_dvb_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err);
+		goto fail4;
+	}
+	err = mantis_uart_init(mantis);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err);
+		goto fail6;
+	}
+
+	devs++;
+
+	return err;
+
+
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err);
+	mantis_uart_exit(mantis);
+
+fail6:
+fail4:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err);
+	mantis_dma_exit(mantis);
+
+fail3:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err);
+	mantis_i2c_exit(mantis);
+
+fail2:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err);
+	mantis_pci_exit(mantis);
+
+fail1:
+	dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err);
+	kfree(mantis);
+
+fail0:
+	return err;
+}
+
+static void __devexit mantis_pci_remove(struct pci_dev *pdev)
+{
+	struct mantis_pci *mantis = pci_get_drvdata(pdev);
+
+	if (mantis) {
+
+		mantis_uart_exit(mantis);
+		mantis_dvb_exit(mantis);
+		mantis_dma_exit(mantis);
+		mantis_i2c_exit(mantis);
+		mantis_pci_exit(mantis);
+		kfree(mantis);
+	}
+	return;
+}
+
+static struct pci_device_id mantis_pci_table[] = {
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config),
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config),
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config),
+	MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config),
+	MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config),
+	MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config),
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config),
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config),
+	MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config),
+	MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config),
+	MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config),
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, mantis_pci_table);
+
+static struct pci_driver mantis_pci_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= mantis_pci_table,
+	.probe		= mantis_pci_probe,
+	.remove		= mantis_pci_remove,
+};
+
+static int __devinit mantis_init(void)
+{
+	return pci_register_driver(&mantis_pci_driver);
+}
+
+static void __devexit mantis_exit(void)
+{
+	return pci_unregister_driver(&mantis_pci_driver);
+}
+
+module_init(mantis_init);
+module_exit(mantis_exit);
+
+MODULE_DESCRIPTION("MANTIS driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h
new file mode 100644
index 000000000000..f2410cf0a6bf
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_common.h
@@ -0,0 +1,179 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_COMMON_H
+#define __MANTIS_COMMON_H
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include "mantis_uart.h"
+
+#include "mantis_link.h"
+
+#define MANTIS_ERROR		0
+#define MANTIS_NOTICE		1
+#define MANTIS_INFO		2
+#define MANTIS_DEBUG		3
+#define MANTIS_TMG		9
+
+#define dprintk(y, z, format, arg...) do {								\
+	if (z) {											\
+		if	((mantis->verbose > MANTIS_ERROR) && (mantis->verbose > y))			\
+			printk(KERN_ERR "%s (%d): " format "\n" , __func__ , mantis->num , ##arg);	\
+		else if	((mantis->verbose > MANTIS_NOTICE) && (mantis->verbose > y))			\
+			printk(KERN_NOTICE "%s (%d): " format "\n" , __func__ , mantis->num , ##arg);	\
+		else if ((mantis->verbose > MANTIS_INFO) && (mantis->verbose > y))			\
+			printk(KERN_INFO "%s (%d): " format "\n" , __func__ , mantis->num , ##arg);	\
+		else if ((mantis->verbose > MANTIS_DEBUG) && (mantis->verbose > y))			\
+			printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg);	\
+		else if ((mantis->verbose > MANTIS_TMG) && (mantis->verbose > y))			\
+			printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg);	\
+	} else {											\
+		if (mantis->verbose > y)								\
+			printk(format , ##arg);								\
+	}												\
+} while(0)
+
+#define mwrite(dat, addr)	writel((dat), addr)
+#define mread(addr)		readl(addr)
+
+#define mmwrite(dat, addr)	mwrite((dat), (mantis->mmio + (addr)))
+#define mmread(addr)		mread(mantis->mmio + (addr))
+
+#define MANTIS_TS_188		0
+#define MANTIS_TS_204		1
+
+#define TWINHAN_TECHNOLOGIES	0x1822
+#define MANTIS			0x4e35
+
+#define TECHNISAT		0x1ae4
+#define TERRATEC		0x153b
+
+#define MAKE_ENTRY(__subven, __subdev, __configptr) {			\
+		.vendor		= TWINHAN_TECHNOLOGIES,			\
+		.device		= MANTIS,				\
+		.subvendor	= (__subven),				\
+		.subdevice	= (__subdev),				\
+		.driver_data	= (unsigned long) (__configptr)		\
+}
+
+enum mantis_i2c_mode {
+	MANTIS_PAGE_MODE = 0,
+	MANTIS_BYTE_MODE,
+};
+
+struct mantis_pci;
+
+struct mantis_hwconfig {
+	char			*model_name;
+	char			*dev_type;
+	u32			ts_size;
+
+	enum mantis_baud	baud_rate;
+	enum mantis_parity	parity;
+	u32			bytes;
+
+	irqreturn_t (*irq_handler)(int irq, void *dev_id);
+	int (*frontend_init)(struct mantis_pci *mantis, struct dvb_frontend *fe);
+
+	u8			power;
+	u8			reset;
+
+	enum mantis_i2c_mode	i2c_mode;
+};
+
+struct mantis_pci {
+	unsigned int		verbose;
+
+	/*	PCI stuff		*/
+	u16			vendor_id;
+	u16			device_id;
+	u16			subsystem_vendor;
+	u16			subsystem_device;
+
+	u8			latency;
+
+	struct pci_dev		*pdev;
+
+	unsigned long		mantis_addr;
+	void __iomem		*mmio;
+
+	u8			irq;
+	u8			revision;
+
+	unsigned int		num;
+
+	/*	RISC Core		*/
+	u32			busy_block;
+	u32			last_block;
+	u8			*buf_cpu;
+	dma_addr_t		buf_dma;
+	u32			*risc_cpu;
+	dma_addr_t		risc_dma;
+
+	struct tasklet_struct	tasklet;
+
+	struct i2c_adapter	adapter;
+	int			i2c_rc;
+	wait_queue_head_t	i2c_wq;
+	struct mutex		i2c_lock;
+
+	/*	DVB stuff		*/
+	struct dvb_adapter	dvb_adapter;
+	struct dvb_frontend	*fe;
+	struct dvb_demux	demux;
+	struct dmxdev		dmxdev;
+	struct dmx_frontend	fe_hw;
+	struct dmx_frontend	fe_mem;
+	struct dvb_net		dvbnet;
+
+	u8			feeds;
+
+	struct mantis_hwconfig	*hwconfig;
+
+	u32			mantis_int_stat;
+	u32			mantis_int_mask;
+
+	/*	board specific		*/
+	u8			mac_address[8];
+	u32			sub_vendor_id;
+	u32			sub_device_id;
+
+	 /*	A12 A13 A14		*/
+	u32			gpio_status;
+
+	u32			gpif_status;
+
+	struct mantis_ca	*mantis_ca;
+
+	wait_queue_head_t	uart_wq;
+	struct work_struct	uart_work;
+	spinlock_t		uart_lock;
+
+	struct rc_dev		*rc;
+	char			input_name[80];
+	char			input_phys[80];
+};
+
+#define MANTIS_HIF_STATUS	(mantis->gpio_status)
+
+#endif /* __MANTIS_COMMON_H */
diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c
new file mode 100644
index 000000000000..684d9061fe2a
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_core.c
@@ -0,0 +1,235 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "mantis_common.h"
+#include "mantis_core.h"
+#include "mantis_vp1033.h"
+#include "mantis_vp1034.h"
+#include "mantis_vp1041.h"
+#include "mantis_vp2033.h"
+#include "mantis_vp2040.h"
+#include "mantis_vp3030.h"
+
+static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
+{
+	int err;
+	struct i2c_msg msg[] = {
+		{
+			.addr = 0x50,
+			.flags = 0,
+			.buf = data,
+			.len = 1
+		}, {
+			.addr = 0x50,
+			.flags = I2C_M_RD,
+			.buf = data,
+			.len = length
+		},
+	};
+
+	err = i2c_transfer(&mantis->adapter, msg, 2);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1,
+			"ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >",
+			err, data[0], data[1]);
+
+		return err;
+	}
+
+	return 0;
+}
+
+static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
+{
+	int err;
+
+	struct i2c_msg msg = {
+		.addr = 0x50,
+		.flags = 0,
+		.buf = data,
+		.len = length
+	};
+
+	err = i2c_transfer(&mantis->adapter, &msg, 1);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1,
+			"ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >",
+			err, length, data[0], data[1]);
+
+		return err;
+	}
+
+	return 0;
+}
+
+static int get_mac_address(struct mantis_pci *mantis)
+{
+	int err;
+
+	mantis->mac_address[0] = 0x08;
+	err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
+
+		return err;
+	}
+	dprintk(verbose, MANTIS_ERROR, 0,
+		"    MAC Address=[%pM]\n", mantis->mac_address);
+
+	return 0;
+}
+
+#define MANTIS_MODEL_UNKNOWN	"UNKNOWN"
+#define MANTIS_DEV_UNKNOWN	"UNKNOWN"
+
+struct mantis_hwconfig unknown_device = {
+	.model_name	= MANTIS_MODEL_UNKNOWN,
+	.dev_type	= MANTIS_DEV_UNKNOWN,
+};
+
+static void mantis_load_config(struct mantis_pci *mantis)
+{
+	switch (mantis->subsystem_device) {
+	case MANTIS_VP_1033_DVB_S:	/* VP-1033 */
+		mantis->hwconfig = &vp1033_mantis_config;
+		break;
+	case MANTIS_VP_1034_DVB_S:	/* VP-1034 */
+		mantis->hwconfig = &vp1034_mantis_config;
+		break;
+	case MANTIS_VP_1041_DVB_S2:	/* VP-1041 */
+	case TECHNISAT_SKYSTAR_HD2:
+		mantis->hwconfig = &vp1041_mantis_config;
+		break;
+	case MANTIS_VP_2033_DVB_C:	/* VP-2033 */
+		mantis->hwconfig = &vp2033_mantis_config;
+		break;
+	case MANTIS_VP_2040_DVB_C:	/* VP-2040 */
+	case CINERGY_C:	/* VP-2040 clone */
+	case TECHNISAT_CABLESTAR_HD2:
+		mantis->hwconfig = &vp2040_mantis_config;
+		break;
+	case MANTIS_VP_3030_DVB_T:	/* VP-3030 */
+		mantis->hwconfig = &vp3030_mantis_config;
+		break;
+	default:
+		mantis->hwconfig = &unknown_device;
+		break;
+	}
+}
+
+int mantis_core_init(struct mantis_pci *mantis)
+{
+	int err = 0;
+
+	mantis_load_config(mantis);
+	dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n",
+		mantis->hwconfig->model_name, mantis->hwconfig->dev_type,
+		mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn));
+	dprintk(verbose, MANTIS_ERROR, 0, "    Mantis Rev %d [%04x:%04x], ",
+		mantis->revision,
+		mantis->subsystem_vendor, mantis->subsystem_device);
+	dprintk(verbose, MANTIS_ERROR, 0,
+		"irq: %d, latency: %d\n    memory: 0x%lx, mmio: 0x%p\n",
+		mantis->pdev->irq, mantis->latency,
+		mantis->mantis_addr, mantis->mantis_mmio);
+
+	err = mantis_i2c_init(mantis);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed");
+		return err;
+	}
+	err = get_mac_address(mantis);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed");
+		return err;
+	}
+	err = mantis_dma_init(mantis);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed");
+		return err;
+	}
+	err = mantis_dvb_init(mantis);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed");
+		return err;
+	}
+	err = mantis_uart_init(mantis);
+	if (err < 0) {
+		dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed");
+		return err;
+	}
+
+	return 0;
+}
+
+int mantis_core_exit(struct mantis_pci *mantis)
+{
+	mantis_dma_stop(mantis);
+	dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping");
+
+	mantis_uart_exit(mantis);
+	dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed");
+
+	if (mantis_dma_exit(mantis) < 0)
+		dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed");
+	if (mantis_dvb_exit(mantis) < 0)
+		dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed");
+	if (mantis_i2c_exit(mantis) < 0)
+		dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed");
+
+	return 0;
+}
+
+/* Turn the given bit on or off. */
+void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value)
+{
+	u32 cur;
+
+	cur = mmread(MANTIS_GPIF_ADDR);
+	if (value)
+		mantis->gpio_status = cur | (1 << bitpos);
+	else
+		mantis->gpio_status = cur & (~(1 << bitpos));
+
+	mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR);
+	mmwrite(0x00, MANTIS_GPIF_DOUT);
+	udelay(100);
+}
+
+/* direction = 0 , no CI passthrough ; 1 , CI passthrough */
+void mantis_set_direction(struct mantis_pci *mantis, int direction)
+{
+	u32 reg;
+
+	reg = mmread(0x28);
+	dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup");
+	if (direction == 0x01) {
+		/* to CI */
+		reg |= 0x04;
+		mmwrite(reg, 0x28);
+		reg &= 0xff - 0x04;
+		mmwrite(reg, 0x28);
+	} else {
+		reg &= 0xff - 0x04;
+		mmwrite(reg, 0x28);
+		reg |= 0x04;
+		mmwrite(reg, 0x28);
+	}
+}
diff --git a/drivers/media/pci/mantis/mantis_core.h b/drivers/media/pci/mantis/mantis_core.h
new file mode 100644
index 000000000000..833ee42e694e
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_core.h
@@ -0,0 +1,57 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_CORE_H
+#define __MANTIS_CORE_H
+
+#include "mantis_common.h"
+
+
+#define FE_TYPE_SAT	0
+#define FE_TYPE_CAB	1
+#define FE_TYPE_TER	2
+
+#define FE_TYPE_TS204	0
+#define FE_TYPE_TS188	1
+
+
+struct vendorname {
+	u8  *sub_vendor_name;
+	u32 sub_vendor_id;
+};
+
+struct devicetype {
+	u8  *sub_device_name;
+	u32 sub_device_id;
+	u8  device_type;
+	u32 type_flags;
+};
+
+
+extern int mantis_dma_init(struct mantis_pci *mantis);
+extern int mantis_dma_exit(struct mantis_pci *mantis);
+extern void mantis_dma_start(struct mantis_pci *mantis);
+extern void mantis_dma_stop(struct mantis_pci *mantis);
+extern int mantis_i2c_init(struct mantis_pci *mantis);
+extern int mantis_i2c_exit(struct mantis_pci *mantis);
+extern int mantis_core_init(struct mantis_pci *mantis);
+extern int mantis_core_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_CORE_H */
diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c
new file mode 100644
index 000000000000..566c407175a4
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_dma.c
@@ -0,0 +1,230 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <asm/page.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+
+#include <asm/irq.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_dma.h"
+
+#define RISC_WRITE		(0x01 << 28)
+#define RISC_JUMP		(0x07 << 28)
+#define RISC_IRQ		(0x01 << 24)
+
+#define RISC_STATUS(status)	((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16))
+#define RISC_FLUSH(risc_pos)		(risc_pos = 0)
+#define RISC_INSTR(risc_pos, opcode)	(mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode))
+
+#define MANTIS_BUF_SIZE		(64 * 1024)
+#define MANTIS_BLOCK_BYTES      (MANTIS_BUF_SIZE / 4)
+#define MANTIS_DMA_TR_BYTES     (2 * 1024) /* upper limit: 4095 bytes. */
+#define MANTIS_BLOCK_COUNT	(MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES)
+
+#define MANTIS_DMA_TR_UNITS     (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES)
+/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */
+#define MANTIS_RISC_SIZE	PAGE_SIZE /* RISC program must fit here. */
+
+int mantis_dma_exit(struct mantis_pci *mantis)
+{
+	if (mantis->buf_cpu) {
+		dprintk(MANTIS_ERROR, 1,
+			"DMA=0x%lx cpu=0x%p size=%d",
+			(unsigned long) mantis->buf_dma,
+			 mantis->buf_cpu,
+			 MANTIS_BUF_SIZE);
+
+		pci_free_consistent(mantis->pdev, MANTIS_BUF_SIZE,
+				    mantis->buf_cpu, mantis->buf_dma);
+
+		mantis->buf_cpu = NULL;
+	}
+	if (mantis->risc_cpu) {
+		dprintk(MANTIS_ERROR, 1,
+			"RISC=0x%lx cpu=0x%p size=%lx",
+			(unsigned long) mantis->risc_dma,
+			mantis->risc_cpu,
+			MANTIS_RISC_SIZE);
+
+		pci_free_consistent(mantis->pdev, MANTIS_RISC_SIZE,
+				    mantis->risc_cpu, mantis->risc_dma);
+
+		mantis->risc_cpu = NULL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_dma_exit);
+
+static inline int mantis_alloc_buffers(struct mantis_pci *mantis)
+{
+	if (!mantis->buf_cpu) {
+		mantis->buf_cpu = pci_alloc_consistent(mantis->pdev,
+						       MANTIS_BUF_SIZE,
+						       &mantis->buf_dma);
+		if (!mantis->buf_cpu) {
+			dprintk(MANTIS_ERROR, 1,
+				"DMA buffer allocation failed");
+
+			goto err;
+		}
+		dprintk(MANTIS_ERROR, 1,
+			"DMA=0x%lx cpu=0x%p size=%d",
+			(unsigned long) mantis->buf_dma,
+			mantis->buf_cpu, MANTIS_BUF_SIZE);
+	}
+	if (!mantis->risc_cpu) {
+		mantis->risc_cpu = pci_alloc_consistent(mantis->pdev,
+							MANTIS_RISC_SIZE,
+							&mantis->risc_dma);
+
+		if (!mantis->risc_cpu) {
+			dprintk(MANTIS_ERROR, 1,
+				"RISC program allocation failed");
+
+			mantis_dma_exit(mantis);
+
+			goto err;
+		}
+		dprintk(MANTIS_ERROR, 1,
+			"RISC=0x%lx cpu=0x%p size=%lx",
+			(unsigned long) mantis->risc_dma,
+			mantis->risc_cpu, MANTIS_RISC_SIZE);
+	}
+
+	return 0;
+err:
+	dprintk(MANTIS_ERROR, 1, "Out of memory (?) .....");
+	return -ENOMEM;
+}
+
+int mantis_dma_init(struct mantis_pci *mantis)
+{
+	int err = 0;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis DMA init");
+	if (mantis_alloc_buffers(mantis) < 0) {
+		dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer");
+
+		/* Stop RISC Engine */
+		mmwrite(0, MANTIS_DMA_CTL);
+
+		goto err;
+	}
+
+	return 0;
+err:
+	return err;
+}
+EXPORT_SYMBOL_GPL(mantis_dma_init);
+
+static inline void mantis_risc_program(struct mantis_pci *mantis)
+{
+	u32 buf_pos = 0;
+	u32 line, step;
+	u32 risc_pos;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program");
+	RISC_FLUSH(risc_pos);
+
+	dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u",
+		MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES);
+
+	for (line = 0; line < MANTIS_BLOCK_COUNT; line++) {
+		for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) {
+			dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step);
+			if (step == 0) {
+				RISC_INSTR(risc_pos, RISC_WRITE	|
+					   RISC_IRQ	|
+					   RISC_STATUS(line) |
+					   MANTIS_DMA_TR_BYTES);
+			} else {
+				RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES);
+			}
+			RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos);
+			buf_pos += MANTIS_DMA_TR_BYTES;
+		  }
+	}
+	RISC_INSTR(risc_pos, RISC_JUMP);
+	RISC_INSTR(risc_pos, mantis->risc_dma);
+}
+
+void mantis_dma_start(struct mantis_pci *mantis)
+{
+	dprintk(MANTIS_DEBUG, 1, "Mantis Start DMA engine");
+
+	mantis_risc_program(mantis);
+	mmwrite(mantis->risc_dma, MANTIS_RISC_START);
+	mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR);
+
+	mmwrite(0, MANTIS_DMA_CTL);
+	mantis->last_block = mantis->busy_block = 0;
+
+	mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK);
+
+	mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN
+			       | MANTIS_RISC_EN, MANTIS_DMA_CTL);
+
+}
+
+void mantis_dma_stop(struct mantis_pci *mantis)
+{
+	dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine");
+
+	mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR);
+
+	mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN |
+					    MANTIS_DCAP_EN |
+					    MANTIS_RISC_EN)), MANTIS_DMA_CTL);
+
+	mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT);
+
+	mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI |
+					    MANTIS_INT_RISCEN), MANTIS_INT_MASK);
+}
+
+
+void mantis_dma_xfer(unsigned long data)
+{
+	struct mantis_pci *mantis = (struct mantis_pci *) data;
+	struct mantis_hwconfig *config = mantis->hwconfig;
+
+	while (mantis->last_block != mantis->busy_block) {
+		dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]",
+			mantis->last_block, mantis->busy_block);
+
+		(config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
+		(&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES);
+		mantis->last_block = (mantis->last_block + 1) % MANTIS_BLOCK_COUNT;
+	}
+}
diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h
new file mode 100644
index 000000000000..6be00fa82094
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_dma.h
@@ -0,0 +1,30 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_DMA_H
+#define __MANTIS_DMA_H
+
+extern int mantis_dma_init(struct mantis_pci *mantis);
+extern int mantis_dma_exit(struct mantis_pci *mantis);
+extern void mantis_dma_start(struct mantis_pci *mantis);
+extern void mantis_dma_stop(struct mantis_pci *mantis);
+extern void mantis_dma_xfer(unsigned long data);
+
+#endif /* __MANTIS_DMA_H */
diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c
new file mode 100644
index 000000000000..5d15c6b74d9b
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_dvb.c
@@ -0,0 +1,301 @@
+/*
+	Mantis PCI bridge driver
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_dma.h"
+#include "mantis_ca.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power)
+{
+	struct mantis_hwconfig *config = mantis->hwconfig;
+
+	switch (power) {
+	case POWER_ON:
+		dprintk(MANTIS_DEBUG, 1, "Power ON");
+		mantis_gpio_set_bits(mantis, config->power, POWER_ON);
+		msleep(100);
+		mantis_gpio_set_bits(mantis, config->power, POWER_ON);
+		msleep(100);
+		break;
+
+	case POWER_OFF:
+		dprintk(MANTIS_DEBUG, 1, "Power OFF");
+		mantis_gpio_set_bits(mantis, config->power, POWER_OFF);
+		msleep(100);
+		break;
+
+	default:
+		dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power);
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_frontend_power);
+
+void mantis_frontend_soft_reset(struct mantis_pci *mantis)
+{
+	struct mantis_hwconfig *config = mantis->hwconfig;
+
+	dprintk(MANTIS_DEBUG, 1, "Frontend RESET");
+	mantis_gpio_set_bits(mantis, config->reset, 0);
+	msleep(100);
+	mantis_gpio_set_bits(mantis, config->reset, 0);
+	msleep(100);
+	mantis_gpio_set_bits(mantis, config->reset, 1);
+	msleep(100);
+	mantis_gpio_set_bits(mantis, config->reset, 1);
+	msleep(100);
+
+	return;
+}
+EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset);
+
+static int mantis_frontend_shutdown(struct mantis_pci *mantis)
+{
+	int err;
+
+	mantis_frontend_soft_reset(mantis);
+	err = mantis_frontend_power(mantis, POWER_OFF);
+	if (err != 0) {
+		dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct mantis_pci *mantis = dvbdmx->priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed");
+	if (!dvbdmx->dmx.frontend) {
+		dprintk(MANTIS_DEBUG, 1, "no frontend ?");
+		return -EINVAL;
+	}
+
+	mantis->feeds++;
+	dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d",	mantis->feeds);
+
+	if (mantis->feeds == 1)	 {
+		dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma");
+		mantis_dma_start(mantis);
+		tasklet_enable(&mantis->tasklet);
+	}
+
+	return mantis->feeds;
+}
+
+static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct mantis_pci *mantis = dvbdmx->priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed");
+	if (!dvbdmx->dmx.frontend) {
+		dprintk(MANTIS_DEBUG, 1, "no frontend ?");
+		return -EINVAL;
+	}
+
+	mantis->feeds--;
+	if (mantis->feeds == 0) {
+		dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma");
+		tasklet_disable(&mantis->tasklet);
+		mantis_dma_stop(mantis);
+	}
+
+	return 0;
+}
+
+int __devinit mantis_dvb_init(struct mantis_pci *mantis)
+{
+	struct mantis_hwconfig *config = mantis->hwconfig;
+	int result = -1;
+
+	dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter");
+
+	result = dvb_register_adapter(&mantis->dvb_adapter,
+				      "Mantis DVB adapter",
+				      THIS_MODULE,
+				      &mantis->pdev->dev,
+				      adapter_nr);
+
+	if (result < 0) {
+
+		dprintk(MANTIS_ERROR, 1, "Error registering adapter");
+		return -ENODEV;
+	}
+
+	mantis->dvb_adapter.priv	= mantis;
+	mantis->demux.dmx.capabilities	= DMX_TS_FILTERING	|
+					 DMX_SECTION_FILTERING	|
+					 DMX_MEMORY_BASED_FILTERING;
+
+	mantis->demux.priv		= mantis;
+	mantis->demux.filternum		= 256;
+	mantis->demux.feednum		= 256;
+	mantis->demux.start_feed	= mantis_dvb_start_feed;
+	mantis->demux.stop_feed		= mantis_dvb_stop_feed;
+	mantis->demux.write_to_decoder	= NULL;
+
+	dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init");
+	result = dvb_dmx_init(&mantis->demux);
+	if (result < 0) {
+		dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+
+		goto err0;
+	}
+
+	mantis->dmxdev.filternum	= 256;
+	mantis->dmxdev.demux		= &mantis->demux.dmx;
+	mantis->dmxdev.capabilities	= 0;
+	dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init");
+
+	result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter);
+	if (result < 0) {
+
+		dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
+		goto err1;
+	}
+
+	mantis->fe_hw.source		= DMX_FRONTEND_0;
+	result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+	if (result < 0) {
+
+		dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+		goto err2;
+	}
+
+	mantis->fe_mem.source		= DMX_MEMORY_FE;
+	result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+	if (result < 0) {
+		dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+		goto err3;
+	}
+
+	result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+	if (result < 0) {
+		dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+		goto err4;
+	}
+
+	dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx);
+	tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis);
+	tasklet_disable(&mantis->tasklet);
+	if (mantis->hwconfig) {
+		result = config->frontend_init(mantis, mantis->fe);
+		if (result < 0) {
+			dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!");
+			goto err5;
+		} else {
+			if (mantis->fe == NULL) {
+				dprintk(MANTIS_ERROR, 1, "FE <NULL>");
+				goto err5;
+			}
+
+			if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) {
+				dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed");
+
+				if (mantis->fe->ops.release)
+					mantis->fe->ops.release(mantis->fe);
+
+				mantis->fe = NULL;
+				goto err5;
+			}
+		}
+	}
+
+	return 0;
+
+	/* Error conditions ..	*/
+err5:
+	tasklet_kill(&mantis->tasklet);
+	dvb_net_release(&mantis->dvbnet);
+	if (mantis->fe) {
+		dvb_unregister_frontend(mantis->fe);
+		dvb_frontend_detach(mantis->fe);
+	}
+err4:
+	mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+
+err3:
+	mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+
+err2:
+	dvb_dmxdev_release(&mantis->dmxdev);
+
+err1:
+	dvb_dmx_release(&mantis->demux);
+
+err0:
+	dvb_unregister_adapter(&mantis->dvb_adapter);
+
+	return result;
+}
+EXPORT_SYMBOL_GPL(mantis_dvb_init);
+
+int __devexit mantis_dvb_exit(struct mantis_pci *mantis)
+{
+	int err;
+
+	if (mantis->fe) {
+		/* mantis_ca_exit(mantis); */
+		err = mantis_frontend_shutdown(mantis);
+		if (err != 0)
+			dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err);
+		dvb_unregister_frontend(mantis->fe);
+		dvb_frontend_detach(mantis->fe);
+	}
+
+	tasklet_kill(&mantis->tasklet);
+	dvb_net_release(&mantis->dvbnet);
+
+	mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+	mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+
+	dvb_dmxdev_release(&mantis->dmxdev);
+	dvb_dmx_release(&mantis->demux);
+
+	dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter");
+	dvb_unregister_adapter(&mantis->dvb_adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_dvb_exit);
diff --git a/drivers/media/pci/mantis/mantis_dvb.h b/drivers/media/pci/mantis/mantis_dvb.h
new file mode 100644
index 000000000000..464199db304e
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_dvb.h
@@ -0,0 +1,35 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_DVB_H
+#define __MANTIS_DVB_H
+
+enum mantis_power {
+	POWER_OFF	= 0,
+	POWER_ON	= 1
+};
+
+extern int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power);
+extern void mantis_frontend_soft_reset(struct mantis_pci *mantis);
+
+extern int mantis_dvb_init(struct mantis_pci *mantis);
+extern int mantis_dvb_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_DVB_H */
diff --git a/drivers/media/pci/mantis/mantis_evm.c b/drivers/media/pci/mantis/mantis_evm.c
new file mode 100644
index 000000000000..909ff54868a3
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_evm.c
@@ -0,0 +1,117 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_link.h"
+#include "mantis_hif.h"
+#include "mantis_reg.h"
+
+static void mantis_hifevm_work(struct work_struct *work)
+{
+	struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work);
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	u32 gpif_stat;
+
+	gpif_stat = mmread(MANTIS_GPIF_STATUS);
+
+	if (gpif_stat & MANTIS_GPIF_DETSTAT) {
+		if (gpif_stat & MANTIS_CARD_PLUGIN) {
+			dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num);
+			mmwrite(0xdada0000, MANTIS_CARD_RESET);
+			mantis_event_cam_plugin(ca);
+			dvb_ca_en50221_camchange_irq(&ca->en50221,
+						     0,
+						     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+		}
+	} else {
+		if (gpif_stat & MANTIS_CARD_PLUGOUT) {
+			dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num);
+			mmwrite(0xdada0000, MANTIS_CARD_RESET);
+			mantis_event_cam_unplug(ca);
+			dvb_ca_en50221_camchange_irq(&ca->en50221,
+						     0,
+						     DVB_CA_EN50221_CAMCHANGE_REMOVED);
+		}
+	}
+
+	if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num);
+
+	if (mantis->gpif_status & MANTIS_SBUF_WSTO)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num);
+
+	if (mantis->gpif_status & MANTIS_GPIF_OTHERR)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num);
+
+	if (gpif_stat & MANTIS_SBUF_OVFLW)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num);
+
+	if (gpif_stat & MANTIS_GPIF_BRRDY)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num);
+
+	if (gpif_stat & MANTIS_GPIF_INTSTAT)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num);
+
+	if (gpif_stat & MANTIS_SBUF_EMPTY)
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num);
+
+	if (gpif_stat & MANTIS_SBUF_OPDONE) {
+		dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num);
+		ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL;
+		ca->hif_event = MANTIS_SBUF_OPDONE;
+		wake_up(&ca->hif_opdone_wq);
+	}
+}
+
+int mantis_evmgr_init(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager");
+	INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work);
+	mantis_pcmcia_init(ca);
+	schedule_work(&ca->hif_evm_work);
+	mantis_hif_init(ca);
+	return 0;
+}
+
+void mantis_evmgr_exit(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting");
+	flush_work(&ca->hif_evm_work);
+	mantis_hif_exit(ca);
+	mantis_pcmcia_exit(ca);
+}
diff --git a/drivers/media/pci/mantis/mantis_hif.c b/drivers/media/pci/mantis/mantis_hif.c
new file mode 100644
index 000000000000..10c68df7e16f
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_hif.c
@@ -0,0 +1,239 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+
+#include "mantis_hif.h"
+#include "mantis_link.h" /* temporary due to physical layer stuff */
+
+#include "mantis_reg.h"
+
+
+static int mantis_hif_sbuf_opdone_wait(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	int rc = 0;
+
+	if (wait_event_timeout(ca->hif_opdone_wq,
+			       ca->hif_event & MANTIS_SBUF_OPDONE,
+			       msecs_to_jiffies(500)) == -ERESTARTSYS) {
+
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Smart buffer operation timeout !", mantis->num);
+		rc = -EREMOTEIO;
+	}
+	dprintk(MANTIS_DEBUG, 1, "Smart Buffer Operation complete");
+	ca->hif_event &= ~MANTIS_SBUF_OPDONE;
+	return rc;
+}
+
+static int mantis_hif_write_wait(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 opdone = 0, timeout = 0;
+	int rc = 0;
+
+	if (wait_event_timeout(ca->hif_write_wq,
+			       mantis->gpif_status & MANTIS_GPIF_WRACK,
+			       msecs_to_jiffies(500)) == -ERESTARTSYS) {
+
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write ACK timed out !", mantis->num);
+		rc = -EREMOTEIO;
+	}
+	dprintk(MANTIS_DEBUG, 1, "Write Acknowledged");
+	mantis->gpif_status &= ~MANTIS_GPIF_WRACK;
+	while (!opdone) {
+		opdone = (mmread(MANTIS_GPIF_STATUS) & MANTIS_SBUF_OPDONE);
+		udelay(500);
+		timeout++;
+		if (timeout > 100) {
+			dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num);
+			rc = -ETIMEDOUT;
+			break;
+		}
+	}
+	dprintk(MANTIS_DEBUG, 1, "HIF Write success");
+	return rc;
+}
+
+
+int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 hif_addr = 0, data, count = 4;
+
+	dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Read", mantis->num);
+	mutex_lock(&ca->ca_lock);
+	hif_addr &= ~MANTIS_GPIF_PCMCIAREG;
+	hif_addr &= ~MANTIS_GPIF_PCMCIAIOM;
+	hif_addr |=  MANTIS_HIF_STATUS;
+	hif_addr |=  addr;
+
+	mmwrite(hif_addr, MANTIS_GPIF_BRADDR);
+	mmwrite(count, MANTIS_GPIF_BRBYTES);
+	udelay(20);
+	mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR);
+
+	if (mantis_hif_sbuf_opdone_wait(ca) != 0) {
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): GPIF Smart Buffer operation failed", mantis->num);
+		mutex_unlock(&ca->ca_lock);
+		return -EREMOTEIO;
+	}
+	data = mmread(MANTIS_GPIF_DIN);
+	mutex_unlock(&ca->ca_lock);
+	dprintk(MANTIS_DEBUG, 1, "Mem Read: 0x%02x", data);
+	return (data >> 24) & 0xff;
+}
+
+int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data)
+{
+	struct mantis_slot *slot = ca->slot;
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 hif_addr = 0;
+
+	dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Write", mantis->num);
+	mutex_lock(&ca->ca_lock);
+	hif_addr &= ~MANTIS_GPIF_HIFRDWRN;
+	hif_addr &= ~MANTIS_GPIF_PCMCIAREG;
+	hif_addr &= ~MANTIS_GPIF_PCMCIAIOM;
+	hif_addr |=  MANTIS_HIF_STATUS;
+	hif_addr |=  addr;
+
+	mmwrite(slot->slave_cfg, MANTIS_GPIF_CFGSLA); /* Slot0 alone for now */
+	mmwrite(hif_addr, MANTIS_GPIF_ADDR);
+	mmwrite(data, MANTIS_GPIF_DOUT);
+
+	if (mantis_hif_write_wait(ca) != 0) {
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num);
+		mutex_unlock(&ca->ca_lock);
+		return -EREMOTEIO;
+	}
+	dprintk(MANTIS_DEBUG, 1, "Mem Write: (0x%02x to 0x%02x)", data, addr);
+	mutex_unlock(&ca->ca_lock);
+
+	return 0;
+}
+
+int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 data, hif_addr = 0;
+
+	dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Read", mantis->num);
+	mutex_lock(&ca->ca_lock);
+	hif_addr &= ~MANTIS_GPIF_PCMCIAREG;
+	hif_addr |=  MANTIS_GPIF_PCMCIAIOM;
+	hif_addr |=  MANTIS_HIF_STATUS;
+	hif_addr |=  addr;
+
+	mmwrite(hif_addr, MANTIS_GPIF_BRADDR);
+	mmwrite(1, MANTIS_GPIF_BRBYTES);
+	udelay(20);
+	mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR);
+
+	if (mantis_hif_sbuf_opdone_wait(ca) != 0) {
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num);
+		mutex_unlock(&ca->ca_lock);
+		return -EREMOTEIO;
+	}
+	data = mmread(MANTIS_GPIF_DIN);
+	dprintk(MANTIS_DEBUG, 1, "I/O Read: 0x%02x", data);
+	udelay(50);
+	mutex_unlock(&ca->ca_lock);
+
+	return (u8) data;
+}
+
+int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 hif_addr = 0;
+
+	dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Write", mantis->num);
+	mutex_lock(&ca->ca_lock);
+	hif_addr &= ~MANTIS_GPIF_PCMCIAREG;
+	hif_addr &= ~MANTIS_GPIF_HIFRDWRN;
+	hif_addr |=  MANTIS_GPIF_PCMCIAIOM;
+	hif_addr |=  MANTIS_HIF_STATUS;
+	hif_addr |=  addr;
+
+	mmwrite(hif_addr, MANTIS_GPIF_ADDR);
+	mmwrite(data, MANTIS_GPIF_DOUT);
+
+	if (mantis_hif_write_wait(ca) != 0) {
+		dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num);
+		mutex_unlock(&ca->ca_lock);
+		return -EREMOTEIO;
+	}
+	dprintk(MANTIS_DEBUG, 1, "I/O Write: (0x%02x to 0x%02x)", data, addr);
+	mutex_unlock(&ca->ca_lock);
+	udelay(50);
+
+	return 0;
+}
+
+int mantis_hif_init(struct mantis_ca *ca)
+{
+	struct mantis_slot *slot = ca->slot;
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 irqcfg;
+
+	slot[0].slave_cfg = 0x70773028;
+	dprintk(MANTIS_ERROR, 1, "Adapter(%d) Initializing Mantis Host Interface", mantis->num);
+
+	mutex_lock(&ca->ca_lock);
+	irqcfg = mmread(MANTIS_GPIF_IRQCFG);
+	irqcfg = MANTIS_MASK_BRRDY	|
+		 MANTIS_MASK_WRACK	|
+		 MANTIS_MASK_EXTIRQ	|
+		 MANTIS_MASK_WSTO	|
+		 MANTIS_MASK_OTHERR	|
+		 MANTIS_MASK_OVFLW;
+
+	mmwrite(irqcfg, MANTIS_GPIF_IRQCFG);
+	mutex_unlock(&ca->ca_lock);
+
+	return 0;
+}
+
+void mantis_hif_exit(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+	u32 irqcfg;
+
+	dprintk(MANTIS_ERROR, 1, "Adapter(%d) Exiting Mantis Host Interface", mantis->num);
+	mutex_lock(&ca->ca_lock);
+	irqcfg = mmread(MANTIS_GPIF_IRQCFG);
+	irqcfg &= ~MANTIS_MASK_BRRDY;
+	mmwrite(irqcfg, MANTIS_GPIF_IRQCFG);
+	mutex_unlock(&ca->ca_lock);
+}
diff --git a/drivers/media/pci/mantis/mantis_hif.h b/drivers/media/pci/mantis/mantis_hif.h
new file mode 100644
index 000000000000..9094f9ed2362
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_hif.h
@@ -0,0 +1,29 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_HIF_H
+#define __MANTIS_HIF_H
+
+#define MANTIS_HIF_MEMRD		1
+#define MANTIS_HIF_MEMWR		2
+#define MANTIS_HIF_IOMRD		3
+#define MANTIS_HIF_IOMWR		4
+
+#endif /* __MANTIS_HIF_H */
diff --git a/drivers/media/pci/mantis/mantis_i2c.c b/drivers/media/pci/mantis/mantis_i2c.c
new file mode 100644
index 000000000000..e7794517fe26
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_i2c.c
@@ -0,0 +1,266 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_i2c.h"
+
+#define TRIALS			10000
+
+static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg)
+{
+	u32 rxd, i, stat, trials;
+
+	dprintk(MANTIS_INFO, 0, "        %s:  Address=[0x%02x] <R>[ ",
+		__func__, msg->addr);
+
+	for (i = 0; i < msg->len; i++) {
+		rxd = (msg->addr << 25) | (1 << 24)
+					| MANTIS_I2C_RATE_3
+					| MANTIS_I2C_STOP
+					| MANTIS_I2C_PGMODE;
+
+		if (i == (msg->len - 1))
+			rxd &= ~MANTIS_I2C_STOP;
+
+		mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
+		mmwrite(rxd, MANTIS_I2CDATA_CTL);
+
+		/* wait for xfer completion */
+		for (trials = 0; trials < TRIALS; trials++) {
+			stat = mmread(MANTIS_INT_STAT);
+			if (stat & MANTIS_INT_I2CDONE)
+				break;
+		}
+
+		dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
+
+		/* wait for xfer completion */
+		for (trials = 0; trials < TRIALS; trials++) {
+			stat = mmread(MANTIS_INT_STAT);
+			if (stat & MANTIS_INT_I2CRACK)
+				break;
+		}
+
+		dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+
+		rxd = mmread(MANTIS_I2CDATA_CTL);
+		msg->buf[i] = (u8)((rxd >> 8) & 0xFF);
+		dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
+	}
+	dprintk(MANTIS_INFO, 0, "]\n");
+
+	return 0;
+}
+
+static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg)
+{
+	int i;
+	u32 txd = 0, stat, trials;
+
+	dprintk(MANTIS_INFO, 0, "        %s: Address=[0x%02x] <W>[ ",
+		__func__, msg->addr);
+
+	for (i = 0; i < msg->len; i++) {
+		dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
+		txd = (msg->addr << 25) | (msg->buf[i] << 8)
+					| MANTIS_I2C_RATE_3
+					| MANTIS_I2C_STOP
+					| MANTIS_I2C_PGMODE;
+
+		if (i == (msg->len - 1))
+			txd &= ~MANTIS_I2C_STOP;
+
+		mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
+		mmwrite(txd, MANTIS_I2CDATA_CTL);
+
+		/* wait for xfer completion */
+		for (trials = 0; trials < TRIALS; trials++) {
+			stat = mmread(MANTIS_INT_STAT);
+			if (stat & MANTIS_INT_I2CDONE)
+				break;
+		}
+
+		dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
+
+		/* wait for xfer completion */
+		for (trials = 0; trials < TRIALS; trials++) {
+			stat = mmread(MANTIS_INT_STAT);
+			if (stat & MANTIS_INT_I2CRACK)
+				break;
+		}
+
+		dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+	}
+	dprintk(MANTIS_INFO, 0, "]\n");
+
+	return 0;
+}
+
+static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+{
+	int ret = 0, i = 0, trials;
+	u32 stat, data, txd;
+	struct mantis_pci *mantis;
+	struct mantis_hwconfig *config;
+
+	mantis = i2c_get_adapdata(adapter);
+	BUG_ON(!mantis);
+	config = mantis->hwconfig;
+	BUG_ON(!config);
+
+	dprintk(MANTIS_DEBUG, 1, "Messages:%d", num);
+	mutex_lock(&mantis->i2c_lock);
+
+	while (i < num) {
+		/* Byte MODE */
+		if ((config->i2c_mode & MANTIS_BYTE_MODE) &&
+		    ((i + 1) < num)			&&
+		    (msgs[i].len < 2)			&&
+		    (msgs[i + 1].len < 2)		&&
+		    (msgs[i + 1].flags & I2C_M_RD)) {
+
+			dprintk(MANTIS_DEBUG, 0, "        Byte MODE:\n");
+
+			/* Read operation */
+			txd = msgs[i].addr << 25 | (0x1 << 24)
+						 | (msgs[i].buf[0] << 16)
+						 | MANTIS_I2C_RATE_3;
+
+			mmwrite(txd, MANTIS_I2CDATA_CTL);
+			/* wait for xfer completion */
+			for (trials = 0; trials < TRIALS; trials++) {
+				stat = mmread(MANTIS_INT_STAT);
+				if (stat & MANTIS_INT_I2CDONE)
+					break;
+			}
+
+			/* check for xfer completion */
+			if (stat & MANTIS_INT_I2CDONE) {
+				/* check xfer was acknowledged */
+				if (stat & MANTIS_INT_I2CRACK) {
+					data = mmread(MANTIS_I2CDATA_CTL);
+					msgs[i + 1].buf[0] = (data >> 8) & 0xff;
+					dprintk(MANTIS_DEBUG, 0, "        Byte <%d> RXD=0x%02x  [%02x]\n", 0x0, data, msgs[i + 1].buf[0]);
+				} else {
+					/* I/O error */
+					dprintk(MANTIS_ERROR, 1, "        I/O error, LINE:%d", __LINE__);
+					ret = -EIO;
+					break;
+				}
+			} else {
+				/* I/O error */
+				dprintk(MANTIS_ERROR, 1, "        I/O error, LINE:%d", __LINE__);
+				ret = -EIO;
+				break;
+			}
+			i += 2; /* Write/Read operation in one go */
+		}
+
+		if (i < num) {
+			if (msgs[i].flags & I2C_M_RD)
+				ret = mantis_i2c_read(mantis, &msgs[i]);
+			else
+				ret = mantis_i2c_write(mantis, &msgs[i]);
+
+			i++;
+			if (ret < 0)
+				goto bail_out;
+		}
+
+	}
+
+	mutex_unlock(&mantis->i2c_lock);
+
+	return num;
+
+bail_out:
+	mutex_unlock(&mantis->i2c_lock);
+	return ret;
+}
+
+static u32 mantis_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm mantis_algo = {
+	.master_xfer		= mantis_i2c_xfer,
+	.functionality		= mantis_i2c_func,
+};
+
+int __devinit mantis_i2c_init(struct mantis_pci *mantis)
+{
+	u32 intstat, intmask;
+	struct i2c_adapter *i2c_adapter = &mantis->adapter;
+	struct pci_dev *pdev		= mantis->pdev;
+
+	init_waitqueue_head(&mantis->i2c_wq);
+	mutex_init(&mantis->i2c_lock);
+	strncpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name));
+	i2c_set_adapdata(i2c_adapter, mantis);
+
+	i2c_adapter->owner	= THIS_MODULE;
+	i2c_adapter->algo	= &mantis_algo;
+	i2c_adapter->algo_data	= NULL;
+	i2c_adapter->timeout	= 500;
+	i2c_adapter->retries	= 3;
+	i2c_adapter->dev.parent	= &pdev->dev;
+
+	mantis->i2c_rc		= i2c_add_adapter(i2c_adapter);
+	if (mantis->i2c_rc < 0)
+		return mantis->i2c_rc;
+
+	dprintk(MANTIS_DEBUG, 1, "Initializing I2C ..");
+
+	intstat = mmread(MANTIS_INT_STAT);
+	intmask = mmread(MANTIS_INT_MASK);
+	mmwrite(intstat, MANTIS_INT_STAT);
+	dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
+	intmask = mmread(MANTIS_INT_MASK);
+	mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_i2c_init);
+
+int mantis_i2c_exit(struct mantis_pci *mantis)
+{
+	u32 intmask;
+
+	dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
+	intmask = mmread(MANTIS_INT_MASK);
+	mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK);
+
+	dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter");
+	return i2c_del_adapter(&mantis->adapter);
+}
+EXPORT_SYMBOL_GPL(mantis_i2c_exit);
diff --git a/drivers/media/pci/mantis/mantis_i2c.h b/drivers/media/pci/mantis/mantis_i2c.h
new file mode 100644
index 000000000000..1342df2faed8
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_i2c.h
@@ -0,0 +1,30 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_I2C_H
+#define __MANTIS_I2C_H
+
+#define I2C_STOP		(1 <<  0)
+#define I2C_READ		(1 <<  1)
+
+extern int mantis_i2c_init(struct mantis_pci *mantis);
+extern int mantis_i2c_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_I2C_H */
diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
new file mode 100644
index 000000000000..db6d54d3fec0
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_input.c
@@ -0,0 +1,159 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <media/rc-core.h>
+#include <linux/pci.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_uart.h"
+
+#define MODULE_NAME "mantis_core"
+#define RC_MAP_MANTIS "rc-mantis"
+
+static struct rc_map_table mantis_ir_table[] = {
+	{ 0x29, KEY_POWER	},
+	{ 0x28, KEY_FAVORITES	},
+	{ 0x30, KEY_TEXT	},
+	{ 0x17, KEY_INFO	}, /* Preview */
+	{ 0x23, KEY_EPG		},
+	{ 0x3b, KEY_F22		}, /* Record List */
+	{ 0x3c, KEY_1		},
+	{ 0x3e, KEY_2		},
+	{ 0x39, KEY_3		},
+	{ 0x36, KEY_4		},
+	{ 0x22, KEY_5		},
+	{ 0x20, KEY_6		},
+	{ 0x32, KEY_7		},
+	{ 0x26, KEY_8		},
+	{ 0x24, KEY_9		},
+	{ 0x2a, KEY_0		},
+
+	{ 0x33, KEY_CANCEL	},
+	{ 0x2c, KEY_BACK	},
+	{ 0x15, KEY_CLEAR	},
+	{ 0x3f, KEY_TAB		},
+	{ 0x10, KEY_ENTER	},
+	{ 0x14, KEY_UP		},
+	{ 0x0d, KEY_RIGHT	},
+	{ 0x0e, KEY_DOWN	},
+	{ 0x11, KEY_LEFT	},
+
+	{ 0x21, KEY_VOLUMEUP	},
+	{ 0x35, KEY_VOLUMEDOWN	},
+	{ 0x3d, KEY_CHANNELDOWN	},
+	{ 0x3a, KEY_CHANNELUP	},
+	{ 0x2e, KEY_RECORD	},
+	{ 0x2b, KEY_PLAY	},
+	{ 0x13, KEY_PAUSE	},
+	{ 0x25, KEY_STOP	},
+
+	{ 0x1f, KEY_REWIND	},
+	{ 0x2d, KEY_FASTFORWARD	},
+	{ 0x1e, KEY_PREVIOUS	}, /* Replay |< */
+	{ 0x1d, KEY_NEXT	}, /* Skip   >| */
+
+	{ 0x0b, KEY_CAMERA	}, /* Capture */
+	{ 0x0f, KEY_LANGUAGE	}, /* SAP */
+	{ 0x18, KEY_MODE	}, /* PIP */
+	{ 0x12, KEY_ZOOM	}, /* Full screen */
+	{ 0x1c, KEY_SUBTITLE	},
+	{ 0x2f, KEY_MUTE	},
+	{ 0x16, KEY_F20		}, /* L/R */
+	{ 0x38, KEY_F21		}, /* Hibernate */
+
+	{ 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */
+	{ 0x31, KEY_AGAIN	}, /* Recall */
+	{ 0x1a, KEY_KPPLUS	}, /* Zoom+ */
+	{ 0x19, KEY_KPMINUS	}, /* Zoom- */
+	{ 0x27, KEY_RED		},
+	{ 0x0C, KEY_GREEN	},
+	{ 0x01, KEY_YELLOW	},
+	{ 0x00, KEY_BLUE	},
+};
+
+static struct rc_map_list ir_mantis_map = {
+	.map = {
+		.scan = mantis_ir_table,
+		.size = ARRAY_SIZE(mantis_ir_table),
+		.rc_type = RC_TYPE_UNKNOWN,
+		.name = RC_MAP_MANTIS,
+	}
+};
+
+int mantis_input_init(struct mantis_pci *mantis)
+{
+	struct rc_dev *dev;
+	int err;
+
+	err = rc_map_register(&ir_mantis_map);
+	if (err)
+		goto out;
+
+	dev = rc_allocate_device();
+	if (!dev) {
+		dprintk(MANTIS_ERROR, 1, "Remote device allocation failed");
+		err = -ENOMEM;
+		goto out_map;
+	}
+
+	sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name);
+	sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev));
+
+	dev->input_name         = mantis->input_name;
+	dev->input_phys         = mantis->input_phys;
+	dev->input_id.bustype   = BUS_PCI;
+	dev->input_id.vendor    = mantis->vendor_id;
+	dev->input_id.product   = mantis->device_id;
+	dev->input_id.version   = 1;
+	dev->driver_name        = MODULE_NAME;
+	dev->map_name           = RC_MAP_MANTIS;
+	dev->dev.parent         = &mantis->pdev->dev;
+
+	err = rc_register_device(dev);
+	if (err) {
+		dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err);
+		goto out_dev;
+	}
+
+	mantis->rc = dev;
+	return 0;
+
+out_dev:
+	rc_free_device(dev);
+out_map:
+	rc_map_unregister(&ir_mantis_map);
+out:
+	return err;
+}
+
+int mantis_exit(struct mantis_pci *mantis)
+{
+	rc_unregister_device(mantis->rc);
+	rc_map_unregister(&ir_mantis_map);
+	return 0;
+}
+
diff --git a/drivers/media/pci/mantis/mantis_ioc.c b/drivers/media/pci/mantis/mantis_ioc.c
new file mode 100644
index 000000000000..24fcdc63d6d5
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_ioc.c
@@ -0,0 +1,124 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_ioc.h"
+
+static int read_eeprom_bytes(struct mantis_pci *mantis, u8 reg, u8 *data, u8 length)
+{
+	struct i2c_adapter *adapter = &mantis->adapter;
+	int err;
+	u8 buf = reg;
+
+	struct i2c_msg msg[] = {
+		{ .addr = 0x50, .flags = 0, .buf = &buf, .len = 1 },
+		{ .addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = length },
+	};
+
+	err = i2c_transfer(adapter, msg, 2);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >",
+			err, data[0], data[1]);
+
+		return err;
+	}
+
+	return 0;
+}
+int mantis_get_mac(struct mantis_pci *mantis)
+{
+	int err;
+	u8 mac_addr[6] = {0};
+
+	err = read_eeprom_bytes(mantis, 0x08, mac_addr, 6);
+	if (err < 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Mantis EEPROM read error <%d>", err);
+
+		return err;
+	}
+
+	dprintk(MANTIS_ERROR, 0, "    MAC Address=[%pM]\n", mac_addr);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_get_mac);
+
+/* Turn the given bit on or off. */
+void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value)
+{
+	u32 cur;
+
+	dprintk(MANTIS_DEBUG, 1, "Set Bit <%d> to <%d>", bitpos, value);
+	cur = mmread(MANTIS_GPIF_ADDR);
+	if (value)
+		mantis->gpio_status = cur | (1 << bitpos);
+	else
+		mantis->gpio_status = cur & (~(1 << bitpos));
+
+	dprintk(MANTIS_DEBUG, 1, "GPIO Value <%02x>", mantis->gpio_status);
+	mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR);
+	mmwrite(0x00, MANTIS_GPIF_DOUT);
+}
+EXPORT_SYMBOL_GPL(mantis_gpio_set_bits);
+
+int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl)
+{
+	u32 reg;
+
+	reg = mmread(MANTIS_CONTROL);
+	switch (stream_ctl) {
+	case STREAM_TO_HIF:
+		dprintk(MANTIS_DEBUG, 1, "Set stream to HIF");
+		reg &= 0xff - MANTIS_BYPASS;
+		mmwrite(reg, MANTIS_CONTROL);
+		reg |= MANTIS_BYPASS;
+		mmwrite(reg, MANTIS_CONTROL);
+		break;
+
+	case STREAM_TO_CAM:
+		dprintk(MANTIS_DEBUG, 1, "Set stream to CAM");
+		reg |= MANTIS_BYPASS;
+		mmwrite(reg, MANTIS_CONTROL);
+		reg &= 0xff - MANTIS_BYPASS;
+		mmwrite(reg, MANTIS_CONTROL);
+		break;
+	default:
+		dprintk(MANTIS_ERROR, 1, "Unknown MODE <%02x>", stream_ctl);
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_stream_control);
diff --git a/drivers/media/pci/mantis/mantis_ioc.h b/drivers/media/pci/mantis/mantis_ioc.h
new file mode 100644
index 000000000000..d56e002b2955
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_ioc.h
@@ -0,0 +1,51 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_IOC_H
+#define __MANTIS_IOC_H
+
+#define GPIF_A00		0x00
+#define GPIF_A01		0x01
+#define GPIF_A02		0x02
+#define GPIF_A03		0x03
+#define GPIF_A04		0x04
+#define GPIF_A05		0x05
+#define GPIF_A06		0x06
+#define GPIF_A07		0x07
+#define GPIF_A08		0x08
+#define GPIF_A09		0x09
+#define GPIF_A10		0x0a
+#define GPIF_A11		0x0b
+
+#define GPIF_A12		0x0c
+#define GPIF_A13		0x0d
+#define GPIF_A14		0x0e
+
+enum mantis_stream_control {
+	STREAM_TO_HIF = 0,
+	STREAM_TO_CAM
+};
+
+extern int mantis_get_mac(struct mantis_pci *mantis);
+extern void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value);
+
+extern int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl);
+
+#endif /* __MANTIS_IOC_H */
diff --git a/drivers/media/pci/mantis/mantis_link.h b/drivers/media/pci/mantis/mantis_link.h
new file mode 100644
index 000000000000..2a814774a001
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_link.h
@@ -0,0 +1,83 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_LINK_H
+#define __MANTIS_LINK_H
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include "dvb_ca_en50221.h"
+
+enum mantis_sbuf_status {
+	MANTIS_SBUF_DATA_AVAIL		= 1,
+	MANTIS_SBUF_DATA_EMPTY		= 2,
+	MANTIS_SBUF_DATA_OVFLW		= 3
+};
+
+struct mantis_slot {
+	u32				timeout;
+	u32				slave_cfg;
+	u32				bar;
+};
+
+/* Physical layer */
+enum mantis_slot_state {
+	MODULE_INSERTED			= 3,
+	MODULE_XTRACTED			= 4
+};
+
+struct mantis_ca {
+	struct mantis_slot		slot[4];
+
+	struct work_struct		hif_evm_work;
+
+	u32				hif_event;
+	wait_queue_head_t		hif_opdone_wq;
+	wait_queue_head_t		hif_brrdyw_wq;
+	wait_queue_head_t		hif_data_wq;
+	wait_queue_head_t		hif_write_wq; /* HIF Write op */
+
+	enum mantis_sbuf_status		sbuf_status;
+
+	enum mantis_slot_state		slot_state;
+
+	void				*ca_priv;
+
+	struct dvb_ca_en50221		en50221;
+	struct mutex			ca_lock;
+};
+
+/* CA */
+extern void mantis_event_cam_plugin(struct mantis_ca *ca);
+extern void mantis_event_cam_unplug(struct mantis_ca *ca);
+extern int mantis_pcmcia_init(struct mantis_ca *ca);
+extern void mantis_pcmcia_exit(struct mantis_ca *ca);
+extern int mantis_evmgr_init(struct mantis_ca *ca);
+extern void mantis_evmgr_exit(struct mantis_ca *ca);
+
+/* HIF */
+extern int mantis_hif_init(struct mantis_ca *ca);
+extern void mantis_hif_exit(struct mantis_ca *ca);
+extern int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr);
+extern int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data);
+extern int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr);
+extern int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data);
+
+#endif /* __MANTIS_LINK_H */
diff --git a/drivers/media/pci/mantis/mantis_pci.c b/drivers/media/pci/mantis/mantis_pci.c
new file mode 100644
index 000000000000..371558af2d96
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_pci.c
@@ -0,0 +1,170 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+
+#include <asm/irq.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_pci.h"
+
+#define DRIVER_NAME		"Mantis Core"
+
+int __devinit mantis_pci_init(struct mantis_pci *mantis)
+{
+	u8 latency;
+	struct mantis_hwconfig *config	= mantis->hwconfig;
+	struct pci_dev *pdev		= mantis->pdev;
+	int err, ret = 0;
+
+	dprintk(MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n",
+		config->model_name,
+		config->dev_type,
+		mantis->pdev->bus->number,
+		PCI_SLOT(mantis->pdev->devfn),
+		PCI_FUNC(mantis->pdev->devfn));
+
+	err = pci_enable_device(pdev);
+	if (err != 0) {
+		ret = -ENODEV;
+		dprintk(MANTIS_ERROR, 1, "ERROR: PCI enable failed <%i>", err);
+		goto fail0;
+	}
+
+	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (err != 0) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: Unable to obtain 32 bit DMA <%i>", err);
+		ret = -ENOMEM;
+		goto fail1;
+	}
+
+	pci_set_master(pdev);
+
+	if (!request_mem_region(pci_resource_start(pdev, 0),
+				pci_resource_len(pdev, 0),
+				DRIVER_NAME)) {
+
+		dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 Request failed !");
+		ret = -ENODEV;
+		goto fail1;
+	}
+
+	mantis->mmio = ioremap(pci_resource_start(pdev, 0),
+			       pci_resource_len(pdev, 0));
+
+	if (!mantis->mmio) {
+		dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 remap failed !");
+		ret = -ENODEV;
+		goto fail2;
+	}
+
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency);
+	mantis->latency = latency;
+	mantis->revision = pdev->revision;
+
+	dprintk(MANTIS_ERROR, 0, "    Mantis Rev %d [%04x:%04x], ",
+		mantis->revision,
+		mantis->pdev->subsystem_vendor,
+		mantis->pdev->subsystem_device);
+
+	dprintk(MANTIS_ERROR, 0,
+		"irq: %d, latency: %d\n    memory: 0x%lx, mmio: 0x%p\n",
+		mantis->pdev->irq,
+		mantis->latency,
+		mantis->mantis_addr,
+		mantis->mmio);
+
+	err = request_irq(pdev->irq,
+			  config->irq_handler,
+			  IRQF_SHARED,
+			  DRIVER_NAME,
+			  mantis);
+
+	if (err != 0) {
+
+		dprintk(MANTIS_ERROR, 1, "ERROR: IRQ registration failed ! <%d>", err);
+		ret = -ENODEV;
+		goto fail3;
+	}
+
+	pci_set_drvdata(pdev, mantis);
+	return ret;
+
+	/* Error conditions */
+fail3:
+	dprintk(MANTIS_ERROR, 1, "ERROR: <%d> I/O unmap", ret);
+	if (mantis->mmio)
+		iounmap(mantis->mmio);
+
+fail2:
+	dprintk(MANTIS_ERROR, 1, "ERROR: <%d> releasing regions", ret);
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+
+fail1:
+	dprintk(MANTIS_ERROR, 1, "ERROR: <%d> disabling device", ret);
+	pci_disable_device(pdev);
+
+fail0:
+	dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret);
+	pci_set_drvdata(pdev, NULL);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mantis_pci_init);
+
+void mantis_pci_exit(struct mantis_pci *mantis)
+{
+	struct pci_dev *pdev = mantis->pdev;
+
+	dprintk(MANTIS_NOTICE, 1, " mem: 0x%p", mantis->mmio);
+	free_irq(pdev->irq, mantis);
+	if (mantis->mmio) {
+		iounmap(mantis->mmio);
+		release_mem_region(pci_resource_start(pdev, 0),
+				   pci_resource_len(pdev, 0));
+	}
+
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+EXPORT_SYMBOL_GPL(mantis_pci_exit);
+
+MODULE_DESCRIPTION("Mantis PCI DTV bridge driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/mantis/mantis_pci.h b/drivers/media/pci/mantis/mantis_pci.h
new file mode 100644
index 000000000000..65f004519086
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_pci.h
@@ -0,0 +1,27 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_PCI_H
+#define __MANTIS_PCI_H
+
+extern int mantis_pci_init(struct mantis_pci *mantis);
+extern void mantis_pci_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_PCI_H */
diff --git a/drivers/media/pci/mantis/mantis_pcmcia.c b/drivers/media/pci/mantis/mantis_pcmcia.c
new file mode 100644
index 000000000000..2f188c089666
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_pcmcia.c
@@ -0,0 +1,121 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_link.h" /* temporary due to physical layer stuff */
+#include "mantis_reg.h"
+
+/*
+ * If Slot state is already PLUG_IN event and we are called
+ * again, definitely it is jitter alone
+ */
+void mantis_event_cam_plugin(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	u32 gpif_irqcfg;
+
+	if (ca->slot_state == MODULE_XTRACTED) {
+		dprintk(MANTIS_DEBUG, 1, "Event: CAM Plugged IN: Adapter(%d) Slot(0)", mantis->num);
+		udelay(50);
+		mmwrite(0xda000000, MANTIS_CARD_RESET);
+		gpif_irqcfg  = mmread(MANTIS_GPIF_IRQCFG);
+		gpif_irqcfg |= MANTIS_MASK_PLUGOUT;
+		gpif_irqcfg &= ~MANTIS_MASK_PLUGIN;
+		mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG);
+		udelay(500);
+		ca->slot_state = MODULE_INSERTED;
+	}
+	udelay(100);
+}
+
+/*
+ * If Slot state is already UN_PLUG event and we are called
+ * again, definitely it is jitter alone
+ */
+void mantis_event_cam_unplug(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	u32 gpif_irqcfg;
+
+	if (ca->slot_state == MODULE_INSERTED) {
+		dprintk(MANTIS_DEBUG, 1, "Event: CAM Unplugged: Adapter(%d) Slot(0)", mantis->num);
+		udelay(50);
+		mmwrite(0x00da0000, MANTIS_CARD_RESET);
+		gpif_irqcfg  = mmread(MANTIS_GPIF_IRQCFG);
+		gpif_irqcfg |= MANTIS_MASK_PLUGIN;
+		gpif_irqcfg &= ~MANTIS_MASK_PLUGOUT;
+		mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG);
+		udelay(500);
+		ca->slot_state = MODULE_XTRACTED;
+	}
+	udelay(100);
+}
+
+int mantis_pcmcia_init(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	u32 gpif_stat, card_stat;
+
+	mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK);
+	gpif_stat = mmread(MANTIS_GPIF_STATUS);
+	card_stat = mmread(MANTIS_GPIF_IRQCFG);
+
+	if (gpif_stat & MANTIS_GPIF_DETSTAT) {
+		dprintk(MANTIS_DEBUG, 1, "CAM found on Adapter(%d) Slot(0)", mantis->num);
+		mmwrite(card_stat | MANTIS_MASK_PLUGOUT, MANTIS_GPIF_IRQCFG);
+		ca->slot_state = MODULE_INSERTED;
+		dvb_ca_en50221_camchange_irq(&ca->en50221,
+					     0,
+					     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+	} else {
+		dprintk(MANTIS_DEBUG, 1, "Empty Slot on Adapter(%d) Slot(0)", mantis->num);
+		mmwrite(card_stat | MANTIS_MASK_PLUGIN, MANTIS_GPIF_IRQCFG);
+		ca->slot_state = MODULE_XTRACTED;
+		dvb_ca_en50221_camchange_irq(&ca->en50221,
+					     0,
+					     DVB_CA_EN50221_CAMCHANGE_REMOVED);
+	}
+
+	return 0;
+}
+
+void mantis_pcmcia_exit(struct mantis_ca *ca)
+{
+	struct mantis_pci *mantis = ca->ca_priv;
+
+	mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS);
+	mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK);
+}
diff --git a/drivers/media/pci/mantis/mantis_reg.h b/drivers/media/pci/mantis/mantis_reg.h
new file mode 100644
index 000000000000..7761f9dc7fe0
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_reg.h
@@ -0,0 +1,197 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_REG_H
+#define __MANTIS_REG_H
+
+/* Interrupts */
+#define MANTIS_INT_STAT			0x00
+#define MANTIS_INT_MASK			0x04
+
+#define MANTIS_INT_RISCSTAT		(0x0f << 28)
+#define MANTIS_INT_RISCEN		(0x01 << 27)
+#define MANTIS_INT_I2CRACK		(0x01 << 26)
+
+/* #define MANTIS_INT_GPIF			(0xff << 12) */
+
+#define MANTIS_INT_PCMCIA7		(0x01 << 19)
+#define MANTIS_INT_PCMCIA6		(0x01 << 18)
+#define MANTIS_INT_PCMCIA5		(0x01 << 17)
+#define MANTIS_INT_PCMCIA4		(0x01 << 16)
+#define MANTIS_INT_PCMCIA3		(0x01 << 15)
+#define MANTIS_INT_PCMCIA2		(0x01 << 14)
+#define MANTIS_INT_PCMCIA1		(0x01 << 13)
+#define MANTIS_INT_PCMCIA0		(0x01 << 12)
+#define MANTIS_INT_IRQ1			(0x01 << 11)
+#define MANTIS_INT_IRQ0			(0x01 << 10)
+#define MANTIS_INT_OCERR		(0x01 <<  8)
+#define MANTIS_INT_PABORT		(0x01 <<  7)
+#define MANTIS_INT_RIPERR		(0x01 <<  6)
+#define MANTIS_INT_PPERR		(0x01 <<  5)
+#define MANTIS_INT_FTRGT		(0x01 <<  3)
+#define MANTIS_INT_RISCI		(0x01 <<  1)
+#define MANTIS_INT_I2CDONE		(0x01 <<  0)
+
+/* DMA */
+#define MANTIS_DMA_CTL			0x08
+#define MANTIS_GPIF_RD			(0xff << 24)
+#define MANTIS_GPIF_WR			(0xff << 16)
+#define MANTIS_CPU_DO			(0x01 << 10)
+#define MANTIS_DRV_DO			(0x01 <<  9)
+#define	MANTIS_I2C_RD			(0x01 <<  7)
+#define MANTIS_I2C_WR			(0x01 <<  6)
+#define MANTIS_DCAP_MODE		(0x01 <<  5)
+#define MANTIS_FIFO_TP_4		(0x00 <<  3)
+#define MANTIS_FIFO_TP_8		(0x01 <<  3)
+#define MANTIS_FIFO_TP_16		(0x02 <<  3)
+#define MANTIS_FIFO_EN			(0x01 <<  2)
+#define MANTIS_DCAP_EN			(0x01 <<  1)
+#define MANTIS_RISC_EN			(0x01 <<  0)
+
+/* DEBUG */
+#define MANTIS_DEBUGREG			0x0c
+#define MANTIS_DATINV			(0x0e <<  7)
+#define MANTIS_TOP_DEBUGSEL		(0x07 <<  4)
+#define MANTIS_PCMCIA_DEBUGSEL		(0x0f <<  0)
+
+#define MANTIS_RISC_START		0x10
+#define MANTIS_RISC_PC			0x14
+
+/* I2C */
+#define MANTIS_I2CDATA_CTL		0x18
+#define MANTIS_I2C_RATE_1		(0x00 <<  6)
+#define MANTIS_I2C_RATE_2		(0x01 <<  6)
+#define MANTIS_I2C_RATE_3		(0x02 <<  6)
+#define MANTIS_I2C_RATE_4		(0x03 <<  6)
+#define MANTIS_I2C_STOP			(0x01 <<  5)
+#define MANTIS_I2C_PGMODE		(0x01 <<  3)
+
+/* DATA */
+#define MANTIS_CMD_DATA_R1		0x20
+#define MANTIS_CMD_DATA_3		(0xff << 24)
+#define MANTIS_CMD_DATA_2		(0xff << 16)
+#define MANTIS_CMD_DATA_1		(0xff <<  8)
+#define MANTIS_CMD_DATA_0		(0xff <<  0)
+
+#define MANTIS_CMD_DATA_R2		0x24
+#define MANTIS_CMD_DATA_7		(0xff << 24)
+#define MANTIS_CMD_DATA_6		(0xff << 16)
+#define MANTIS_CMD_DATA_5		(0xff <<  8)
+#define MANTIS_CMD_DATA_4		(0xff <<  0)
+
+#define MANTIS_CONTROL			0x28
+#define MANTIS_DET			(0x01 <<  7)
+#define MANTIS_DAT_CF_EN		(0x01 <<  6)
+#define MANTIS_ACS			(0x03 <<  4)
+#define MANTIS_VCCEN			(0x01 <<  3)
+#define MANTIS_BYPASS			(0x01 <<  2)
+#define MANTIS_MRST			(0x01 <<  1)
+#define MANTIS_CRST_INT			(0x01 <<  0)
+
+#define MANTIS_GPIF_CFGSLA		0x84
+#define MANTIS_GPIF_WAITSMPL		(0x07 << 28)
+#define MANTIS_GPIF_BYTEADDRSUB		(0x01 << 25)
+#define MANTIS_GPIF_WAITPOL		(0x01 << 24)
+#define MANTIS_GPIF_NCDELAY		(0x07 << 20)
+#define MANTIS_GPIF_RW2CSDELAY		(0x07 << 16)
+#define MANTIS_GPIF_SLFTIMEDMODE	(0x01 << 15)
+#define MANTIS_GPIF_SLFTIMEDDELY	(0x7f <<  8)
+#define MANTIS_GPIF_DEVTYPE		(0x07 <<  4)
+#define MANTIS_GPIF_BIGENDIAN		(0x01 <<  3)
+#define MANTIS_GPIF_FETCHCMD		(0x03 <<  1)
+#define MANTIS_GPIF_HWORDDEV		(0x01 <<  0)
+
+#define MANTIS_GPIF_WSTOPER		0x90
+#define MANTIS_GPIF_WSTOPERWREN3	(0x01 << 31)
+#define MANTIS_GPIF_PARBOOTN		(0x01 << 29)
+#define MANTIS_GPIF_WSTOPERSLID3	(0x1f << 24)
+#define MANTIS_GPIF_WSTOPERWREN2	(0x01 << 23)
+#define MANTIS_GPIF_WSTOPERSLID2	(0x1f << 16)
+#define MANTIS_GPIF_WSTOPERWREN1	(0x01 << 15)
+#define MANTIS_GPIF_WSTOPERSLID1	(0x1f <<  8)
+#define MANTIS_GPIF_WSTOPERWREN0	(0x01 <<  7)
+#define MANTIS_GPIF_WSTOPERSLID0	(0x1f <<  0)
+
+#define MANTIS_GPIF_CS2RW		0x94
+#define MANTIS_GPIF_CS2RWWREN3		(0x01 << 31)
+#define MANTIS_GPIF_CS2RWDELY3		(0x3f << 24)
+#define MANTIS_GPIF_CS2RWWREN2		(0x01 << 23)
+#define MANTIS_GPIF_CS2RWDELY2		(0x3f << 16)
+#define MANTIS_GPIF_CS2RWWREN1		(0x01 << 15)
+#define MANTIS_GPIF_CS2RWDELY1		(0x3f <<  8)
+#define MANTIS_GPIF_CS2RWWREN0		(0x01 <<  7)
+#define MANTIS_GPIF_CS2RWDELY0		(0x3f <<  0)
+
+#define MANTIS_GPIF_IRQCFG		0x98
+#define MANTIS_GPIF_IRQPOL		(0x01 <<  8)
+#define MANTIS_MASK_WRACK		(0x01 <<  7)
+#define MANTIS_MASK_BRRDY		(0x01 <<  6)
+#define MANTIS_MASK_OVFLW		(0x01 <<  5)
+#define MANTIS_MASK_OTHERR		(0x01 <<  4)
+#define MANTIS_MASK_WSTO		(0x01 <<  3)
+#define MANTIS_MASK_EXTIRQ		(0x01 <<  2)
+#define MANTIS_MASK_PLUGIN		(0x01 <<  1)
+#define MANTIS_MASK_PLUGOUT		(0x01 <<  0)
+
+#define MANTIS_GPIF_STATUS		0x9c
+#define MANTIS_SBUF_KILLOP		(0x01 << 15)
+#define MANTIS_SBUF_OPDONE		(0x01 << 14)
+#define MANTIS_SBUF_EMPTY		(0x01 << 13)
+#define MANTIS_GPIF_DETSTAT		(0x01 <<  9)
+#define MANTIS_GPIF_INTSTAT		(0x01 <<  8)
+#define MANTIS_GPIF_WRACK		(0x01 <<  7)
+#define MANTIS_GPIF_BRRDY		(0x01 <<  6)
+#define MANTIS_SBUF_OVFLW		(0x01 <<  5)
+#define MANTIS_GPIF_OTHERR		(0x01 <<  4)
+#define MANTIS_SBUF_WSTO		(0x01 <<  3)
+#define MANTIS_GPIF_EXTIRQ		(0x01 <<  2)
+#define MANTIS_CARD_PLUGIN		(0x01 <<  1)
+#define MANTIS_CARD_PLUGOUT		(0x01 <<  0)
+
+#define MANTIS_GPIF_BRADDR		0xa0
+#define MANTIS_GPIF_PCMCIAREG		(0x01 		<< 27)
+#define MANTIS_GPIF_PCMCIAIOM		(0x01 		<< 26)
+#define MANTIS_GPIF_BR_ADDR		(0xfffffff	<<  0)
+
+#define MANTIS_GPIF_BRBYTES		0xa4
+#define MANTIS_GPIF_BRCNT		(0xfff 		<<  0)
+
+#define MANTIS_PCMCIA_RESET		0xa8
+#define MANTIS_PCMCIA_RSTVAL		(0xff << 0)
+
+#define MANTIS_CARD_RESET		0xac
+
+#define MANTIS_GPIF_ADDR		0xb0
+#define MANTIS_GPIF_HIFRDWRN		(0x01		<< 31)
+#define MANTIS_GPIF_PCMCIAREG		(0x01		<< 27)
+#define MANTIS_GPIF_PCMCIAIOM		(0x01		<< 26)
+#define MANTIS_GPIF_HIFADDR		(0xfffffff	<<  0)
+
+#define MANTIS_GPIF_DOUT		0xb4
+#define MANTIS_GPIF_HIFDOUT		(0xfffffff	<<  0)
+
+#define MANTIS_GPIF_DIN			0xb8
+#define MANTIS_GPIF_HIFDIN		(0xfffffff	<<  0)
+
+#define MANTIS_GPIF_SPARE		0xbc
+#define MANTIS_GPIF_LOGICRD		(0xffff		<< 16)
+#define MANTIS_GPIF_LOGICRW		(0xffff		<<  0)
+
+#endif /* __MANTIS_REG_H */
diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c
new file mode 100644
index 000000000000..85e977861b4a
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_uart.c
@@ -0,0 +1,188 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_reg.h"
+#include "mantis_uart.h"
+
+struct mantis_uart_params {
+	enum mantis_baud	baud_rate;
+	enum mantis_parity	parity;
+};
+
+static struct {
+	char string[7];
+} rates[5] = {
+	{ "9600" },
+	{ "19200" },
+	{ "38400" },
+	{ "57600" },
+	{ "115200" }
+};
+
+static struct {
+	char string[5];
+} parity[3] = {
+	{ "NONE" },
+	{ "ODD" },
+	{ "EVEN" }
+};
+
+#define UART_MAX_BUF			16
+
+int mantis_uart_read(struct mantis_pci *mantis, u8 *data)
+{
+	struct mantis_hwconfig *config = mantis->hwconfig;
+	u32 stat = 0, i;
+
+	/* get data */
+	for (i = 0; i < (config->bytes + 1); i++) {
+
+		stat = mmread(MANTIS_UART_STAT);
+
+		if (stat & MANTIS_UART_RXFIFO_FULL) {
+			dprintk(MANTIS_ERROR, 1, "RX Fifo FULL");
+		}
+		data[i] = mmread(MANTIS_UART_RXD) & 0x3f;
+
+		dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f);
+
+		if (data[i] & (1 << 7)) {
+			dprintk(MANTIS_ERROR, 1, "UART framing error");
+			return -EINVAL;
+		}
+		if (data[i] & (1 << 6)) {
+			dprintk(MANTIS_ERROR, 1, "UART parity error");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void mantis_uart_work(struct work_struct *work)
+{
+	struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work);
+	struct mantis_hwconfig *config = mantis->hwconfig;
+	u8 buf[16];
+	int i;
+
+	mantis_uart_read(mantis, buf);
+
+	for (i = 0; i < (config->bytes + 1); i++)
+		dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]);
+
+	dprintk(MANTIS_DEBUG, 0, "\n");
+}
+
+static int mantis_uart_setup(struct mantis_pci *mantis,
+			     struct mantis_uart_params *params)
+{
+	u32 reg;
+
+	mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL);
+
+	reg = mmread(MANTIS_UART_BAUD);
+
+	switch (params->baud_rate) {
+	case MANTIS_BAUD_9600:
+		reg |= 0xd8;
+		break;
+	case MANTIS_BAUD_19200:
+		reg |= 0x6c;
+		break;
+	case MANTIS_BAUD_38400:
+		reg |= 0x36;
+		break;
+	case MANTIS_BAUD_57600:
+		reg |= 0x23;
+		break;
+	case MANTIS_BAUD_115200:
+		reg |= 0x11;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mmwrite(reg, MANTIS_UART_BAUD);
+
+	return 0;
+}
+
+int mantis_uart_init(struct mantis_pci *mantis)
+{
+	struct mantis_hwconfig *config = mantis->hwconfig;
+	struct mantis_uart_params params;
+
+	/* default parity: */
+	params.baud_rate = config->baud_rate;
+	params.parity = config->parity;
+	dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s",
+		rates[params.baud_rate].string,
+		parity[params.parity].string);
+
+	init_waitqueue_head(&mantis->uart_wq);
+	spin_lock_init(&mantis->uart_lock);
+
+	INIT_WORK(&mantis->uart_work, mantis_uart_work);
+
+	/* disable interrupt */
+	mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL);
+
+	mantis_uart_setup(mantis, &params);
+
+	/* default 1 byte */
+	mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD);
+
+	/* flush buffer */
+	mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL);
+
+	/* enable interrupt */
+	mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK);
+	mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL);
+
+	schedule_work(&mantis->uart_work);
+	dprintk(MANTIS_DEBUG, 1, "UART successfully initialized");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_uart_init);
+
+void mantis_uart_exit(struct mantis_pci *mantis)
+{
+	/* disable interrupt */
+	mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL);
+	flush_work(&mantis->uart_work);
+}
+EXPORT_SYMBOL_GPL(mantis_uart_exit);
diff --git a/drivers/media/pci/mantis/mantis_uart.h b/drivers/media/pci/mantis/mantis_uart.h
new file mode 100644
index 000000000000..ffb62a0a5a13
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_uart.h
@@ -0,0 +1,58 @@
+/*
+	Mantis PCI bridge driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_UART_H
+#define __MANTIS_UART_H
+
+#define MANTIS_UART_CTL			0xe0
+#define MANTIS_UART_RXINT		(1 << 4)
+#define MANTIS_UART_RXFLUSH		(1 << 2)
+
+#define MANTIS_UART_RXD			0xe8
+#define MANTIS_UART_BAUD		0xec
+
+#define MANTIS_UART_STAT		0xf0
+#define MANTIS_UART_RXFIFO_DATA		(1 << 7)
+#define MANTIS_UART_RXFIFO_EMPTY	(1 << 6)
+#define MANTIS_UART_RXFIFO_FULL		(1 << 3)
+#define MANTIS_UART_FRAME_ERR		(1 << 2)
+#define MANTIS_UART_PARITY_ERR		(1 << 1)
+#define MANTIS_UART_RXTHRESH_INT	(1 << 0)
+
+enum mantis_baud {
+	MANTIS_BAUD_9600	= 0,
+	MANTIS_BAUD_19200,
+	MANTIS_BAUD_38400,
+	MANTIS_BAUD_57600,
+	MANTIS_BAUD_115200
+};
+
+enum mantis_parity {
+	MANTIS_PARITY_NONE	= 0,
+	MANTIS_PARITY_EVEN,
+	MANTIS_PARITY_ODD,
+};
+
+struct mantis_pci;
+
+extern int mantis_uart_init(struct mantis_pci *mantis);
+extern void mantis_uart_exit(struct mantis_pci *mantis);
+
+#endif /* __MANTIS_UART_H */
diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c
new file mode 100644
index 000000000000..ad013e93ed11
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1033.c
@@ -0,0 +1,212 @@
+/*
+	Mantis VP-1033 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "stv0299.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp1033.h"
+#include "mantis_reg.h"
+
+u8 lgtdqcs001f_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x2a,
+	0x05, 0x85,
+	0x06, 0x02,
+	0x07, 0x00,
+	0x08, 0x00,
+	0x0c, 0x01,
+	0x0d, 0x81,
+	0x0e, 0x44,
+	0x0f, 0x94,
+	0x10, 0x3c,
+	0x11, 0x84,
+	0x12, 0xb9,
+	0x13, 0xb5,
+	0x14, 0x4f,
+	0x15, 0xc9,
+	0x16, 0x80,
+	0x17, 0x36,
+	0x18, 0xfb,
+	0x19, 0xcf,
+	0x1a, 0xbc,
+	0x1c, 0x2b,
+	0x1d, 0x27,
+	0x1e, 0x00,
+	0x1f, 0x0b,
+	0x20, 0xa1,
+	0x21, 0x60,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,
+	0x29, 0x28,
+	0x2a, 0x14,
+	0x2b, 0x0f,
+	0x2c, 0x09,
+	0x2d, 0x05,
+	0x31, 0x1f,
+	0x32, 0x19,
+	0x33, 0xfc,
+	0x34, 0x13,
+	0xff, 0xff,
+};
+
+#define MANTIS_MODEL_NAME	"VP-1033"
+#define MANTIS_DEV_TYPE		"DVB-S/DSS"
+
+int lgtdqcs001f_tuner_set(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct mantis_pci *mantis	= fe->dvb->priv;
+	struct i2c_adapter *adapter	= &mantis->adapter;
+
+	u8 buf[4];
+	u32 div;
+
+
+	struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf)};
+
+	div = p->frequency / 250;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] =  div & 0xff;
+	buf[2] =  0x83;
+	buf[3] =  0xc0;
+
+	if (p->frequency < 1531000)
+		buf[3] |= 0x04;
+	else
+		buf[3] &= ~0x04;
+	if (i2c_transfer(adapter, &msg, 1) < 0) {
+		dprintk(MANTIS_ERROR, 1, "Write: I2C Transfer failed");
+		return -EIO;
+	}
+	msleep_interruptible(100);
+
+	return 0;
+}
+
+int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe,
+				u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) {
+		aclk = 0xb7;
+		bclk = 0x47;
+	} else if (srate < 3000000) {
+		aclk = 0xb7;
+		bclk = 0x4b;
+	} else if (srate < 7000000) {
+		aclk = 0xb7;
+		bclk = 0x4f;
+	} else if (srate < 14000000) {
+		aclk = 0xb7;
+		bclk = 0x53;
+	} else if (srate < 30000000) {
+		aclk = 0xb6;
+		bclk = 0x53;
+	} else if (srate < 45000000) {
+		aclk = 0xb4;
+		bclk = 0x51;
+	}
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg(fe, 0x21,  ratio & 0xf0);
+
+	return 0;
+}
+
+struct stv0299_config lgtdqcs001f_config = {
+	.demod_address		= 0x68,
+	.inittab		= lgtdqcs001f_inittab,
+	.mclk			= 88000000UL,
+	.invert			= 0,
+	.skip_reinit		= 0,
+	.volt13_op0_op1		= STV0299_VOLT13_OP0,
+	.min_delay_ms		= 100,
+	.set_symbol_rate	= lgtdqcs001f_set_symbol_rate,
+};
+
+static int vp1033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter	= &mantis->adapter;
+
+	int err = 0;
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		mantis_frontend_soft_reset(mantis);
+		msleep(250);
+
+		dprintk(MANTIS_ERROR, 1, "Probing for STV0299 (DVB-S)");
+		fe = dvb_attach(stv0299_attach, &lgtdqcs001f_config, adapter);
+
+		if (fe) {
+			fe->ops.tuner_ops.set_params = lgtdqcs001f_tuner_set;
+			dprintk(MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x",
+				lgtdqcs001f_config.demod_address);
+
+			dprintk(MANTIS_ERROR, 1, "Mantis DVB-S STV0299 frontend attach success");
+		} else {
+			return -1;
+		}
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+	mantis->fe = fe;
+	dprintk(MANTIS_ERROR, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp1033_config = {
+	.model_name		= MANTIS_MODEL_NAME,
+	.dev_type		= MANTIS_DEV_TYPE,
+	.ts_size		= MANTIS_TS_204,
+
+	.baud_rate		= MANTIS_BAUD_9600,
+	.parity			= MANTIS_PARITY_NONE,
+	.bytes			= 0,
+
+	.frontend_init		= vp1033_frontend_init,
+	.power			= GPIF_A12,
+	.reset			= GPIF_A13,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp1033.h b/drivers/media/pci/mantis/mantis_vp1033.h
new file mode 100644
index 000000000000..7daaa1bf127d
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1033.h
@@ -0,0 +1,30 @@
+/*
+	Mantis VP-1033 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP1033_H
+#define __MANTIS_VP1033_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_1033_DVB_S	0x0016
+
+extern struct mantis_hwconfig vp1033_config;
+
+#endif /* __MANTIS_VP1033_H */
diff --git a/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c
new file mode 100644
index 000000000000..430ae84ce528
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1034.c
@@ -0,0 +1,120 @@
+/*
+	Mantis VP-1034 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mb86a16.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp1034.h"
+#include "mantis_reg.h"
+
+struct mb86a16_config vp1034_mb86a16_config = {
+	.demod_address	= 0x08,
+	.set_voltage	= vp1034_set_voltage,
+};
+
+#define MANTIS_MODEL_NAME	"VP-1034"
+#define MANTIS_DEV_TYPE		"DVB-S/DSS"
+
+int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct mantis_pci *mantis = fe->dvb->priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk(MANTIS_ERROR, 1, "Polarization=[13V]");
+		mantis_gpio_set_bits(mantis, 13, 1);
+		mantis_gpio_set_bits(mantis, 14, 0);
+		break;
+	case SEC_VOLTAGE_18:
+		dprintk(MANTIS_ERROR, 1, "Polarization=[18V]");
+		mantis_gpio_set_bits(mantis, 13, 1);
+		mantis_gpio_set_bits(mantis, 14, 1);
+		break;
+	case SEC_VOLTAGE_OFF:
+		dprintk(MANTIS_ERROR, 1, "Frontend (dummy) POWERDOWN");
+		break;
+	default:
+		dprintk(MANTIS_ERROR, 1, "Invalid = (%d)", (u32) voltage);
+		return -EINVAL;
+	}
+	mmwrite(0x00, MANTIS_GPIF_DOUT);
+
+	return 0;
+}
+
+static int vp1034_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter	= &mantis->adapter;
+
+	int err = 0;
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		mantis_frontend_soft_reset(mantis);
+		msleep(250);
+
+		dprintk(MANTIS_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)");
+		fe = dvb_attach(mb86a16_attach, &vp1034_mb86a16_config, adapter);
+		if (fe) {
+			dprintk(MANTIS_ERROR, 1,
+			"found MB86A16 DVB-S/DSS frontend @0x%02x",
+			vp1034_mb86a16_config.demod_address);
+
+		} else {
+			return -1;
+		}
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+	mantis->fe = fe;
+	dprintk(MANTIS_ERROR, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp1034_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_204,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp1034_frontend_init,
+	.power		= GPIF_A12,
+	.reset		= GPIF_A13,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp1034.h b/drivers/media/pci/mantis/mantis_vp1034.h
new file mode 100644
index 000000000000..323f38ef8e3d
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1034.h
@@ -0,0 +1,33 @@
+/*
+	Mantis VP-1034 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP1034_H
+#define __MANTIS_VP1034_H
+
+#include "dvb_frontend.h"
+#include "mantis_common.h"
+
+
+#define MANTIS_VP_1034_DVB_S	0x0014
+
+extern struct mantis_hwconfig vp1034_config;
+extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+
+#endif /* __MANTIS_VP1034_H */
diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c
new file mode 100644
index 000000000000..07aa887a4b4a
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1041.c
@@ -0,0 +1,357 @@
+/*
+	Mantis VP-1041 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp1041.h"
+#include "stb0899_reg.h"
+#include "stb0899_drv.h"
+#include "stb0899_cfg.h"
+#include "stb6100_cfg.h"
+#include "stb6100.h"
+#include "lnbp21.h"
+
+#define MANTIS_MODEL_NAME	"VP-1041"
+#define MANTIS_DEV_TYPE		"DSS/DVB-S/DVB-S2"
+
+static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = {
+
+	/* 0x0000000b, *//* SYSREG */
+	{ STB0899_DEV_ID		, 0x30 },
+	{ STB0899_DISCNTRL1		, 0x32 },
+	{ STB0899_DISCNTRL2     	, 0x80 },
+	{ STB0899_DISRX_ST0     	, 0x04 },
+	{ STB0899_DISRX_ST1     	, 0x00 },
+	{ STB0899_DISPARITY     	, 0x00 },
+	{ STB0899_DISSTATUS		, 0x20 },
+	{ STB0899_DISF22        	, 0x99 },
+	{ STB0899_DISF22RX      	, 0xa8 },
+	/* SYSREG ? */
+	{ STB0899_ACRPRESC      	, 0x11 },
+	{ STB0899_ACRDIV1       	, 0x0a },
+	{ STB0899_ACRDIV2       	, 0x05 },
+	{ STB0899_DACR1         	, 0x00 },
+	{ STB0899_DACR2         	, 0x00 },
+	{ STB0899_OUTCFG        	, 0x00 },
+	{ STB0899_MODECFG       	, 0x00 },
+	{ STB0899_IRQSTATUS_3		, 0xfe },
+	{ STB0899_IRQSTATUS_2		, 0x03 },
+	{ STB0899_IRQSTATUS_1		, 0x7c },
+	{ STB0899_IRQSTATUS_0		, 0xf4 },
+	{ STB0899_IRQMSK_3      	, 0xf3 },
+	{ STB0899_IRQMSK_2      	, 0xfc },
+	{ STB0899_IRQMSK_1      	, 0xff },
+	{ STB0899_IRQMSK_0		, 0xff },
+	{ STB0899_IRQCFG		, 0x00 },
+	{ STB0899_I2CCFG        	, 0x88 },
+	{ STB0899_I2CRPT        	, 0x58 },
+	{ STB0899_IOPVALUE5		, 0x00 },
+	{ STB0899_IOPVALUE4		, 0x33 },
+	{ STB0899_IOPVALUE3		, 0x6d },
+	{ STB0899_IOPVALUE2		, 0x90 },
+	{ STB0899_IOPVALUE1		, 0x60 },
+	{ STB0899_IOPVALUE0		, 0x00 },
+	{ STB0899_GPIO00CFG     	, 0x82 },
+	{ STB0899_GPIO01CFG     	, 0x82 },
+	{ STB0899_GPIO02CFG     	, 0x82 },
+	{ STB0899_GPIO03CFG     	, 0x82 },
+	{ STB0899_GPIO04CFG     	, 0x82 },
+	{ STB0899_GPIO05CFG     	, 0x82 },
+	{ STB0899_GPIO06CFG     	, 0x82 },
+	{ STB0899_GPIO07CFG     	, 0x82 },
+	{ STB0899_GPIO08CFG     	, 0x82 },
+	{ STB0899_GPIO09CFG     	, 0x82 },
+	{ STB0899_GPIO10CFG     	, 0x82 },
+	{ STB0899_GPIO11CFG     	, 0x82 },
+	{ STB0899_GPIO12CFG     	, 0x82 },
+	{ STB0899_GPIO13CFG     	, 0x82 },
+	{ STB0899_GPIO14CFG     	, 0x82 },
+	{ STB0899_GPIO15CFG     	, 0x82 },
+	{ STB0899_GPIO16CFG     	, 0x82 },
+	{ STB0899_GPIO17CFG     	, 0x82 },
+	{ STB0899_GPIO18CFG     	, 0x82 },
+	{ STB0899_GPIO19CFG     	, 0x82 },
+	{ STB0899_GPIO20CFG     	, 0x82 },
+	{ STB0899_SDATCFG       	, 0xb8 },
+	{ STB0899_SCLTCFG       	, 0xba },
+	{ STB0899_AGCRFCFG      	, 0x1c }, /* 0x11 */
+	{ STB0899_GPIO22        	, 0x82 }, /* AGCBB2CFG */
+	{ STB0899_GPIO21        	, 0x91 }, /* AGCBB1CFG */
+	{ STB0899_DIRCLKCFG     	, 0x82 },
+	{ STB0899_CLKOUT27CFG   	, 0x7e },
+	{ STB0899_STDBYCFG      	, 0x82 },
+	{ STB0899_CS0CFG        	, 0x82 },
+	{ STB0899_CS1CFG        	, 0x82 },
+	{ STB0899_DISEQCOCFG    	, 0x20 },
+	{ STB0899_GPIO32CFG		, 0x82 },
+	{ STB0899_GPIO33CFG		, 0x82 },
+	{ STB0899_GPIO34CFG		, 0x82 },
+	{ STB0899_GPIO35CFG		, 0x82 },
+	{ STB0899_GPIO36CFG		, 0x82 },
+	{ STB0899_GPIO37CFG		, 0x82 },
+	{ STB0899_GPIO38CFG		, 0x82 },
+	{ STB0899_GPIO39CFG		, 0x82 },
+	{ STB0899_NCOARSE       	, 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+	{ STB0899_SYNTCTRL      	, 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+	{ STB0899_FILTCTRL      	, 0x00 },
+	{ STB0899_SYSCTRL       	, 0x01 },
+	{ STB0899_STOPCLK1      	, 0x20 },
+	{ STB0899_STOPCLK2      	, 0x00 },
+	{ STB0899_INTBUFSTATUS		, 0x00 },
+	{ STB0899_INTBUFCTRL    	, 0x0a },
+	{ 0xffff			, 0xff },
+};
+
+static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = {
+	{ STB0899_DEMOD         	, 0x00 },
+	{ STB0899_RCOMPC        	, 0xc9 },
+	{ STB0899_AGC1CN        	, 0x01 },
+	{ STB0899_AGC1REF       	, 0x10 },
+	{ STB0899_RTC			, 0x23 },
+	{ STB0899_TMGCFG        	, 0x4e },
+	{ STB0899_AGC2REF       	, 0x34 },
+	{ STB0899_TLSR          	, 0x84 },
+	{ STB0899_CFD           	, 0xf7 },
+	{ STB0899_ACLC			, 0x87 },
+	{ STB0899_BCLC          	, 0x94 },
+	{ STB0899_EQON          	, 0x41 },
+	{ STB0899_LDT           	, 0xf1 },
+	{ STB0899_LDT2          	, 0xe3 },
+	{ STB0899_EQUALREF      	, 0xb4 },
+	{ STB0899_TMGRAMP       	, 0x10 },
+	{ STB0899_TMGTHD        	, 0x30 },
+	{ STB0899_IDCCOMP		, 0xfd },
+	{ STB0899_QDCCOMP		, 0xff },
+	{ STB0899_POWERI		, 0x0c },
+	{ STB0899_POWERQ		, 0x0f },
+	{ STB0899_RCOMP			, 0x6c },
+	{ STB0899_AGCIQIN		, 0x80 },
+	{ STB0899_AGC2I1		, 0x06 },
+	{ STB0899_AGC2I2		, 0x00 },
+	{ STB0899_TLIR			, 0x30 },
+	{ STB0899_RTF			, 0x7f },
+	{ STB0899_DSTATUS		, 0x00 },
+	{ STB0899_LDI			, 0xbc },
+	{ STB0899_CFRM			, 0xea },
+	{ STB0899_CFRL			, 0x31 },
+	{ STB0899_NIRM			, 0x2b },
+	{ STB0899_NIRL			, 0x80 },
+	{ STB0899_ISYMB			, 0x1d },
+	{ STB0899_QSYMB			, 0xa6 },
+	{ STB0899_SFRH          	, 0x2f },
+	{ STB0899_SFRM          	, 0x68 },
+	{ STB0899_SFRL          	, 0x40 },
+	{ STB0899_SFRUPH        	, 0x2f },
+	{ STB0899_SFRUPM        	, 0x68 },
+	{ STB0899_SFRUPL        	, 0x40 },
+	{ STB0899_EQUAI1		, 0x02 },
+	{ STB0899_EQUAQ1		, 0xff },
+	{ STB0899_EQUAI2		, 0x04 },
+	{ STB0899_EQUAQ2		, 0x05 },
+	{ STB0899_EQUAI3		, 0x02 },
+	{ STB0899_EQUAQ3		, 0xfd },
+	{ STB0899_EQUAI4		, 0x03 },
+	{ STB0899_EQUAQ4		, 0x07 },
+	{ STB0899_EQUAI5		, 0x08 },
+	{ STB0899_EQUAQ5		, 0xf5 },
+	{ STB0899_DSTATUS2		, 0x00 },
+	{ STB0899_VSTATUS       	, 0x00 },
+	{ STB0899_VERROR		, 0x86 },
+	{ STB0899_IQSWAP		, 0x2a },
+	{ STB0899_ECNT1M		, 0x00 },
+	{ STB0899_ECNT1L		, 0x00 },
+	{ STB0899_ECNT2M		, 0x00 },
+	{ STB0899_ECNT2L		, 0x00 },
+	{ STB0899_ECNT3M		, 0x0a },
+	{ STB0899_ECNT3L		, 0xad },
+	{ STB0899_FECAUTO1      	, 0x06 },
+	{ STB0899_FECM			, 0x01 },
+	{ STB0899_VTH12         	, 0xb0 },
+	{ STB0899_VTH23         	, 0x7a },
+	{ STB0899_VTH34			, 0x58 },
+	{ STB0899_VTH56         	, 0x38 },
+	{ STB0899_VTH67         	, 0x34 },
+	{ STB0899_VTH78         	, 0x24 },
+	{ STB0899_PRVIT         	, 0xff },
+	{ STB0899_VITSYNC       	, 0x19 },
+	{ STB0899_RSULC         	, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+	{ STB0899_TSULC         	, 0x42 },
+	{ STB0899_RSLLC         	, 0x41 },
+	{ STB0899_TSLPL			, 0x12 },
+	{ STB0899_TSCFGH        	, 0x0c },
+	{ STB0899_TSCFGM        	, 0x00 },
+	{ STB0899_TSCFGL        	, 0x00 },
+	{ STB0899_TSOUT			, 0x69 }, /* 0x0d for CAM */
+	{ STB0899_RSSYNCDEL     	, 0x00 },
+	{ STB0899_TSINHDELH     	, 0x02 },
+	{ STB0899_TSINHDELM		, 0x00 },
+	{ STB0899_TSINHDELL		, 0x00 },
+	{ STB0899_TSLLSTKM		, 0x1b },
+	{ STB0899_TSLLSTKL		, 0xb3 },
+	{ STB0899_TSULSTKM		, 0x00 },
+	{ STB0899_TSULSTKL		, 0x00 },
+	{ STB0899_PCKLENUL		, 0xbc },
+	{ STB0899_PCKLENLL		, 0xcc },
+	{ STB0899_RSPCKLEN		, 0xbd },
+	{ STB0899_TSSTATUS		, 0x90 },
+	{ STB0899_ERRCTRL1      	, 0xb6 },
+	{ STB0899_ERRCTRL2      	, 0x95 },
+	{ STB0899_ERRCTRL3      	, 0x8d },
+	{ STB0899_DMONMSK1		, 0x27 },
+	{ STB0899_DMONMSK0		, 0x03 },
+	{ STB0899_DEMAPVIT      	, 0x5c },
+	{ STB0899_PLPARM		, 0x19 },
+	{ STB0899_PDELCTRL      	, 0x48 },
+	{ STB0899_PDELCTRL2     	, 0x00 },
+	{ STB0899_BBHCTRL1      	, 0x00 },
+	{ STB0899_BBHCTRL2      	, 0x00 },
+	{ STB0899_HYSTTHRESH    	, 0x77 },
+	{ STB0899_MATCSTM		, 0x00 },
+	{ STB0899_MATCSTL		, 0x00 },
+	{ STB0899_UPLCSTM		, 0x00 },
+	{ STB0899_UPLCSTL		, 0x00 },
+	{ STB0899_DFLCSTM		, 0x00 },
+	{ STB0899_DFLCSTL		, 0x00 },
+	{ STB0899_SYNCCST		, 0x00 },
+	{ STB0899_SYNCDCSTM		, 0x00 },
+	{ STB0899_SYNCDCSTL		, 0x00 },
+	{ STB0899_ISI_ENTRY		, 0x00 },
+	{ STB0899_ISI_BIT_EN		, 0x00 },
+	{ STB0899_MATSTRM		, 0xf0 },
+	{ STB0899_MATSTRL		, 0x02 },
+	{ STB0899_UPLSTRM		, 0x45 },
+	{ STB0899_UPLSTRL		, 0x60 },
+	{ STB0899_DFLSTRM		, 0xe3 },
+	{ STB0899_DFLSTRL		, 0x00 },
+	{ STB0899_SYNCSTR		, 0x47 },
+	{ STB0899_SYNCDSTRM		, 0x05 },
+	{ STB0899_SYNCDSTRL		, 0x18 },
+	{ STB0899_CFGPDELSTATUS1	, 0x19 },
+	{ STB0899_CFGPDELSTATUS2	, 0x2b },
+	{ STB0899_BBFERRORM		, 0x00 },
+	{ STB0899_BBFERRORL		, 0x01 },
+	{ STB0899_UPKTERRORM		, 0x00 },
+	{ STB0899_UPKTERRORL		, 0x00 },
+	{ 0xffff			, 0xff },
+};
+
+struct stb0899_config vp1041_stb0899_config = {
+	.init_dev		= vp1041_stb0899_s1_init_1,
+	.init_s2_demod		= stb0899_s2_init_2,
+	.init_s1_demod		= vp1041_stb0899_s1_init_3,
+	.init_s2_fec		= stb0899_s2_init_4,
+	.init_tst		= stb0899_s1_init_5,
+
+	.demod_address 		= 0x68, /*  0xd0 >> 1 */
+
+	.xtal_freq		= 27000000,
+	.inversion		= IQ_SWAP_ON, /* 1 */
+
+	.lo_clk			= 76500000,
+	.hi_clk			= 99000000,
+
+	.esno_ave		= STB0899_DVBS2_ESNO_AVE,
+	.esno_quant		= STB0899_DVBS2_ESNO_QUANT,
+	.avframes_coarse	= STB0899_DVBS2_AVFRAMES_COARSE,
+	.avframes_fine		= STB0899_DVBS2_AVFRAMES_FINE,
+	.miss_threshold		= STB0899_DVBS2_MISS_THRESHOLD,
+	.uwp_threshold_acq	= STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+	.uwp_threshold_track	= STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+	.uwp_threshold_sof	= STB0899_DVBS2_UWP_THRESHOLD_SOF,
+	.sof_search_timeout	= STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+	.btr_nco_bits		= STB0899_DVBS2_BTR_NCO_BITS,
+	.btr_gain_shift_offset	= STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+	.crl_nco_bits		= STB0899_DVBS2_CRL_NCO_BITS,
+	.ldpc_max_iter		= STB0899_DVBS2_LDPC_MAX_ITER,
+
+	.tuner_get_frequency	= stb6100_get_frequency,
+	.tuner_set_frequency	= stb6100_set_frequency,
+	.tuner_set_bandwidth	= stb6100_set_bandwidth,
+	.tuner_get_bandwidth	= stb6100_get_bandwidth,
+	.tuner_set_rfsiggain	= NULL,
+};
+
+struct stb6100_config vp1041_stb6100_config = {
+	.tuner_address	= 0x60,
+	.refclock	= 27000000,
+};
+
+static int vp1041_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter	= &mantis->adapter;
+
+	int err = 0;
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		mantis_frontend_soft_reset(mantis);
+		msleep(250);
+		mantis->fe = dvb_attach(stb0899_attach, &vp1041_stb0899_config, adapter);
+		if (mantis->fe) {
+			dprintk(MANTIS_ERROR, 1,
+				"found STB0899 DVB-S/DVB-S2 frontend @0x%02x",
+				vp1041_stb0899_config.demod_address);
+
+			if (dvb_attach(stb6100_attach, mantis->fe, &vp1041_stb6100_config, adapter)) {
+				if (!dvb_attach(lnbp21_attach, mantis->fe, adapter, 0, 0))
+					dprintk(MANTIS_ERROR, 1, "No LNBP21 found!");
+			}
+		} else {
+			return -EREMOTEIO;
+		}
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+
+
+	dprintk(MANTIS_ERROR, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp1041_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_188,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp1041_frontend_init,
+	.power		= GPIF_A12,
+	.reset		= GPIF_A13,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp1041.h b/drivers/media/pci/mantis/mantis_vp1041.h
new file mode 100644
index 000000000000..1ae5b3de8081
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp1041.h
@@ -0,0 +1,33 @@
+/*
+	Mantis VP-1041 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP1041_H
+#define __MANTIS_VP1041_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_1041_DVB_S2	0x0031
+#define SKYSTAR_HD2_10		0x0001
+#define SKYSTAR_HD2_20		0x0003
+#define CINERGY_S2_PCI_HD	0x1179
+
+extern struct mantis_hwconfig vp1041_config;
+
+#endif /* __MANTIS_VP1041_H */
diff --git a/drivers/media/pci/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c
new file mode 100644
index 000000000000..1ca6837fbe46
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp2033.c
@@ -0,0 +1,188 @@
+/*
+	Mantis VP-2033 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "tda1002x.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp2033.h"
+
+#define MANTIS_MODEL_NAME	"VP-2033"
+#define MANTIS_DEV_TYPE		"DVB-C"
+
+struct tda1002x_config vp2033_tda1002x_cu1216_config = {
+	.demod_address = 0x18 >> 1,
+	.invert = 1,
+};
+
+struct tda10023_config vp2033_tda10023_cu1216_config = {
+	.demod_address = 0x18 >> 1,
+	.invert = 1,
+};
+
+static u8 read_pwm(struct mantis_pci *mantis)
+{
+	struct i2c_adapter *adapter = &mantis->adapter;
+
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = {
+		{.addr = 0x50, .flags = 0, .buf = &b, .len = 1},
+		{.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1}
+	};
+
+	if ((i2c_transfer(adapter, msg, 2) != 2)
+	    || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct mantis_pci *mantis = fe->dvb->priv;
+	struct i2c_adapter *adapter = &mantis->adapter;
+
+	u8 buf[6];
+	struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)};
+	int i;
+
+#define CU1216_IF 36125000
+#define TUNER_MUL 62500
+
+	u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0xce;
+	buf[3] = (p->frequency < 150000000 ? 0x01 :
+		  p->frequency < 445000000 ? 0x02 : 0x04);
+	buf[4] = 0xde;
+	buf[5] = 0x20;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (i2c_transfer(adapter, &msg, 1) != 1)
+		return -EIO;
+
+	/* wait for the pll lock */
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	for (i = 0; i < 20; i++) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+
+		if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40))
+			break;
+
+		msleep(10);
+	}
+
+	/* switch the charge pump to the lower current */
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = &buf[2];
+	buf[2] &= ~0x40;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (i2c_transfer(adapter, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static int vp2033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter = &mantis->adapter;
+
+	int err = 0;
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		mantis_frontend_soft_reset(mantis);
+		msleep(250);
+
+		dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)");
+		fe = dvb_attach(tda10021_attach, &vp2033_tda1002x_cu1216_config,
+				     adapter,
+				     read_pwm(mantis));
+
+		if (fe) {
+			dprintk(MANTIS_ERROR, 1,
+				"found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x",
+				vp2033_tda1002x_cu1216_config.demod_address);
+		} else {
+			fe = dvb_attach(tda10023_attach, &vp2033_tda10023_cu1216_config,
+					     adapter,
+					     read_pwm(mantis));
+
+			if (fe) {
+				dprintk(MANTIS_ERROR, 1,
+					"found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x",
+					vp2033_tda1002x_cu1216_config.demod_address);
+			}
+		}
+
+		if (fe) {
+			fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set;
+			dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success");
+		} else {
+			return -1;
+		}
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+
+	mantis->fe = fe;
+	dprintk(MANTIS_DEBUG, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp2033_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_204,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp2033_frontend_init,
+	.power		= GPIF_A12,
+	.reset		= GPIF_A13,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp2033.h b/drivers/media/pci/mantis/mantis_vp2033.h
new file mode 100644
index 000000000000..c55242b79d54
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp2033.h
@@ -0,0 +1,30 @@
+/*
+	Mantis VP-2033 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP2033_H
+#define __MANTIS_VP2033_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_2033_DVB_C	0x0008
+
+extern struct mantis_hwconfig vp2033_config;
+
+#endif /* __MANTIS_VP2033_H */
diff --git a/drivers/media/pci/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c
new file mode 100644
index 000000000000..d480741afd78
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp2040.c
@@ -0,0 +1,187 @@
+/*
+	Mantis VP-2040 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "tda1002x.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp2040.h"
+
+#define MANTIS_MODEL_NAME	"VP-2040"
+#define MANTIS_DEV_TYPE		"DVB-C"
+
+struct tda1002x_config vp2040_tda1002x_cu1216_config = {
+	.demod_address	= 0x18 >> 1,
+	.invert		= 1,
+};
+
+struct tda10023_config vp2040_tda10023_cu1216_config = {
+	.demod_address	= 0x18 >> 1,
+	.invert		= 1,
+};
+
+static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct mantis_pci *mantis	= fe->dvb->priv;
+	struct i2c_adapter *adapter	= &mantis->adapter;
+
+	u8 buf[6];
+	struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)};
+	int i;
+
+#define CU1216_IF 36125000
+#define TUNER_MUL 62500
+
+	u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0xce;
+	buf[3] = (p->frequency < 150000000 ? 0x01 :
+		  p->frequency < 445000000 ? 0x02 : 0x04);
+	buf[4] = 0xde;
+	buf[5] = 0x20;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (i2c_transfer(adapter, &msg, 1) != 1)
+		return -EIO;
+
+	/* wait for the pll lock */
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	for (i = 0; i < 20; i++) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+
+		if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40))
+			break;
+
+		msleep(10);
+	}
+
+	/* switch the charge pump to the lower current */
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = &buf[2];
+	buf[2] &= ~0x40;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (i2c_transfer(adapter, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static u8 read_pwm(struct mantis_pci *mantis)
+{
+	struct i2c_adapter *adapter = &mantis->adapter;
+
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = {
+		{.addr = 0x50, .flags = 0, .buf = &b, .len = 1},
+		{.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1}
+	};
+
+	if ((i2c_transfer(adapter, msg, 2) != 2)
+	    || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter = &mantis->adapter;
+
+	int err = 0;
+
+	err = mantis_frontend_power(mantis, POWER_ON);
+	if (err == 0) {
+		mantis_frontend_soft_reset(mantis);
+		msleep(250);
+
+		dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)");
+		fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config,
+				     adapter,
+				     read_pwm(mantis));
+
+		if (fe) {
+			dprintk(MANTIS_ERROR, 1,
+				"found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x",
+				vp2040_tda1002x_cu1216_config.demod_address);
+		} else {
+			fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config,
+					     adapter,
+					     read_pwm(mantis));
+
+			if (fe) {
+				dprintk(MANTIS_ERROR, 1,
+					"found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x",
+					vp2040_tda1002x_cu1216_config.demod_address);
+			}
+		}
+
+		if (fe) {
+			fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set;
+			dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success");
+		} else {
+			return -1;
+		}
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+	}
+	mantis->fe = fe;
+	dprintk(MANTIS_DEBUG, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp2040_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_204,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp2040_frontend_init,
+	.power		= GPIF_A12,
+	.reset		= GPIF_A13,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp2040.h b/drivers/media/pci/mantis/mantis_vp2040.h
new file mode 100644
index 000000000000..d125e219b685
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp2040.h
@@ -0,0 +1,32 @@
+/*
+	Mantis VP-2040 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP2040_H
+#define __MANTIS_VP2040_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_2040_DVB_C	0x0043
+#define CINERGY_C		0x1178
+#define CABLESTAR_HD2		0x0002
+
+extern struct mantis_hwconfig vp2040_config;
+
+#endif /* __MANTIS_VP2040_H */
diff --git a/drivers/media/pci/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c
new file mode 100644
index 000000000000..4155c838a18a
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp3028.c
@@ -0,0 +1,38 @@
+/*
+	Mantis VP-3028 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "mantis_common.h"
+#include "mantis_vp3028.h"
+
+struct zl10353_config mantis_vp3028_config = {
+	.demod_address	= 0x0f,
+};
+
+#define MANTIS_MODEL_NAME	"VP-3028"
+#define MANTIS_DEV_TYPE		"DVB-T"
+
+struct mantis_hwconfig vp3028_mantis_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_188,
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+};
diff --git a/drivers/media/pci/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h
new file mode 100644
index 000000000000..b07be6adc522
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp3028.h
@@ -0,0 +1,33 @@
+/*
+	Mantis VP-3028 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP3028_H
+#define __MANTIS_VP3028_H
+
+#include "dvb_frontend.h"
+#include "mantis_common.h"
+#include "zl10353.h"
+
+#define MANTIS_VP_3028_DVB_T	0x0028
+
+extern struct zl10353_config mantis_vp3028_config;
+extern struct mantis_hwconfig vp3028_mantis_config;
+
+#endif /* __MANTIS_VP3028_H */
diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c
new file mode 100644
index 000000000000..c09308cd3ac6
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp3030.c
@@ -0,0 +1,105 @@
+/*
+	Mantis VP-3030 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "zl10353.h"
+#include "tda665x.h"
+#include "mantis_common.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+#include "mantis_vp3030.h"
+
+struct zl10353_config mantis_vp3030_config = {
+	.demod_address		= 0x0f,
+};
+
+struct tda665x_config env57h12d5_config = {
+	.name			= "ENV57H12D5 (ET-50DT)",
+	.addr			= 0x60,
+	.frequency_min		=  47000000,
+	.frequency_max		= 862000000,
+	.frequency_offst	=   3616667,
+	.ref_multiplier		= 6, /* 1/6 MHz */
+	.ref_divider		= 100000, /* 1/6 MHz */
+};
+
+#define MANTIS_MODEL_NAME	"VP-3030"
+#define MANTIS_DEV_TYPE		"DVB-T"
+
+
+static int vp3030_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe)
+{
+	struct i2c_adapter *adapter	= &mantis->adapter;
+	struct mantis_hwconfig *config	= mantis->hwconfig;
+	int err = 0;
+
+	mantis_gpio_set_bits(mantis, config->reset, 0);
+	msleep(100);
+	err = mantis_frontend_power(mantis, POWER_ON);
+	msleep(100);
+	mantis_gpio_set_bits(mantis, config->reset, 1);
+
+	if (err == 0) {
+		msleep(250);
+		dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)");
+		fe = dvb_attach(zl10353_attach, &mantis_vp3030_config, adapter);
+
+		if (!fe)
+			return -1;
+
+		dvb_attach(tda665x_attach, fe, &env57h12d5_config, adapter);
+	} else {
+		dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>",
+			adapter->name,
+			err);
+
+		return -EIO;
+
+	}
+	mantis->fe = fe;
+	dprintk(MANTIS_ERROR, 1, "Done!");
+
+	return 0;
+}
+
+struct mantis_hwconfig vp3030_config = {
+	.model_name	= MANTIS_MODEL_NAME,
+	.dev_type	= MANTIS_DEV_TYPE,
+	.ts_size	= MANTIS_TS_188,
+
+	.baud_rate	= MANTIS_BAUD_9600,
+	.parity		= MANTIS_PARITY_NONE,
+	.bytes		= 0,
+
+	.frontend_init	= vp3030_frontend_init,
+	.power		= GPIF_A12,
+	.reset		= GPIF_A13,
+
+	.i2c_mode	= MANTIS_BYTE_MODE
+};
diff --git a/drivers/media/pci/mantis/mantis_vp3030.h b/drivers/media/pci/mantis/mantis_vp3030.h
new file mode 100644
index 000000000000..5f12c4266277
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_vp3030.h
@@ -0,0 +1,30 @@
+/*
+	Mantis VP-3030 driver
+
+	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __MANTIS_VP3030_H
+#define __MANTIS_VP3030_H
+
+#include "mantis_common.h"
+
+#define MANTIS_VP_3030_DVB_T	0x0024
+
+extern struct mantis_hwconfig vp3030_config;
+
+#endif /* __MANTIS_VP3030_H */
diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig
new file mode 100644
index 000000000000..b4bf848be5a0
--- /dev/null
+++ b/drivers/media/pci/meye/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_MEYE
+	tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
+	depends on PCI && SONY_LAPTOP && VIDEO_V4L2
+	---help---
+	  This is the video4linux driver for the Motion Eye camera found
+	  in the Vaio Picturebook laptops. Please read the material in
+	  <file:Documentation/video4linux/meye.txt> for more information.
+
+	  If you say Y or M here, you need to say Y or M to "Sony Laptop
+	  Extras" in the misc device section.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called meye.
diff --git a/drivers/media/pci/meye/Makefile b/drivers/media/pci/meye/Makefile
new file mode 100644
index 000000000000..49388518cd01
--- /dev/null
+++ b/drivers/media/pci/meye/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_MEYE) += meye.o
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
new file mode 100644
index 000000000000..7bc775219f97
--- /dev/null
+++ b/drivers/media/pci/meye/meye.c
@@ -0,0 +1,1964 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * 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/pci.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+
+#include "meye.h"
+#include <linux/meye.h>
+
+MODULE_AUTHOR("Stelian Pop <stelian@popies.net>");
+MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MEYE_DRIVER_VERSION);
+
+/* number of grab buffers */
+static unsigned int gbuffers = 2;
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)");
+
+/* size of a grab buffer */
+static unsigned int gbufsize = MEYE_MAX_BUFSIZE;
+module_param(gbufsize, int, 0444);
+MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400"
+		 " (will be rounded up to a page multiple)");
+
+/* /dev/videoX registration number */
+static int video_nr = -1;
+module_param(video_nr, int, 0444);
+MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
+
+/* driver structure - only one possible */
+static struct meye meye;
+
+/****************************************************************************/
+/* Memory allocation routines (stolen from bttv-driver.c)                   */
+/****************************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+	void *mem;
+	unsigned long adr;
+
+	size = PAGE_ALIGN(size);
+	mem = vmalloc_32(size);
+	if (mem) {
+		memset(mem, 0, size);
+		adr = (unsigned long) mem;
+		while (size > 0) {
+			SetPageReserved(vmalloc_to_page((void *)adr));
+			adr += PAGE_SIZE;
+			size -= PAGE_SIZE;
+		}
+	}
+	return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+	unsigned long adr;
+
+	if (mem) {
+		adr = (unsigned long) mem;
+		while ((long) size > 0) {
+			ClearPageReserved(vmalloc_to_page((void *)adr));
+			adr += PAGE_SIZE;
+			size -= PAGE_SIZE;
+		}
+		vfree(mem);
+	}
+}
+
+/*
+ * return a page table pointing to N pages of locked memory
+ *
+ * NOTE: The meye device expects DMA addresses on 32 bits, we build
+ * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes.
+ */
+static int ptable_alloc(void)
+{
+	u32 *pt;
+	int i;
+
+	memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+
+	/* give only 32 bit DMA addresses */
+	if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32)))
+		return -1;
+
+	meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev,
+						   PAGE_SIZE,
+						   &meye.mchip_dmahandle,
+						   GFP_KERNEL);
+	if (!meye.mchip_ptable_toc) {
+		meye.mchip_dmahandle = 0;
+		return -1;
+	}
+
+	pt = meye.mchip_ptable_toc;
+	for (i = 0; i < MCHIP_NB_PAGES; i++) {
+		dma_addr_t dma;
+		meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev,
+							  PAGE_SIZE,
+							  &dma,
+							  GFP_KERNEL);
+		if (!meye.mchip_ptable[i]) {
+			int j;
+			pt = meye.mchip_ptable_toc;
+			for (j = 0; j < i; ++j) {
+				dma = (dma_addr_t) *pt;
+				dma_free_coherent(&meye.mchip_dev->dev,
+						  PAGE_SIZE,
+						  meye.mchip_ptable[j], dma);
+				pt++;
+			}
+			dma_free_coherent(&meye.mchip_dev->dev,
+					  PAGE_SIZE,
+					  meye.mchip_ptable_toc,
+					  meye.mchip_dmahandle);
+			meye.mchip_ptable_toc = NULL;
+			meye.mchip_dmahandle = 0;
+			return -1;
+		}
+		*pt = (u32) dma;
+		pt++;
+	}
+	return 0;
+}
+
+static void ptable_free(void)
+{
+	u32 *pt;
+	int i;
+
+	pt = meye.mchip_ptable_toc;
+	for (i = 0; i < MCHIP_NB_PAGES; i++) {
+		dma_addr_t dma = (dma_addr_t) *pt;
+		if (meye.mchip_ptable[i])
+			dma_free_coherent(&meye.mchip_dev->dev,
+					  PAGE_SIZE,
+					  meye.mchip_ptable[i], dma);
+		pt++;
+	}
+
+	if (meye.mchip_ptable_toc)
+		dma_free_coherent(&meye.mchip_dev->dev,
+				  PAGE_SIZE,
+				  meye.mchip_ptable_toc,
+				  meye.mchip_dmahandle);
+
+	memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+	meye.mchip_ptable_toc = NULL;
+	meye.mchip_dmahandle = 0;
+}
+
+/* copy data from ptable into buf */
+static void ptable_copy(u8 *buf, int start, int size, int pt_pages)
+{
+	int i;
+
+	for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) {
+		memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE);
+		if (start >= pt_pages)
+			start = 0;
+	}
+	memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE);
+}
+
+/****************************************************************************/
+/* JPEG tables at different qualities to load into the VRJ chip             */
+/****************************************************************************/
+
+/* return a set of quantisation tables based on a quality from 1 to 10 */
+static u16 *jpeg_quantisation_tables(int *length, int quality)
+{
+	static u16 jpeg_tables[][70] = { {
+		0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff,
+		0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff,
+	},
+	{
+		0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46,
+		0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff,
+		0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+		0xffff, 0xffff, 0xffff,
+	},
+	{
+		0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23,
+		0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164,
+		0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad,
+		0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff,
+		0xe6ff, 0xfffd, 0xfff8,
+		0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876,
+		0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+		0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+		0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+		0xf8f8, 0xf8f8, 0xfff8,
+	},
+	{
+		0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17,
+		0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042,
+		0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73,
+		0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba,
+		0x99c7, 0xaba8, 0xffa4,
+		0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e,
+		0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+		0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+		0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+		0xa4a4, 0xa4a4, 0xffa4,
+	},
+	{
+		0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712,
+		0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932,
+		0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556,
+		0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c,
+		0x7396, 0x817e, 0xff7c,
+		0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b,
+		0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+		0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+		0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+		0x7c7c, 0x7c7c, 0xff7c,
+	},
+	{
+		0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e,
+		0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28,
+		0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745,
+		0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470,
+		0x5c78, 0x6765, 0xff63,
+		0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f,
+		0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+		0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+		0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+		0x6363, 0x6363, 0xff63,
+	},
+	{
+		0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b,
+		0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20,
+		0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37,
+		0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a,
+		0x4a60, 0x5251, 0xff4f,
+		0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26,
+		0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+		0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+		0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+		0x4f4f, 0x4f4f, 0xff4f,
+	},
+	{
+		0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08,
+		0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318,
+		0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129,
+		0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43,
+		0x3748, 0x3e3d, 0xff3b,
+		0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c,
+		0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+		0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+		0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+		0x3b3b, 0x3b3b, 0xff3b,
+	},
+	{
+		0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706,
+		0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710,
+		0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c,
+		0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d,
+		0x2530, 0x2928, 0xff28,
+		0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813,
+		0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+		0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+		0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+		0x2828, 0x2828, 0xff28,
+	},
+	{
+		0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403,
+		0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08,
+		0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e,
+		0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416,
+		0x1218, 0x1514, 0xff14,
+		0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409,
+		0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+		0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+		0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+		0x1414, 0x1414, 0xff14,
+	},
+	{
+		0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0xff01,
+		0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0101, 0x0101, 0xff01,
+	} };
+
+	if (quality < 0 || quality > 10) {
+		printk(KERN_WARNING
+		       "meye: invalid quality level %d - using 8\n", quality);
+		quality = 8;
+	}
+
+	*length = ARRAY_SIZE(jpeg_tables[quality]);
+	return jpeg_tables[quality];
+}
+
+/* return a generic set of huffman tables */
+static u16 *jpeg_huffman_tables(int *length)
+{
+	static u16 tables[] = {
+		0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405,
+		0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131,
+		0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142,
+		0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918,
+		0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443,
+		0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463,
+		0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483,
+		0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A,
+		0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8,
+		0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6,
+		0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2,
+		0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+		0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405,
+		0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206,
+		0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1,
+		0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125,
+		0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A,
+		0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A,
+		0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A,
+		0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998,
+		0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6,
+		0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4,
+		0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2,
+		0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+		0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000,
+		0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+		0xFF0B,
+		0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101,
+		0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+		0xFF0B
+	};
+
+	*length = ARRAY_SIZE(tables);
+	return tables;
+}
+
+/****************************************************************************/
+/* MCHIP low-level functions                                                */
+/****************************************************************************/
+
+/* returns the horizontal capture size */
+static inline int mchip_hsize(void)
+{
+	return meye.params.subsample ? 320 : 640;
+}
+
+/* returns the vertical capture size */
+static inline int mchip_vsize(void)
+{
+	return meye.params.subsample ? 240 : 480;
+}
+
+/* waits for a register to be available */
+static void mchip_sync(int reg)
+{
+	u32 status;
+	int i;
+
+	if (reg == MCHIP_MM_FIFO_DATA) {
+		for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+			status = readl(meye.mchip_mmregs +
+				       MCHIP_MM_FIFO_STATUS);
+			if (!(status & MCHIP_MM_FIFO_WAIT)) {
+				printk(KERN_WARNING "meye: fifo not ready\n");
+				return;
+			}
+			if (status & MCHIP_MM_FIFO_READY)
+				return;
+			udelay(1);
+		}
+	} else if (reg > 0x80) {
+		u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY
+					 : MCHIP_HIC_STATUS_VRJ_RDY;
+		for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+			status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS);
+			if (status & mask)
+				return;
+			udelay(1);
+		}
+	} else
+		return;
+	printk(KERN_WARNING
+	       "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n",
+	       reg, status);
+}
+
+/* sets a value into the register */
+static inline void mchip_set(int reg, u32 v)
+{
+	mchip_sync(reg);
+	writel(v, meye.mchip_mmregs + reg);
+}
+
+/* get the register value */
+static inline u32 mchip_read(int reg)
+{
+	mchip_sync(reg);
+	return readl(meye.mchip_mmregs + reg);
+}
+
+/* wait for a register to become a particular value */
+static inline int mchip_delay(u32 reg, u32 v)
+{
+	int n = 10;
+	while (--n && mchip_read(reg) != v)
+		udelay(1);
+	return n;
+}
+
+/* setup subsampling */
+static void mchip_subsample(void)
+{
+	mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample);
+	mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize());
+	mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize());
+	mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize());
+	mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize());
+	mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+}
+
+/* set the framerate into the mchip */
+static void mchip_set_framerate(void)
+{
+	mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate);
+}
+
+/* load some huffman and quantisation tables into the VRJ chip ready
+   for JPEG compression */
+static void mchip_load_tables(void)
+{
+	int i;
+	int length;
+	u16 *tables;
+
+	tables = jpeg_huffman_tables(&length);
+	for (i = 0; i < length; i++)
+		writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+
+	tables = jpeg_quantisation_tables(&length, meye.params.quality);
+	for (i = 0; i < length; i++)
+		writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+}
+
+/* setup the VRJ parameters in the chip */
+static void mchip_vrj_setup(u8 mode)
+{
+	mchip_set(MCHIP_VRJ_BUS_MODE, 5);
+	mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f);
+	mchip_set(MCHIP_VRJ_PDAT_USE, 1);
+	mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0);
+	mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode);
+	mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize());
+	mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize());
+	mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b);
+	mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF);
+	mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF);
+	mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC);
+	mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0);
+	mchip_set(MCHIP_VRJ_SOF1, 0x601);
+	mchip_set(MCHIP_VRJ_SOF2, 0x1502);
+	mchip_set(MCHIP_VRJ_SOF3, 0x1503);
+	mchip_set(MCHIP_VRJ_SOF4, 0x1596);
+	mchip_set(MCHIP_VRJ_SOS, 0x0ed0);
+
+	mchip_load_tables();
+}
+
+/* sets the DMA parameters into the chip */
+static void mchip_dma_setup(dma_addr_t dma_addr)
+{
+	int i;
+
+	mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr);
+	for (i = 0; i < 4; i++)
+		mchip_set(MCHIP_MM_FIR(i), 0);
+	meye.mchip_fnum = 0;
+}
+
+/* setup for DMA transfers - also zeros the framebuffer */
+static int mchip_dma_alloc(void)
+{
+	if (!meye.mchip_dmahandle)
+		if (ptable_alloc())
+			return -1;
+	return 0;
+}
+
+/* frees the DMA buffer */
+static void mchip_dma_free(void)
+{
+	if (meye.mchip_dmahandle) {
+		mchip_dma_setup(0);
+		ptable_free();
+	}
+}
+
+/* stop any existing HIC action and wait for any dma to complete then
+   reset the dma engine */
+static void mchip_hic_stop(void)
+{
+	int i, j;
+
+	meye.mchip_mode = MCHIP_HIC_MODE_NOOP;
+	if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY))
+		return;
+	for (i = 0; i < 20; ++i) {
+		mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP);
+		mchip_delay(MCHIP_HIC_CMD, 0);
+		for (j = 0; j < 100; ++j) {
+			if (mchip_delay(MCHIP_HIC_STATUS,
+					MCHIP_HIC_STATUS_IDLE))
+				return;
+			msleep(1);
+		}
+		printk(KERN_ERR "meye: need to reset HIC!\n");
+
+		mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET);
+		msleep(250);
+	}
+	printk(KERN_ERR "meye: resetting HIC hanged!\n");
+}
+
+/****************************************************************************/
+/* MCHIP frame processing functions                                         */
+/****************************************************************************/
+
+/* get the next ready frame from the dma engine */
+static u32 mchip_get_frame(void)
+{
+	u32 v;
+
+	v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum));
+	return v;
+}
+
+/* frees the current frame from the dma engine */
+static void mchip_free_frame(void)
+{
+	mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0);
+	meye.mchip_fnum++;
+	meye.mchip_fnum %= 4;
+}
+
+/* read one frame from the framebuffer assuming it was captured using
+   a uncompressed transfer */
+static void mchip_cont_read_frame(u32 v, u8 *buf, int size)
+{
+	int pt_id;
+
+	pt_id = (v >> 17) & 0x3FF;
+
+	ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES);
+}
+
+/* read a compressed frame from the framebuffer */
+static int mchip_comp_read_frame(u32 v, u8 *buf, int size)
+{
+	int pt_start, pt_end, trailer;
+	int fsize;
+	int i;
+
+	pt_start = (v >> 19) & 0xFF;
+	pt_end = (v >> 11) & 0xFF;
+	trailer = (v >> 1) & 0x3FF;
+
+	if (pt_end < pt_start)
+		fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE +
+			pt_end * PAGE_SIZE + trailer * 4;
+	else
+		fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4;
+
+	if (fsize > size) {
+		printk(KERN_WARNING "meye: oversized compressed frame %d\n",
+		       fsize);
+		return -1;
+	}
+
+	ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG);
+
+#ifdef MEYE_JPEG_CORRECTION
+
+	/* Some mchip generated jpeg frames are incorrect. In most
+	 * (all ?) of those cases, the final EOI (0xff 0xd9) marker
+	 * is not present at the end of the frame.
+	 *
+	 * Since adding the final marker is not enough to restore
+	 * the jpeg integrity, we drop the frame.
+	 */
+
+	for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ;
+
+	if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9)
+		return -1;
+
+#endif
+
+	return fsize;
+}
+
+/* take a picture into SDRAM */
+static void mchip_take_picture(void)
+{
+	int i;
+
+	mchip_hic_stop();
+	mchip_subsample();
+	mchip_dma_setup(meye.mchip_dmahandle);
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+
+	for (i = 0; i < 100; ++i) {
+		if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+			break;
+		msleep(1);
+	}
+}
+
+/* dma a previously taken picture into a buffer */
+static void mchip_get_picture(u8 *buf, int bufsize)
+{
+	u32 v;
+	int i;
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+	for (i = 0; i < 100; ++i) {
+		if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+			break;
+		msleep(1);
+	}
+	for (i = 0; i < 4; ++i) {
+		v = mchip_get_frame();
+		if (v & MCHIP_MM_FIR_RDY) {
+			mchip_cont_read_frame(v, buf, bufsize);
+			break;
+		}
+		mchip_free_frame();
+	}
+}
+
+/* start continuous dma capture */
+static void mchip_continuous_start(void)
+{
+	mchip_hic_stop();
+	mchip_subsample();
+	mchip_set_framerate();
+	mchip_dma_setup(meye.mchip_dmahandle);
+
+	meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/* compress one frame into a buffer */
+static int mchip_compress_frame(u8 *buf, int bufsize)
+{
+	u32 v;
+	int len = -1, i;
+
+	mchip_vrj_setup(0x3f);
+	udelay(50);
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+	for (i = 0; i < 100; ++i) {
+		if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+			break;
+		msleep(1);
+	}
+
+	for (i = 0; i < 4; ++i) {
+		v = mchip_get_frame();
+		if (v & MCHIP_MM_FIR_RDY) {
+			len = mchip_comp_read_frame(v, buf, bufsize);
+			break;
+		}
+		mchip_free_frame();
+	}
+	return len;
+}
+
+#if 0
+/* uncompress one image into a buffer */
+static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize)
+{
+	mchip_vrj_setup(0x3f);
+	udelay(50);
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+
+	return mchip_comp_read_frame(buf, bufsize);
+}
+#endif
+
+/* start continuous compressed capture */
+static void mchip_cont_compression_start(void)
+{
+	mchip_hic_stop();
+	mchip_vrj_setup(0x3f);
+	mchip_subsample();
+	mchip_set_framerate();
+	mchip_dma_setup(meye.mchip_dmahandle);
+
+	meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+
+	mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP);
+	mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/****************************************************************************/
+/* Interrupt handling                                                       */
+/****************************************************************************/
+
+static irqreturn_t meye_irq(int irq, void *dev_id)
+{
+	u32 v;
+	int reqnr;
+	static int sequence;
+
+	v = mchip_read(MCHIP_MM_INTA);
+
+	if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT &&
+	    meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+		return IRQ_NONE;
+
+again:
+	v = mchip_get_frame();
+	if (!(v & MCHIP_MM_FIR_RDY))
+		return IRQ_HANDLED;
+
+	if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) {
+		if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr,
+			      sizeof(int), &meye.grabq_lock) != sizeof(int)) {
+			mchip_free_frame();
+			return IRQ_HANDLED;
+		}
+		mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr,
+				      mchip_hsize() * mchip_vsize() * 2);
+		meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2;
+		meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+		do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+		meye.grab_buffer[reqnr].sequence = sequence++;
+		kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
+				sizeof(int), &meye.doneq_lock);
+		wake_up_interruptible(&meye.proc_list);
+	} else {
+		int size;
+		size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize);
+		if (size == -1) {
+			mchip_free_frame();
+			goto again;
+		}
+		if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr,
+			      sizeof(int), &meye.grabq_lock) != sizeof(int)) {
+			mchip_free_frame();
+			goto again;
+		}
+		memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp,
+		       size);
+		meye.grab_buffer[reqnr].size = size;
+		meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+		do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+		meye.grab_buffer[reqnr].sequence = sequence++;
+		kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
+				sizeof(int), &meye.doneq_lock);
+		wake_up_interruptible(&meye.proc_list);
+	}
+	mchip_free_frame();
+	goto again;
+}
+
+/****************************************************************************/
+/* video4linux integration                                                  */
+/****************************************************************************/
+
+static int meye_open(struct file *file)
+{
+	int i;
+
+	if (test_and_set_bit(0, &meye.in_use))
+		return -EBUSY;
+
+	mchip_hic_stop();
+
+	if (mchip_dma_alloc()) {
+		printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
+		clear_bit(0, &meye.in_use);
+		return -ENOBUFS;
+	}
+
+	for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+		meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+	kfifo_reset(&meye.grabq);
+	kfifo_reset(&meye.doneq);
+	return 0;
+}
+
+static int meye_release(struct file *file)
+{
+	mchip_hic_stop();
+	mchip_dma_free();
+	clear_bit(0, &meye.in_use);
+	return 0;
+}
+
+static int meyeioc_g_params(struct meye_params *p)
+{
+	*p = meye.params;
+	return 0;
+}
+
+static int meyeioc_s_params(struct meye_params *jp)
+{
+	if (jp->subsample > 1)
+		return -EINVAL;
+
+	if (jp->quality > 10)
+		return -EINVAL;
+
+	if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63)
+		return -EINVAL;
+
+	if (jp->framerate > 31)
+		return -EINVAL;
+
+	mutex_lock(&meye.lock);
+
+	if (meye.params.subsample != jp->subsample ||
+	    meye.params.quality != jp->quality)
+		mchip_hic_stop();	/* need restart */
+
+	meye.params = *jp;
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS,
+			      meye.params.sharpness);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC,
+			      meye.params.agc);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE,
+			      meye.params.picture);
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int meyeioc_qbuf_capt(int *nb)
+{
+	if (!meye.grab_fbuffer)
+		return -EINVAL;
+
+	if (*nb >= gbuffers)
+		return -EINVAL;
+
+	if (*nb < 0) {
+		/* stop capture */
+		mchip_hic_stop();
+		return 0;
+	}
+
+	if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED)
+		return -EBUSY;
+
+	mutex_lock(&meye.lock);
+
+	if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+		mchip_cont_compression_start();
+
+	meye.grab_buffer[*nb].state = MEYE_BUF_USING;
+	kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int),
+			 &meye.grabq_lock);
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int meyeioc_sync(struct file *file, void *fh, int *i)
+{
+	int unused;
+
+	if (*i < 0 || *i >= gbuffers)
+		return -EINVAL;
+
+	mutex_lock(&meye.lock);
+	switch (meye.grab_buffer[*i].state) {
+
+	case MEYE_BUF_UNUSED:
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	case MEYE_BUF_USING:
+		if (file->f_flags & O_NONBLOCK) {
+			mutex_unlock(&meye.lock);
+			return -EAGAIN;
+		}
+		if (wait_event_interruptible(meye.proc_list,
+			(meye.grab_buffer[*i].state != MEYE_BUF_USING))) {
+			mutex_unlock(&meye.lock);
+			return -EINTR;
+		}
+		/* fall through */
+	case MEYE_BUF_DONE:
+		meye.grab_buffer[*i].state = MEYE_BUF_UNUSED;
+		if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused,
+				sizeof(int), &meye.doneq_lock) != sizeof(int))
+					break;
+	}
+	*i = meye.grab_buffer[*i].size;
+	mutex_unlock(&meye.lock);
+	return 0;
+}
+
+static int meyeioc_stillcapt(void)
+{
+	if (!meye.grab_fbuffer)
+		return -EINVAL;
+
+	if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+		return -EBUSY;
+
+	mutex_lock(&meye.lock);
+	meye.grab_buffer[0].state = MEYE_BUF_USING;
+	mchip_take_picture();
+
+	mchip_get_picture(meye.grab_fbuffer,
+			mchip_hsize() * mchip_vsize() * 2);
+
+	meye.grab_buffer[0].state = MEYE_BUF_DONE;
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int meyeioc_stilljcapt(int *len)
+{
+	if (!meye.grab_fbuffer)
+		return -EINVAL;
+
+	if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+		return -EBUSY;
+
+	mutex_lock(&meye.lock);
+	meye.grab_buffer[0].state = MEYE_BUF_USING;
+	*len = -1;
+
+	while (*len == -1) {
+		mchip_take_picture();
+		*len = mchip_compress_frame(meye.grab_fbuffer, gbufsize);
+	}
+
+	meye.grab_buffer[0].state = MEYE_BUF_DONE;
+	mutex_unlock(&meye.lock);
+	return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+				struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "meye");
+	strcpy(cap->card, "meye");
+	sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev));
+
+	cap->version = (MEYE_DRIVER_MAJORVERSION << 8) +
+		       MEYE_DRIVER_MINORVERSION;
+
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+			    V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	if (i->index != 0)
+		return -EINVAL;
+
+	strcpy(i->name, "Camera");
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+				struct v4l2_queryctrl *c)
+{
+	switch (c->id) {
+
+	case V4L2_CID_BRIGHTNESS:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Brightness");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 32;
+		c->flags = 0;
+		break;
+	case V4L2_CID_HUE:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Hue");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 32;
+		c->flags = 0;
+		break;
+	case V4L2_CID_CONTRAST:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Contrast");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 32;
+		c->flags = 0;
+		break;
+	case V4L2_CID_SATURATION:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Saturation");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 32;
+		c->flags = 0;
+		break;
+	case V4L2_CID_AGC:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Agc");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 48;
+		c->flags = 0;
+		break;
+	case V4L2_CID_MEYE_SHARPNESS:
+	case V4L2_CID_SHARPNESS:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Sharpness");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 32;
+
+		/* Continue to report legacy private SHARPNESS ctrl but
+		 * say it is disabled in preference to ctrl in the spec
+		 */
+		c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 :
+						V4L2_CTRL_FLAG_DISABLED;
+		break;
+	case V4L2_CID_PICTURE:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Picture");
+		c->minimum = 0;
+		c->maximum = 63;
+		c->step = 1;
+		c->default_value = 0;
+		c->flags = 0;
+		break;
+	case V4L2_CID_JPEGQUAL:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "JPEG quality");
+		c->minimum = 0;
+		c->maximum = 10;
+		c->step = 1;
+		c->default_value = 8;
+		c->flags = 0;
+		break;
+	case V4L2_CID_FRAMERATE:
+		c->type = V4L2_CTRL_TYPE_INTEGER;
+		strcpy(c->name, "Framerate");
+		c->minimum = 0;
+		c->maximum = 31;
+		c->step = 1;
+		c->default_value = 0;
+		c->flags = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	mutex_lock(&meye.lock);
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value);
+		meye.brightness = c->value << 10;
+		break;
+	case V4L2_CID_HUE:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERAHUE, c->value);
+		meye.hue = c->value << 10;
+		break;
+	case V4L2_CID_CONTRAST:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value);
+		meye.contrast = c->value << 10;
+		break;
+	case V4L2_CID_SATURATION:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERACOLOR, c->value);
+		meye.colour = c->value << 10;
+		break;
+	case V4L2_CID_AGC:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERAAGC, c->value);
+		meye.params.agc = c->value;
+		break;
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_MEYE_SHARPNESS:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value);
+		meye.params.sharpness = c->value;
+		break;
+	case V4L2_CID_PICTURE:
+		sony_pic_camera_command(
+			SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value);
+		meye.params.picture = c->value;
+		break;
+	case V4L2_CID_JPEGQUAL:
+		meye.params.quality = c->value;
+		break;
+	case V4L2_CID_FRAMERATE:
+		meye.params.framerate = c->value;
+		break;
+	default:
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	mutex_lock(&meye.lock);
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value = meye.brightness >> 10;
+		break;
+	case V4L2_CID_HUE:
+		c->value = meye.hue >> 10;
+		break;
+	case V4L2_CID_CONTRAST:
+		c->value = meye.contrast >> 10;
+		break;
+	case V4L2_CID_SATURATION:
+		c->value = meye.colour >> 10;
+		break;
+	case V4L2_CID_AGC:
+		c->value = meye.params.agc;
+		break;
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_MEYE_SHARPNESS:
+		c->value = meye.params.sharpness;
+		break;
+	case V4L2_CID_PICTURE:
+		c->value = meye.params.picture;
+		break;
+	case V4L2_CID_JPEGQUAL:
+		c->value = meye.params.quality;
+		break;
+	case V4L2_CID_FRAMERATE:
+		c->value = meye.params.framerate;
+		break;
+	default:
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_fmtdesc *f)
+{
+	if (f->index > 1)
+		return -EINVAL;
+
+	if (f->index == 0) {
+		/* standard YUV 422 capture */
+		f->flags = 0;
+		strcpy(f->description, "YUV422");
+		f->pixelformat = V4L2_PIX_FMT_YUYV;
+	} else {
+		/* compressed MJPEG capture */
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+		strcpy(f->description, "MJPEG");
+		f->pixelformat = V4L2_PIX_FMT_MJPEG;
+	}
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+	    f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+		return -EINVAL;
+
+	if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+	    f->fmt.pix.field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+
+	if (f->fmt.pix.width <= 320) {
+		f->fmt.pix.width = 320;
+		f->fmt.pix.height = 240;
+	} else {
+		f->fmt.pix.width = 640;
+		f->fmt.pix.height = 480;
+	}
+
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height *
+			       f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = 0;
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+				    struct v4l2_format *f)
+{
+	switch (meye.mchip_mode) {
+	case MCHIP_HIC_MODE_CONT_OUT:
+	default:
+		f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+		break;
+	case MCHIP_HIC_MODE_CONT_COMP:
+		f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+		break;
+	}
+
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.width = mchip_hsize();
+	f->fmt.pix.height = mchip_vsize();
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height *
+			       f->fmt.pix.bytesperline;
+
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+				    struct v4l2_format *f)
+{
+	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+	    f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+		return -EINVAL;
+
+	if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+	    f->fmt.pix.field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	mutex_lock(&meye.lock);
+
+	if (f->fmt.pix.width <= 320) {
+		f->fmt.pix.width = 320;
+		f->fmt.pix.height = 240;
+		meye.params.subsample = 1;
+	} else {
+		f->fmt.pix.width = 640;
+		f->fmt.pix.height = 480;
+		meye.params.subsample = 0;
+	}
+
+	switch (f->fmt.pix.pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+		meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+		break;
+	case V4L2_PIX_FMT_MJPEG:
+		meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+		break;
+	}
+
+	mutex_unlock(&meye.lock);
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.height *
+			       f->fmt.pix.bytesperline;
+	f->fmt.pix.colorspace = 0;
+	f->fmt.pix.priv = 0;
+
+	return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+				struct v4l2_requestbuffers *req)
+{
+	int i;
+
+	if (req->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+
+	if (meye.grab_fbuffer && req->count == gbuffers) {
+		/* already allocated, no modifications */
+		return 0;
+	}
+
+	mutex_lock(&meye.lock);
+	if (meye.grab_fbuffer) {
+		for (i = 0; i < gbuffers; i++)
+			if (meye.vma_use_count[i]) {
+				mutex_unlock(&meye.lock);
+				return -EINVAL;
+			}
+		rvfree(meye.grab_fbuffer, gbuffers * gbufsize);
+		meye.grab_fbuffer = NULL;
+	}
+
+	gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS));
+	req->count = gbuffers;
+	meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize);
+
+	if (!meye.grab_fbuffer) {
+		printk(KERN_ERR "meye: v4l framebuffer allocation"
+				" failed\n");
+		mutex_unlock(&meye.lock);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < gbuffers; i++)
+		meye.vma_use_count[i] = 0;
+
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	unsigned int index = buf->index;
+
+	if (index >= gbuffers)
+		return -EINVAL;
+
+	buf->bytesused = meye.grab_buffer[index].size;
+	buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+	if (meye.grab_buffer[index].state == MEYE_BUF_USING)
+		buf->flags |= V4L2_BUF_FLAG_QUEUED;
+
+	if (meye.grab_buffer[index].state == MEYE_BUF_DONE)
+		buf->flags |= V4L2_BUF_FLAG_DONE;
+
+	buf->field = V4L2_FIELD_NONE;
+	buf->timestamp = meye.grab_buffer[index].timestamp;
+	buf->sequence = meye.grab_buffer[index].sequence;
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->m.offset = index * gbufsize;
+	buf->length = gbufsize;
+
+	return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	if (buf->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+
+	if (buf->index >= gbuffers)
+		return -EINVAL;
+
+	if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED)
+		return -EINVAL;
+
+	mutex_lock(&meye.lock);
+	buf->flags |= V4L2_BUF_FLAG_QUEUED;
+	buf->flags &= ~V4L2_BUF_FLAG_DONE;
+	meye.grab_buffer[buf->index].state = MEYE_BUF_USING;
+	kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index,
+			sizeof(int), &meye.grabq_lock);
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	int reqnr;
+
+	if (buf->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+
+	mutex_lock(&meye.lock);
+
+	if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) {
+		mutex_unlock(&meye.lock);
+		return -EAGAIN;
+	}
+
+	if (wait_event_interruptible(meye.proc_list,
+				     kfifo_len(&meye.doneq) != 0) < 0) {
+		mutex_unlock(&meye.lock);
+		return -EINTR;
+	}
+
+	if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr,
+		       sizeof(int), &meye.doneq_lock)) {
+		mutex_unlock(&meye.lock);
+		return -EBUSY;
+	}
+
+	if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) {
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	}
+
+	buf->index = reqnr;
+	buf->bytesused = meye.grab_buffer[reqnr].size;
+	buf->flags = V4L2_BUF_FLAG_MAPPED;
+	buf->field = V4L2_FIELD_NONE;
+	buf->timestamp = meye.grab_buffer[reqnr].timestamp;
+	buf->sequence = meye.grab_buffer[reqnr].sequence;
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->m.offset = reqnr * gbufsize;
+	buf->length = gbufsize;
+	meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED;
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+	mutex_lock(&meye.lock);
+
+	switch (meye.mchip_mode) {
+	case MCHIP_HIC_MODE_CONT_OUT:
+		mchip_continuous_start();
+		break;
+	case MCHIP_HIC_MODE_CONT_COMP:
+		mchip_cont_compression_start();
+		break;
+	default:
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&meye.lock);
+
+	return 0;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+	mutex_lock(&meye.lock);
+	mchip_hic_stop();
+	kfifo_reset(&meye.grabq);
+	kfifo_reset(&meye.doneq);
+
+	for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+		meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+
+	mutex_unlock(&meye.lock);
+	return 0;
+}
+
+static long vidioc_default(struct file *file, void *fh, bool valid_prio,
+						int cmd, void *arg)
+{
+	switch (cmd) {
+	case MEYEIOC_G_PARAMS:
+		return meyeioc_g_params((struct meye_params *) arg);
+
+	case MEYEIOC_S_PARAMS:
+		return meyeioc_s_params((struct meye_params *) arg);
+
+	case MEYEIOC_QBUF_CAPT:
+		return meyeioc_qbuf_capt((int *) arg);
+
+	case MEYEIOC_SYNC:
+		return meyeioc_sync(file, fh, (int *) arg);
+
+	case MEYEIOC_STILLCAPT:
+		return meyeioc_stillcapt();
+
+	case MEYEIOC_STILLJCAPT:
+		return meyeioc_stilljcapt((int *) arg);
+
+	default:
+		return -ENOTTY;
+	}
+
+}
+
+static unsigned int meye_poll(struct file *file, poll_table *wait)
+{
+	unsigned int res = 0;
+
+	mutex_lock(&meye.lock);
+	poll_wait(file, &meye.proc_list, wait);
+	if (kfifo_len(&meye.doneq))
+		res = POLLIN | POLLRDNORM;
+	mutex_unlock(&meye.lock);
+	return res;
+}
+
+static void meye_vm_open(struct vm_area_struct *vma)
+{
+	long idx = (long)vma->vm_private_data;
+	meye.vma_use_count[idx]++;
+}
+
+static void meye_vm_close(struct vm_area_struct *vma)
+{
+	long idx = (long)vma->vm_private_data;
+	meye.vma_use_count[idx]--;
+}
+
+static const struct vm_operations_struct meye_vm_ops = {
+	.open		= meye_vm_open,
+	.close		= meye_vm_close,
+};
+
+static int meye_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long start = vma->vm_start;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	unsigned long page, pos;
+
+	mutex_lock(&meye.lock);
+	if (size > gbuffers * gbufsize) {
+		mutex_unlock(&meye.lock);
+		return -EINVAL;
+	}
+	if (!meye.grab_fbuffer) {
+		int i;
+
+		/* lazy allocation */
+		meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize);
+		if (!meye.grab_fbuffer) {
+			printk(KERN_ERR "meye: v4l framebuffer allocation failed\n");
+			mutex_unlock(&meye.lock);
+			return -ENOMEM;
+		}
+		for (i = 0; i < gbuffers; i++)
+			meye.vma_use_count[i] = 0;
+	}
+	pos = (unsigned long)meye.grab_fbuffer + offset;
+
+	while (size > 0) {
+		page = vmalloc_to_pfn((void *)pos);
+		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+			mutex_unlock(&meye.lock);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	vma->vm_ops = &meye_vm_ops;
+	vma->vm_flags &= ~VM_IO;	/* not I/O memory */
+	vma->vm_flags |= VM_RESERVED;	/* avoid to swap out this VMA */
+	vma->vm_private_data = (void *) (offset / gbufsize);
+	meye_vm_open(vma);
+
+	mutex_unlock(&meye.lock);
+	return 0;
+}
+
+static const struct v4l2_file_operations meye_fops = {
+	.owner		= THIS_MODULE,
+	.open		= meye_open,
+	.release	= meye_release,
+	.mmap		= meye_mmap,
+	.unlocked_ioctl	= video_ioctl2,
+	.poll		= meye_poll,
+};
+
+static const struct v4l2_ioctl_ops meye_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+	.vidioc_enum_input	= vidioc_enum_input,
+	.vidioc_g_input		= vidioc_g_input,
+	.vidioc_s_input		= vidioc_s_input,
+	.vidioc_queryctrl	= vidioc_queryctrl,
+	.vidioc_s_ctrl		= vidioc_s_ctrl,
+	.vidioc_g_ctrl		= vidioc_g_ctrl,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs		= vidioc_reqbufs,
+	.vidioc_querybuf	= vidioc_querybuf,
+	.vidioc_qbuf		= vidioc_qbuf,
+	.vidioc_dqbuf		= vidioc_dqbuf,
+	.vidioc_streamon	= vidioc_streamon,
+	.vidioc_streamoff	= vidioc_streamoff,
+	.vidioc_default		= vidioc_default,
+};
+
+static struct video_device meye_template = {
+	.name		= "meye",
+	.fops		= &meye_fops,
+	.ioctl_ops 	= &meye_ioctl_ops,
+	.release	= video_device_release,
+};
+
+#ifdef CONFIG_PM
+static int meye_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	pci_save_state(pdev);
+	meye.pm_mchip_mode = meye.mchip_mode;
+	mchip_hic_stop();
+	mchip_set(MCHIP_MM_INTA, 0x0);
+	return 0;
+}
+
+static int meye_resume(struct pci_dev *pdev)
+{
+	pci_restore_state(pdev);
+	pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+	mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+	msleep(1);
+	mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+	msleep(1);
+	mchip_set(MCHIP_MM_PCI_MODE, 5);
+	msleep(1);
+	mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+	switch (meye.pm_mchip_mode) {
+	case MCHIP_HIC_MODE_CONT_OUT:
+		mchip_continuous_start();
+		break;
+	case MCHIP_HIC_MODE_CONT_COMP:
+		mchip_cont_compression_start();
+		break;
+	}
+	return 0;
+}
+#endif
+
+static int __devinit meye_probe(struct pci_dev *pcidev,
+				const struct pci_device_id *ent)
+{
+	struct v4l2_device *v4l2_dev = &meye.v4l2_dev;
+	int ret = -EBUSY;
+	unsigned long mchip_adr;
+
+	if (meye.mchip_dev != NULL) {
+		printk(KERN_ERR "meye: only one device allowed!\n");
+		goto outnotdev;
+	}
+
+	ret = v4l2_device_register(&pcidev->dev, v4l2_dev);
+	if (ret < 0) {
+		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+		return ret;
+	}
+	ret = -ENOMEM;
+	meye.mchip_dev = pcidev;
+	meye.vdev = video_device_alloc();
+	if (!meye.vdev) {
+		v4l2_err(v4l2_dev, "video_device_alloc() failed!\n");
+		goto outnotdev;
+	}
+
+	meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
+	if (!meye.grab_temp) {
+		v4l2_err(v4l2_dev, "grab buffer allocation failed\n");
+		goto outvmalloc;
+	}
+
+	spin_lock_init(&meye.grabq_lock);
+	if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS,
+				GFP_KERNEL)) {
+		v4l2_err(v4l2_dev, "fifo allocation failed\n");
+		goto outkfifoalloc1;
+	}
+	spin_lock_init(&meye.doneq_lock);
+	if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS,
+				GFP_KERNEL)) {
+		v4l2_err(v4l2_dev, "fifo allocation failed\n");
+		goto outkfifoalloc2;
+	}
+
+	memcpy(meye.vdev, &meye_template, sizeof(meye_template));
+	meye.vdev->v4l2_dev = &meye.v4l2_dev;
+
+	ret = -EIO;
+	if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
+		v4l2_err(v4l2_dev, "meye: unable to power on the camera\n");
+		v4l2_err(v4l2_dev, "meye: did you enable the camera in "
+				"sonypi using the module options ?\n");
+		goto outsonypienable;
+	}
+
+	if ((ret = pci_enable_device(meye.mchip_dev))) {
+		v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n");
+		goto outenabledev;
+	}
+
+	mchip_adr = pci_resource_start(meye.mchip_dev,0);
+	if (!mchip_adr) {
+		v4l2_err(v4l2_dev, "meye: mchip has no device base address\n");
+		goto outregions;
+	}
+	if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0),
+				pci_resource_len(meye.mchip_dev, 0),
+				"meye")) {
+		v4l2_err(v4l2_dev, "meye: request_mem_region failed\n");
+		goto outregions;
+	}
+	meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS);
+	if (!meye.mchip_mmregs) {
+		v4l2_err(v4l2_dev, "meye: ioremap failed\n");
+		goto outremap;
+	}
+
+	meye.mchip_irq = pcidev->irq;
+	if (request_irq(meye.mchip_irq, meye_irq,
+			IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) {
+		v4l2_err(v4l2_dev, "request_irq failed\n");
+		goto outreqirq;
+	}
+
+	pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8);
+	pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64);
+
+	pci_set_master(meye.mchip_dev);
+
+	/* Ask the camera to perform a soft reset. */
+	pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+	mchip_delay(MCHIP_HIC_CMD, 0);
+	mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+
+	msleep(1);
+	mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+
+	msleep(1);
+	mchip_set(MCHIP_MM_PCI_MODE, 5);
+
+	msleep(1);
+	mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+	mutex_init(&meye.lock);
+	init_waitqueue_head(&meye.proc_list);
+	meye.brightness = 32 << 10;
+	meye.hue = 32 << 10;
+	meye.colour = 32 << 10;
+	meye.contrast = 32 << 10;
+	meye.params.subsample = 0;
+	meye.params.quality = 8;
+	meye.params.sharpness = 32;
+	meye.params.agc = 48;
+	meye.params.picture = 0;
+	meye.params.framerate = 0;
+
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0);
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48);
+
+	if (video_register_device(meye.vdev, VFL_TYPE_GRABBER,
+				  video_nr) < 0) {
+		v4l2_err(v4l2_dev, "video_register_device failed\n");
+		goto outvideoreg;
+	}
+
+	v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n",
+	       MEYE_DRIVER_VERSION);
+	v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n",
+	       meye.mchip_dev->revision, mchip_adr, meye.mchip_irq);
+
+	return 0;
+
+outvideoreg:
+	free_irq(meye.mchip_irq, meye_irq);
+outreqirq:
+	iounmap(meye.mchip_mmregs);
+outremap:
+	release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+			   pci_resource_len(meye.mchip_dev, 0));
+outregions:
+	pci_disable_device(meye.mchip_dev);
+outenabledev:
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
+outsonypienable:
+	kfifo_free(&meye.doneq);
+outkfifoalloc2:
+	kfifo_free(&meye.grabq);
+outkfifoalloc1:
+	vfree(meye.grab_temp);
+outvmalloc:
+	video_device_release(meye.vdev);
+outnotdev:
+	return ret;
+}
+
+static void __devexit meye_remove(struct pci_dev *pcidev)
+{
+	video_unregister_device(meye.vdev);
+
+	mchip_hic_stop();
+
+	mchip_dma_free();
+
+	/* disable interrupts */
+	mchip_set(MCHIP_MM_INTA, 0x0);
+
+	free_irq(meye.mchip_irq, meye_irq);
+
+	iounmap(meye.mchip_mmregs);
+
+	release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+			   pci_resource_len(meye.mchip_dev, 0));
+
+	pci_disable_device(meye.mchip_dev);
+
+	sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
+
+	kfifo_free(&meye.doneq);
+	kfifo_free(&meye.grabq);
+
+	vfree(meye.grab_temp);
+
+	if (meye.grab_fbuffer) {
+		rvfree(meye.grab_fbuffer, gbuffers*gbufsize);
+		meye.grab_fbuffer = NULL;
+	}
+
+	printk(KERN_INFO "meye: removed\n");
+}
+
+static struct pci_device_id meye_pci_tbl[] = {
+	{ PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, meye_pci_tbl);
+
+static struct pci_driver meye_driver = {
+	.name		= "meye",
+	.id_table	= meye_pci_tbl,
+	.probe		= meye_probe,
+	.remove		= __devexit_p(meye_remove),
+#ifdef CONFIG_PM
+	.suspend	= meye_suspend,
+	.resume		= meye_resume,
+#endif
+};
+
+static int __init meye_init(void)
+{
+	gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS));
+	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) "
+			 "for capture\n",
+			 gbuffers,
+			 gbufsize / 1024, gbuffers * gbufsize / 1024);
+	return pci_register_driver(&meye_driver);
+}
+
+static void __exit meye_exit(void)
+{
+	pci_unregister_driver(&meye_driver);
+}
+
+module_init(meye_init);
+module_exit(meye_exit);
diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h
new file mode 100644
index 000000000000..4bdeb03f1644
--- /dev/null
+++ b/drivers/media/pci/meye/meye.h
@@ -0,0 +1,324 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * 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 _MEYE_PRIV_H_
+#define _MEYE_PRIV_H_
+
+#define MEYE_DRIVER_MAJORVERSION	 1
+#define MEYE_DRIVER_MINORVERSION	14
+
+#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \
+			    __stringify(MEYE_DRIVER_MINORVERSION)
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kfifo.h>
+
+/****************************************************************************/
+/* Motion JPEG chip registers                                               */
+/****************************************************************************/
+
+/* Motion JPEG chip PCI configuration registers */
+#define MCHIP_PCI_POWER_CSR		0x54
+#define MCHIP_PCI_MCORE_STATUS		0x60		/* see HIC_STATUS   */
+#define MCHIP_PCI_HOSTUSEREQ_SET	0x64
+#define MCHIP_PCI_HOSTUSEREQ_CLR	0x68
+#define MCHIP_PCI_LOWPOWER_SET		0x6c
+#define MCHIP_PCI_LOWPOWER_CLR		0x70
+#define MCHIP_PCI_SOFTRESET_SET		0x74
+
+/* Motion JPEG chip memory mapped registers */
+#define MCHIP_MM_REGS			0x200		/* 512 bytes        */
+#define MCHIP_REG_TIMEOUT		1000		/* reg access, ~us  */
+#define MCHIP_MCC_VRJ_TIMEOUT		1000		/* MCC & VRJ access */
+
+#define MCHIP_MM_PCI_MODE		0x00		/* PCI access mode */
+#define MCHIP_MM_PCI_MODE_RETRY		0x00000001	/* retry mode */
+#define MCHIP_MM_PCI_MODE_MASTER	0x00000002	/* master access */
+#define MCHIP_MM_PCI_MODE_READ_LINE	0x00000004	/* read line */
+
+#define MCHIP_MM_INTA			0x04		/* Int status/mask */
+#define MCHIP_MM_INTA_MCC		0x00000001	/* MCC interrupt */
+#define MCHIP_MM_INTA_VRJ		0x00000002	/* VRJ interrupt */
+#define MCHIP_MM_INTA_HIC_1		0x00000004	/* one frame done */
+#define MCHIP_MM_INTA_HIC_1_MASK	0x00000400	/* 1: enable */
+#define MCHIP_MM_INTA_HIC_END		0x00000008	/* all frames done */
+#define MCHIP_MM_INTA_HIC_END_MASK	0x00000800
+#define MCHIP_MM_INTA_JPEG		0x00000010	/* decompress. error */
+#define MCHIP_MM_INTA_JPEG_MASK		0x00001000
+#define MCHIP_MM_INTA_CAPTURE		0x00000020	/* capture end */
+#define MCHIP_MM_INTA_PCI_ERR		0x00000040	/* PCI error */
+#define MCHIP_MM_INTA_PCI_ERR_MASK	0x00004000
+
+#define MCHIP_MM_PT_ADDR		0x08		/* page table address*/
+							/* n*4kB */
+#define MCHIP_NB_PAGES			1024		/* pages for display */
+#define MCHIP_NB_PAGES_MJPEG		256		/* pages for mjpeg */
+
+#define MCHIP_MM_FIR(n)			(0x0c+(n)*4)	/* Frame info 0-3 */
+#define MCHIP_MM_FIR_RDY		0x00000001	/* frame ready */
+#define MCHIP_MM_FIR_FAILFR_MASK	0xf8000000	/* # of failed frames */
+#define MCHIP_MM_FIR_FAILFR_SHIFT	27
+
+	/* continuous comp/decomp mode */
+#define MCHIP_MM_FIR_C_ENDL_MASK	0x000007fe	/* end DW [10] */
+#define MCHIP_MM_FIR_C_ENDL_SHIFT	1
+#define MCHIP_MM_FIR_C_ENDP_MASK	0x0007f800	/* end page [8] */
+#define MCHIP_MM_FIR_C_ENDP_SHIFT	11
+#define MCHIP_MM_FIR_C_STARTP_MASK	0x07f80000	/* start page [8] */
+#define MCHIP_MM_FIR_C_STARTP_SHIFT	19
+
+	/* continuous picture output mode */
+#define MCHIP_MM_FIR_O_STARTP_MASK	0x7ffe0000	/* start page [10] */
+#define MCHIP_MM_FIR_O_STARTP_SHIFT	17
+
+#define MCHIP_MM_FIFO_DATA		0x1c		/* PCI TGT FIFO data */
+#define MCHIP_MM_FIFO_STATUS		0x20		/* PCI TGT FIFO stat */
+#define MCHIP_MM_FIFO_MASK		0x00000003
+#define MCHIP_MM_FIFO_WAIT_OR_READY	0x00000002      /* Bits common to WAIT & READY*/
+#define MCHIP_MM_FIFO_IDLE		0x0		/* HIC idle */
+#define MCHIP_MM_FIFO_IDLE1		0x1		/* idem ??? */
+#define	MCHIP_MM_FIFO_WAIT		0x2		/* wait request */
+#define MCHIP_MM_FIFO_READY		0x3		/* data ready */
+
+#define MCHIP_HIC_HOST_USEREQ		0x40		/* host uses MCORE */
+
+#define MCHIP_HIC_TP_BUSY		0x44		/* taking picture */
+
+#define MCHIP_HIC_PIC_SAVED		0x48		/* pic in SDRAM */
+
+#define MCHIP_HIC_LOWPOWER		0x4c		/* clock stopped */
+
+#define MCHIP_HIC_CTL			0x50		/* HIC control */
+#define MCHIP_HIC_CTL_SOFT_RESET	0x00000001	/* MCORE reset */
+#define MCHIP_HIC_CTL_MCORE_RDY		0x00000002	/* MCORE ready */
+
+#define MCHIP_HIC_CMD			0x54		/* HIC command */
+#define MCHIP_HIC_CMD_BITS		0x00000003      /* cmd width=[1:0]*/
+#define MCHIP_HIC_CMD_NOOP		0x0
+#define MCHIP_HIC_CMD_START		0x1
+#define MCHIP_HIC_CMD_STOP		0x2
+
+#define MCHIP_HIC_MODE			0x58
+#define MCHIP_HIC_MODE_NOOP		0x0
+#define MCHIP_HIC_MODE_STILL_CAP	0x1		/* still pic capt */
+#define MCHIP_HIC_MODE_DISPLAY		0x2		/* display */
+#define MCHIP_HIC_MODE_STILL_COMP	0x3		/* still pic comp. */
+#define MCHIP_HIC_MODE_STILL_DECOMP	0x4		/* still pic decomp. */
+#define MCHIP_HIC_MODE_CONT_COMP	0x5		/* cont capt+comp */
+#define MCHIP_HIC_MODE_CONT_DECOMP	0x6		/* cont decomp+disp */
+#define MCHIP_HIC_MODE_STILL_OUT	0x7		/* still pic output */
+#define MCHIP_HIC_MODE_CONT_OUT		0x8		/* cont output */
+
+#define MCHIP_HIC_STATUS		0x5c
+#define MCHIP_HIC_STATUS_MCC_RDY	0x00000001	/* MCC reg acc ok */
+#define MCHIP_HIC_STATUS_VRJ_RDY	0x00000002	/* VRJ reg acc ok */
+#define MCHIP_HIC_STATUS_IDLE           0x00000003
+#define MCHIP_HIC_STATUS_CAPDIS		0x00000004	/* cap/disp in prog */
+#define MCHIP_HIC_STATUS_COMPDEC	0x00000008	/* (de)comp in prog */
+#define MCHIP_HIC_STATUS_BUSY		0x00000010	/* HIC busy */
+
+#define MCHIP_HIC_S_RATE		0x60		/* MJPEG # frames */
+
+#define MCHIP_HIC_PCI_VFMT		0x64		/* video format */
+#define MCHIP_HIC_PCI_VFMT_YVYU		0x00000001	/* 0: V Y' U Y */
+							/* 1: Y' V Y U */
+
+#define MCHIP_MCC_CMD			0x80		/* MCC commands */
+#define MCHIP_MCC_CMD_INITIAL		0x0		/* idle ? */
+#define MCHIP_MCC_CMD_IIC_START_SET	0x1
+#define MCHIP_MCC_CMD_IIC_END_SET	0x2
+#define MCHIP_MCC_CMD_FM_WRITE		0x3		/* frame memory */
+#define MCHIP_MCC_CMD_FM_READ		0x4
+#define MCHIP_MCC_CMD_FM_STOP		0x5
+#define MCHIP_MCC_CMD_CAPTURE		0x6
+#define MCHIP_MCC_CMD_DISPLAY		0x7
+#define MCHIP_MCC_CMD_END_DISP		0x8
+#define MCHIP_MCC_CMD_STILL_COMP	0x9
+#define MCHIP_MCC_CMD_STILL_DECOMP	0xa
+#define MCHIP_MCC_CMD_STILL_OUTPUT	0xb
+#define MCHIP_MCC_CMD_CONT_OUTPUT	0xc
+#define MCHIP_MCC_CMD_CONT_COMP		0xd
+#define MCHIP_MCC_CMD_CONT_DECOMP	0xe
+#define MCHIP_MCC_CMD_RESET		0xf		/* MCC reset */
+
+#define MCHIP_MCC_IIC_WR		0x84
+
+#define MCHIP_MCC_MCC_WR		0x88
+
+#define MCHIP_MCC_MCC_RD		0x8c
+
+#define MCHIP_MCC_STATUS		0x90
+#define MCHIP_MCC_STATUS_CAPT		0x00000001	/* capturing */
+#define MCHIP_MCC_STATUS_DISP		0x00000002	/* displaying */
+#define MCHIP_MCC_STATUS_COMP		0x00000004	/* compressing */
+#define MCHIP_MCC_STATUS_DECOMP		0x00000008	/* decompressing */
+#define MCHIP_MCC_STATUS_MCC_WR		0x00000010	/* register ready */
+#define MCHIP_MCC_STATUS_MCC_RD		0x00000020	/* register ready */
+#define MCHIP_MCC_STATUS_IIC_WR		0x00000040	/* register ready */
+#define MCHIP_MCC_STATUS_OUTPUT		0x00000080	/* output in prog */
+
+#define MCHIP_MCC_SIG_POLARITY		0x94
+#define MCHIP_MCC_SIG_POL_VS_H		0x00000001	/* VS active-high */
+#define MCHIP_MCC_SIG_POL_HS_H		0x00000002	/* HS active-high */
+#define MCHIP_MCC_SIG_POL_DOE_H		0x00000004	/* DOE active-high */
+
+#define MCHIP_MCC_IRQ			0x98
+#define MCHIP_MCC_IRQ_CAPDIS_STRT	0x00000001	/* cap/disp started */
+#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK	0x00000010
+#define MCHIP_MCC_IRQ_CAPDIS_END	0x00000002	/* cap/disp ended */
+#define MCHIP_MCC_IRQ_CAPDIS_END_MASK	0x00000020
+#define MCHIP_MCC_IRQ_COMPDEC_STRT	0x00000004	/* (de)comp started */
+#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK	0x00000040
+#define MCHIP_MCC_IRQ_COMPDEC_END	0x00000008	/* (de)comp ended */
+#define MCHIP_MCC_IRQ_COMPDEC_END_MASK	0x00000080
+
+#define MCHIP_MCC_HSTART		0x9c		/* video in */
+#define MCHIP_MCC_VSTART		0xa0
+#define MCHIP_MCC_HCOUNT		0xa4
+#define MCHIP_MCC_VCOUNT		0xa8
+#define MCHIP_MCC_R_XBASE		0xac		/* capt/disp */
+#define MCHIP_MCC_R_YBASE		0xb0
+#define MCHIP_MCC_R_XRANGE		0xb4
+#define MCHIP_MCC_R_YRANGE		0xb8
+#define MCHIP_MCC_B_XBASE		0xbc		/* comp/decomp */
+#define MCHIP_MCC_B_YBASE		0xc0
+#define MCHIP_MCC_B_XRANGE		0xc4
+#define MCHIP_MCC_B_YRANGE		0xc8
+
+#define MCHIP_MCC_R_SAMPLING		0xcc		/* 1: 1:4 */
+
+#define MCHIP_VRJ_CMD			0x100		/* VRJ commands */
+
+/* VRJ registers (see table 12.2.4) */
+#define MCHIP_VRJ_COMPRESSED_DATA	0x1b0
+#define MCHIP_VRJ_PIXEL_DATA		0x1b8
+
+#define MCHIP_VRJ_BUS_MODE		0x100
+#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL	0x108
+#define MCHIP_VRJ_PDAT_USE		0x110
+#define MCHIP_VRJ_MODE_SPECIFY		0x118
+#define MCHIP_VRJ_LIMIT_COMPRESSED_LO	0x120
+#define MCHIP_VRJ_LIMIT_COMPRESSED_HI	0x124
+#define MCHIP_VRJ_COMP_DATA_FORMAT	0x128
+#define MCHIP_VRJ_TABLE_DATA		0x140
+#define MCHIP_VRJ_RESTART_INTERVAL	0x148
+#define MCHIP_VRJ_NUM_LINES		0x150
+#define MCHIP_VRJ_NUM_PIXELS		0x158
+#define MCHIP_VRJ_NUM_COMPONENTS	0x160
+#define MCHIP_VRJ_SOF1			0x168
+#define MCHIP_VRJ_SOF2			0x170
+#define MCHIP_VRJ_SOF3			0x178
+#define MCHIP_VRJ_SOF4			0x180
+#define MCHIP_VRJ_SOS			0x188
+#define MCHIP_VRJ_SOFT_RESET		0x190
+
+#define MCHIP_VRJ_STATUS		0x1c0
+#define MCHIP_VRJ_STATUS_BUSY		0x00001
+#define MCHIP_VRJ_STATUS_COMP_ACCESS	0x00002
+#define MCHIP_VRJ_STATUS_PIXEL_ACCESS	0x00004
+#define MCHIP_VRJ_STATUS_ERROR		0x00008
+
+#define MCHIP_VRJ_IRQ_FLAG		0x1c8
+#define MCHIP_VRJ_ERROR_REPORT		0x1d8
+
+#define MCHIP_VRJ_START_COMMAND		0x1a0
+
+/****************************************************************************/
+/* Driver definitions.                                                      */
+/****************************************************************************/
+
+/* Sony Programmable I/O Controller for accessing the camera commands */
+#include <linux/sony-laptop.h>
+
+/* private API definitions */
+#include <linux/meye.h>
+#include <linux/mutex.h>
+
+
+/* Enable jpg software correction */
+#define MEYE_JPEG_CORRECTION	1
+
+/* Maximum size of a buffer */
+#define MEYE_MAX_BUFSIZE	614400	/* 640 * 480 * 2 */
+
+/* Maximum number of buffers */
+#define MEYE_MAX_BUFNBRS	32
+
+/* State of a buffer */
+#define MEYE_BUF_UNUSED	0	/* not used */
+#define MEYE_BUF_USING	1	/* currently grabbing / playing */
+#define MEYE_BUF_DONE	2	/* done */
+
+/* grab buffer */
+struct meye_grab_buffer {
+	int state;			/* state of buffer */
+	unsigned long size;		/* size of jpg frame */
+	struct timeval timestamp;	/* timestamp */
+	unsigned long sequence;		/* sequence number */
+};
+
+/* size of kfifos containings buffer indices */
+#define MEYE_QUEUE_SIZE	MEYE_MAX_BUFNBRS
+
+/* Motion Eye device structure */
+struct meye {
+	struct v4l2_device v4l2_dev;	/* Main v4l2_device struct */
+	struct pci_dev *mchip_dev;	/* pci device */
+	u8 mchip_irq;			/* irq */
+	u8 mchip_mode;			/* actual mchip mode: HIC_MODE... */
+	u8 mchip_fnum;			/* current mchip frame number */
+	unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */
+	u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */
+	void *mchip_ptable_toc;		/* mchip: ptable toc */
+	dma_addr_t mchip_dmahandle;	/* mchip: dma handle to ptable toc */
+	unsigned char *grab_fbuffer;	/* capture framebuffer */
+	unsigned char *grab_temp;	/* temporary buffer */
+					/* list of buffers */
+	struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS];
+	int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */
+	struct mutex lock;		/* mutex for open/mmap... */
+	struct kfifo grabq;		/* queue for buffers to be grabbed */
+	spinlock_t grabq_lock;		/* lock protecting the queue */
+	struct kfifo doneq;		/* queue for grabbed buffers */
+	spinlock_t doneq_lock;		/* lock protecting the queue */
+	wait_queue_head_t proc_list;	/* wait queue */
+	struct video_device *vdev;	/* video device parameters */
+	u16 brightness;
+	u16 hue;
+	u16 contrast;
+	u16 colour;
+	struct meye_params params;	/* additional parameters */
+	unsigned long in_use;		/* set to 1 if the device is in use */
+#ifdef CONFIG_PM
+	u8 pm_mchip_mode;		/* old mchip mode */
+#endif
+};
+
+#endif
diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig
new file mode 100644
index 000000000000..637d506b23c5
--- /dev/null
+++ b/drivers/media/pci/ngene/Kconfig
@@ -0,0 +1,13 @@
+config DVB_NGENE
+	tristate "Micronas nGene support"
+	depends on DVB_CORE && PCI && I2C
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  Support for Micronas PCI express cards with nGene bridge.
+
diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile
new file mode 100644
index 000000000000..5c0b5d6b9d69
--- /dev/null
+++ b/drivers/media/pci/ngene/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the nGene device driver
+#
+
+ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o
+
+obj-$(CONFIG_DVB_NGENE) += ngene.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
+
+# For the staging CI driver cxd2099
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
new file mode 100644
index 000000000000..96a13ed197d0
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -0,0 +1,823 @@
+/*
+ * ngene-cards.c: nGene PCIe bridge driver - card specific info
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ *                         Modifications for new nGene firmware,
+ *                         support for EEPROM-copying,
+ *                         support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include "ngene.h"
+
+/* demods/tuners */
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "lgdt330x.h"
+#include "mt2131.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
+#include "drxd.h"
+#include "dvb-pll.h"
+
+
+/****************************************************************************/
+/* Demod/tuner attachment ***************************************************/
+/****************************************************************************/
+
+static int tuner_attach_stv6110(struct ngene_channel *chan)
+{
+	struct i2c_adapter *i2c;
+	struct stv090x_config *feconf = (struct stv090x_config *)
+		chan->dev->card_info->fe_config[chan->number];
+	struct stv6110x_config *tunerconf = (struct stv6110x_config *)
+		chan->dev->card_info->tuner_config[chan->number];
+	struct stv6110x_devctl *ctl;
+
+	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+	if (chan->number < 2)
+		i2c = &chan->dev->channel[0].i2c_adapter;
+	else
+		i2c = &chan->dev->channel[1].i2c_adapter;
+
+	ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
+	if (ctl == NULL) {
+		printk(KERN_ERR	DEVICE_NAME ": No STV6110X found!\n");
+		return -ENODEV;
+	}
+
+	feconf->tuner_init          = ctl->tuner_init;
+	feconf->tuner_sleep         = ctl->tuner_sleep;
+	feconf->tuner_set_mode      = ctl->tuner_set_mode;
+	feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+	feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+	feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+	feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+	feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
+	feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
+	feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
+	feconf->tuner_get_status    = ctl->tuner_get_status;
+
+	return 0;
+}
+
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct ngene_channel *chan = fe->sec_priv;
+	int status;
+
+	if (enable) {
+		down(&chan->dev->pll_mutex);
+		status = chan->gate_ctrl(fe, 1);
+	} else {
+		status = chan->gate_ctrl(fe, 0);
+		up(&chan->dev->pll_mutex);
+	}
+	return status;
+}
+
+static int tuner_attach_tda18271(struct ngene_channel *chan)
+{
+	struct i2c_adapter *i2c;
+	struct dvb_frontend *fe;
+
+	i2c = &chan->dev->channel[0].i2c_adapter;
+	if (chan->fe->ops.i2c_gate_ctrl)
+		chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+	fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+	if (chan->fe->ops.i2c_gate_ctrl)
+		chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+	if (!fe) {
+		printk(KERN_ERR "No TDA18271 found!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int tuner_attach_probe(struct ngene_channel *chan)
+{
+	if (chan->demod_type == 0)
+		return tuner_attach_stv6110(chan);
+	if (chan->demod_type == 1)
+		return tuner_attach_tda18271(chan);
+	return -EINVAL;
+}
+
+static int demod_attach_stv0900(struct ngene_channel *chan)
+{
+	struct i2c_adapter *i2c;
+	struct stv090x_config *feconf = (struct stv090x_config *)
+		chan->dev->card_info->fe_config[chan->number];
+
+	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+	/* Note: Both adapters share the same i2c bus, but the demod     */
+	/*       driver requires that each demod has its own i2c adapter */
+	if (chan->number < 2)
+		i2c = &chan->dev->channel[0].i2c_adapter;
+	else
+		i2c = &chan->dev->channel[1].i2c_adapter;
+
+	chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
+			(chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
+						: STV090x_DEMODULATOR_1);
+	if (chan->fe == NULL) {
+		printk(KERN_ERR	DEVICE_NAME ": No STV0900 found!\n");
+		return -ENODEV;
+	}
+
+	/* store channel info */
+	if (feconf->tuner_i2c_lock)
+		chan->fe->analog_demod_priv = chan;
+
+	if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
+			0, chan->dev->card_info->lnb[chan->number])) {
+		printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+		dvb_frontend_detach(chan->fe);
+		chan->fe = NULL;
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
+{
+	struct ngene_channel *chan = fe->analog_demod_priv;
+
+	if (lock)
+		down(&chan->dev->pll_mutex);
+	else
+		up(&chan->dev->pll_mutex);
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+			  u16 reg, u8 *val)
+{
+	u8 msg[2] = {reg>>8, reg&0xff};
+	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+				   .buf  = msg, .len   = 2},
+				  {.addr = adr, .flags = I2C_M_RD,
+				   .buf  = val, .len   = 1} };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+{
+	u8 val;
+	if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_drxk(struct i2c_adapter *i2c, int port)
+{
+	u8 val;
+
+	if (i2c_read(i2c, 0x29+port, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int demod_attach_drxk(struct ngene_channel *chan,
+			     struct i2c_adapter *i2c)
+{
+	struct drxk_config config;
+
+	memset(&config, 0, sizeof(config));
+	config.microcode_name = "drxk_a3.mc";
+	config.qam_demod_parameter_count = 4;
+	config.adr = 0x29 + (chan->number ^ 2);
+
+	chan->fe = dvb_attach(drxk_attach, &config, i2c);
+	if (!chan->fe) {
+		printk(KERN_ERR "No DRXK found!\n");
+		return -ENODEV;
+	}
+	chan->fe->sec_priv = chan;
+	chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+	chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+	return 0;
+}
+
+static int cineS2_probe(struct ngene_channel *chan)
+{
+	struct i2c_adapter *i2c;
+	struct stv090x_config *fe_conf;
+	u8 buf[3];
+	struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
+	int rc;
+
+	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+	if (chan->number < 2)
+		i2c = &chan->dev->channel[0].i2c_adapter;
+	else
+		i2c = &chan->dev->channel[1].i2c_adapter;
+
+	if (port_has_stv0900(i2c, chan->number)) {
+		chan->demod_type = 0;
+		fe_conf = chan->dev->card_info->fe_config[chan->number];
+		/* demod found, attach it */
+		rc = demod_attach_stv0900(chan);
+		if (rc < 0 || chan->number < 2)
+			return rc;
+
+		/* demod #2: reprogram outputs DPN1 & DPN2 */
+		i2c_msg.addr = fe_conf->address;
+		i2c_msg.len = 3;
+		buf[0] = 0xf1;
+		switch (chan->number) {
+		case 2:
+			buf[1] = 0x5c;
+			buf[2] = 0xc2;
+			break;
+		case 3:
+			buf[1] = 0x61;
+			buf[2] = 0xcc;
+			break;
+		default:
+			return -ENODEV;
+		}
+		rc = i2c_transfer(i2c, &i2c_msg, 1);
+		if (rc != 1) {
+			printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+			return -EIO;
+		}
+	} else if (port_has_drxk(i2c, chan->number^2)) {
+		chan->demod_type = 1;
+		demod_attach_drxk(chan, i2c);
+	} else {
+		printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static struct lgdt330x_config aver_m780 = {
+	.demod_address = 0xb2 >> 1,
+	.demod_chip    = LGDT3303,
+	.serial_mpeg   = 0x00, /* PARALLEL */
+	.clock_polarity_flip = 1,
+};
+
+static struct mt2131_config m780_tunerconfig = {
+	0xc0 >> 1
+};
+
+/* A single func to attach the demo and tuner, rather than
+ * use two sep funcs like the current design mandates.
+ */
+static int demod_attach_lg330x(struct ngene_channel *chan)
+{
+	chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
+	if (chan->fe == NULL) {
+		printk(KERN_ERR	DEVICE_NAME ": No LGDT330x found!\n");
+		return -ENODEV;
+	}
+
+	dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
+		   &m780_tunerconfig, 0);
+
+	return (chan->fe) ? 0 : -ENODEV;
+}
+
+static int demod_attach_drxd(struct ngene_channel *chan)
+{
+	struct drxd_config *feconf;
+
+	feconf = chan->dev->card_info->fe_config[chan->number];
+
+	chan->fe = dvb_attach(drxd_attach, feconf, chan,
+			&chan->i2c_adapter, &chan->dev->pci_dev->dev);
+	if (!chan->fe) {
+		pr_err("No DRXD found!\n");
+		return -ENODEV;
+	}
+
+	if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
+			&chan->i2c_adapter,
+			feconf->pll_type)) {
+		pr_err("No pll(%d) found!\n", feconf->pll_type);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/****************************************************************************/
+/* EEPROM TAGS **************************************************************/
+/****************************************************************************/
+
+#define MICNG_EE_START      0x0100
+#define MICNG_EE_END        0x0FF0
+
+#define MICNG_EETAG_END0    0x0000
+#define MICNG_EETAG_END1    0xFFFF
+
+/* 0x0001 - 0x000F reserved for housekeeping */
+/* 0xFFFF - 0xFFFE reserved for housekeeping */
+
+/* Micronas assigned tags
+   EEProm tags for hardware support */
+
+#define MICNG_EETAG_DRXD1_OSCDEVIATION  0x1000  /* 2 Bytes data */
+#define MICNG_EETAG_DRXD2_OSCDEVIATION  0x1001  /* 2 Bytes data */
+
+#define MICNG_EETAG_MT2060_1_1STIF      0x1100  /* 2 Bytes data */
+#define MICNG_EETAG_MT2060_2_1STIF      0x1101  /* 2 Bytes data */
+
+/* Tag range for OEMs */
+
+#define MICNG_EETAG_OEM_FIRST  0xC000
+#define MICNG_EETAG_OEM_LAST   0xFFEF
+
+static int i2c_write_eeprom(struct i2c_adapter *adapter,
+			    u8 adr, u16 reg, u8 data)
+{
+	u8 m[3] = {(reg >> 8), (reg & 0xff), data};
+	struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
+			      .len = sizeof(m)};
+
+	if (i2c_transfer(adapter, &msg, 1) != 1) {
+		pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int i2c_read_eeprom(struct i2c_adapter *adapter,
+			   u8 adr, u16 reg, u8 *data, int len)
+{
+	u8 msg[2] = {(reg >> 8), (reg & 0xff)};
+	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+				   .buf = msg, .len = 2 },
+				  {.addr = adr, .flags = I2C_M_RD,
+				   .buf = data, .len = len} };
+
+	if (i2c_transfer(adapter, msgs, 2) != 2) {
+		pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int ReadEEProm(struct i2c_adapter *adapter,
+		      u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
+{
+	int status = 0;
+	u16 Addr = MICNG_EE_START, Length, tag = 0;
+	u8  EETag[3];
+
+	while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+		if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+			return -1;
+		tag = (EETag[0] << 8) | EETag[1];
+		if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+			return -1;
+		if (tag == Tag)
+			break;
+		Addr += sizeof(u16) + 1 + EETag[2];
+	}
+	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+		pr_err(DEVICE_NAME
+		       ": Reached EOEE @ Tag = %04x Length = %3d\n",
+		       tag, EETag[2]);
+		return -1;
+	}
+	Length = EETag[2];
+	if (Length > MaxLen)
+		Length = (u16) MaxLen;
+	if (Length > 0) {
+		Addr += sizeof(u16) + 1;
+		status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
+		if (!status) {
+			*pLength = EETag[2];
+			if (Length < EETag[2])
+				; /*status=STATUS_BUFFER_OVERFLOW; */
+		}
+	}
+	return status;
+}
+
+static int WriteEEProm(struct i2c_adapter *adapter,
+		       u16 Tag, u32 Length, u8 *data)
+{
+	int status = 0;
+	u16 Addr = MICNG_EE_START;
+	u8 EETag[3];
+	u16 tag = 0;
+	int retry, i;
+
+	while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+		if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+			return -1;
+		tag = (EETag[0] << 8) | EETag[1];
+		if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+			return -1;
+		if (tag == Tag)
+			break;
+		Addr += sizeof(u16) + 1 + EETag[2];
+	}
+	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+		pr_err(DEVICE_NAME
+		       ": Reached EOEE @ Tag = %04x Length = %3d\n",
+		       tag, EETag[2]);
+		return -1;
+	}
+
+	if (Length > EETag[2])
+		return -EINVAL;
+	/* Note: We write the data one byte at a time to avoid
+	   issues with page sizes. (which are different for
+	   each manufacture and eeprom size)
+	 */
+	Addr += sizeof(u16) + 1;
+	for (i = 0; i < Length; i++, Addr++) {
+		status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
+
+		if (status)
+			break;
+
+		/* Poll for finishing write cycle */
+		retry = 10;
+		while (retry) {
+			u8 Tmp;
+
+			msleep(50);
+			status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
+			if (status)
+				break;
+			if (Tmp != data[i])
+				pr_err(DEVICE_NAME
+				       "eeprom write error\n");
+			retry -= 1;
+		}
+		if (status) {
+			pr_err(DEVICE_NAME
+			       ": Timeout polling eeprom\n");
+			break;
+		}
+	}
+	return status;
+}
+
+static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
+{
+	int stat;
+	u8 buf[2];
+	u32 len = 0;
+
+	stat = ReadEEProm(adapter, tag, 2, buf, &len);
+	if (stat)
+		return stat;
+	if (len != 2)
+		return -EINVAL;
+
+	*data = (buf[0] << 8) | buf[1];
+	return 0;
+}
+
+static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
+{
+	int stat;
+	u8 buf[2];
+
+	buf[0] = data >> 8;
+	buf[1] = data & 0xff;
+	stat = WriteEEProm(adapter, tag, 2, buf);
+	if (stat)
+		return stat;
+	return 0;
+}
+
+static s16 osc_deviation(void *priv, s16 deviation, int flag)
+{
+	struct ngene_channel *chan = priv;
+	struct i2c_adapter *adap = &chan->i2c_adapter;
+	u16 data = 0;
+
+	if (flag) {
+		data = (u16) deviation;
+		pr_info(DEVICE_NAME ": write deviation %d\n",
+		       deviation);
+		eeprom_write_ushort(adap, 0x1000 + chan->number, data);
+	} else {
+		if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
+			data = 0;
+		pr_info(DEVICE_NAME ": read deviation %d\n",
+		       (s16) data);
+	}
+
+	return (s16) data;
+}
+
+/****************************************************************************/
+/* Switch control (I2C gates, etc.) *****************************************/
+/****************************************************************************/
+
+
+static struct stv090x_config fe_cineS2 = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x68,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+
+	.tuner_i2c_lock = cineS2_tuner_i2c_lock,
+};
+
+static struct stv090x_config fe_cineS2_2 = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x69,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+
+	.tuner_i2c_lock = cineS2_tuner_i2c_lock,
+};
+
+static struct stv6110x_config tuner_cineS2_0 = {
+	.addr	= 0x60,
+	.refclk	= 27000000,
+	.clk_div = 1,
+};
+
+static struct stv6110x_config tuner_cineS2_1 = {
+	.addr	= 0x63,
+	.refclk	= 27000000,
+	.clk_div = 1,
+};
+
+static struct ngene_info ngene_info_cineS2 = {
+	.type		= NGENE_SIDEWINDER,
+	.name		= "Linux4Media cineS2 DVB-S2 Twin Tuner",
+	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN},
+	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110},
+	.fe_config	= {&fe_cineS2, &fe_cineS2},
+	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1},
+	.lnb		= {0x0b, 0x08},
+	.tsf		= {3, 3},
+	.fw_version	= 18,
+	.msi_supported	= true,
+};
+
+static struct ngene_info ngene_info_satixS2 = {
+	.type		= NGENE_SIDEWINDER,
+	.name		= "Mystique SaTiX-S2 Dual",
+	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN},
+	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110},
+	.fe_config	= {&fe_cineS2, &fe_cineS2},
+	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1},
+	.lnb		= {0x0b, 0x08},
+	.tsf		= {3, 3},
+	.fw_version	= 18,
+	.msi_supported	= true,
+};
+
+static struct ngene_info ngene_info_satixS2v2 = {
+	.type		= NGENE_SIDEWINDER,
+	.name		= "Mystique SaTiX-S2 Dual (v2)",
+	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+			   NGENE_IO_TSOUT},
+	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
+	.tsf		= {3, 3},
+	.fw_version	= 18,
+	.msi_supported	= true,
+};
+
+static struct ngene_info ngene_info_cineS2v5 = {
+	.type		= NGENE_SIDEWINDER,
+	.name		= "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
+	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+			   NGENE_IO_TSOUT},
+	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
+	.tsf		= {3, 3},
+	.fw_version	= 18,
+	.msi_supported	= true,
+};
+
+
+static struct ngene_info ngene_info_duoFlex = {
+	.type           = NGENE_SIDEWINDER,
+	.name           = "Digital Devices DuoFlex PCIe or miniPCIe",
+	.io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+			   NGENE_IO_TSOUT},
+	.demod_attach   = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
+	.tuner_attach   = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
+	.fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+	.tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+	.lnb            = {0x0a, 0x08, 0x0b, 0x09},
+	.tsf            = {3, 3},
+	.fw_version     = 18,
+	.msi_supported	= true,
+};
+
+static struct ngene_info ngene_info_m780 = {
+	.type           = NGENE_APP,
+	.name           = "Aver M780 ATSC/QAM-B",
+
+	/* Channel 0 is analog, which is currently unsupported */
+	.io_type        = { NGENE_IO_NONE, NGENE_IO_TSIN },
+	.demod_attach   = { NULL, demod_attach_lg330x },
+
+	/* Ensure these are NULL else the frame will call them (as funcs) */
+	.tuner_attach   = { 0, 0, 0, 0 },
+	.fe_config      = { NULL, &aver_m780 },
+	.avf            = { 0 },
+
+	/* A custom electrical interface config for the demod to bridge */
+	.tsf		= { 4, 4 },
+	.fw_version	= 15,
+};
+
+static struct drxd_config fe_terratec_dvbt_0 = {
+	.index          = 0,
+	.demod_address  = 0x70,
+	.demod_revision = 0xa2,
+	.demoda_address = 0x00,
+	.pll_address    = 0x60,
+	.pll_type       = DVB_PLL_THOMSON_DTT7520X,
+	.clock          = 20000,
+	.osc_deviation  = osc_deviation,
+};
+
+static struct drxd_config fe_terratec_dvbt_1 = {
+	.index          = 1,
+	.demod_address  = 0x71,
+	.demod_revision = 0xa2,
+	.demoda_address = 0x00,
+	.pll_address    = 0x60,
+	.pll_type       = DVB_PLL_THOMSON_DTT7520X,
+	.clock          = 20000,
+	.osc_deviation  = osc_deviation,
+};
+
+static struct ngene_info ngene_info_terratec = {
+	.type           = NGENE_TERRATEC,
+	.name           = "Terratec Integra/Cinergy2400i Dual DVB-T",
+	.io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+	.demod_attach   = {demod_attach_drxd, demod_attach_drxd},
+	.fe_config      = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
+	.i2c_access     = 1,
+};
+
+/****************************************************************************/
+
+
+
+/****************************************************************************/
+/* PCI Subsystem ID *********************************************************/
+/****************************************************************************/
+
+#define NGENE_ID(_subvend, _subdev, _driverdata) { \
+	.vendor = NGENE_VID, .device = NGENE_PID, \
+	.subvendor = _subvend, .subdevice = _subdev, \
+	.driver_data = (unsigned long) &_driverdata }
+
+/****************************************************************************/
+
+static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
+	NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
+	NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
+	NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
+	NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
+	NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
+	NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+	NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
+	NGENE_ID(0x1461, 0x062e, ngene_info_m780),
+	NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
+	{0}
+};
+MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
+
+/****************************************************************************/
+/* Init/Exit ****************************************************************/
+/****************************************************************************/
+
+static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
+					     enum pci_channel_state state)
+{
+	printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+	if (state == pci_channel_io_perm_failure)
+		return PCI_ERS_RESULT_DISCONNECT;
+	if (state == pci_channel_io_frozen)
+		return PCI_ERS_RESULT_NEED_RESET;
+	return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
+{
+	printk(KERN_INFO DEVICE_NAME ": link reset\n");
+	return 0;
+}
+
+static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
+{
+	printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+	return 0;
+}
+
+static void ngene_resume(struct pci_dev *dev)
+{
+	printk(KERN_INFO DEVICE_NAME ": resume\n");
+}
+
+static const struct pci_error_handlers ngene_errors = {
+	.error_detected = ngene_error_detected,
+	.link_reset = ngene_link_reset,
+	.slot_reset = ngene_slot_reset,
+	.resume = ngene_resume,
+};
+
+static struct pci_driver ngene_pci_driver = {
+	.name        = "ngene",
+	.id_table    = ngene_id_tbl,
+	.probe       = ngene_probe,
+	.remove      = __devexit_p(ngene_remove),
+	.err_handler = &ngene_errors,
+	.shutdown    = ngene_shutdown,
+};
+
+static __init int module_init_ngene(void)
+{
+	printk(KERN_INFO
+	       "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+	return pci_register_driver(&ngene_pci_driver);
+}
+
+static __exit void module_exit_ngene(void)
+{
+	pci_unregister_driver(&ngene_pci_driver);
+}
+
+module_init(module_init_ngene);
+module_exit(module_exit_ngene);
+
+MODULE_DESCRIPTION("nGene");
+MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
new file mode 100644
index 000000000000..c8e0d5b99d4c
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -0,0 +1,1707 @@
+/*
+ * ngene.c: nGene PCIe bridge driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ *                         Modifications for new nGene firmware,
+ *                         support for EEPROM-copying,
+ *                         support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/byteorder/generic.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include "ngene.h"
+
+static int one_adapter;
+module_param(one_adapter, int, 0444);
+MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
+
+static int shutdown_workaround;
+module_param(shutdown_workaround, int, 0644);
+MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets.");
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Print debugging information.");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk	if (debug) printk
+
+#define ngwriteb(dat, adr)         writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngwritel(dat, adr)         writel((dat), (char *)(dev->iomem + (adr)))
+#define ngwriteb(dat, adr)         writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngreadl(adr)               readl(dev->iomem + (adr))
+#define ngreadb(adr)               readb(dev->iomem + (adr))
+#define ngcpyto(adr, src, count)   memcpy_toio((char *) \
+				   (dev->iomem + (adr)), (src), (count))
+#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
+				   (dev->iomem + (adr)), (count))
+
+/****************************************************************************/
+/* nGene interrupt handler **************************************************/
+/****************************************************************************/
+
+static void event_tasklet(unsigned long data)
+{
+	struct ngene *dev = (struct ngene *)data;
+
+	while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
+		struct EVENT_BUFFER Event =
+			dev->EventQueue[dev->EventQueueReadIndex];
+		dev->EventQueueReadIndex =
+			(dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
+
+		if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
+			dev->TxEventNotify(dev, Event.TimeStamp);
+		if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
+			dev->RxEventNotify(dev, Event.TimeStamp,
+					   Event.RXCharacter);
+	}
+}
+
+static void demux_tasklet(unsigned long data)
+{
+	struct ngene_channel *chan = (struct ngene_channel *)data;
+	struct SBufferHeader *Cur = chan->nextBuffer;
+
+	spin_lock_irq(&chan->state_lock);
+
+	while (Cur->ngeneBuffer.SR.Flags & 0x80) {
+		if (chan->mode & NGENE_IO_TSOUT) {
+			u32 Flags = chan->DataFormatFlags;
+			if (Cur->ngeneBuffer.SR.Flags & 0x20)
+				Flags |= BEF_OVERFLOW;
+			if (chan->pBufferExchange) {
+				if (!chan->pBufferExchange(chan,
+							   Cur->Buffer1,
+							   chan->Capture1Length,
+							   Cur->ngeneBuffer.SR.
+							   Clock, Flags)) {
+					/*
+					   We didn't get data
+					   Clear in service flag to make sure we
+					   get called on next interrupt again.
+					   leave fill/empty (0x80) flag alone
+					   to avoid hardware running out of
+					   buffers during startup, we hold only
+					   in run state ( the source may be late
+					   delivering data )
+					*/
+
+					if (chan->HWState == HWSTATE_RUN) {
+						Cur->ngeneBuffer.SR.Flags &=
+							~0x40;
+						break;
+						/* Stop processing stream */
+					}
+				} else {
+					/* We got a valid buffer,
+					   so switch to run state */
+					chan->HWState = HWSTATE_RUN;
+				}
+			} else {
+				printk(KERN_ERR DEVICE_NAME ": OOPS\n");
+				if (chan->HWState == HWSTATE_RUN) {
+					Cur->ngeneBuffer.SR.Flags &= ~0x40;
+					break;	/* Stop processing stream */
+				}
+			}
+			if (chan->AudioDTOUpdated) {
+				printk(KERN_INFO DEVICE_NAME
+				       ": Update AudioDTO = %d\n",
+				       chan->AudioDTOValue);
+				Cur->ngeneBuffer.SR.DTOUpdate =
+					chan->AudioDTOValue;
+				chan->AudioDTOUpdated = 0;
+			}
+		} else {
+			if (chan->HWState == HWSTATE_RUN) {
+				u32 Flags = chan->DataFormatFlags;
+				IBufferExchange *exch1 = chan->pBufferExchange;
+				IBufferExchange *exch2 = chan->pBufferExchange2;
+				if (Cur->ngeneBuffer.SR.Flags & 0x01)
+					Flags |= BEF_EVEN_FIELD;
+				if (Cur->ngeneBuffer.SR.Flags & 0x20)
+					Flags |= BEF_OVERFLOW;
+				spin_unlock_irq(&chan->state_lock);
+				if (exch1)
+					exch1(chan, Cur->Buffer1,
+						chan->Capture1Length,
+						Cur->ngeneBuffer.SR.Clock,
+						Flags);
+				if (exch2)
+					exch2(chan, Cur->Buffer2,
+						chan->Capture2Length,
+						Cur->ngeneBuffer.SR.Clock,
+						Flags);
+				spin_lock_irq(&chan->state_lock);
+			} else if (chan->HWState != HWSTATE_STOP)
+				chan->HWState = HWSTATE_RUN;
+		}
+		Cur->ngeneBuffer.SR.Flags = 0x00;
+		Cur = Cur->Next;
+	}
+	chan->nextBuffer = Cur;
+
+	spin_unlock_irq(&chan->state_lock);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+	struct ngene *dev = (struct ngene *)dev_id;
+	u32 icounts = 0;
+	irqreturn_t rc = IRQ_NONE;
+	u32 i = MAX_STREAM;
+	u8 *tmpCmdDoneByte;
+
+	if (dev->BootFirmware) {
+		icounts = ngreadl(NGENE_INT_COUNTS);
+		if (icounts != dev->icounts) {
+			ngwritel(0, FORCE_NMI);
+			dev->cmd_done = 1;
+			wake_up(&dev->cmd_wq);
+			dev->icounts = icounts;
+			rc = IRQ_HANDLED;
+		}
+		return rc;
+	}
+
+	ngwritel(0, FORCE_NMI);
+
+	spin_lock(&dev->cmd_lock);
+	tmpCmdDoneByte = dev->CmdDoneByte;
+	if (tmpCmdDoneByte &&
+	    (*tmpCmdDoneByte ||
+	    (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
+		dev->CmdDoneByte = NULL;
+		dev->cmd_done = 1;
+		wake_up(&dev->cmd_wq);
+		rc = IRQ_HANDLED;
+	}
+	spin_unlock(&dev->cmd_lock);
+
+	if (dev->EventBuffer->EventStatus & 0x80) {
+		u8 nextWriteIndex =
+			(dev->EventQueueWriteIndex + 1) &
+			(EVENT_QUEUE_SIZE - 1);
+		if (nextWriteIndex != dev->EventQueueReadIndex) {
+			dev->EventQueue[dev->EventQueueWriteIndex] =
+				*(dev->EventBuffer);
+			dev->EventQueueWriteIndex = nextWriteIndex;
+		} else {
+			printk(KERN_ERR DEVICE_NAME ": event overflow\n");
+			dev->EventQueueOverflowCount += 1;
+			dev->EventQueueOverflowFlag = 1;
+		}
+		dev->EventBuffer->EventStatus &= ~0x80;
+		tasklet_schedule(&dev->event_tasklet);
+		rc = IRQ_HANDLED;
+	}
+
+	while (i > 0) {
+		i--;
+		spin_lock(&dev->channel[i].state_lock);
+		/* if (dev->channel[i].State>=KSSTATE_RUN) { */
+		if (dev->channel[i].nextBuffer) {
+			if ((dev->channel[i].nextBuffer->
+			     ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
+				dev->channel[i].nextBuffer->
+					ngeneBuffer.SR.Flags |= 0x40;
+				tasklet_schedule(
+					&dev->channel[i].demux_tasklet);
+				rc = IRQ_HANDLED;
+			}
+		}
+		spin_unlock(&dev->channel[i].state_lock);
+	}
+
+	/* Request might have been processed by a previous call. */
+	return IRQ_HANDLED;
+}
+
+/****************************************************************************/
+/* nGene command interface **************************************************/
+/****************************************************************************/
+
+static void dump_command_io(struct ngene *dev)
+{
+	u8 buf[8], *b;
+
+	ngcpyfrom(buf, HOST_TO_NGENE, 8);
+	printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf);
+
+	ngcpyfrom(buf, NGENE_TO_HOST, 8);
+	printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf);
+
+	b = dev->hosttongene;
+	printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b);
+
+	b = dev->ngenetohost;
+	printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b);
+}
+
+static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
+{
+	int ret;
+	u8 *tmpCmdDoneByte;
+
+	dev->cmd_done = 0;
+
+	if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
+		dev->BootFirmware = 1;
+		dev->icounts = ngreadl(NGENE_INT_COUNTS);
+		ngwritel(0, NGENE_COMMAND);
+		ngwritel(0, NGENE_COMMAND_HI);
+		ngwritel(0, NGENE_STATUS);
+		ngwritel(0, NGENE_STATUS_HI);
+		ngwritel(0, NGENE_EVENT);
+		ngwritel(0, NGENE_EVENT_HI);
+	} else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
+		u64 fwio = dev->PAFWInterfaceBuffer;
+
+		ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
+		ngwritel(fwio >> 32, NGENE_COMMAND_HI);
+		ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
+		ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
+		ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
+		ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
+	}
+
+	memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
+
+	if (dev->BootFirmware)
+		ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
+
+	spin_lock_irq(&dev->cmd_lock);
+	tmpCmdDoneByte = dev->ngenetohost + com->out_len;
+	if (!com->out_len)
+		tmpCmdDoneByte++;
+	*tmpCmdDoneByte = 0;
+	dev->ngenetohost[0] = 0;
+	dev->ngenetohost[1] = 0;
+	dev->CmdDoneByte = tmpCmdDoneByte;
+	spin_unlock_irq(&dev->cmd_lock);
+
+	/* Notify 8051. */
+	ngwritel(1, FORCE_INT);
+
+	ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
+	if (!ret) {
+		/*ngwritel(0, FORCE_NMI);*/
+
+		printk(KERN_ERR DEVICE_NAME
+		       ": Command timeout cmd=%02x prev=%02x\n",
+		       com->cmd.hdr.Opcode, dev->prev_cmd);
+		dump_command_io(dev);
+		return -1;
+	}
+	if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
+		dev->BootFirmware = 0;
+
+	dev->prev_cmd = com->cmd.hdr.Opcode;
+
+	if (!com->out_len)
+		return 0;
+
+	memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
+
+	return 0;
+}
+
+int ngene_command(struct ngene *dev, struct ngene_command *com)
+{
+	int result;
+
+	down(&dev->cmd_mutex);
+	result = ngene_command_mutex(dev, com);
+	up(&dev->cmd_mutex);
+	return result;
+}
+
+
+static int ngene_command_load_firmware(struct ngene *dev,
+				       u8 *ngene_fw, u32 size)
+{
+#define FIRSTCHUNK (1024)
+	u32 cleft;
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
+	com.cmd.hdr.Length = 0;
+	com.in_len = 0;
+	com.out_len = 0;
+
+	ngene_command(dev, &com);
+
+	cleft = (size + 3) & ~3;
+	if (cleft > FIRSTCHUNK) {
+		ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
+			cleft - FIRSTCHUNK);
+		cleft = FIRSTCHUNK;
+	}
+	ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
+
+	memset(&com, 0, sizeof(struct ngene_command));
+	com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
+	com.cmd.hdr.Length = 4;
+	com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
+	com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
+	com.in_len = 4;
+	com.out_len = 0;
+
+	return ngene_command(dev, &com);
+}
+
+
+static int ngene_command_config_buf(struct ngene *dev, u8 config)
+{
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
+	com.cmd.hdr.Length = 1;
+	com.cmd.ConfigureBuffers.config = config;
+	com.in_len = 1;
+	com.out_len = 0;
+
+	if (ngene_command(dev, &com) < 0)
+		return -EIO;
+	return 0;
+}
+
+static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
+{
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
+	com.cmd.hdr.Length = 6;
+	memcpy(&com.cmd.ConfigureBuffers.config, config, 6);
+	com.in_len = 6;
+	com.out_len = 0;
+
+	if (ngene_command(dev, &com) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
+{
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
+	com.cmd.hdr.Length = 1;
+	com.cmd.SetGpioPin.select = select | (level << 7);
+	com.in_len = 1;
+	com.out_len = 0;
+
+	return ngene_command(dev, &com);
+}
+
+
+/*
+ 02000640 is sample on rising edge.
+ 02000740 is sample on falling edge.
+ 02000040 is ignore "valid" signal
+
+ 0: FD_CTL1 Bit 7,6 must be 0,1
+    7   disable(fw controlled)
+    6   0-AUX,1-TS
+    5   0-par,1-ser
+    4   0-lsb/1-msb
+    3,2 reserved
+    1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
+ 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
+ 2: FD_STA is read-only. 0-sync
+ 3: FD_INSYNC is number of 47s to trigger "in sync".
+ 4: FD_OUTSYNC is number of 47s to trigger "out of sync".
+ 5: FD_MAXBYTE1 is low-order of bytes per packet.
+ 6: FD_MAXBYTE2 is high-order of bytes per packet.
+ 7: Top byte is unused.
+*/
+
+/****************************************************************************/
+
+static u8 TSFeatureDecoderSetup[8 * 5] = {
+	0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
+	0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXH */
+	0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXHser */
+	0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* S2ser */
+	0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
+};
+
+/* Set NGENE I2S Config to 16 bit packed */
+static u8 I2SConfiguration[] = {
+	0x00, 0x10, 0x00, 0x00,
+	0x80, 0x10, 0x00, 0x00,
+};
+
+static u8 SPDIFConfiguration[10] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Set NGENE I2S Config to transport stream compatible mode */
+
+static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 };
+
+static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 };
+
+static u8 ITUDecoderSetup[4][16] = {
+	{0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20,  /* SDTV */
+	 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
+	{0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
+	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+	{0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00,  /* HDTV 1080i50 */
+	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+	{0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,  /* HDTV 1080i60 */
+	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+};
+
+/*
+ * 50 48 60 gleich
+ * 27p50 9f 00 22 80 42 69 18 ...
+ * 27p60 93 00 22 80 82 69 1c ...
+ */
+
+/* Maxbyte to 1144 (for raw data) */
+static u8 ITUFeatureDecoderSetup[8] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
+};
+
+void FillTSBuffer(void *Buffer, int Length, u32 Flags)
+{
+	u32 *ptr = Buffer;
+
+	memset(Buffer, TS_FILLER, Length);
+	while (Length > 0) {
+		if (Flags & DF_SWAP32)
+			*ptr = 0x471FFF10;
+		else
+			*ptr = 0x10FF1F47;
+		ptr += (188 / 4);
+		Length -= 188;
+	}
+}
+
+
+static void flush_buffers(struct ngene_channel *chan)
+{
+	u8 val;
+
+	do {
+		msleep(1);
+		spin_lock_irq(&chan->state_lock);
+		val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
+		spin_unlock_irq(&chan->state_lock);
+	} while (val);
+}
+
+static void clear_buffers(struct ngene_channel *chan)
+{
+	struct SBufferHeader *Cur = chan->nextBuffer;
+
+	do {
+		memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
+		if (chan->mode & NGENE_IO_TSOUT)
+			FillTSBuffer(Cur->Buffer1,
+				     chan->Capture1Length,
+				     chan->DataFormatFlags);
+		Cur = Cur->Next;
+	} while (Cur != chan->nextBuffer);
+
+	if (chan->mode & NGENE_IO_TSOUT) {
+		chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
+			chan->AudioDTOValue;
+		chan->AudioDTOUpdated = 0;
+
+		Cur = chan->TSIdleBuffer.Head;
+
+		do {
+			memset(&Cur->ngeneBuffer.SR, 0,
+			       sizeof(Cur->ngeneBuffer.SR));
+			FillTSBuffer(Cur->Buffer1,
+				     chan->Capture1Length,
+				     chan->DataFormatFlags);
+			Cur = Cur->Next;
+		} while (Cur != chan->TSIdleBuffer.Head);
+	}
+}
+
+static int ngene_command_stream_control(struct ngene *dev, u8 stream,
+					u8 control, u8 mode, u8 flags)
+{
+	struct ngene_channel *chan = &dev->channel[stream];
+	struct ngene_command com;
+	u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
+	u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
+	u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
+	u16 BsSDO = 0x9B00;
+
+	down(&dev->stream_mutex);
+	memset(&com, 0, sizeof(com));
+	com.cmd.hdr.Opcode = CMD_CONTROL;
+	com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
+	com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
+	if (chan->mode & NGENE_IO_TSOUT)
+		com.cmd.StreamControl.Stream |= 0x07;
+	com.cmd.StreamControl.Control = control |
+		(flags & SFLAG_ORDER_LUMA_CHROMA);
+	com.cmd.StreamControl.Mode = mode;
+	com.in_len = sizeof(struct FW_STREAM_CONTROL);
+	com.out_len = 0;
+
+	dprintk(KERN_INFO DEVICE_NAME
+		": Stream=%02x, Control=%02x, Mode=%02x\n",
+		com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
+		com.cmd.StreamControl.Mode);
+
+	chan->Mode = mode;
+
+	if (!(control & 0x80)) {
+		spin_lock_irq(&chan->state_lock);
+		if (chan->State == KSSTATE_RUN) {
+			chan->State = KSSTATE_ACQUIRE;
+			chan->HWState = HWSTATE_STOP;
+			spin_unlock_irq(&chan->state_lock);
+			if (ngene_command(dev, &com) < 0) {
+				up(&dev->stream_mutex);
+				return -1;
+			}
+			/* clear_buffers(chan); */
+			flush_buffers(chan);
+			up(&dev->stream_mutex);
+			return 0;
+		}
+		spin_unlock_irq(&chan->state_lock);
+		up(&dev->stream_mutex);
+		return 0;
+	}
+
+	if (mode & SMODE_AUDIO_CAPTURE) {
+		com.cmd.StreamControl.CaptureBlockCount =
+			chan->Capture1Length / AUDIO_BLOCK_SIZE;
+		com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+	} else if (mode & SMODE_TRANSPORT_STREAM) {
+		com.cmd.StreamControl.CaptureBlockCount =
+			chan->Capture1Length / TS_BLOCK_SIZE;
+		com.cmd.StreamControl.MaxLinesPerField =
+			chan->Capture1Length / TS_BLOCK_SIZE;
+		com.cmd.StreamControl.Buffer_Address =
+			chan->TSRingBuffer.PAHead;
+		if (chan->mode & NGENE_IO_TSOUT) {
+			com.cmd.StreamControl.BytesPerVBILine =
+				chan->Capture1Length / TS_BLOCK_SIZE;
+			com.cmd.StreamControl.Stream |= 0x07;
+		}
+	} else {
+		com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
+		com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
+		com.cmd.StreamControl.MinLinesPerField = 100;
+		com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+
+		if (mode & SMODE_VBI_CAPTURE) {
+			com.cmd.StreamControl.MaxVBILinesPerField =
+				chan->nVBILines;
+			com.cmd.StreamControl.MinVBILinesPerField = 0;
+			com.cmd.StreamControl.BytesPerVBILine =
+				chan->nBytesPerVBILine;
+		}
+		if (flags & SFLAG_COLORBAR)
+			com.cmd.StreamControl.Stream |= 0x04;
+	}
+
+	spin_lock_irq(&chan->state_lock);
+	if (mode & SMODE_AUDIO_CAPTURE) {
+		chan->nextBuffer = chan->RingBuffer.Head;
+		if (mode & SMODE_AUDIO_SPDIF) {
+			com.cmd.StreamControl.SetupDataLen =
+				sizeof(SPDIFConfiguration);
+			com.cmd.StreamControl.SetupDataAddr = BsSPI;
+			memcpy(com.cmd.StreamControl.SetupData,
+			       SPDIFConfiguration, sizeof(SPDIFConfiguration));
+		} else {
+			com.cmd.StreamControl.SetupDataLen = 4;
+			com.cmd.StreamControl.SetupDataAddr = BsSDI;
+			memcpy(com.cmd.StreamControl.SetupData,
+			       I2SConfiguration +
+			       4 * dev->card_info->i2s[stream], 4);
+		}
+	} else if (mode & SMODE_TRANSPORT_STREAM) {
+		chan->nextBuffer = chan->TSRingBuffer.Head;
+		if (stream >= STREAM_AUDIOIN1) {
+			if (chan->mode & NGENE_IO_TSOUT) {
+				com.cmd.StreamControl.SetupDataLen =
+					sizeof(TS_I2SOutConfiguration);
+				com.cmd.StreamControl.SetupDataAddr = BsSDO;
+				memcpy(com.cmd.StreamControl.SetupData,
+				       TS_I2SOutConfiguration,
+				       sizeof(TS_I2SOutConfiguration));
+			} else {
+				com.cmd.StreamControl.SetupDataLen =
+					sizeof(TS_I2SConfiguration);
+				com.cmd.StreamControl.SetupDataAddr = BsSDI;
+				memcpy(com.cmd.StreamControl.SetupData,
+				       TS_I2SConfiguration,
+				       sizeof(TS_I2SConfiguration));
+			}
+		} else {
+			com.cmd.StreamControl.SetupDataLen = 8;
+			com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
+			memcpy(com.cmd.StreamControl.SetupData,
+			       TSFeatureDecoderSetup +
+			       8 * dev->card_info->tsf[stream], 8);
+		}
+	} else {
+		chan->nextBuffer = chan->RingBuffer.Head;
+		com.cmd.StreamControl.SetupDataLen =
+			16 + sizeof(ITUFeatureDecoderSetup);
+		com.cmd.StreamControl.SetupDataAddr = BsUVI;
+		memcpy(com.cmd.StreamControl.SetupData,
+		       ITUDecoderSetup[chan->itumode], 16);
+		memcpy(com.cmd.StreamControl.SetupData + 16,
+		       ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
+	}
+	clear_buffers(chan);
+	chan->State = KSSTATE_RUN;
+	if (mode & SMODE_TRANSPORT_STREAM)
+		chan->HWState = HWSTATE_RUN;
+	else
+		chan->HWState = HWSTATE_STARTUP;
+	spin_unlock_irq(&chan->state_lock);
+
+	if (ngene_command(dev, &com) < 0) {
+		up(&dev->stream_mutex);
+		return -1;
+	}
+	up(&dev->stream_mutex);
+	return 0;
+}
+
+void set_transfer(struct ngene_channel *chan, int state)
+{
+	u8 control = 0, mode = 0, flags = 0;
+	struct ngene *dev = chan->dev;
+	int ret;
+
+	/*
+	printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
+	msleep(100);
+	*/
+
+	if (state) {
+		if (chan->running) {
+			printk(KERN_INFO DEVICE_NAME ": already running\n");
+			return;
+		}
+	} else {
+		if (!chan->running) {
+			printk(KERN_INFO DEVICE_NAME ": already stopped\n");
+			return;
+		}
+	}
+
+	if (dev->card_info->switch_ctrl)
+		dev->card_info->switch_ctrl(chan, 1, state ^ 1);
+
+	if (state) {
+		spin_lock_irq(&chan->state_lock);
+
+		/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+			  ngreadl(0x9310)); */
+		dvb_ringbuffer_flush(&dev->tsout_rbuf);
+		control = 0x80;
+		if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+			chan->Capture1Length = 512 * 188;
+			mode = SMODE_TRANSPORT_STREAM;
+		}
+		if (chan->mode & NGENE_IO_TSOUT) {
+			chan->pBufferExchange = tsout_exchange;
+			/* 0x66666666 = 50MHz *2^33 /250MHz */
+			chan->AudioDTOValue = 0x80000000;
+			chan->AudioDTOUpdated = 1;
+		}
+		if (chan->mode & NGENE_IO_TSIN)
+			chan->pBufferExchange = tsin_exchange;
+		spin_unlock_irq(&chan->state_lock);
+	} else
+		;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+			   ngreadl(0x9310)); */
+
+	ret = ngene_command_stream_control(dev, chan->number,
+					   control, mode, flags);
+	if (!ret)
+		chan->running = state;
+	else
+		printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
+		       state);
+	if (!state) {
+		spin_lock_irq(&chan->state_lock);
+		chan->pBufferExchange = NULL;
+		dvb_ringbuffer_flush(&dev->tsout_rbuf);
+		spin_unlock_irq(&chan->state_lock);
+	}
+}
+
+
+/****************************************************************************/
+/* nGene hardware init and release functions ********************************/
+/****************************************************************************/
+
+static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
+{
+	struct SBufferHeader *Cur = rb->Head;
+	u32 j;
+
+	if (!Cur)
+		return;
+
+	for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
+		if (Cur->Buffer1)
+			pci_free_consistent(dev->pci_dev,
+					    rb->Buffer1Length,
+					    Cur->Buffer1,
+					    Cur->scList1->Address);
+
+		if (Cur->Buffer2)
+			pci_free_consistent(dev->pci_dev,
+					    rb->Buffer2Length,
+					    Cur->Buffer2,
+					    Cur->scList2->Address);
+	}
+
+	if (rb->SCListMem)
+		pci_free_consistent(dev->pci_dev, rb->SCListMemSize,
+				    rb->SCListMem, rb->PASCListMem);
+
+	pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead);
+}
+
+static void free_idlebuffer(struct ngene *dev,
+		     struct SRingBufferDescriptor *rb,
+		     struct SRingBufferDescriptor *tb)
+{
+	int j;
+	struct SBufferHeader *Cur = tb->Head;
+
+	if (!rb->Head)
+		return;
+	free_ringbuffer(dev, rb);
+	for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
+		Cur->Buffer2 = NULL;
+		Cur->scList2 = NULL;
+		Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
+		Cur->ngeneBuffer.Number_of_entries_2 = 0;
+	}
+}
+
+static void free_common_buffers(struct ngene *dev)
+{
+	u32 i;
+	struct ngene_channel *chan;
+
+	for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+		chan = &dev->channel[i];
+		free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
+		free_ringbuffer(dev, &chan->RingBuffer);
+		free_ringbuffer(dev, &chan->TSRingBuffer);
+	}
+
+	if (dev->OverflowBuffer)
+		pci_free_consistent(dev->pci_dev,
+				    OVERFLOW_BUFFER_SIZE,
+				    dev->OverflowBuffer, dev->PAOverflowBuffer);
+
+	if (dev->FWInterfaceBuffer)
+		pci_free_consistent(dev->pci_dev,
+				    4096,
+				    dev->FWInterfaceBuffer,
+				    dev->PAFWInterfaceBuffer);
+}
+
+/****************************************************************************/
+/* Ring buffer handling *****************************************************/
+/****************************************************************************/
+
+static int create_ring_buffer(struct pci_dev *pci_dev,
+		       struct SRingBufferDescriptor *descr, u32 NumBuffers)
+{
+	dma_addr_t tmp;
+	struct SBufferHeader *Head;
+	u32 i;
+	u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
+	u64 PARingBufferHead;
+	u64 PARingBufferCur;
+	u64 PARingBufferNext;
+	struct SBufferHeader *Cur, *Next;
+
+	descr->Head = NULL;
+	descr->MemSize = 0;
+	descr->PAHead = 0;
+	descr->NumBuffers = 0;
+
+	if (MemSize < 4096)
+		MemSize = 4096;
+
+	Head = pci_alloc_consistent(pci_dev, MemSize, &tmp);
+	PARingBufferHead = tmp;
+
+	if (!Head)
+		return -ENOMEM;
+
+	memset(Head, 0, MemSize);
+
+	PARingBufferCur = PARingBufferHead;
+	Cur = Head;
+
+	for (i = 0; i < NumBuffers - 1; i++) {
+		Next = (struct SBufferHeader *)
+			(((u8 *) Cur) + SIZEOF_SBufferHeader);
+		PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
+		Cur->Next = Next;
+		Cur->ngeneBuffer.Next = PARingBufferNext;
+		Cur = Next;
+		PARingBufferCur = PARingBufferNext;
+	}
+	/* Last Buffer points back to first one */
+	Cur->Next = Head;
+	Cur->ngeneBuffer.Next = PARingBufferHead;
+
+	descr->Head       = Head;
+	descr->MemSize    = MemSize;
+	descr->PAHead     = PARingBufferHead;
+	descr->NumBuffers = NumBuffers;
+
+	return 0;
+}
+
+static int AllocateRingBuffers(struct pci_dev *pci_dev,
+			       dma_addr_t of,
+			       struct SRingBufferDescriptor *pRingBuffer,
+			       u32 Buffer1Length, u32 Buffer2Length)
+{
+	dma_addr_t tmp;
+	u32 i, j;
+	int status = 0;
+	u32 SCListMemSize = pRingBuffer->NumBuffers
+		* ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
+		    NUM_SCATTER_GATHER_ENTRIES)
+		* sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+	u64 PASCListMem;
+	struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
+	u64 PASCListEntry;
+	struct SBufferHeader *Cur;
+	void *SCListMem;
+
+	if (SCListMemSize < 4096)
+		SCListMemSize = 4096;
+
+	SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp);
+
+	PASCListMem = tmp;
+	if (SCListMem == NULL)
+		return -ENOMEM;
+
+	memset(SCListMem, 0, SCListMemSize);
+
+	pRingBuffer->SCListMem = SCListMem;
+	pRingBuffer->PASCListMem = PASCListMem;
+	pRingBuffer->SCListMemSize = SCListMemSize;
+	pRingBuffer->Buffer1Length = Buffer1Length;
+	pRingBuffer->Buffer2Length = Buffer2Length;
+
+	SCListEntry = SCListMem;
+	PASCListEntry = PASCListMem;
+	Cur = pRingBuffer->Head;
+
+	for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
+		u64 PABuffer;
+
+		void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length,
+						    &tmp);
+		PABuffer = tmp;
+
+		if (Buffer == NULL)
+			return -ENOMEM;
+
+		Cur->Buffer1 = Buffer;
+
+		SCListEntry->Address = PABuffer;
+		SCListEntry->Length  = Buffer1Length;
+
+		Cur->scList1 = SCListEntry;
+		Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
+		Cur->ngeneBuffer.Number_of_entries_1 =
+			NUM_SCATTER_GATHER_ENTRIES;
+
+		SCListEntry += 1;
+		PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+		for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
+			SCListEntry->Address = of;
+			SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+			SCListEntry += 1;
+			PASCListEntry +=
+				sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+		}
+#endif
+
+		if (!Buffer2Length)
+			continue;
+
+		Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp);
+		PABuffer = tmp;
+
+		if (Buffer == NULL)
+			return -ENOMEM;
+
+		Cur->Buffer2 = Buffer;
+
+		SCListEntry->Address = PABuffer;
+		SCListEntry->Length  = Buffer2Length;
+
+		Cur->scList2 = SCListEntry;
+		Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
+		Cur->ngeneBuffer.Number_of_entries_2 =
+			NUM_SCATTER_GATHER_ENTRIES;
+
+		SCListEntry   += 1;
+		PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+		for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
+			SCListEntry->Address = of;
+			SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+			SCListEntry += 1;
+			PASCListEntry +=
+				sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+		}
+#endif
+
+	}
+
+	return status;
+}
+
+static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
+			    struct SRingBufferDescriptor *pRingBuffer)
+{
+	int status = 0;
+
+	/* Copy pointer to scatter gather list in TSRingbuffer
+	   structure for buffer 2
+	   Load number of buffer
+	*/
+	u32 n = pRingBuffer->NumBuffers;
+
+	/* Point to first buffer entry */
+	struct SBufferHeader *Cur = pRingBuffer->Head;
+	int i;
+	/* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
+	for (i = 0; i < n; i++) {
+		Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
+		Cur->scList2 = pIdleBuffer->Head->scList1;
+		Cur->ngeneBuffer.Address_of_first_entry_2 =
+			pIdleBuffer->Head->ngeneBuffer.
+			Address_of_first_entry_1;
+		Cur->ngeneBuffer.Number_of_entries_2 =
+			pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
+		Cur = Cur->Next;
+	}
+	return status;
+}
+
+static u32 RingBufferSizes[MAX_STREAM] = {
+	RING_SIZE_VIDEO,
+	RING_SIZE_VIDEO,
+	RING_SIZE_AUDIO,
+	RING_SIZE_AUDIO,
+	RING_SIZE_AUDIO,
+};
+
+static u32 Buffer1Sizes[MAX_STREAM] = {
+	MAX_VIDEO_BUFFER_SIZE,
+	MAX_VIDEO_BUFFER_SIZE,
+	MAX_AUDIO_BUFFER_SIZE,
+	MAX_AUDIO_BUFFER_SIZE,
+	MAX_AUDIO_BUFFER_SIZE
+};
+
+static u32 Buffer2Sizes[MAX_STREAM] = {
+	MAX_VBI_BUFFER_SIZE,
+	MAX_VBI_BUFFER_SIZE,
+	0,
+	0,
+	0
+};
+
+
+static int AllocCommonBuffers(struct ngene *dev)
+{
+	int status = 0, i;
+
+	dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096,
+						     &dev->PAFWInterfaceBuffer);
+	if (!dev->FWInterfaceBuffer)
+		return -ENOMEM;
+	dev->hosttongene = dev->FWInterfaceBuffer;
+	dev->ngenetohost = dev->FWInterfaceBuffer + 256;
+	dev->EventBuffer = dev->FWInterfaceBuffer + 512;
+
+	dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev,
+						   OVERFLOW_BUFFER_SIZE,
+						   &dev->PAOverflowBuffer);
+	if (!dev->OverflowBuffer)
+		return -ENOMEM;
+	memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE);
+
+	for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+		int type = dev->card_info->io_type[i];
+
+		dev->channel[i].State = KSSTATE_STOP;
+
+		if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
+			status = create_ring_buffer(dev->pci_dev,
+						    &dev->channel[i].RingBuffer,
+						    RingBufferSizes[i]);
+			if (status < 0)
+				break;
+
+			if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
+				status = AllocateRingBuffers(dev->pci_dev,
+							     dev->
+							     PAOverflowBuffer,
+							     &dev->channel[i].
+							     RingBuffer,
+							     Buffer1Sizes[i],
+							     Buffer2Sizes[i]);
+				if (status < 0)
+					break;
+			} else if (type & NGENE_IO_HDTV) {
+				status = AllocateRingBuffers(dev->pci_dev,
+							     dev->
+							     PAOverflowBuffer,
+							     &dev->channel[i].
+							     RingBuffer,
+							   MAX_HDTV_BUFFER_SIZE,
+							     0);
+				if (status < 0)
+					break;
+			}
+		}
+
+		if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+
+			status = create_ring_buffer(dev->pci_dev,
+						    &dev->channel[i].
+						    TSRingBuffer, RING_SIZE_TS);
+			if (status < 0)
+				break;
+
+			status = AllocateRingBuffers(dev->pci_dev,
+						     dev->PAOverflowBuffer,
+						     &dev->channel[i].
+						     TSRingBuffer,
+						     MAX_TS_BUFFER_SIZE, 0);
+			if (status)
+				break;
+		}
+
+		if (type & NGENE_IO_TSOUT) {
+			status = create_ring_buffer(dev->pci_dev,
+						    &dev->channel[i].
+						    TSIdleBuffer, 1);
+			if (status < 0)
+				break;
+			status = AllocateRingBuffers(dev->pci_dev,
+						     dev->PAOverflowBuffer,
+						     &dev->channel[i].
+						     TSIdleBuffer,
+						     MAX_TS_BUFFER_SIZE, 0);
+			if (status)
+				break;
+			FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
+					 &dev->channel[i].TSRingBuffer);
+		}
+	}
+	return status;
+}
+
+static void ngene_release_buffers(struct ngene *dev)
+{
+	if (dev->iomem)
+		iounmap(dev->iomem);
+	free_common_buffers(dev);
+	vfree(dev->tsout_buf);
+	vfree(dev->tsin_buf);
+	vfree(dev->ain_buf);
+	vfree(dev->vin_buf);
+	vfree(dev);
+}
+
+static int ngene_get_buffers(struct ngene *dev)
+{
+	if (AllocCommonBuffers(dev))
+		return -ENOMEM;
+	if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
+		dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
+		if (!dev->tsout_buf)
+			return -ENOMEM;
+		dvb_ringbuffer_init(&dev->tsout_rbuf,
+				    dev->tsout_buf, TSOUT_BUF_SIZE);
+	}
+	if (dev->card_info->io_type[2]&NGENE_IO_TSIN) {
+		dev->tsin_buf = vmalloc(TSIN_BUF_SIZE);
+		if (!dev->tsin_buf)
+			return -ENOMEM;
+		dvb_ringbuffer_init(&dev->tsin_rbuf,
+				    dev->tsin_buf, TSIN_BUF_SIZE);
+	}
+	if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
+		dev->ain_buf = vmalloc(AIN_BUF_SIZE);
+		if (!dev->ain_buf)
+			return -ENOMEM;
+		dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
+	}
+	if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
+		dev->vin_buf = vmalloc(VIN_BUF_SIZE);
+		if (!dev->vin_buf)
+			return -ENOMEM;
+		dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
+	}
+	dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
+			     pci_resource_len(dev->pci_dev, 0));
+	if (!dev->iomem)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void ngene_init(struct ngene *dev)
+{
+	int i;
+
+	tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
+
+	memset_io(dev->iomem + 0xc000, 0x00, 0x220);
+	memset_io(dev->iomem + 0xc400, 0x00, 0x100);
+
+	for (i = 0; i < MAX_STREAM; i++) {
+		dev->channel[i].dev = dev;
+		dev->channel[i].number = i;
+	}
+
+	dev->fw_interface_version = 0;
+
+	ngwritel(0, NGENE_INT_ENABLE);
+
+	dev->icounts = ngreadl(NGENE_INT_COUNTS);
+
+	dev->device_version = ngreadl(DEV_VER) & 0x0f;
+	printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
+	       dev->device_version);
+}
+
+static int ngene_load_firm(struct ngene *dev)
+{
+	u32 size;
+	const struct firmware *fw = NULL;
+	u8 *ngene_fw;
+	char *fw_name;
+	int err, version;
+
+	version = dev->card_info->fw_version;
+
+	switch (version) {
+	default:
+	case 15:
+		version = 15;
+		size = 23466;
+		fw_name = "ngene_15.fw";
+		dev->cmd_timeout_workaround = true;
+		break;
+	case 16:
+		size = 23498;
+		fw_name = "ngene_16.fw";
+		dev->cmd_timeout_workaround = true;
+		break;
+	case 17:
+		size = 24446;
+		fw_name = "ngene_17.fw";
+		dev->cmd_timeout_workaround = true;
+		break;
+	case 18:
+		size = 0;
+		fw_name = "ngene_18.fw";
+		break;
+	}
+
+	if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
+		printk(KERN_ERR DEVICE_NAME
+			": Could not load firmware file %s.\n", fw_name);
+		printk(KERN_INFO DEVICE_NAME
+			": Copy %s to your hotplug directory!\n", fw_name);
+		return -1;
+	}
+	if (size == 0)
+		size = fw->size;
+	if (size != fw->size) {
+		printk(KERN_ERR DEVICE_NAME
+			": Firmware %s has invalid size!", fw_name);
+		err = -1;
+	} else {
+		printk(KERN_INFO DEVICE_NAME
+			": Loading firmware file %s.\n", fw_name);
+		ngene_fw = (u8 *) fw->data;
+		err = ngene_command_load_firmware(dev, ngene_fw, size);
+	}
+
+	release_firmware(fw);
+
+	return err;
+}
+
+static void ngene_stop(struct ngene *dev)
+{
+	down(&dev->cmd_mutex);
+	i2c_del_adapter(&(dev->channel[0].i2c_adapter));
+	i2c_del_adapter(&(dev->channel[1].i2c_adapter));
+	ngwritel(0, NGENE_INT_ENABLE);
+	ngwritel(0, NGENE_COMMAND);
+	ngwritel(0, NGENE_COMMAND_HI);
+	ngwritel(0, NGENE_STATUS);
+	ngwritel(0, NGENE_STATUS_HI);
+	ngwritel(0, NGENE_EVENT);
+	ngwritel(0, NGENE_EVENT_HI);
+	free_irq(dev->pci_dev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+	if (dev->msi_enabled)
+		pci_disable_msi(dev->pci_dev);
+#endif
+}
+
+static int ngene_buffer_config(struct ngene *dev)
+{
+	int stat;
+
+	if (dev->card_info->fw_version >= 17) {
+		u8 tsin12_config[6]   = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
+		u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 };
+		u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 };
+		u8 *bconf = tsin12_config;
+
+		if (dev->card_info->io_type[2]&NGENE_IO_TSIN &&
+		    dev->card_info->io_type[3]&NGENE_IO_TSIN) {
+			bconf = tsin1234_config;
+			if (dev->card_info->io_type[4]&NGENE_IO_TSOUT &&
+			    dev->ci.en)
+				bconf = tsio1235_config;
+		}
+		stat = ngene_command_config_free_buf(dev, bconf);
+	} else {
+		int bconf = BUFFER_CONFIG_4422;
+
+		if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
+			bconf = BUFFER_CONFIG_3333;
+		stat = ngene_command_config_buf(dev, bconf);
+	}
+	return stat;
+}
+
+
+static int ngene_start(struct ngene *dev)
+{
+	int stat;
+	int i;
+
+	pci_set_master(dev->pci_dev);
+	ngene_init(dev);
+
+	stat = request_irq(dev->pci_dev->irq, irq_handler,
+			   IRQF_SHARED, "nGene",
+			   (void *)dev);
+	if (stat < 0)
+		return stat;
+
+	init_waitqueue_head(&dev->cmd_wq);
+	init_waitqueue_head(&dev->tx_wq);
+	init_waitqueue_head(&dev->rx_wq);
+	sema_init(&dev->cmd_mutex, 1);
+	sema_init(&dev->stream_mutex, 1);
+	sema_init(&dev->pll_mutex, 1);
+	sema_init(&dev->i2c_switch_mutex, 1);
+	spin_lock_init(&dev->cmd_lock);
+	for (i = 0; i < MAX_STREAM; i++)
+		spin_lock_init(&dev->channel[i].state_lock);
+	ngwritel(1, TIMESTAMPS);
+
+	ngwritel(1, NGENE_INT_ENABLE);
+
+	stat = ngene_load_firm(dev);
+	if (stat < 0)
+		goto fail;
+
+#ifdef CONFIG_PCI_MSI
+	/* enable MSI if kernel and card support it */
+	if (pci_msi_enabled() && dev->card_info->msi_supported) {
+		unsigned long flags;
+
+		ngwritel(0, NGENE_INT_ENABLE);
+		free_irq(dev->pci_dev->irq, dev);
+		stat = pci_enable_msi(dev->pci_dev);
+		if (stat) {
+			printk(KERN_INFO DEVICE_NAME
+				": MSI not available\n");
+			flags = IRQF_SHARED;
+		} else {
+			flags = 0;
+			dev->msi_enabled = true;
+		}
+		stat = request_irq(dev->pci_dev->irq, irq_handler,
+					flags, "nGene", dev);
+		if (stat < 0)
+			goto fail2;
+		ngwritel(1, NGENE_INT_ENABLE);
+	}
+#endif
+
+	stat = ngene_i2c_init(dev, 0);
+	if (stat < 0)
+		goto fail;
+
+	stat = ngene_i2c_init(dev, 1);
+	if (stat < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	ngwritel(0, NGENE_INT_ENABLE);
+	free_irq(dev->pci_dev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+fail2:
+	if (dev->msi_enabled)
+		pci_disable_msi(dev->pci_dev);
+#endif
+	return stat;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void release_channel(struct ngene_channel *chan)
+{
+	struct dvb_demux *dvbdemux = &chan->demux;
+	struct ngene *dev = chan->dev;
+
+	if (chan->running)
+		set_transfer(chan, 0);
+
+	tasklet_kill(&chan->demux_tasklet);
+
+	if (chan->ci_dev) {
+		dvb_unregister_device(chan->ci_dev);
+		chan->ci_dev = NULL;
+	}
+
+	if (chan->fe2)
+		dvb_unregister_frontend(chan->fe2);
+
+	if (chan->fe) {
+		dvb_unregister_frontend(chan->fe);
+		dvb_frontend_detach(chan->fe);
+		chan->fe = NULL;
+	}
+
+	if (chan->has_demux) {
+		dvb_net_release(&chan->dvbnet);
+		dvbdemux->dmx.close(&dvbdemux->dmx);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &chan->hw_frontend);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &chan->mem_frontend);
+		dvb_dmxdev_release(&chan->dmxdev);
+		dvb_dmx_release(&chan->demux);
+		chan->has_demux = false;
+	}
+
+	if (chan->has_adapter) {
+		dvb_unregister_adapter(&dev->adapter[chan->number]);
+		chan->has_adapter = false;
+	}
+}
+
+static int init_channel(struct ngene_channel *chan)
+{
+	int ret = 0, nr = chan->number;
+	struct dvb_adapter *adapter = NULL;
+	struct dvb_demux *dvbdemux = &chan->demux;
+	struct ngene *dev = chan->dev;
+	struct ngene_info *ni = dev->card_info;
+	int io = ni->io_type[nr];
+
+	tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan);
+	chan->users = 0;
+	chan->type = io;
+	chan->mode = chan->type;	/* for now only one mode */
+
+	if (io & NGENE_IO_TSIN) {
+		chan->fe = NULL;
+		if (ni->demod_attach[nr]) {
+			ret = ni->demod_attach[nr](chan);
+			if (ret < 0)
+				goto err;
+		}
+		if (chan->fe && ni->tuner_attach[nr]) {
+			ret = ni->tuner_attach[nr](chan);
+			if (ret < 0)
+				goto err;
+		}
+	}
+
+	if (!dev->ci.en && (io & NGENE_IO_TSOUT))
+		return 0;
+
+	if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+		if (nr >= STREAM_AUDIOIN1)
+			chan->DataFormatFlags = DF_SWAP32;
+
+		if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
+			adapter = &dev->adapter[nr];
+			ret = dvb_register_adapter(adapter, "nGene",
+						   THIS_MODULE,
+						   &chan->dev->pci_dev->dev,
+						   adapter_nr);
+			if (ret < 0)
+				goto err;
+			if (dev->first_adapter == NULL)
+				dev->first_adapter = adapter;
+			chan->has_adapter = true;
+		} else
+			adapter = dev->first_adapter;
+	}
+
+	if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
+		dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
+		set_transfer(chan, 1);
+		chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
+		set_transfer(&chan->dev->channel[2], 1);
+		dvb_register_device(adapter, &chan->ci_dev,
+				    &ngene_dvbdev_ci, (void *) chan,
+				    DVB_DEVICE_SEC);
+		if (!chan->ci_dev)
+			goto err;
+	}
+
+	if (chan->fe) {
+		if (dvb_register_frontend(adapter, chan->fe) < 0)
+			goto err;
+		chan->has_demux = true;
+	}
+	if (chan->fe2) {
+		if (dvb_register_frontend(adapter, chan->fe2) < 0)
+			goto err;
+		chan->fe2->tuner_priv = chan->fe->tuner_priv;
+		memcpy(&chan->fe2->ops.tuner_ops,
+		       &chan->fe->ops.tuner_ops,
+		       sizeof(struct dvb_tuner_ops));
+	}
+
+	if (chan->has_demux) {
+		ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+					      ngene_start_feed,
+					      ngene_stop_feed, chan);
+		ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
+						 &chan->hw_frontend,
+						 &chan->mem_frontend, adapter);
+		ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx);
+	}
+
+	return ret;
+
+err:
+	if (chan->fe) {
+		dvb_frontend_detach(chan->fe);
+		chan->fe = NULL;
+	}
+	release_channel(chan);
+	return 0;
+}
+
+static int init_channels(struct ngene *dev)
+{
+	int i, j;
+
+	for (i = 0; i < MAX_STREAM; i++) {
+		dev->channel[i].number = i;
+		if (init_channel(&dev->channel[i]) < 0) {
+			for (j = i - 1; j >= 0; j--)
+				release_channel(&dev->channel[j]);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static struct cxd2099_cfg cxd_cfg = {
+	.bitrate = 62000,
+	.adr = 0x40,
+	.polarity = 0,
+	.clock_mode = 0,
+};
+
+static void cxd_attach(struct ngene *dev)
+{
+	struct ngene_ci *ci = &dev->ci;
+
+	ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
+	ci->dev = dev;
+	return;
+}
+
+static void cxd_detach(struct ngene *dev)
+{
+	struct ngene_ci *ci = &dev->ci;
+
+	dvb_ca_en50221_release(ci->en);
+	kfree(ci->en);
+	ci->en = 0;
+}
+
+/***********************************/
+/* workaround for shutdown failure */
+/***********************************/
+
+static void ngene_unlink(struct ngene *dev)
+{
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_MEM_WRITE;
+	com.cmd.hdr.Length = 3;
+	com.cmd.MemoryWrite.address = 0x910c;
+	com.cmd.MemoryWrite.data = 0xff;
+	com.in_len = 3;
+	com.out_len = 1;
+
+	down(&dev->cmd_mutex);
+	ngwritel(0, NGENE_INT_ENABLE);
+	ngene_command_mutex(dev, &com);
+	up(&dev->cmd_mutex);
+}
+
+void ngene_shutdown(struct pci_dev *pdev)
+{
+	struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
+
+	if (!dev || !shutdown_workaround)
+		return;
+
+	printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n");
+	ngene_unlink(dev);
+	pci_disable_device(pdev);
+}
+
+/****************************************************************************/
+/* device probe/remove calls ************************************************/
+/****************************************************************************/
+
+void __devexit ngene_remove(struct pci_dev *pdev)
+{
+	struct ngene *dev = pci_get_drvdata(pdev);
+	int i;
+
+	tasklet_kill(&dev->event_tasklet);
+	for (i = MAX_STREAM - 1; i >= 0; i--)
+		release_channel(&dev->channel[i]);
+	if (dev->ci.en)
+		cxd_detach(dev);
+	ngene_stop(dev);
+	ngene_release_buffers(dev);
+	pci_set_drvdata(pdev, NULL);
+	pci_disable_device(pdev);
+}
+
+int __devinit ngene_probe(struct pci_dev *pci_dev,
+			  const struct pci_device_id *id)
+{
+	struct ngene *dev;
+	int stat = 0;
+
+	if (pci_enable_device(pci_dev) < 0)
+		return -ENODEV;
+
+	dev = vzalloc(sizeof(struct ngene));
+	if (dev == NULL) {
+		stat = -ENOMEM;
+		goto fail0;
+	}
+
+	dev->pci_dev = pci_dev;
+	dev->card_info = (struct ngene_info *)id->driver_data;
+	printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
+
+	pci_set_drvdata(pci_dev, dev);
+
+	/* Alloc buffers and start nGene */
+	stat = ngene_get_buffers(dev);
+	if (stat < 0)
+		goto fail1;
+	stat = ngene_start(dev);
+	if (stat < 0)
+		goto fail1;
+
+	cxd_attach(dev);
+
+	stat = ngene_buffer_config(dev);
+	if (stat < 0)
+		goto fail1;
+
+
+	dev->i2c_current_bus = -1;
+
+	/* Register DVB adapters and devices for both channels */
+	if (init_channels(dev) < 0)
+		goto fail2;
+
+	return 0;
+
+fail2:
+	ngene_stop(dev);
+fail1:
+	ngene_release_buffers(dev);
+fail0:
+	pci_disable_device(pci_dev);
+	pci_set_drvdata(pci_dev, NULL);
+	return stat;
+}
diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c
new file mode 100644
index 000000000000..fcb16a615aab
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-dvb.c
@@ -0,0 +1,261 @@
+/*
+ * ngene-dvb.c: nGene PCIe bridge driver - DVB functions
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ *                         Modifications for new nGene firmware,
+ *                         support for EEPROM-copying,
+ *                         support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/byteorder/generic.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include "ngene.h"
+
+
+/****************************************************************************/
+/* COMMAND API interface ****************************************************/
+/****************************************************************************/
+
+static ssize_t ts_write(struct file *file, const char *buf,
+			size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ngene_channel *chan = dvbdev->priv;
+	struct ngene *dev = chan->dev;
+
+	if (wait_event_interruptible(dev->tsout_rbuf.queue,
+				     dvb_ringbuffer_free
+				     (&dev->tsout_rbuf) >= count) < 0)
+		return 0;
+
+	dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count);
+
+	return count;
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+		       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ngene_channel *chan = dvbdev->priv;
+	struct ngene *dev = chan->dev;
+	int left, avail;
+
+	left = count;
+	while (left) {
+		if (wait_event_interruptible(
+			    dev->tsin_rbuf.queue,
+			    dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0)
+			return -EAGAIN;
+		avail = dvb_ringbuffer_avail(&dev->tsin_rbuf);
+		if (avail > left)
+			avail = left;
+		dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail);
+		left -= avail;
+		buf += avail;
+	}
+	return count;
+}
+
+static const struct file_operations ci_fops = {
+	.owner   = THIS_MODULE,
+	.read    = ts_read,
+	.write   = ts_write,
+	.open    = dvb_generic_open,
+	.release = dvb_generic_release,
+};
+
+struct dvb_device ngene_dvbdev_ci = {
+	.priv    = 0,
+	.readers = -1,
+	.writers = -1,
+	.users   = -1,
+	.fops    = &ci_fops,
+};
+
+
+/****************************************************************************/
+/* DVB functions and API interface ******************************************/
+/****************************************************************************/
+
+static void swap_buffer(u32 *p, u32 len)
+{
+	while (len) {
+		*p = swab32(*p);
+		p++;
+		len -= 4;
+	}
+}
+
+/* start of filler packet */
+static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
+
+/* #define DEBUG_CI_XFER */
+#ifdef DEBUG_CI_XFER
+static u32 ok;
+static u32 overflow;
+static u32 stripped;
+#endif
+
+void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
+{
+	struct ngene_channel *chan = priv;
+	struct ngene *dev = chan->dev;
+
+
+	if (flags & DF_SWAP32)
+		swap_buffer(buf, len);
+
+	if (dev->ci.en && chan->number == 2) {
+		while (len >= 188) {
+			if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) {
+				if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
+					dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
+					wake_up(&dev->tsin_rbuf.queue);
+#ifdef DEBUG_CI_XFER
+					ok++;
+#endif
+				}
+#ifdef DEBUG_CI_XFER
+				else
+					overflow++;
+#endif
+			}
+#ifdef DEBUG_CI_XFER
+			else
+				stripped++;
+
+			if (ok % 100 == 0 && overflow)
+				printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped);
+#endif
+			buf += 188;
+			len -= 188;
+		}
+		return NULL;
+	}
+
+	if (chan->users > 0)
+		dvb_dmx_swfilter(&chan->demux, buf, len);
+
+	return NULL;
+}
+
+void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
+{
+	struct ngene_channel *chan = priv;
+	struct ngene *dev = chan->dev;
+	u32 alen;
+
+	alen = dvb_ringbuffer_avail(&dev->tsout_rbuf);
+	alen -= alen % 188;
+
+	if (alen < len)
+		FillTSBuffer(buf + alen, len - alen, flags);
+	else
+		alen = len;
+	dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen);
+	if (flags & DF_SWAP32)
+		swap_buffer((u32 *)buf, alen);
+	wake_up_interruptible(&dev->tsout_rbuf.queue);
+	return buf;
+}
+
+
+
+int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ngene_channel *chan = dvbdmx->priv;
+
+	if (chan->users == 0) {
+		if (!chan->dev->cmd_timeout_workaround || !chan->running)
+			set_transfer(chan, 1);
+	}
+
+	return ++chan->users;
+}
+
+int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ngene_channel *chan = dvbdmx->priv;
+
+	if (--chan->users)
+		return chan->users;
+
+	if (!chan->dev->cmd_timeout_workaround)
+		set_transfer(chan, 0);
+
+	return 0;
+}
+
+int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+			    int (*start_feed)(struct dvb_demux_feed *),
+			    int (*stop_feed)(struct dvb_demux_feed *),
+			    void *priv)
+{
+	dvbdemux->priv = priv;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = start_feed;
+	dvbdemux->stop_feed = stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+				      DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+	return dvb_dmx_init(dvbdemux);
+}
+
+int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+			       struct dvb_demux *dvbdemux,
+			       struct dmx_frontend *hw_frontend,
+			       struct dmx_frontend *mem_frontend,
+			       struct dvb_adapter *dvb_adapter)
+{
+	int ret;
+
+	dmxdev->filternum = 256;
+	dmxdev->demux = &dvbdemux->dmx;
+	dmxdev->capabilities = 0;
+	ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+	if (ret < 0)
+		return ret;
+
+	hw_frontend->source = DMX_FRONTEND_0;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+	mem_frontend->source = DMX_MEMORY_FE;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+	return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c
new file mode 100644
index 000000000000..d28554f8ce99
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-i2c.c
@@ -0,0 +1,176 @@
+/*
+ * ngene-i2c.c: nGene PCIe bridge driver i2c functions
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ *                         Modifications for new nGene firmware,
+ *                         support for EEPROM-copying,
+ *                         support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* FIXME - some of these can probably be removed */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/timer.h>
+#include <linux/byteorder/generic.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include "ngene.h"
+
+/* Firmware command for i2c operations */
+static int ngene_command_i2c_read(struct ngene *dev, u8 adr,
+			   u8 *out, u8 outlen, u8 *in, u8 inlen, int flag)
+{
+	struct ngene_command com;
+
+	com.cmd.hdr.Opcode = CMD_I2C_READ;
+	com.cmd.hdr.Length = outlen + 3;
+	com.cmd.I2CRead.Device = adr << 1;
+	memcpy(com.cmd.I2CRead.Data, out, outlen);
+	com.cmd.I2CRead.Data[outlen] = inlen;
+	com.cmd.I2CRead.Data[outlen + 1] = 0;
+	com.in_len = outlen + 3;
+	com.out_len = inlen + 1;
+
+	if (ngene_command(dev, &com) < 0)
+		return -EIO;
+
+	if ((com.cmd.raw8[0] >> 1) != adr)
+		return -EIO;
+
+	if (flag)
+		memcpy(in, com.cmd.raw8, inlen + 1);
+	else
+		memcpy(in, com.cmd.raw8 + 1, inlen);
+	return 0;
+}
+
+static int ngene_command_i2c_write(struct ngene *dev, u8 adr,
+				   u8 *out, u8 outlen)
+{
+	struct ngene_command com;
+
+
+	com.cmd.hdr.Opcode = CMD_I2C_WRITE;
+	com.cmd.hdr.Length = outlen + 1;
+	com.cmd.I2CRead.Device = adr << 1;
+	memcpy(com.cmd.I2CRead.Data, out, outlen);
+	com.in_len = outlen + 1;
+	com.out_len = 1;
+
+	if (ngene_command(dev, &com) < 0)
+		return -EIO;
+
+	if (com.cmd.raw8[0] == 1)
+		return -EIO;
+
+	return 0;
+}
+
+static void ngene_i2c_set_bus(struct ngene *dev, int bus)
+{
+	if (!(dev->card_info->i2c_access & 2))
+		return;
+	if (dev->i2c_current_bus == bus)
+		return;
+
+	switch (bus) {
+	case 0:
+		ngene_command_gpio_set(dev, 3, 0);
+		ngene_command_gpio_set(dev, 2, 1);
+		break;
+
+	case 1:
+		ngene_command_gpio_set(dev, 2, 0);
+		ngene_command_gpio_set(dev, 3, 1);
+		break;
+	}
+	dev->i2c_current_bus = bus;
+}
+
+static int ngene_i2c_master_xfer(struct i2c_adapter *adapter,
+				 struct i2c_msg msg[], int num)
+{
+	struct ngene_channel *chan =
+		(struct ngene_channel *)i2c_get_adapdata(adapter);
+	struct ngene *dev = chan->dev;
+
+	down(&dev->i2c_switch_mutex);
+	ngene_i2c_set_bus(dev, chan->number);
+
+	if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD))
+		if (!ngene_command_i2c_read(dev, msg[0].addr,
+					    msg[0].buf, msg[0].len,
+					    msg[1].buf, msg[1].len, 0))
+			goto done;
+
+	if (num == 1 && !(msg[0].flags & I2C_M_RD))
+		if (!ngene_command_i2c_write(dev, msg[0].addr,
+					     msg[0].buf, msg[0].len))
+			goto done;
+	if (num == 1 && (msg[0].flags & I2C_M_RD))
+		if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0,
+					    msg[0].buf, msg[0].len, 0))
+			goto done;
+
+	up(&dev->i2c_switch_mutex);
+	return -EIO;
+
+done:
+	up(&dev->i2c_switch_mutex);
+	return num;
+}
+
+
+static u32 ngene_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ngene_i2c_algo = {
+	.master_xfer = ngene_i2c_master_xfer,
+	.functionality = ngene_i2c_functionality,
+};
+
+int ngene_i2c_init(struct ngene *dev, int dev_nr)
+{
+	struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter);
+
+	i2c_set_adapdata(adap, &(dev->channel[dev_nr]));
+
+	strcpy(adap->name, "nGene");
+
+	adap->algo = &ngene_i2c_algo;
+	adap->algo_data = (void *)&(dev->channel[dev_nr]);
+	adap->dev.parent = &dev->pci_dev->dev;
+
+	return i2c_add_adapter(adap);
+}
+
diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h
new file mode 100644
index 000000000000..5443dc0caea5
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene.h
@@ -0,0 +1,921 @@
+/*
+ * ngene.h: nGene PCIe bridge driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _NGENE_H_
+#define _NGENE_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <asm/dma.h>
+#include <linux/scatterlist.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_ca_en50221.h"
+#include "dvb_frontend.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_net.h"
+#include "cxd2099.h"
+
+#define DEVICE_NAME "ngene"
+
+#define NGENE_VID       0x18c3
+#define NGENE_PID       0x0720
+
+#ifndef VIDEO_CAP_VC1
+#define VIDEO_CAP_AVC   128
+#define VIDEO_CAP_H264  128
+#define VIDEO_CAP_VC1   256
+#define VIDEO_CAP_WMV9  256
+#define VIDEO_CAP_MPEG4 512
+#endif
+
+enum STREAM {
+	STREAM_VIDEOIN1 = 0,        /* ITU656 or TS Input */
+	STREAM_VIDEOIN2,
+	STREAM_AUDIOIN1,            /* I2S or SPI Input */
+	STREAM_AUDIOIN2,
+	STREAM_AUDIOOUT,
+	MAX_STREAM
+};
+
+enum SMODE_BITS {
+	SMODE_AUDIO_SPDIF = 0x20,
+	SMODE_AVSYNC = 0x10,
+	SMODE_TRANSPORT_STREAM = 0x08,
+	SMODE_AUDIO_CAPTURE = 0x04,
+	SMODE_VBI_CAPTURE = 0x02,
+	SMODE_VIDEO_CAPTURE = 0x01
+};
+
+enum STREAM_FLAG_BITS {
+	SFLAG_CHROMA_FORMAT_2COMP  = 0x01, /* Chroma Format : 2's complement */
+	SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */
+	SFLAG_ORDER_LUMA_CHROMA    = 0x02, /* Byte order: Y,Cb,Y,Cr */
+	SFLAG_ORDER_CHROMA_LUMA    = 0x00, /* Byte order: Cb,Y,Cr,Y */
+	SFLAG_COLORBAR             = 0x04, /* Select colorbar */
+};
+
+#define PROGRAM_ROM     0x0000
+#define PROGRAM_SRAM    0x1000
+#define PERIPHERALS0    0x8000
+#define PERIPHERALS1    0x9000
+#define SHARED_BUFFER   0xC000
+
+#define HOST_TO_NGENE    (SHARED_BUFFER+0x0000)
+#define NGENE_TO_HOST    (SHARED_BUFFER+0x0100)
+#define NGENE_COMMAND    (SHARED_BUFFER+0x0200)
+#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204)
+#define NGENE_STATUS     (SHARED_BUFFER+0x0208)
+#define NGENE_STATUS_HI  (SHARED_BUFFER+0x020C)
+#define NGENE_EVENT      (SHARED_BUFFER+0x0210)
+#define NGENE_EVENT_HI   (SHARED_BUFFER+0x0214)
+#define VARIABLES        (SHARED_BUFFER+0x0210)
+
+#define NGENE_INT_COUNTS       (SHARED_BUFFER+0x0260)
+#define NGENE_INT_ENABLE       (SHARED_BUFFER+0x0264)
+#define NGENE_VBI_LINE_COUNT   (SHARED_BUFFER+0x0268)
+
+#define BUFFER_GP_XMIT  (SHARED_BUFFER+0x0800)
+#define BUFFER_GP_RECV  (SHARED_BUFFER+0x0900)
+#define EEPROM_AREA     (SHARED_BUFFER+0x0A00)
+
+#define SG_V_IN_1       (SHARED_BUFFER+0x0A80)
+#define SG_VBI_1        (SHARED_BUFFER+0x0B00)
+#define SG_A_IN_1       (SHARED_BUFFER+0x0B80)
+#define SG_V_IN_2       (SHARED_BUFFER+0x0C00)
+#define SG_VBI_2        (SHARED_BUFFER+0x0C80)
+#define SG_A_IN_2       (SHARED_BUFFER+0x0D00)
+#define SG_V_OUT        (SHARED_BUFFER+0x0D80)
+#define SG_A_OUT2       (SHARED_BUFFER+0x0E00)
+
+#define DATA_A_IN_1     (SHARED_BUFFER+0x0E80)
+#define DATA_A_IN_2     (SHARED_BUFFER+0x0F00)
+#define DATA_A_OUT      (SHARED_BUFFER+0x0F80)
+#define DATA_V_IN_1     (SHARED_BUFFER+0x1000)
+#define DATA_V_IN_2     (SHARED_BUFFER+0x2000)
+#define DATA_V_OUT      (SHARED_BUFFER+0x3000)
+
+#define DATA_FIFO_AREA  (SHARED_BUFFER+0x1000)
+
+#define TIMESTAMPS      0xA000
+#define SCRATCHPAD      0xA080
+#define FORCE_INT       0xA088
+#define FORCE_NMI       0xA090
+#define INT_STATUS      0xA0A0
+
+#define DEV_VER         0x9004
+
+#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF)
+
+struct SG_ADDR {
+	u64 start;
+	u64 curr;
+	u16 curr_ptr;
+	u16 elements;
+	u32 pad[3];
+} __attribute__ ((__packed__));
+
+struct SHARED_MEMORY {
+	/* C000 */
+	u32 HostToNgene[64];
+
+	/* C100 */
+	u32 NgeneToHost[64];
+
+	/* C200 */
+	u64 NgeneCommand;
+	u64 NgeneStatus;
+	u64 NgeneEvent;
+
+	/* C210 */
+	u8 pad1[0xc260 - 0xc218];
+
+	/* C260 */
+	u32 IntCounts;
+	u32 IntEnable;
+
+	/* C268 */
+	u8 pad2[0xd000 - 0xc268];
+
+} __attribute__ ((__packed__));
+
+struct BUFFER_STREAM_RESULTS {
+	u32 Clock;           /* Stream time in 100ns units */
+	u16 RemainingLines;  /* Remaining lines in this field.
+				0 for complete field */
+	u8  FieldCount;      /* Video field number */
+	u8  Flags;           /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow,
+				Bit 0 = FieldID */
+	u16 BlockCount;      /* Audio block count (unused) */
+	u8  Reserved[2];
+	u32 DTOUpdate;
+} __attribute__ ((__packed__));
+
+struct HW_SCATTER_GATHER_ELEMENT {
+	u64 Address;
+	u32 Length;
+	u32 Reserved;
+} __attribute__ ((__packed__));
+
+struct BUFFER_HEADER {
+	u64    Next;
+	struct BUFFER_STREAM_RESULTS SR;
+
+	u32    Number_of_entries_1;
+	u32    Reserved5;
+	u64    Address_of_first_entry_1;
+
+	u32    Number_of_entries_2;
+	u32    Reserved7;
+	u64    Address_of_first_entry_2;
+} __attribute__ ((__packed__));
+
+struct EVENT_BUFFER {
+	u32    TimeStamp;
+	u8     GPIOStatus;
+	u8     UARTStatus;
+	u8     RXCharacter;
+	u8     EventStatus;
+	u32    Reserved[2];
+} __attribute__ ((__packed__));
+
+/* Firmware commands. */
+
+enum OPCODES {
+	CMD_NOP = 0,
+	CMD_FWLOAD_PREPARE  = 0x01,
+	CMD_FWLOAD_FINISH   = 0x02,
+	CMD_I2C_READ        = 0x03,
+	CMD_I2C_WRITE       = 0x04,
+
+	CMD_I2C_WRITE_NOSTOP = 0x05,
+	CMD_I2C_CONTINUE_WRITE = 0x06,
+	CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07,
+
+	CMD_DEBUG_OUTPUT    = 0x09,
+
+	CMD_CONTROL         = 0x10,
+	CMD_CONFIGURE_BUFFER = 0x11,
+	CMD_CONFIGURE_FREE_BUFFER = 0x12,
+
+	CMD_SPI_READ        = 0x13,
+	CMD_SPI_WRITE       = 0x14,
+
+	CMD_MEM_READ        = 0x20,
+	CMD_MEM_WRITE	    = 0x21,
+	CMD_SFR_READ	    = 0x22,
+	CMD_SFR_WRITE	    = 0x23,
+	CMD_IRAM_READ	    = 0x24,
+	CMD_IRAM_WRITE	    = 0x25,
+	CMD_SET_GPIO_PIN    = 0x26,
+	CMD_SET_GPIO_INT    = 0x27,
+	CMD_CONFIGURE_UART  = 0x28,
+	CMD_WRITE_UART      = 0x29,
+	MAX_CMD
+};
+
+enum RESPONSES {
+	OK = 0,
+	ERROR = 1
+};
+
+struct FW_HEADER {
+	u8 Opcode;
+	u8 Length;
+} __attribute__ ((__packed__));
+
+struct FW_I2C_WRITE {
+	struct FW_HEADER hdr;
+	u8 Device;
+	u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_I2C_CONTINUE_WRITE {
+	struct FW_HEADER hdr;
+	u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_I2C_READ {
+	struct FW_HEADER hdr;
+	u8 Device;
+	u8 Data[252];    /* followed by two bytes of read data count */
+} __attribute__ ((__packed__));
+
+struct FW_SPI_WRITE {
+	struct FW_HEADER hdr;
+	u8 ModeSelect;
+	u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_SPI_READ {
+	struct FW_HEADER hdr;
+	u8 ModeSelect;
+	u8 Data[252];    /* followed by two bytes of read data count */
+} __attribute__ ((__packed__));
+
+struct FW_FWLOAD_PREPARE {
+	struct FW_HEADER hdr;
+} __attribute__ ((__packed__));
+
+struct FW_FWLOAD_FINISH {
+	struct FW_HEADER hdr;
+	u16 Address;     /* address of final block */
+	u16 Length;
+} __attribute__ ((__packed__));
+
+/*
+ * Meaning of FW_STREAM_CONTROL::Mode bits:
+ *  Bit 7: Loopback PEXin to PEXout using TVOut channel
+ *  Bit 6: AVLOOP
+ *  Bit 5: Audio select; 0=I2S, 1=SPDIF
+ *  Bit 4: AVSYNC
+ *  Bit 3: Enable transport stream
+ *  Bit 2: Enable audio capture
+ *  Bit 1: Enable ITU-Video VBI capture
+ *  Bit 0: Enable ITU-Video capture
+ *
+ * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL)
+ *  Bit 7: continuous capture
+ *  Bit 6: capture one field
+ *  Bit 5: capture one frame
+ *  Bit 4: unused
+ *  Bit 3: starting field; 0=odd, 1=even
+ *  Bit 2: sample size; 0=8-bit, 1=10-bit
+ *  Bit 1: data format; 0=UYVY, 1=YUY2
+ *  Bit 0: resets buffer pointers
+*/
+
+enum FSC_MODE_BITS {
+	SMODE_LOOPBACK          = 0x80,
+	SMODE_AVLOOP            = 0x40,
+	_SMODE_AUDIO_SPDIF      = 0x20,
+	_SMODE_AVSYNC           = 0x10,
+	_SMODE_TRANSPORT_STREAM = 0x08,
+	_SMODE_AUDIO_CAPTURE    = 0x04,
+	_SMODE_VBI_CAPTURE      = 0x02,
+	_SMODE_VIDEO_CAPTURE    = 0x01
+};
+
+
+/* Meaning of FW_STREAM_CONTROL::Stream bits:
+ * Bit 3: Audio sample count:  0 = relative, 1 = absolute
+ * Bit 2: color bar select; 1=color bars, 0=CV3 decoder
+ * Bits 1-0: stream select, UVI1, UVI2, TVOUT
+ */
+
+struct FW_STREAM_CONTROL {
+	struct FW_HEADER hdr;
+	u8     Stream;             /* Stream number (UVI1, UVI2, TVOUT) */
+	u8     Control;            /* Value written to UVI1_CTL */
+	u8     Mode;               /* Controls clock source */
+	u8     SetupDataLen;	   /* Length of setup data, MSB=1 write
+				      backwards */
+	u16    CaptureBlockCount;  /* Blocks (a 256 Bytes) to capture per buffer
+				      for TS and Audio */
+	u64    Buffer_Address;	   /* Address of first buffer header */
+	u16    BytesPerVideoLine;
+	u16    MaxLinesPerField;
+	u16    MinLinesPerField;
+	u16    Reserved_1;
+	u16    BytesPerVBILine;
+	u16    MaxVBILinesPerField;
+	u16    MinVBILinesPerField;
+	u16    SetupDataAddr;      /* ngene relative address of setup data */
+	u8     SetupData[32];      /* setup data */
+} __attribute__((__packed__));
+
+#define AUDIO_BLOCK_SIZE    256
+#define TS_BLOCK_SIZE       256
+
+struct FW_MEM_READ {
+	struct FW_HEADER hdr;
+	u16   address;
+} __attribute__ ((__packed__));
+
+struct FW_MEM_WRITE {
+	struct FW_HEADER hdr;
+	u16   address;
+	u8    data;
+} __attribute__ ((__packed__));
+
+struct FW_SFR_IRAM_READ {
+	struct FW_HEADER hdr;
+	u8    address;
+} __attribute__ ((__packed__));
+
+struct FW_SFR_IRAM_WRITE {
+	struct FW_HEADER hdr;
+	u8    address;
+	u8    data;
+} __attribute__ ((__packed__));
+
+struct FW_SET_GPIO_PIN {
+	struct FW_HEADER hdr;
+	u8    select;
+} __attribute__ ((__packed__));
+
+struct FW_SET_GPIO_INT {
+	struct FW_HEADER hdr;
+	u8    select;
+} __attribute__ ((__packed__));
+
+struct FW_SET_DEBUGMODE {
+	struct FW_HEADER hdr;
+	u8   debug_flags;
+} __attribute__ ((__packed__));
+
+struct FW_CONFIGURE_BUFFERS {
+	struct FW_HEADER hdr;
+	u8   config;
+} __attribute__ ((__packed__));
+
+enum _BUFFER_CONFIGS {
+	/* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2  (standard usage) */
+	BUFFER_CONFIG_4422 = 0,
+	/* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2  (4x TS input usage) */
+	BUFFER_CONFIG_3333 = 1,
+	/* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut  (HDTV decoder usage) */
+	BUFFER_CONFIG_8022 = 2,
+	BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */
+};
+
+struct FW_CONFIGURE_FREE_BUFFERS {
+	struct FW_HEADER hdr;
+	u8   UVI1_BufferLength;
+	u8   UVI2_BufferLength;
+	u8   TVO_BufferLength;
+	u8   AUD1_BufferLength;
+	u8   AUD2_BufferLength;
+	u8   TVA_BufferLength;
+} __attribute__ ((__packed__));
+
+struct FW_CONFIGURE_UART {
+	struct FW_HEADER hdr;
+	u8 UartControl;
+} __attribute__ ((__packed__));
+
+enum _UART_CONFIG {
+	_UART_BAUDRATE_19200 = 0,
+	_UART_BAUDRATE_9600  = 1,
+	_UART_BAUDRATE_4800  = 2,
+	_UART_BAUDRATE_2400  = 3,
+	_UART_RX_ENABLE      = 0x40,
+	_UART_TX_ENABLE      = 0x80,
+};
+
+struct FW_WRITE_UART {
+	struct FW_HEADER hdr;
+	u8 Data[252];
+} __attribute__ ((__packed__));
+
+
+struct ngene_command {
+	u32 in_len;
+	u32 out_len;
+	union {
+		u32                              raw[64];
+		u8                               raw8[256];
+		struct FW_HEADER                 hdr;
+		struct FW_I2C_WRITE              I2CWrite;
+		struct FW_I2C_CONTINUE_WRITE     I2CContinueWrite;
+		struct FW_I2C_READ               I2CRead;
+		struct FW_STREAM_CONTROL         StreamControl;
+		struct FW_FWLOAD_PREPARE         FWLoadPrepare;
+		struct FW_FWLOAD_FINISH          FWLoadFinish;
+		struct FW_MEM_READ		 MemoryRead;
+		struct FW_MEM_WRITE		 MemoryWrite;
+		struct FW_SFR_IRAM_READ		 SfrIramRead;
+		struct FW_SFR_IRAM_WRITE         SfrIramWrite;
+		struct FW_SPI_WRITE              SPIWrite;
+		struct FW_SPI_READ               SPIRead;
+		struct FW_SET_GPIO_PIN           SetGpioPin;
+		struct FW_SET_GPIO_INT           SetGpioInt;
+		struct FW_SET_DEBUGMODE          SetDebugMode;
+		struct FW_CONFIGURE_BUFFERS      ConfigureBuffers;
+		struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers;
+		struct FW_CONFIGURE_UART         ConfigureUart;
+		struct FW_WRITE_UART             WriteUart;
+	} cmd;
+} __attribute__ ((__packed__));
+
+#define NGENE_INTERFACE_VERSION 0x103
+#define MAX_VIDEO_BUFFER_SIZE   (417792) /* 288*1440 rounded up to next page */
+#define MAX_AUDIO_BUFFER_SIZE     (8192) /* Gives room for about 23msec@48KHz */
+#define MAX_VBI_BUFFER_SIZE      (28672) /* 1144*18 rounded up to next page */
+#define MAX_TS_BUFFER_SIZE       (98304) /* 512*188 rounded up to next page */
+#define MAX_HDTV_BUFFER_SIZE   (2080768) /* 541*1920*2 rounded up to next page
+					    Max: (1920x1080i60) */
+
+#define OVERFLOW_BUFFER_SIZE    (8192)
+
+#define RING_SIZE_VIDEO     4
+#define RING_SIZE_AUDIO     8
+#define RING_SIZE_TS        8
+
+#define NUM_SCATTER_GATHER_ENTRIES  8
+
+#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \
+			RING_SIZE_VIDEO * 2) + \
+			(MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \
+			(MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \
+			(RING_SIZE_VIDEO * PAGE_SIZE * 2) + \
+			(RING_SIZE_AUDIO * PAGE_SIZE * 2) + \
+			(RING_SIZE_TS    * PAGE_SIZE * 4) + \
+			 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE)
+
+#define EVENT_QUEUE_SIZE    16
+
+/* Gathers the current state of a single channel. */
+
+struct SBufferHeader {
+	struct BUFFER_HEADER   ngeneBuffer; /* Physical descriptor */
+	struct SBufferHeader  *Next;
+	void                  *Buffer1;
+	struct HW_SCATTER_GATHER_ELEMENT *scList1;
+	void                  *Buffer2;
+	struct HW_SCATTER_GATHER_ELEMENT *scList2;
+};
+
+/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */
+#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63)
+
+enum HWSTATE {
+	HWSTATE_STOP,
+	HWSTATE_STARTUP,
+	HWSTATE_RUN,
+	HWSTATE_PAUSE,
+};
+
+enum KSSTATE {
+	KSSTATE_STOP,
+	KSSTATE_ACQUIRE,
+	KSSTATE_PAUSE,
+	KSSTATE_RUN,
+};
+
+struct SRingBufferDescriptor {
+	struct SBufferHeader *Head; /* Points to first buffer in ring buffer
+				       structure*/
+	u64   PAHead;         /* Physical address of first buffer */
+	u32   MemSize;        /* Memory size of allocated ring buffers
+				 (needed for freeing) */
+	u32   NumBuffers;     /* Number of buffers in the ring */
+	u32   Buffer1Length;  /* Allocated length of Buffer 1 */
+	u32   Buffer2Length;  /* Allocated length of Buffer 2 */
+	void *SCListMem;      /* Memory to hold scatter gather lists for this
+				 ring */
+	u64   PASCListMem;    /* Physical address  .. */
+	u32   SCListMemSize;  /* Size of this memory */
+};
+
+enum STREAMMODEFLAGS {
+	StreamMode_NONE   = 0, /* Stream not used */
+	StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */
+	StreamMode_TSIN   = 2, /* Transport stream input (all) */
+	StreamMode_HDTV   = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60
+				  (only stream 0) */
+	StreamMode_TSOUT  = 8, /* Transport stream output (only stream 3) */
+};
+
+
+enum BufferExchangeFlags {
+	BEF_EVEN_FIELD   = 0x00000001,
+	BEF_CONTINUATION = 0x00000002,
+	BEF_MORE_DATA    = 0x00000004,
+	BEF_OVERFLOW     = 0x00000008,
+	DF_SWAP32        = 0x00010000,
+};
+
+typedef void *(IBufferExchange)(void *, void *, u32, u32, u32);
+
+struct MICI_STREAMINFO {
+	IBufferExchange    *pExchange;
+	IBufferExchange    *pExchangeVBI;     /* Secondary (VBI, ancillary) */
+	u8  Stream;
+	u8  Flags;
+	u8  Mode;
+	u8  Reserved;
+	u16 nLinesVideo;
+	u16 nBytesPerLineVideo;
+	u16 nLinesVBI;
+	u16 nBytesPerLineVBI;
+	u32 CaptureLength;    /* Used for audio and transport stream */
+};
+
+/****************************************************************************/
+/* STRUCTS ******************************************************************/
+/****************************************************************************/
+
+/* sound hardware definition */
+#define MIXER_ADDR_TVTUNER      0
+#define MIXER_ADDR_LAST         0
+
+struct ngene_channel;
+
+/*struct sound chip*/
+
+struct mychip {
+	struct ngene_channel *chan;
+	struct snd_card *card;
+	struct pci_dev *pci;
+	struct snd_pcm_substream *substream;
+	struct snd_pcm *pcm;
+	unsigned long port;
+	int irq;
+	spinlock_t mixer_lock;
+	spinlock_t lock;
+	int mixer_volume[MIXER_ADDR_LAST + 1][2];
+	int capture_source[MIXER_ADDR_LAST + 1][2];
+};
+
+#ifdef NGENE_V4L
+struct ngene_overlay {
+	int                    tvnorm;
+	struct v4l2_rect       w;
+	enum v4l2_field        field;
+	struct v4l2_clip       *clips;
+	int                    nclips;
+	int                    setup_ok;
+};
+
+struct ngene_tvnorm {
+	int   v4l2_id;
+	char  *name;
+	u16   swidth, sheight; /* scaled standard width, height */
+	int   tuner_norm;
+	int   soundstd;
+};
+
+struct ngene_vopen {
+	struct ngene_channel      *ch;
+	enum v4l2_priority         prio;
+	int                        width;
+	int                        height;
+	int                        depth;
+	struct videobuf_queue      vbuf_q;
+	struct videobuf_queue      vbi;
+	int                        fourcc;
+	int                        picxcount;
+	int                        resources;
+	enum v4l2_buf_type         type;
+	const struct ngene_format *fmt;
+
+	const struct ngene_format *ovfmt;
+	struct ngene_overlay       ov;
+};
+#endif
+
+struct ngene_channel {
+	struct device         device;
+	struct i2c_adapter    i2c_adapter;
+
+	struct ngene         *dev;
+	int                   number;
+	int                   type;
+	int                   mode;
+	bool                  has_adapter;
+	bool                  has_demux;
+	int                   demod_type;
+	int (*gate_ctrl)(struct dvb_frontend *, int);
+
+	struct dvb_frontend  *fe;
+	struct dvb_frontend  *fe2;
+	struct dmxdev         dmxdev;
+	struct dvb_demux      demux;
+	struct dvb_net        dvbnet;
+	struct dmx_frontend   hw_frontend;
+	struct dmx_frontend   mem_frontend;
+	int                   users;
+	struct video_device  *v4l_dev;
+	struct dvb_device    *ci_dev;
+	struct tasklet_struct demux_tasklet;
+
+	struct SBufferHeader *nextBuffer;
+	enum KSSTATE          State;
+	enum HWSTATE          HWState;
+	u8                    Stream;
+	u8                    Flags;
+	u8                    Mode;
+	IBufferExchange      *pBufferExchange;
+	IBufferExchange      *pBufferExchange2;
+
+	spinlock_t            state_lock;
+	u16                   nLines;
+	u16                   nBytesPerLine;
+	u16                   nVBILines;
+	u16                   nBytesPerVBILine;
+	u16                   itumode;
+	u32                   Capture1Length;
+	u32                   Capture2Length;
+	struct SRingBufferDescriptor RingBuffer;
+	struct SRingBufferDescriptor TSRingBuffer;
+	struct SRingBufferDescriptor TSIdleBuffer;
+
+	u32                   DataFormatFlags;
+
+	int                   AudioDTOUpdated;
+	u32                   AudioDTOValue;
+
+	int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t);
+	u8 lnbh;
+
+	/* stuff from analog driver */
+
+	int minor;
+	struct mychip        *mychip;
+	struct snd_card      *soundcard;
+	u8                   *evenbuffer;
+	u8                    dma_on;
+	int                   soundstreamon;
+	int                   audiomute;
+	int                   soundbuffisallocated;
+	int                   sndbuffflag;
+	int                   tun_rdy;
+	int                   dec_rdy;
+	int                   tun_dec_rdy;
+	int                   lastbufferflag;
+
+	struct ngene_tvnorm  *tvnorms;
+	int                   tvnorm_num;
+	int                   tvnorm;
+
+#ifdef NGENE_V4L
+	int                   videousers;
+	struct v4l2_prio_state prio;
+	struct ngene_vopen    init;
+	int                   resources;
+	struct v4l2_framebuffer fbuf;
+	struct ngene_buffer  *screen;     /* overlay             */
+	struct list_head      capture;    /* video capture queue */
+	spinlock_t s_lock;
+	struct semaphore reslock;
+#endif
+
+	int running;
+};
+
+
+struct ngene_ci {
+	struct device         device;
+	struct i2c_adapter    i2c_adapter;
+
+	struct ngene         *dev;
+	struct dvb_ca_en50221 *en;
+};
+
+struct ngene;
+
+typedef void (rx_cb_t)(struct ngene *, u32, u8);
+typedef void (tx_cb_t)(struct ngene *, u32);
+
+struct ngene {
+	int                   nr;
+	struct pci_dev       *pci_dev;
+	unsigned char        *iomem;
+
+	/*struct i2c_adapter  i2c_adapter;*/
+
+	u32                   device_version;
+	u32                   fw_interface_version;
+	u32                   icounts;
+	bool                  msi_enabled;
+	bool                  cmd_timeout_workaround;
+
+	u8                   *CmdDoneByte;
+	int                   BootFirmware;
+	void                 *OverflowBuffer;
+	dma_addr_t            PAOverflowBuffer;
+	void                 *FWInterfaceBuffer;
+	dma_addr_t            PAFWInterfaceBuffer;
+	u8                   *ngenetohost;
+	u8                   *hosttongene;
+
+	struct EVENT_BUFFER   EventQueue[EVENT_QUEUE_SIZE];
+	int                   EventQueueOverflowCount;
+	int                   EventQueueOverflowFlag;
+	struct tasklet_struct event_tasklet;
+	struct EVENT_BUFFER  *EventBuffer;
+	int                   EventQueueWriteIndex;
+	int                   EventQueueReadIndex;
+
+	wait_queue_head_t     cmd_wq;
+	int                   cmd_done;
+	struct semaphore      cmd_mutex;
+	struct semaphore      stream_mutex;
+	struct semaphore      pll_mutex;
+	struct semaphore      i2c_switch_mutex;
+	int                   i2c_current_channel;
+	int                   i2c_current_bus;
+	spinlock_t            cmd_lock;
+
+	struct dvb_adapter    adapter[MAX_STREAM];
+	struct dvb_adapter    *first_adapter; /* "one_adapter" modprobe opt */
+	struct ngene_channel  channel[MAX_STREAM];
+
+	struct ngene_info    *card_info;
+
+	tx_cb_t              *TxEventNotify;
+	rx_cb_t              *RxEventNotify;
+	int                   tx_busy;
+	wait_queue_head_t     tx_wq;
+	wait_queue_head_t     rx_wq;
+#define UART_RBUF_LEN 4096
+	u8                    uart_rbuf[UART_RBUF_LEN];
+	int                   uart_rp, uart_wp;
+
+#define TS_FILLER  0x6f
+
+	u8                   *tsout_buf;
+#define TSOUT_BUF_SIZE (512*188*8)
+	struct dvb_ringbuffer tsout_rbuf;
+
+	u8                   *tsin_buf;
+#define TSIN_BUF_SIZE (512*188*8)
+	struct dvb_ringbuffer tsin_rbuf;
+
+	u8                   *ain_buf;
+#define AIN_BUF_SIZE (128*1024)
+	struct dvb_ringbuffer ain_rbuf;
+
+
+	u8                   *vin_buf;
+#define VIN_BUF_SIZE (4*1920*1080)
+	struct dvb_ringbuffer vin_rbuf;
+
+	unsigned long         exp_val;
+	int prev_cmd;
+
+	struct ngene_ci       ci;
+};
+
+struct ngene_info {
+	int   type;
+#define NGENE_APP        0
+#define NGENE_TERRATEC   1
+#define NGENE_SIDEWINDER 2
+#define NGENE_RACER      3
+#define NGENE_VIPER      4
+#define NGENE_PYTHON     5
+#define NGENE_VBOX_V1	 6
+#define NGENE_VBOX_V2	 7
+
+	int   fw_version;
+	bool  msi_supported;
+	char *name;
+
+	int   io_type[MAX_STREAM];
+#define NGENE_IO_NONE    0
+#define NGENE_IO_TV      1
+#define NGENE_IO_HDTV    2
+#define NGENE_IO_TSIN    4
+#define NGENE_IO_TSOUT   8
+#define NGENE_IO_AIN     16
+
+	void *fe_config[4];
+	void *tuner_config[4];
+
+	int (*demod_attach[4])(struct ngene_channel *);
+	int (*tuner_attach[4])(struct ngene_channel *);
+
+	u8    avf[4];
+	u8    msp[4];
+	u8    demoda[4];
+	u8    lnb[4];
+	int   i2c_access;
+	u8    ntsc;
+	u8    tsf[4];
+	u8    i2s[4];
+
+	int (*gate_ctrl)(struct dvb_frontend *, int);
+	int (*switch_ctrl)(struct ngene_channel *, int, int);
+};
+
+#ifdef NGENE_V4L
+struct ngene_format {
+	char *name;
+	int   fourcc;          /* video4linux 2      */
+	int   btformat;        /* BT848_COLOR_FMT_*  */
+	int   format;
+	int   btswap;          /* BT848_COLOR_CTL_*  */
+	int   depth;           /* bit/pixel          */
+	int   flags;
+	int   hshift, vshift;  /* for planar modes   */
+	int   palette;
+};
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_VIDEO         2
+#define RESOURCE_VBI           4
+
+struct ngene_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer     vb;
+
+	/* ngene specific */
+	const struct ngene_format *fmt;
+	int                        tvnorm;
+	int                        btformat;
+	int                        btswap;
+};
+#endif
+
+
+/* Provided by ngene-core.c */
+int __devinit ngene_probe(struct pci_dev *pci_dev,
+			  const struct pci_device_id *id);
+void __devexit ngene_remove(struct pci_dev *pdev);
+void ngene_shutdown(struct pci_dev *pdev);
+int ngene_command(struct ngene *dev, struct ngene_command *com);
+int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level);
+void set_transfer(struct ngene_channel *chan, int state);
+void FillTSBuffer(void *Buffer, int Length, u32 Flags);
+
+/* Provided by ngene-i2c.c */
+int ngene_i2c_init(struct ngene *dev, int dev_nr);
+
+/* Provided by ngene-dvb.c */
+extern struct dvb_device ngene_dvbdev_ci;
+void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags);
+void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags);
+int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed);
+int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed);
+int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+			    int (*start_feed)(struct dvb_demux_feed *),
+			    int (*stop_feed)(struct dvb_demux_feed *),
+			    void *priv);
+int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+			       struct dvb_demux *dvbdemux,
+			       struct dmx_frontend *hw_frontend,
+			       struct dmx_frontend *mem_frontend,
+			       struct dvb_adapter *dvb_adapter);
+
+#endif
+
+/*  LocalWords:  Endif
+ */
diff --git a/drivers/media/pci/pluto2/Kconfig b/drivers/media/pci/pluto2/Kconfig
new file mode 100644
index 000000000000..7d8e6e87bdbb
--- /dev/null
+++ b/drivers/media/pci/pluto2/Kconfig
@@ -0,0 +1,15 @@
+config DVB_PLUTO2
+	tristate "Pluto2 cards"
+	depends on DVB_CORE && PCI && I2C
+	select I2C_ALGOBIT
+	select DVB_TDA1004X
+	help
+	  Support for PCI cards based on the Pluto2 FPGA like the Satelco
+	  Easywatch Mobile Terrestrial DVB-T Receiver.
+
+	  Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y or M if you own such a device and want to use it.
+
diff --git a/drivers/media/pci/pluto2/Makefile b/drivers/media/pci/pluto2/Makefile
new file mode 100644
index 000000000000..524bf841f42b
--- /dev/null
+++ b/drivers/media/pci/pluto2/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_PLUTO2) += pluto2.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c
new file mode 100644
index 000000000000..f148b19a206a
--- /dev/null
+++ b/drivers/media/pci/pluto2/pluto2.c
@@ -0,0 +1,815 @@
+/*
+ * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T]
+ *
+ * Copyright (C) 2005 Andreas Oberritter <obi@linuxtv.org>
+ *
+ * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/
+ * 	by Dany Salman <salmandany@yahoo.fr>
+ *	Copyright (c) 2004 TDF
+ *
+ * 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/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+#include "tda1004x.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define DRIVER_NAME		"pluto2"
+
+#define REG_PIDn(n)		((n) << 2)	/* PID n pattern registers */
+#define REG_PCAR		0x0020		/* PC address register */
+#define REG_TSCR		0x0024		/* TS ctrl & status */
+#define REG_MISC		0x0028		/* miscellaneous */
+#define REG_MMAC		0x002c		/* MSB MAC address */
+#define REG_IMAC		0x0030		/* ISB MAC address */
+#define REG_LMAC		0x0034		/* LSB MAC address */
+#define REG_SPID		0x0038		/* SPI data */
+#define REG_SLCS		0x003c		/* serial links ctrl/status */
+
+#define PID0_NOFIL		(0x0001 << 16)
+#define PIDn_ENP		(0x0001 << 15)
+#define PID0_END		(0x0001 << 14)
+#define PID0_AFIL		(0x0001 << 13)
+#define PIDn_PID		(0x1fff <<  0)
+
+#define TSCR_NBPACKETS		(0x00ff << 24)
+#define TSCR_DEM		(0x0001 << 17)
+#define TSCR_DE			(0x0001 << 16)
+#define TSCR_RSTN		(0x0001 << 15)
+#define TSCR_MSKO		(0x0001 << 14)
+#define TSCR_MSKA		(0x0001 << 13)
+#define TSCR_MSKL		(0x0001 << 12)
+#define TSCR_OVR		(0x0001 << 11)
+#define TSCR_AFUL		(0x0001 << 10)
+#define TSCR_LOCK		(0x0001 <<  9)
+#define TSCR_IACK		(0x0001 <<  8)
+#define TSCR_ADEF		(0x007f <<  0)
+
+#define MISC_DVR		(0x0fff <<  4)
+#define MISC_ALED		(0x0001 <<  3)
+#define MISC_FRST		(0x0001 <<  2)
+#define MISC_LED1		(0x0001 <<  1)
+#define MISC_LED0		(0x0001 <<  0)
+
+#define SPID_SPIDR		(0x00ff <<  0)
+
+#define SLCS_SCL		(0x0001 <<  7)
+#define SLCS_SDA		(0x0001 <<  6)
+#define SLCS_CSN		(0x0001 <<  2)
+#define SLCS_OVR		(0x0001 <<  1)
+#define SLCS_SWC		(0x0001 <<  0)
+
+#define TS_DMA_PACKETS		(8)
+#define TS_DMA_BYTES		(188 * TS_DMA_PACKETS)
+
+#define I2C_ADDR_TDA10046	0x10
+#define I2C_ADDR_TUA6034	0xc2
+#define NHWFILTERS		8
+
+struct pluto {
+	/* pci */
+	struct pci_dev *pdev;
+	u8 __iomem *io_mem;
+
+	/* dvb */
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct dmxdev dmxdev;
+	struct dvb_adapter dvb_adapter;
+	struct dvb_demux demux;
+	struct dvb_frontend *fe;
+	struct dvb_net dvbnet;
+	unsigned int full_ts_users;
+	unsigned int users;
+
+	/* i2c */
+	struct i2c_algo_bit_data i2c_bit;
+	struct i2c_adapter i2c_adap;
+	unsigned int i2cbug;
+
+	/* irq */
+	unsigned int overflow;
+	unsigned int dead;
+
+	/* dma */
+	dma_addr_t dma_addr;
+	u8 dma_buf[TS_DMA_BYTES];
+	u8 dummy[4096];
+};
+
+static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed)
+{
+	return container_of(feed->demux, struct pluto, demux);
+}
+
+static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe)
+{
+	return container_of(fe->dvb, struct pluto, dvb_adapter);
+}
+
+static inline u32 pluto_readreg(struct pluto *pluto, u32 reg)
+{
+	return readl(&pluto->io_mem[reg]);
+}
+
+static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val)
+{
+	writel(val, &pluto->io_mem[reg]);
+}
+
+static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits)
+{
+	u32 val = readl(&pluto->io_mem[reg]);
+	val &= ~mask;
+	val |= bits;
+	writel(val, &pluto->io_mem[reg]);
+}
+
+static void pluto_write_tscr(struct pluto *pluto, u32 val)
+{
+	/* set the number of packets */
+	val &= ~TSCR_ADEF;
+	val |= TS_DMA_PACKETS / 2;
+
+	pluto_writereg(pluto, REG_TSCR, val);
+}
+
+static void pluto_setsda(void *data, int state)
+{
+	struct pluto *pluto = data;
+
+	if (state)
+		pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA);
+	else
+		pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0);
+}
+
+static void pluto_setscl(void *data, int state)
+{
+	struct pluto *pluto = data;
+
+	if (state)
+		pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL);
+	else
+		pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0);
+
+	/* try to detect i2c_inb() to workaround hardware bug:
+	 * reset SDA to high after SCL has been set to low */
+	if ((state) && (pluto->i2cbug == 0)) {
+		pluto->i2cbug = 1;
+	} else {
+		if ((!state) && (pluto->i2cbug == 1))
+			pluto_setsda(pluto, 1);
+		pluto->i2cbug = 0;
+	}
+}
+
+static int pluto_getsda(void *data)
+{
+	struct pluto *pluto = data;
+
+	return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA;
+}
+
+static int pluto_getscl(void *data)
+{
+	struct pluto *pluto = data;
+
+	return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL;
+}
+
+static void pluto_reset_frontend(struct pluto *pluto, int reenable)
+{
+	u32 val = pluto_readreg(pluto, REG_MISC);
+
+	if (val & MISC_FRST) {
+		val &= ~MISC_FRST;
+		pluto_writereg(pluto, REG_MISC, val);
+	}
+	if (reenable) {
+		val |= MISC_FRST;
+		pluto_writereg(pluto, REG_MISC, val);
+	}
+}
+
+static void pluto_reset_ts(struct pluto *pluto, int reenable)
+{
+	u32 val = pluto_readreg(pluto, REG_TSCR);
+
+	if (val & TSCR_RSTN) {
+		val &= ~TSCR_RSTN;
+		pluto_write_tscr(pluto, val);
+	}
+	if (reenable) {
+		val |= TSCR_RSTN;
+		pluto_write_tscr(pluto, val);
+	}
+}
+
+static void pluto_set_dma_addr(struct pluto *pluto)
+{
+	pluto_writereg(pluto, REG_PCAR, pluto->dma_addr);
+}
+
+static int __devinit pluto_dma_map(struct pluto *pluto)
+{
+	pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf,
+			TS_DMA_BYTES, PCI_DMA_FROMDEVICE);
+
+	return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr);
+}
+
+static void pluto_dma_unmap(struct pluto *pluto)
+{
+	pci_unmap_single(pluto->pdev, pluto->dma_addr,
+			TS_DMA_BYTES, PCI_DMA_FROMDEVICE);
+}
+
+static int pluto_start_feed(struct dvb_demux_feed *f)
+{
+	struct pluto *pluto = feed_to_pluto(f);
+
+	/* enable PID filtering */
+	if (pluto->users++ == 0)
+		pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0);
+
+	if ((f->pid < 0x2000) && (f->index < NHWFILTERS))
+		pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid);
+	else if (pluto->full_ts_users++ == 0)
+		pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL);
+
+	return 0;
+}
+
+static int pluto_stop_feed(struct dvb_demux_feed *f)
+{
+	struct pluto *pluto = feed_to_pluto(f);
+
+	/* disable PID filtering */
+	if (--pluto->users == 0)
+		pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL);
+
+	if ((f->pid < 0x2000) && (f->index < NHWFILTERS))
+		pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff);
+	else if (--pluto->full_ts_users == 0)
+		pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0);
+
+	return 0;
+}
+
+static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets)
+{
+	/* synchronize the DMA transfer with the CPU
+	 * first so that we see updated contents. */
+	pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr,
+			TS_DMA_BYTES, PCI_DMA_FROMDEVICE);
+
+	/* Workaround for broken hardware:
+	 * [1] On startup NBPACKETS seems to contain an uninitialized value,
+	 *     but no packets have been transferred.
+	 * [2] Sometimes (actually very often) NBPACKETS stays at zero
+	 *     although one packet has been transferred.
+	 * [3] Sometimes (actually rarely), the card gets into an erroneous
+	 *     mode where it continuously generates interrupts, claiming it
+	 *     has received nbpackets>TS_DMA_PACKETS packets, but no packet
+	 *     has been transferred. Only a reset seems to solve this
+	 */
+	if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) {
+		unsigned int i = 0;
+		while (pluto->dma_buf[i] == 0x47)
+			i += 188;
+		nbpackets = i / 188;
+		if (i == 0) {
+			pluto_reset_ts(pluto, 1);
+			dev_printk(KERN_DEBUG, &pluto->pdev->dev, "resetting TS because of invalid packet counter\n");
+		}
+	}
+
+	dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets);
+
+	/* clear the dma buffer. this is needed to be able to identify
+	 * new valid ts packets above */
+	memset(pluto->dma_buf, 0, nbpackets * 188);
+
+	/* reset the dma address */
+	pluto_set_dma_addr(pluto);
+
+	/* sync the buffer and give it back to the card */
+	pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr,
+			TS_DMA_BYTES, PCI_DMA_FROMDEVICE);
+}
+
+static irqreturn_t pluto_irq(int irq, void *dev_id)
+{
+	struct pluto *pluto = dev_id;
+	u32 tscr;
+
+	/* check whether an interrupt occurred on this device */
+	tscr = pluto_readreg(pluto, REG_TSCR);
+	if (!(tscr & (TSCR_DE | TSCR_OVR)))
+		return IRQ_NONE;
+
+	if (tscr == 0xffffffff) {
+		if (pluto->dead == 0)
+			dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n");
+		/* It's dead Jim */
+		pluto->dead = 1;
+		return IRQ_HANDLED;
+	}
+
+	/* dma end interrupt */
+	if (tscr & TSCR_DE) {
+		pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24);
+		/* overflow interrupt */
+		if (tscr & TSCR_OVR)
+			pluto->overflow++;
+		if (pluto->overflow) {
+			dev_err(&pluto->pdev->dev, "overflow irq (%d)\n",
+					pluto->overflow);
+			pluto_reset_ts(pluto, 1);
+			pluto->overflow = 0;
+		}
+	} else if (tscr & TSCR_OVR) {
+		pluto->overflow++;
+	}
+
+	/* ACK the interrupt */
+	pluto_write_tscr(pluto, tscr | TSCR_IACK);
+
+	return IRQ_HANDLED;
+}
+
+static void __devinit pluto_enable_irqs(struct pluto *pluto)
+{
+	u32 val = pluto_readreg(pluto, REG_TSCR);
+
+	/* disable AFUL and LOCK interrupts */
+	val |= (TSCR_MSKA | TSCR_MSKL);
+	/* enable DMA and OVERFLOW interrupts */
+	val &= ~(TSCR_DEM | TSCR_MSKO);
+	/* clear pending interrupts */
+	val |= TSCR_IACK;
+
+	pluto_write_tscr(pluto, val);
+}
+
+static void pluto_disable_irqs(struct pluto *pluto)
+{
+	u32 val = pluto_readreg(pluto, REG_TSCR);
+
+	/* disable all interrupts */
+	val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL);
+	/* clear pending interrupts */
+	val |= TSCR_IACK;
+
+	pluto_write_tscr(pluto, val);
+}
+
+static int __devinit pluto_hw_init(struct pluto *pluto)
+{
+	pluto_reset_frontend(pluto, 1);
+
+	/* set automatic LED control by FPGA */
+	pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED);
+
+	/* set data endianess */
+#ifdef __LITTLE_ENDIAN
+	pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END);
+#else
+	pluto_rw(pluto, REG_PIDn(0), PID0_END, 0);
+#endif
+	/* map DMA and set address */
+	pluto_dma_map(pluto);
+	pluto_set_dma_addr(pluto);
+
+	/* enable interrupts */
+	pluto_enable_irqs(pluto);
+
+	/* reset TS logic */
+	pluto_reset_ts(pluto, 1);
+
+	return 0;
+}
+
+static void pluto_hw_exit(struct pluto *pluto)
+{
+	/* disable interrupts */
+	pluto_disable_irqs(pluto);
+
+	pluto_reset_ts(pluto, 0);
+
+	/* LED: disable automatic control, enable yellow, disable green */
+	pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1);
+
+	/* unmap DMA */
+	pluto_dma_unmap(pluto);
+
+	pluto_reset_frontend(pluto, 0);
+}
+
+static inline u32 divide(u32 numerator, u32 denominator)
+{
+	if (denominator == 0)
+		return ~0;
+
+	return DIV_ROUND_CLOSEST(numerator, denominator);
+}
+
+/* LG Innotek TDTE-E001P (Infineon TUA6034) */
+static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct pluto *pluto = frontend_to_pluto(fe);
+	struct i2c_msg msg;
+	int ret;
+	u8 buf[4];
+	u32 div;
+
+	// Fref = 166.667 Hz
+	// Fref * 3 = 500.000 Hz
+	// IF = 36166667
+	// IF / Fref = 217
+	//div = divide(p->frequency + 36166667, 166667);
+	div = divide(p->frequency * 3, 500000) + 217;
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = (div >> 0) & 0xff;
+
+	if (p->frequency < 611000000)
+		buf[2] = 0xb4;
+	else if (p->frequency < 811000000)
+		buf[2] = 0xbc;
+	else
+		buf[2] = 0xf4;
+
+	// VHF: 174-230 MHz
+	// center: 350 MHz
+	// UHF: 470-862 MHz
+	if (p->frequency < 350000000)
+		buf[3] = 0x02;
+	else
+		buf[3] = 0x04;
+
+	if (p->bandwidth_hz == 8000000)
+		buf[3] |= 0x08;
+
+	msg.addr = I2C_ADDR_TUA6034 >> 1;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = sizeof(buf);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	ret = i2c_transfer(&pluto->i2c_adap, &msg, 1);
+	if (ret < 0)
+		return ret;
+	else if (ret == 0)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int pluto2_request_firmware(struct dvb_frontend *fe,
+				   const struct firmware **fw, char *name)
+{
+	struct pluto *pluto = frontend_to_pluto(fe);
+
+	return request_firmware(fw, name, &pluto->pdev->dev);
+}
+
+static struct tda1004x_config pluto2_fe_config __devinitdata = {
+	.demod_address = I2C_ADDR_TDA10046 >> 1,
+	.invert = 1,
+	.invert_oclk = 0,
+	.xtal_freq = TDA10046_XTAL_16M,
+	.agc_config = TDA10046_AGC_DEFAULT,
+	.if_freq = TDA10046_FREQ_3617,
+	.request_firmware = pluto2_request_firmware,
+};
+
+static int __devinit frontend_init(struct pluto *pluto)
+{
+	int ret;
+
+	pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap);
+	if (!pluto->fe) {
+		dev_err(&pluto->pdev->dev, "could not attach frontend\n");
+		return -ENODEV;
+	}
+	pluto->fe->ops.tuner_ops.set_params = lg_tdtpe001p_tuner_set_params;
+
+	ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe);
+	if (ret < 0) {
+		if (pluto->fe->ops.release)
+			pluto->fe->ops.release(pluto->fe);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __devinit pluto_read_rev(struct pluto *pluto)
+{
+	u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR;
+	dev_info(&pluto->pdev->dev, "board revision %d.%d\n",
+			(val >> 12) & 0x0f, (val >> 4) & 0xff);
+}
+
+static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac)
+{
+	u32 val = pluto_readreg(pluto, REG_MMAC);
+	mac[0] = (val >> 8) & 0xff;
+	mac[1] = (val >> 0) & 0xff;
+
+	val = pluto_readreg(pluto, REG_IMAC);
+	mac[2] = (val >> 8) & 0xff;
+	mac[3] = (val >> 0) & 0xff;
+
+	val = pluto_readreg(pluto, REG_LMAC);
+	mac[4] = (val >> 8) & 0xff;
+	mac[5] = (val >> 0) & 0xff;
+
+	dev_info(&pluto->pdev->dev, "MAC %pM\n", mac);
+}
+
+static int __devinit pluto_read_serial(struct pluto *pluto)
+{
+	struct pci_dev *pdev = pluto->pdev;
+	unsigned int i, j;
+	u8 __iomem *cis;
+
+	cis = pci_iomap(pdev, 1, 0);
+	if (!cis)
+		return -EIO;
+
+	dev_info(&pdev->dev, "S/N ");
+
+	for (i = 0xe0; i < 0x100; i += 4) {
+		u32 val = readl(&cis[i]);
+		for (j = 0; j < 32; j += 8) {
+			if ((val & 0xff) == 0xff)
+				goto out;
+			printk("%c", val & 0xff);
+			val >>= 8;
+		}
+	}
+out:
+	printk("\n");
+	pci_iounmap(pdev, cis);
+
+	return 0;
+}
+
+static int __devinit pluto2_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	struct pluto *pluto;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret = -ENOMEM;
+
+	pluto = kzalloc(sizeof(struct pluto), GFP_KERNEL);
+	if (!pluto)
+		goto out;
+
+	pluto->pdev = pdev;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_kfree;
+
+	/* enable interrupts */
+	pci_write_config_dword(pdev, 0x6c, 0x8000);
+
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	pluto->io_mem = pci_iomap(pdev, 0, 0x40);
+	if (!pluto->io_mem) {
+		ret = -EIO;
+		goto err_pci_release_regions;
+	}
+
+	pci_set_drvdata(pdev, pluto);
+
+	ret = request_irq(pdev->irq, pluto_irq, IRQF_SHARED, DRIVER_NAME, pluto);
+	if (ret < 0)
+		goto err_pci_iounmap;
+
+	ret = pluto_hw_init(pluto);
+	if (ret < 0)
+		goto err_free_irq;
+
+	/* i2c */
+	i2c_set_adapdata(&pluto->i2c_adap, pluto);
+	strcpy(pluto->i2c_adap.name, DRIVER_NAME);
+	pluto->i2c_adap.owner = THIS_MODULE;
+	pluto->i2c_adap.dev.parent = &pdev->dev;
+	pluto->i2c_adap.algo_data = &pluto->i2c_bit;
+	pluto->i2c_bit.data = pluto;
+	pluto->i2c_bit.setsda = pluto_setsda;
+	pluto->i2c_bit.setscl = pluto_setscl;
+	pluto->i2c_bit.getsda = pluto_getsda;
+	pluto->i2c_bit.getscl = pluto_getscl;
+	pluto->i2c_bit.udelay = 10;
+	pluto->i2c_bit.timeout = 10;
+
+	/* Raise SCL and SDA */
+	pluto_setsda(pluto, 1);
+	pluto_setscl(pluto, 1);
+
+	ret = i2c_bit_add_bus(&pluto->i2c_adap);
+	if (ret < 0)
+		goto err_pluto_hw_exit;
+
+	/* dvb */
+	ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME,
+				   THIS_MODULE, &pdev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	dvb_adapter = &pluto->dvb_adapter;
+
+	pluto_read_rev(pluto);
+	pluto_read_serial(pluto);
+	pluto_read_mac(pluto, dvb_adapter->proposed_mac);
+
+	dvbdemux = &pluto->demux;
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = pluto_start_feed;
+	dvbdemux->stop_feed = pluto_stop_feed;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+			DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+	ret = dvb_dmx_init(dvbdemux);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter;
+
+	dmx = &dvbdemux->dmx;
+
+	pluto->hw_frontend.source = DMX_FRONTEND_0;
+	pluto->mem_frontend.source = DMX_MEMORY_FE;
+	pluto->dmxdev.filternum = NHWFILTERS;
+	pluto->dmxdev.demux = dmx;
+
+	ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter);
+	if (ret < 0)
+		goto err_dvb_dmx_release;
+
+	ret = dmx->add_frontend(dmx, &pluto->hw_frontend);
+	if (ret < 0)
+		goto err_dvb_dmxdev_release;
+
+	ret = dmx->add_frontend(dmx, &pluto->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &pluto->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	ret = frontend_init(pluto);
+	if (ret < 0)
+		goto err_disconnect_frontend;
+
+	dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx);
+out:
+	return ret;
+
+err_disconnect_frontend:
+	dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+	dmx->remove_frontend(dmx, &pluto->mem_frontend);
+err_remove_hw_frontend:
+	dmx->remove_frontend(dmx, &pluto->hw_frontend);
+err_dvb_dmxdev_release:
+	dvb_dmxdev_release(&pluto->dmxdev);
+err_dvb_dmx_release:
+	dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+	dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapter:
+	i2c_del_adapter(&pluto->i2c_adap);
+err_pluto_hw_exit:
+	pluto_hw_exit(pluto);
+err_free_irq:
+	free_irq(pdev->irq, pluto);
+err_pci_iounmap:
+	pci_iounmap(pdev, pluto->io_mem);
+err_pci_release_regions:
+	pci_release_regions(pdev);
+err_pci_disable_device:
+	pci_disable_device(pdev);
+err_kfree:
+	pci_set_drvdata(pdev, NULL);
+	kfree(pluto);
+	goto out;
+}
+
+static void __devexit pluto2_remove(struct pci_dev *pdev)
+{
+	struct pluto *pluto = pci_get_drvdata(pdev);
+	struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter;
+	struct dvb_demux *dvbdemux = &pluto->demux;
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dmx->close(dmx);
+	dvb_net_release(&pluto->dvbnet);
+	if (pluto->fe)
+		dvb_unregister_frontend(pluto->fe);
+
+	dmx->disconnect_frontend(dmx);
+	dmx->remove_frontend(dmx, &pluto->mem_frontend);
+	dmx->remove_frontend(dmx, &pluto->hw_frontend);
+	dvb_dmxdev_release(&pluto->dmxdev);
+	dvb_dmx_release(dvbdemux);
+	dvb_unregister_adapter(dvb_adapter);
+	i2c_del_adapter(&pluto->i2c_adap);
+	pluto_hw_exit(pluto);
+	free_irq(pdev->irq, pluto);
+	pci_iounmap(pdev, pluto->io_mem);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	kfree(pluto);
+}
+
+#ifndef PCI_VENDOR_ID_SCM
+#define PCI_VENDOR_ID_SCM	0x0432
+#endif
+#ifndef PCI_DEVICE_ID_PLUTO2
+#define PCI_DEVICE_ID_PLUTO2	0x0001
+#endif
+
+static struct pci_device_id pluto2_id_table[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_SCM,
+		.device = PCI_DEVICE_ID_PLUTO2,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	}, {
+		/* empty */
+	},
+};
+
+MODULE_DEVICE_TABLE(pci, pluto2_id_table);
+
+static struct pci_driver pluto2_driver = {
+	.name = DRIVER_NAME,
+	.id_table = pluto2_id_table,
+	.probe = pluto2_probe,
+	.remove = __devexit_p(pluto2_remove),
+};
+
+static int __init pluto2_init(void)
+{
+	return pci_register_driver(&pluto2_driver);
+}
+
+static void __exit pluto2_exit(void)
+{
+	pci_unregister_driver(&pluto2_driver);
+}
+
+module_init(pluto2_init);
+module_exit(pluto2_exit);
+
+MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
+MODULE_DESCRIPTION("Pluto2 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig
new file mode 100644
index 000000000000..24501d5bf70d
--- /dev/null
+++ b/drivers/media/pci/pt1/Kconfig
@@ -0,0 +1,12 @@
+config DVB_PT1
+	tristate "PT1 cards"
+	depends on DVB_CORE && PCI && I2C
+	help
+	  Support for Earthsoft PT1 PCI cards.
+
+	  Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y or M if you own such a device and want to use it.
+
diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile
new file mode 100644
index 000000000000..98e391295afe
--- /dev/null
+++ b/drivers/media/pci/pt1/Makefile
@@ -0,0 +1,5 @@
+earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
+
+obj-$(CONFIG_DVB_PT1) += earth-pt1.o
+
+ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
new file mode 100644
index 000000000000..15b35c4725f1
--- /dev/null
+++ b/drivers/media/pci/pt1/pt1.c
@@ -0,0 +1,1246 @@
+/*
+ * driver for Earthsoft PT1/PT2
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * 	by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * 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/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/ratelimit.h>
+
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+#include "va1j5jf8007t.h"
+#include "va1j5jf8007s.h"
+
+#define DRIVER_NAME "earth-pt1"
+
+#define PT1_PAGE_SHIFT 12
+#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT)
+#define PT1_NR_UPACKETS 1024
+#define PT1_NR_BUFS 511
+
+struct pt1_buffer_page {
+	__le32 upackets[PT1_NR_UPACKETS];
+};
+
+struct pt1_table_page {
+	__le32 next_pfn;
+	__le32 buf_pfns[PT1_NR_BUFS];
+};
+
+struct pt1_buffer {
+	struct pt1_buffer_page *page;
+	dma_addr_t addr;
+};
+
+struct pt1_table {
+	struct pt1_table_page *page;
+	dma_addr_t addr;
+	struct pt1_buffer bufs[PT1_NR_BUFS];
+};
+
+#define PT1_NR_ADAPS 4
+
+struct pt1_adapter;
+
+struct pt1 {
+	struct pci_dev *pdev;
+	void __iomem *regs;
+	struct i2c_adapter i2c_adap;
+	int i2c_running;
+	struct pt1_adapter *adaps[PT1_NR_ADAPS];
+	struct pt1_table *tables;
+	struct task_struct *kthread;
+	int table_index;
+	int buf_index;
+
+	struct mutex lock;
+	int power;
+	int reset;
+};
+
+struct pt1_adapter {
+	struct pt1 *pt1;
+	int index;
+
+	u8 *buf;
+	int upacket_count;
+	int packet_count;
+	int st_count;
+
+	struct dvb_adapter adap;
+	struct dvb_demux demux;
+	int users;
+	struct dmxdev dmxdev;
+	struct dvb_frontend *fe;
+	int (*orig_set_voltage)(struct dvb_frontend *fe,
+				fe_sec_voltage_t voltage);
+	int (*orig_sleep)(struct dvb_frontend *fe);
+	int (*orig_init)(struct dvb_frontend *fe);
+
+	fe_sec_voltage_t voltage;
+	int sleep;
+};
+
+#define pt1_printk(level, pt1, format, arg...)	\
+	dev_printk(level, &(pt1)->pdev->dev, format, ##arg)
+
+static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data)
+{
+	writel(data, pt1->regs + reg * 4);
+}
+
+static u32 pt1_read_reg(struct pt1 *pt1, int reg)
+{
+	return readl(pt1->regs + reg * 4);
+}
+
+static int pt1_nr_tables = 8;
+module_param_named(nr_tables, pt1_nr_tables, int, 0);
+
+static void pt1_increment_table_count(struct pt1 *pt1)
+{
+	pt1_write_reg(pt1, 0, 0x00000020);
+}
+
+static void pt1_init_table_count(struct pt1 *pt1)
+{
+	pt1_write_reg(pt1, 0, 0x00000010);
+}
+
+static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn)
+{
+	pt1_write_reg(pt1, 5, first_pfn);
+	pt1_write_reg(pt1, 0, 0x0c000040);
+}
+
+static void pt1_unregister_tables(struct pt1 *pt1)
+{
+	pt1_write_reg(pt1, 0, 0x08080000);
+}
+
+static int pt1_sync(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < 57; i++) {
+		if (pt1_read_reg(pt1, 0) & 0x20000000)
+			return 0;
+		pt1_write_reg(pt1, 0, 0x00000008);
+	}
+	pt1_printk(KERN_ERR, pt1, "could not sync\n");
+	return -EIO;
+}
+
+static u64 pt1_identify(struct pt1 *pt1)
+{
+	int i;
+	u64 id;
+	id = 0;
+	for (i = 0; i < 57; i++) {
+		id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i;
+		pt1_write_reg(pt1, 0, 0x00000008);
+	}
+	return id;
+}
+
+static int pt1_unlock(struct pt1 *pt1)
+{
+	int i;
+	pt1_write_reg(pt1, 0, 0x00000008);
+	for (i = 0; i < 3; i++) {
+		if (pt1_read_reg(pt1, 0) & 0x80000000)
+			return 0;
+		schedule_timeout_uninterruptible((HZ + 999) / 1000);
+	}
+	pt1_printk(KERN_ERR, pt1, "could not unlock\n");
+	return -EIO;
+}
+
+static int pt1_reset_pci(struct pt1 *pt1)
+{
+	int i;
+	pt1_write_reg(pt1, 0, 0x01010000);
+	pt1_write_reg(pt1, 0, 0x01000000);
+	for (i = 0; i < 10; i++) {
+		if (pt1_read_reg(pt1, 0) & 0x00000001)
+			return 0;
+		schedule_timeout_uninterruptible((HZ + 999) / 1000);
+	}
+	pt1_printk(KERN_ERR, pt1, "could not reset PCI\n");
+	return -EIO;
+}
+
+static int pt1_reset_ram(struct pt1 *pt1)
+{
+	int i;
+	pt1_write_reg(pt1, 0, 0x02020000);
+	pt1_write_reg(pt1, 0, 0x02000000);
+	for (i = 0; i < 10; i++) {
+		if (pt1_read_reg(pt1, 0) & 0x00000002)
+			return 0;
+		schedule_timeout_uninterruptible((HZ + 999) / 1000);
+	}
+	pt1_printk(KERN_ERR, pt1, "could not reset RAM\n");
+	return -EIO;
+}
+
+static int pt1_do_enable_ram(struct pt1 *pt1)
+{
+	int i, j;
+	u32 status;
+	status = pt1_read_reg(pt1, 0) & 0x00000004;
+	pt1_write_reg(pt1, 0, 0x00000002);
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 1024; j++) {
+			if ((pt1_read_reg(pt1, 0) & 0x00000004) != status)
+				return 0;
+		}
+		schedule_timeout_uninterruptible((HZ + 999) / 1000);
+	}
+	pt1_printk(KERN_ERR, pt1, "could not enable RAM\n");
+	return -EIO;
+}
+
+static int pt1_enable_ram(struct pt1 *pt1)
+{
+	int i, ret;
+	int phase;
+	schedule_timeout_uninterruptible((HZ + 999) / 1000);
+	phase = pt1->pdev->device == 0x211a ? 128 : 166;
+	for (i = 0; i < phase; i++) {
+		ret = pt1_do_enable_ram(pt1);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static void pt1_disable_ram(struct pt1 *pt1)
+{
+	pt1_write_reg(pt1, 0, 0x0b0b0000);
+}
+
+static void pt1_set_stream(struct pt1 *pt1, int index, int enabled)
+{
+	pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index);
+}
+
+static void pt1_init_streams(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < PT1_NR_ADAPS; i++)
+		pt1_set_stream(pt1, i, 0);
+}
+
+static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
+{
+	u32 upacket;
+	int i;
+	int index;
+	struct pt1_adapter *adap;
+	int offset;
+	u8 *buf;
+	int sc;
+
+	if (!page->upackets[PT1_NR_UPACKETS - 1])
+		return 0;
+
+	for (i = 0; i < PT1_NR_UPACKETS; i++) {
+		upacket = le32_to_cpu(page->upackets[i]);
+		index = (upacket >> 29) - 1;
+		if (index < 0 || index >=  PT1_NR_ADAPS)
+			continue;
+
+		adap = pt1->adaps[index];
+		if (upacket >> 25 & 1)
+			adap->upacket_count = 0;
+		else if (!adap->upacket_count)
+			continue;
+
+		if (upacket >> 24 & 1)
+			printk_ratelimited(KERN_INFO "earth-pt1: device "
+				"buffer overflowing. table[%d] buf[%d]\n",
+				pt1->table_index, pt1->buf_index);
+		sc = upacket >> 26 & 0x7;
+		if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7))
+			printk_ratelimited(KERN_INFO "earth-pt1: data loss"
+				" in streamID(adapter)[%d]\n", index);
+		adap->st_count = sc;
+
+		buf = adap->buf;
+		offset = adap->packet_count * 188 + adap->upacket_count * 3;
+		buf[offset] = upacket >> 16;
+		buf[offset + 1] = upacket >> 8;
+		if (adap->upacket_count != 62)
+			buf[offset + 2] = upacket;
+
+		if (++adap->upacket_count >= 63) {
+			adap->upacket_count = 0;
+			if (++adap->packet_count >= 21) {
+				dvb_dmx_swfilter_packets(&adap->demux, buf, 21);
+				adap->packet_count = 0;
+			}
+		}
+	}
+
+	page->upackets[PT1_NR_UPACKETS - 1] = 0;
+	return 1;
+}
+
+static int pt1_thread(void *data)
+{
+	struct pt1 *pt1;
+	struct pt1_buffer_page *page;
+
+	pt1 = data;
+	set_freezable();
+
+	while (!kthread_should_stop()) {
+		try_to_freeze();
+
+		page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page;
+		if (!pt1_filter(pt1, page)) {
+			schedule_timeout_interruptible((HZ + 999) / 1000);
+			continue;
+		}
+
+		if (++pt1->buf_index >= PT1_NR_BUFS) {
+			pt1_increment_table_count(pt1);
+			pt1->buf_index = 0;
+			if (++pt1->table_index >= pt1_nr_tables)
+				pt1->table_index = 0;
+		}
+	}
+
+	return 0;
+}
+
+static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr)
+{
+	dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr);
+}
+
+static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp)
+{
+	void *page;
+	dma_addr_t addr;
+
+	page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr,
+				  GFP_KERNEL);
+	if (page == NULL)
+		return NULL;
+
+	BUG_ON(addr & (PT1_PAGE_SIZE - 1));
+	BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1);
+
+	*addrp = addr;
+	*pfnp = addr >> PT1_PAGE_SHIFT;
+	return page;
+}
+
+static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf)
+{
+	pt1_free_page(pt1, buf->page, buf->addr);
+}
+
+static int
+pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf,  u32 *pfnp)
+{
+	struct pt1_buffer_page *page;
+	dma_addr_t addr;
+
+	page = pt1_alloc_page(pt1, &addr, pfnp);
+	if (page == NULL)
+		return -ENOMEM;
+
+	page->upackets[PT1_NR_UPACKETS - 1] = 0;
+
+	buf->page = page;
+	buf->addr = addr;
+	return 0;
+}
+
+static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table)
+{
+	int i;
+
+	for (i = 0; i < PT1_NR_BUFS; i++)
+		pt1_cleanup_buffer(pt1, &table->bufs[i]);
+
+	pt1_free_page(pt1, table->page, table->addr);
+}
+
+static int
+pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp)
+{
+	struct pt1_table_page *page;
+	dma_addr_t addr;
+	int i, ret;
+	u32 buf_pfn;
+
+	page = pt1_alloc_page(pt1, &addr, pfnp);
+	if (page == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < PT1_NR_BUFS; i++) {
+		ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn);
+		if (ret < 0)
+			goto err;
+
+		page->buf_pfns[i] = cpu_to_le32(buf_pfn);
+	}
+
+	pt1_increment_table_count(pt1);
+	table->page = page;
+	table->addr = addr;
+	return 0;
+
+err:
+	while (i--)
+		pt1_cleanup_buffer(pt1, &table->bufs[i]);
+
+	pt1_free_page(pt1, page, addr);
+	return ret;
+}
+
+static void pt1_cleanup_tables(struct pt1 *pt1)
+{
+	struct pt1_table *tables;
+	int i;
+
+	tables = pt1->tables;
+	pt1_unregister_tables(pt1);
+
+	for (i = 0; i < pt1_nr_tables; i++)
+		pt1_cleanup_table(pt1, &tables[i]);
+
+	vfree(tables);
+}
+
+static int pt1_init_tables(struct pt1 *pt1)
+{
+	struct pt1_table *tables;
+	int i, ret;
+	u32 first_pfn, pfn;
+
+	tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables);
+	if (tables == NULL)
+		return -ENOMEM;
+
+	pt1_init_table_count(pt1);
+
+	i = 0;
+	if (pt1_nr_tables) {
+		ret = pt1_init_table(pt1, &tables[0], &first_pfn);
+		if (ret)
+			goto err;
+		i++;
+	}
+
+	while (i < pt1_nr_tables) {
+		ret = pt1_init_table(pt1, &tables[i], &pfn);
+		if (ret)
+			goto err;
+		tables[i - 1].page->next_pfn = cpu_to_le32(pfn);
+		i++;
+	}
+
+	tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn);
+
+	pt1_register_tables(pt1, first_pfn);
+	pt1->tables = tables;
+	return 0;
+
+err:
+	while (i--)
+		pt1_cleanup_table(pt1, &tables[i]);
+
+	vfree(tables);
+	return ret;
+}
+
+static int pt1_start_polling(struct pt1 *pt1)
+{
+	int ret = 0;
+
+	mutex_lock(&pt1->lock);
+	if (!pt1->kthread) {
+		pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1");
+		if (IS_ERR(pt1->kthread)) {
+			ret = PTR_ERR(pt1->kthread);
+			pt1->kthread = NULL;
+		}
+	}
+	mutex_unlock(&pt1->lock);
+	return ret;
+}
+
+static int pt1_start_feed(struct dvb_demux_feed *feed)
+{
+	struct pt1_adapter *adap;
+	adap = container_of(feed->demux, struct pt1_adapter, demux);
+	if (!adap->users++) {
+		int ret;
+
+		ret = pt1_start_polling(adap->pt1);
+		if (ret)
+			return ret;
+		pt1_set_stream(adap->pt1, adap->index, 1);
+	}
+	return 0;
+}
+
+static void pt1_stop_polling(struct pt1 *pt1)
+{
+	int i, count;
+
+	mutex_lock(&pt1->lock);
+	for (i = 0, count = 0; i < PT1_NR_ADAPS; i++)
+		count += pt1->adaps[i]->users;
+
+	if (count == 0 && pt1->kthread) {
+		kthread_stop(pt1->kthread);
+		pt1->kthread = NULL;
+	}
+	mutex_unlock(&pt1->lock);
+}
+
+static int pt1_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct pt1_adapter *adap;
+	adap = container_of(feed->demux, struct pt1_adapter, demux);
+	if (!--adap->users) {
+		pt1_set_stream(adap->pt1, adap->index, 0);
+		pt1_stop_polling(adap->pt1);
+	}
+	return 0;
+}
+
+static void
+pt1_update_power(struct pt1 *pt1)
+{
+	int bits;
+	int i;
+	struct pt1_adapter *adap;
+	static const int sleep_bits[] = {
+		1 << 4,
+		1 << 6 | 1 << 7,
+		1 << 5,
+		1 << 6 | 1 << 8,
+	};
+
+	bits = pt1->power | !pt1->reset << 3;
+	mutex_lock(&pt1->lock);
+	for (i = 0; i < PT1_NR_ADAPS; i++) {
+		adap = pt1->adaps[i];
+		switch (adap->voltage) {
+		case SEC_VOLTAGE_13: /* actually 11V */
+			bits |= 1 << 1;
+			break;
+		case SEC_VOLTAGE_18: /* actually 15V */
+			bits |= 1 << 1 | 1 << 2;
+			break;
+		default:
+			break;
+		}
+
+		/* XXX: The bits should be changed depending on adap->sleep. */
+		bits |= sleep_bits[i];
+	}
+	pt1_write_reg(pt1, 1, bits);
+	mutex_unlock(&pt1->lock);
+}
+
+static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct pt1_adapter *adap;
+
+	adap = container_of(fe->dvb, struct pt1_adapter, adap);
+	adap->voltage = voltage;
+	pt1_update_power(adap->pt1);
+
+	if (adap->orig_set_voltage)
+		return adap->orig_set_voltage(fe, voltage);
+	else
+		return 0;
+}
+
+static int pt1_sleep(struct dvb_frontend *fe)
+{
+	struct pt1_adapter *adap;
+
+	adap = container_of(fe->dvb, struct pt1_adapter, adap);
+	adap->sleep = 1;
+	pt1_update_power(adap->pt1);
+
+	if (adap->orig_sleep)
+		return adap->orig_sleep(fe);
+	else
+		return 0;
+}
+
+static int pt1_wakeup(struct dvb_frontend *fe)
+{
+	struct pt1_adapter *adap;
+
+	adap = container_of(fe->dvb, struct pt1_adapter, adap);
+	adap->sleep = 0;
+	pt1_update_power(adap->pt1);
+	schedule_timeout_uninterruptible((HZ + 999) / 1000);
+
+	if (adap->orig_init)
+		return adap->orig_init(fe);
+	else
+		return 0;
+}
+
+static void pt1_free_adapter(struct pt1_adapter *adap)
+{
+	adap->demux.dmx.close(&adap->demux.dmx);
+	dvb_dmxdev_release(&adap->dmxdev);
+	dvb_dmx_release(&adap->demux);
+	dvb_unregister_adapter(&adap->adap);
+	free_page((unsigned long)adap->buf);
+	kfree(adap);
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct pt1_adapter *
+pt1_alloc_adapter(struct pt1 *pt1)
+{
+	struct pt1_adapter *adap;
+	void *buf;
+	struct dvb_adapter *dvb_adap;
+	struct dvb_demux *demux;
+	struct dmxdev *dmxdev;
+	int ret;
+
+	adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL);
+	if (!adap) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	adap->pt1 = pt1;
+
+	adap->voltage = SEC_VOLTAGE_OFF;
+	adap->sleep = 1;
+
+	buf = (u8 *)__get_free_page(GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto err_kfree;
+	}
+
+	adap->buf = buf;
+	adap->upacket_count = 0;
+	adap->packet_count = 0;
+	adap->st_count = -1;
+
+	dvb_adap = &adap->adap;
+	dvb_adap->priv = adap;
+	ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE,
+				   &pt1->pdev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_free_page;
+
+	demux = &adap->demux;
+	demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+	demux->priv = adap;
+	demux->feednum = 256;
+	demux->filternum = 256;
+	demux->start_feed = pt1_start_feed;
+	demux->stop_feed = pt1_stop_feed;
+	demux->write_to_decoder = NULL;
+	ret = dvb_dmx_init(demux);
+	if (ret < 0)
+		goto err_unregister_adapter;
+
+	dmxdev = &adap->dmxdev;
+	dmxdev->filternum = 256;
+	dmxdev->demux = &demux->dmx;
+	dmxdev->capabilities = 0;
+	ret = dvb_dmxdev_init(dmxdev, dvb_adap);
+	if (ret < 0)
+		goto err_dmx_release;
+
+	return adap;
+
+err_dmx_release:
+	dvb_dmx_release(demux);
+err_unregister_adapter:
+	dvb_unregister_adapter(dvb_adap);
+err_free_page:
+	free_page((unsigned long)buf);
+err_kfree:
+	kfree(adap);
+err:
+	return ERR_PTR(ret);
+}
+
+static void pt1_cleanup_adapters(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < PT1_NR_ADAPS; i++)
+		pt1_free_adapter(pt1->adaps[i]);
+}
+
+static int pt1_init_adapters(struct pt1 *pt1)
+{
+	int i;
+	struct pt1_adapter *adap;
+	int ret;
+
+	for (i = 0; i < PT1_NR_ADAPS; i++) {
+		adap = pt1_alloc_adapter(pt1);
+		if (IS_ERR(adap)) {
+			ret = PTR_ERR(adap);
+			goto err;
+		}
+
+		adap->index = i;
+		pt1->adaps[i] = adap;
+	}
+	return 0;
+
+err:
+	while (i--)
+		pt1_free_adapter(pt1->adaps[i]);
+
+	return ret;
+}
+
+static void pt1_cleanup_frontend(struct pt1_adapter *adap)
+{
+	dvb_unregister_frontend(adap->fe);
+}
+
+static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe)
+{
+	int ret;
+
+	adap->orig_set_voltage = fe->ops.set_voltage;
+	adap->orig_sleep = fe->ops.sleep;
+	adap->orig_init = fe->ops.init;
+	fe->ops.set_voltage = pt1_set_voltage;
+	fe->ops.sleep = pt1_sleep;
+	fe->ops.init = pt1_wakeup;
+
+	ret = dvb_register_frontend(&adap->adap, fe);
+	if (ret < 0)
+		return ret;
+
+	adap->fe = fe;
+	return 0;
+}
+
+static void pt1_cleanup_frontends(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < PT1_NR_ADAPS; i++)
+		pt1_cleanup_frontend(pt1->adaps[i]);
+}
+
+struct pt1_config {
+	struct va1j5jf8007s_config va1j5jf8007s_config;
+	struct va1j5jf8007t_config va1j5jf8007t_config;
+};
+
+static const struct pt1_config pt1_configs[2] = {
+	{
+		{
+			.demod_address = 0x1b,
+			.frequency = VA1J5JF8007S_20MHZ,
+		},
+		{
+			.demod_address = 0x1a,
+			.frequency = VA1J5JF8007T_20MHZ,
+		},
+	}, {
+		{
+			.demod_address = 0x19,
+			.frequency = VA1J5JF8007S_20MHZ,
+		},
+		{
+			.demod_address = 0x18,
+			.frequency = VA1J5JF8007T_20MHZ,
+		},
+	},
+};
+
+static const struct pt1_config pt2_configs[2] = {
+	{
+		{
+			.demod_address = 0x1b,
+			.frequency = VA1J5JF8007S_25MHZ,
+		},
+		{
+			.demod_address = 0x1a,
+			.frequency = VA1J5JF8007T_25MHZ,
+		},
+	}, {
+		{
+			.demod_address = 0x19,
+			.frequency = VA1J5JF8007S_25MHZ,
+		},
+		{
+			.demod_address = 0x18,
+			.frequency = VA1J5JF8007T_25MHZ,
+		},
+	},
+};
+
+static int pt1_init_frontends(struct pt1 *pt1)
+{
+	int i, j;
+	struct i2c_adapter *i2c_adap;
+	const struct pt1_config *configs, *config;
+	struct dvb_frontend *fe[4];
+	int ret;
+
+	i = 0;
+	j = 0;
+
+	i2c_adap = &pt1->i2c_adap;
+	configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs;
+	do {
+		config = &configs[i / 2];
+
+		fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config,
+					    i2c_adap);
+		if (!fe[i]) {
+			ret = -ENODEV; /* This does not sound nice... */
+			goto err;
+		}
+		i++;
+
+		fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config,
+					    i2c_adap);
+		if (!fe[i]) {
+			ret = -ENODEV;
+			goto err;
+		}
+		i++;
+
+		ret = va1j5jf8007s_prepare(fe[i - 2]);
+		if (ret < 0)
+			goto err;
+
+		ret = va1j5jf8007t_prepare(fe[i - 1]);
+		if (ret < 0)
+			goto err;
+
+	} while (i < 4);
+
+	do {
+		ret = pt1_init_frontend(pt1->adaps[j], fe[j]);
+		if (ret < 0)
+			goto err;
+	} while (++j < 4);
+
+	return 0;
+
+err:
+	while (i-- > j)
+		fe[i]->ops.release(fe[i]);
+
+	while (j--)
+		dvb_unregister_frontend(fe[j]);
+
+	return ret;
+}
+
+static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable,
+			 int clock, int data, int next_addr)
+{
+	pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 |
+		      !clock << 11 | !data << 10 | next_addr);
+}
+
+static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data)
+{
+	pt1_i2c_emit(pt1, addr,     1, 0, 0, data, addr + 1);
+	pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2);
+	pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3);
+	*addrp = addr + 3;
+}
+
+static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp)
+{
+	pt1_i2c_emit(pt1, addr,     1, 0, 0, 1, addr + 1);
+	pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2);
+	pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3);
+	pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4);
+	*addrp = addr + 4;
+}
+
+static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data)
+{
+	int i;
+	for (i = 0; i < 8; i++)
+		pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1);
+	pt1_i2c_write_bit(pt1, addr, &addr, 1);
+	*addrp = addr;
+}
+
+static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last)
+{
+	int i;
+	for (i = 0; i < 8; i++)
+		pt1_i2c_read_bit(pt1, addr, &addr);
+	pt1_i2c_write_bit(pt1, addr, &addr, last);
+	*addrp = addr;
+}
+
+static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp)
+{
+	pt1_i2c_emit(pt1, addr,     1, 0, 1, 1, addr + 1);
+	pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2);
+	pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3);
+	*addrp = addr + 3;
+}
+
+static void
+pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg)
+{
+	int i;
+	pt1_i2c_prepare(pt1, addr, &addr);
+	pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1);
+	for (i = 0; i < msg->len; i++)
+		pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]);
+	*addrp = addr;
+}
+
+static void
+pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg)
+{
+	int i;
+	pt1_i2c_prepare(pt1, addr, &addr);
+	pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1);
+	for (i = 0; i < msg->len; i++)
+		pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1);
+	*addrp = addr;
+}
+
+static int pt1_i2c_end(struct pt1 *pt1, int addr)
+{
+	pt1_i2c_emit(pt1, addr,     1, 0, 0, 0, addr + 1);
+	pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2);
+	pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0);
+
+	pt1_write_reg(pt1, 0, 0x00000004);
+	do {
+		if (signal_pending(current))
+			return -EINTR;
+		schedule_timeout_interruptible((HZ + 999) / 1000);
+	} while (pt1_read_reg(pt1, 0) & 0x00000080);
+	return 0;
+}
+
+static void pt1_i2c_begin(struct pt1 *pt1, int *addrp)
+{
+	int addr;
+	addr = 0;
+
+	pt1_i2c_emit(pt1, addr,     0, 0, 1, 1, addr /* itself */);
+	addr = addr + 1;
+
+	if (!pt1->i2c_running) {
+		pt1_i2c_emit(pt1, addr,     1, 0, 1, 1, addr + 1);
+		pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2);
+		addr = addr + 2;
+		pt1->i2c_running = 1;
+	}
+	*addrp = addr;
+}
+
+static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct pt1 *pt1;
+	int i;
+	struct i2c_msg *msg, *next_msg;
+	int addr, ret;
+	u16 len;
+	u32 word;
+
+	pt1 = i2c_get_adapdata(adap);
+
+	for (i = 0; i < num; i++) {
+		msg = &msgs[i];
+		if (msg->flags & I2C_M_RD)
+			return -ENOTSUPP;
+
+		if (i + 1 < num)
+			next_msg = &msgs[i + 1];
+		else
+			next_msg = NULL;
+
+		if (next_msg && next_msg->flags & I2C_M_RD) {
+			i++;
+
+			len = next_msg->len;
+			if (len > 4)
+				return -ENOTSUPP;
+
+			pt1_i2c_begin(pt1, &addr);
+			pt1_i2c_write_msg(pt1, addr, &addr, msg);
+			pt1_i2c_read_msg(pt1, addr, &addr, next_msg);
+			ret = pt1_i2c_end(pt1, addr);
+			if (ret < 0)
+				return ret;
+
+			word = pt1_read_reg(pt1, 2);
+			while (len--) {
+				next_msg->buf[len] = word;
+				word >>= 8;
+			}
+		} else {
+			pt1_i2c_begin(pt1, &addr);
+			pt1_i2c_write_msg(pt1, addr, &addr, msg);
+			ret = pt1_i2c_end(pt1, addr);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return num;
+}
+
+static u32 pt1_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm pt1_i2c_algo = {
+	.master_xfer = pt1_i2c_xfer,
+	.functionality = pt1_i2c_func,
+};
+
+static void pt1_i2c_wait(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < 128; i++)
+		pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0);
+}
+
+static void pt1_i2c_init(struct pt1 *pt1)
+{
+	int i;
+	for (i = 0; i < 1024; i++)
+		pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0);
+}
+
+static void __devexit pt1_remove(struct pci_dev *pdev)
+{
+	struct pt1 *pt1;
+	void __iomem *regs;
+
+	pt1 = pci_get_drvdata(pdev);
+	regs = pt1->regs;
+
+	if (pt1->kthread)
+		kthread_stop(pt1->kthread);
+	pt1_cleanup_tables(pt1);
+	pt1_cleanup_frontends(pt1);
+	pt1_disable_ram(pt1);
+	pt1->power = 0;
+	pt1->reset = 1;
+	pt1_update_power(pt1);
+	pt1_cleanup_adapters(pt1);
+	i2c_del_adapter(&pt1->i2c_adap);
+	pci_set_drvdata(pdev, NULL);
+	kfree(pt1);
+	pci_iounmap(pdev, regs);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static int __devinit
+pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int ret;
+	void __iomem *regs;
+	struct pt1 *pt1;
+	struct i2c_adapter *i2c_adap;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err;
+
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	regs = pci_iomap(pdev, 0, 0);
+	if (!regs) {
+		ret = -EIO;
+		goto err_pci_release_regions;
+	}
+
+	pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL);
+	if (!pt1) {
+		ret = -ENOMEM;
+		goto err_pci_iounmap;
+	}
+
+	mutex_init(&pt1->lock);
+	pt1->pdev = pdev;
+	pt1->regs = regs;
+	pci_set_drvdata(pdev, pt1);
+
+	ret = pt1_init_adapters(pt1);
+	if (ret < 0)
+		goto err_kfree;
+
+	mutex_init(&pt1->lock);
+
+	pt1->power = 0;
+	pt1->reset = 1;
+	pt1_update_power(pt1);
+
+	i2c_adap = &pt1->i2c_adap;
+	i2c_adap->algo = &pt1_i2c_algo;
+	i2c_adap->algo_data = NULL;
+	i2c_adap->dev.parent = &pdev->dev;
+	strcpy(i2c_adap->name, DRIVER_NAME);
+	i2c_set_adapdata(i2c_adap, pt1);
+	ret = i2c_add_adapter(i2c_adap);
+	if (ret < 0)
+		goto err_pt1_cleanup_adapters;
+
+	pt1_i2c_init(pt1);
+	pt1_i2c_wait(pt1);
+
+	ret = pt1_sync(pt1);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	pt1_identify(pt1);
+
+	ret = pt1_unlock(pt1);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	ret = pt1_reset_pci(pt1);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	ret = pt1_reset_ram(pt1);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	ret = pt1_enable_ram(pt1);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	pt1_init_streams(pt1);
+
+	pt1->power = 1;
+	pt1_update_power(pt1);
+	schedule_timeout_uninterruptible((HZ + 49) / 50);
+
+	pt1->reset = 0;
+	pt1_update_power(pt1);
+	schedule_timeout_uninterruptible((HZ + 999) / 1000);
+
+	ret = pt1_init_frontends(pt1);
+	if (ret < 0)
+		goto err_pt1_disable_ram;
+
+	ret = pt1_init_tables(pt1);
+	if (ret < 0)
+		goto err_pt1_cleanup_frontends;
+
+	return 0;
+
+err_pt1_cleanup_frontends:
+	pt1_cleanup_frontends(pt1);
+err_pt1_disable_ram:
+	pt1_disable_ram(pt1);
+	pt1->power = 0;
+	pt1->reset = 1;
+	pt1_update_power(pt1);
+err_i2c_del_adapter:
+	i2c_del_adapter(i2c_adap);
+err_pt1_cleanup_adapters:
+	pt1_cleanup_adapters(pt1);
+err_kfree:
+	pci_set_drvdata(pdev, NULL);
+	kfree(pt1);
+err_pci_iounmap:
+	pci_iounmap(pdev, regs);
+err_pci_release_regions:
+	pci_release_regions(pdev);
+err_pci_disable_device:
+	pci_disable_device(pdev);
+err:
+	return ret;
+
+}
+
+static struct pci_device_id pt1_id_table[] = {
+	{ PCI_DEVICE(0x10ee, 0x211a) },
+	{ PCI_DEVICE(0x10ee, 0x222a) },
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, pt1_id_table);
+
+static struct pci_driver pt1_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= pt1_probe,
+	.remove		= __devexit_p(pt1_remove),
+	.id_table	= pt1_id_table,
+};
+
+
+static int __init pt1_init(void)
+{
+	return pci_register_driver(&pt1_driver);
+}
+
+
+static void __exit pt1_cleanup(void)
+{
+	pci_unregister_driver(&pt1_driver);
+}
+
+module_init(pt1_init);
+module_exit(pt1_cleanup);
+
+MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>");
+MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c
new file mode 100644
index 000000000000..1b637b74ef58
--- /dev/null
+++ b/drivers/media/pci/pt1/va1j5jf8007s.c
@@ -0,0 +1,736 @@
+/*
+ * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * 	by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * 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/slab.h>
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+#include "va1j5jf8007s.h"
+
+enum va1j5jf8007s_tune_state {
+	VA1J5JF8007S_IDLE,
+	VA1J5JF8007S_SET_FREQUENCY_1,
+	VA1J5JF8007S_SET_FREQUENCY_2,
+	VA1J5JF8007S_SET_FREQUENCY_3,
+	VA1J5JF8007S_CHECK_FREQUENCY,
+	VA1J5JF8007S_SET_MODULATION,
+	VA1J5JF8007S_CHECK_MODULATION,
+	VA1J5JF8007S_SET_TS_ID,
+	VA1J5JF8007S_CHECK_TS_ID,
+	VA1J5JF8007S_TRACK,
+};
+
+struct va1j5jf8007s_state {
+	const struct va1j5jf8007s_config *config;
+	struct i2c_adapter *adap;
+	struct dvb_frontend fe;
+	enum va1j5jf8007s_tune_state tune_state;
+};
+
+static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct va1j5jf8007s_state *state;
+	u8 addr;
+	int i;
+	u8 write_buf[1], read_buf[1];
+	struct i2c_msg msgs[2];
+	s32 word, x1, x2, x3, x4, x5, y;
+
+	state = fe->demodulator_priv;
+	addr = state->config->demod_address;
+
+	word = 0;
+	for (i = 0; i < 2; i++) {
+		write_buf[0] = 0xbc + i;
+
+		msgs[0].addr = addr;
+		msgs[0].flags = 0;
+		msgs[0].len = sizeof(write_buf);
+		msgs[0].buf = write_buf;
+
+		msgs[1].addr = addr;
+		msgs[1].flags = I2C_M_RD;
+		msgs[1].len = sizeof(read_buf);
+		msgs[1].buf = read_buf;
+
+		if (i2c_transfer(state->adap, msgs, 2) != 2)
+			return -EREMOTEIO;
+
+		word <<= 8;
+		word |= read_buf[0];
+	}
+
+	word -= 3000;
+	if (word < 0)
+		word = 0;
+
+	x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
+	x2 = (s64)x1 * x1 >> 31;
+	x3 = (s64)x2 * x1 >> 31;
+	x4 = (s64)x2 * x2 >> 31;
+	x5 = (s64)x4 * x1 >> 31;
+
+	y = (58857ll << 23) / 1000;
+	y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
+	y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
+	y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
+	y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
+	y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
+
+	*snr = y < 0 ? 0 : y >> 15;
+	return 0;
+}
+
+static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+
+static int
+va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct va1j5jf8007s_state *state;
+
+	state = fe->demodulator_priv;
+
+	switch (state->tune_state) {
+	case VA1J5JF8007S_IDLE:
+	case VA1J5JF8007S_SET_FREQUENCY_1:
+	case VA1J5JF8007S_SET_FREQUENCY_2:
+	case VA1J5JF8007S_SET_FREQUENCY_3:
+	case VA1J5JF8007S_CHECK_FREQUENCY:
+		*status = 0;
+		return 0;
+
+
+	case VA1J5JF8007S_SET_MODULATION:
+	case VA1J5JF8007S_CHECK_MODULATION:
+		*status |= FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007S_SET_TS_ID:
+	case VA1J5JF8007S_CHECK_TS_ID:
+		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+		return 0;
+
+	case VA1J5JF8007S_TRACK:
+		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+		return 0;
+	}
+
+	BUG();
+}
+
+struct va1j5jf8007s_cb_map {
+	u32 frequency;
+	u8 cb;
+};
+
+static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
+	{  986000, 0xb2 },
+	{ 1072000, 0xd2 },
+	{ 1154000, 0xe2 },
+	{ 1291000, 0x20 },
+	{ 1447000, 0x40 },
+	{ 1615000, 0x60 },
+	{ 1791000, 0x80 },
+	{ 1972000, 0xa0 },
+};
+
+static u8 va1j5jf8007s_lookup_cb(u32 frequency)
+{
+	int i;
+	const struct va1j5jf8007s_cb_map *map;
+
+	for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
+		map = &va1j5jf8007s_cb_maps[i];
+		if (frequency < map->frequency)
+			return map->cb;
+	}
+	return 0xc0;
+}
+
+static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
+{
+	u32 frequency;
+	u16 word;
+	u8 buf[6];
+	struct i2c_msg msg;
+
+	frequency = state->fe.dtv_property_cache.frequency;
+
+	word = (frequency + 500) / 1000;
+	if (frequency < 1072000)
+		word = (word << 1 & ~0x1f) | (word & 0x0f);
+
+	buf[0] = 0xfe;
+	buf[1] = 0xc0;
+	buf[2] = 0x40 | word >> 8;
+	buf[3] = word;
+	buf[4] = 0xe0;
+	buf[5] = va1j5jf8007s_lookup_cb(frequency);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
+{
+	u8 buf[3];
+	struct i2c_msg msg;
+
+	buf[0] = 0xfe;
+	buf[1] = 0xc0;
+	buf[2] = 0xe4;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
+{
+	u32 frequency;
+	u8 buf[4];
+	struct i2c_msg msg;
+
+	frequency = state->fe.dtv_property_cache.frequency;
+
+	buf[0] = 0xfe;
+	buf[1] = 0xc0;
+	buf[2] = 0xf4;
+	buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int
+va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
+{
+	u8 addr;
+	u8 write_buf[2], read_buf[1];
+	struct i2c_msg msgs[2];
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0xfe;
+	write_buf[1] = 0xc1;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	*lock = read_buf[0] & 0x40;
+	return 0;
+}
+
+static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
+{
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	buf[0] = 0x03;
+	buf[1] = 0x01;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int
+va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
+{
+	u8 addr;
+	u8 write_buf[1], read_buf[1];
+	struct i2c_msg msgs[2];
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0xc3;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	*lock = !(read_buf[0] & 0x10);
+	return 0;
+}
+
+static int
+va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
+{
+	u32 ts_id;
+	u8 buf[3];
+	struct i2c_msg msg;
+
+	ts_id = state->fe.dtv_property_cache.stream_id;
+	if (!ts_id || ts_id == NO_STREAM_ID_FILTER)
+		return 0;
+
+	buf[0] = 0x8f;
+	buf[1] = ts_id >> 8;
+	buf[2] = ts_id;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int
+va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
+{
+	u8 addr;
+	u8 write_buf[1], read_buf[2];
+	struct i2c_msg msgs[2];
+	u32 ts_id;
+
+	ts_id = state->fe.dtv_property_cache.stream_id;
+	if (!ts_id || ts_id == NO_STREAM_ID_FILTER) {
+		*lock = 1;
+		return 0;
+	}
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0xe6;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	*lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
+	return 0;
+}
+
+static int
+va1j5jf8007s_tune(struct dvb_frontend *fe,
+		  bool re_tune,
+		  unsigned int mode_flags,  unsigned int *delay,
+		  fe_status_t *status)
+{
+	struct va1j5jf8007s_state *state;
+	int ret;
+	int lock = 0;
+
+	state = fe->demodulator_priv;
+
+	if (re_tune)
+		state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
+
+	switch (state->tune_state) {
+	case VA1J5JF8007S_IDLE:
+		*delay = 3 * HZ;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007S_SET_FREQUENCY_1:
+		ret = va1j5jf8007s_set_frequency_1(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
+		*delay = 0;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007S_SET_FREQUENCY_2:
+		ret = va1j5jf8007s_set_frequency_2(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
+		*delay = (HZ + 99) / 100;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007S_SET_FREQUENCY_3:
+		ret = va1j5jf8007s_set_frequency_3(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
+		*delay = 0;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007S_CHECK_FREQUENCY:
+		ret = va1j5jf8007s_check_frequency(state, &lock);
+		if (ret < 0)
+			return ret;
+
+		if (!lock)  {
+			*delay = (HZ + 999) / 1000;
+			*status = 0;
+			return 0;
+		}
+
+		state->tune_state = VA1J5JF8007S_SET_MODULATION;
+		*delay = 0;
+		*status = FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007S_SET_MODULATION:
+		ret = va1j5jf8007s_set_modulation(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
+		*delay = 0;
+		*status = FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007S_CHECK_MODULATION:
+		ret = va1j5jf8007s_check_modulation(state, &lock);
+		if (ret < 0)
+			return ret;
+
+		if (!lock)  {
+			*delay = (HZ + 49) / 50;
+			*status = FE_HAS_SIGNAL;
+			return 0;
+		}
+
+		state->tune_state = VA1J5JF8007S_SET_TS_ID;
+		*delay = 0;
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+		return 0;
+
+	case VA1J5JF8007S_SET_TS_ID:
+		ret = va1j5jf8007s_set_ts_id(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
+		return 0;
+
+	case VA1J5JF8007S_CHECK_TS_ID:
+		ret = va1j5jf8007s_check_ts_id(state, &lock);
+		if (ret < 0)
+			return ret;
+
+		if (!lock)  {
+			*delay = (HZ + 99) / 100;
+			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+			return 0;
+		}
+
+		state->tune_state = VA1J5JF8007S_TRACK;
+		/* fall through */
+
+	case VA1J5JF8007S_TRACK:
+		*delay = 3 * HZ;
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+		return 0;
+	}
+
+	BUG();
+}
+
+static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
+{
+	u8 buf[4];
+	struct i2c_msg msg;
+
+	buf[0] = 0xfe;
+	buf[1] = 0xc0;
+	buf[2] = 0xf0;
+	buf[3] = 0x04;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
+{
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	buf[0] = 0x17;
+	buf[1] = sleep ? 0x01 : 0x00;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007s_state *state;
+	int ret;
+
+	state = fe->demodulator_priv;
+
+	ret = va1j5jf8007s_init_frequency(state);
+	if (ret < 0)
+		return ret;
+
+	return va1j5jf8007s_set_sleep(state, 1);
+}
+
+static int va1j5jf8007s_init(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007s_state *state;
+
+	state = fe->demodulator_priv;
+	state->tune_state = VA1J5JF8007S_IDLE;
+
+	return va1j5jf8007s_set_sleep(state, 0);
+}
+
+static void va1j5jf8007s_release(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007s_state *state;
+	state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops va1j5jf8007s_ops = {
+	.delsys = { SYS_ISDBS },
+	.info = {
+		.name = "VA1J5JF8007/VA1J5JF8011 ISDB-S",
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1000,
+		.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
+			FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_MULTISTREAM,
+	},
+
+	.read_snr = va1j5jf8007s_read_snr,
+	.get_frontend_algo = va1j5jf8007s_get_frontend_algo,
+	.read_status = va1j5jf8007s_read_status,
+	.tune = va1j5jf8007s_tune,
+	.sleep = va1j5jf8007s_sleep,
+	.init = va1j5jf8007s_init,
+	.release = va1j5jf8007s_release,
+};
+
+static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
+{
+	u8 addr;
+	u8 write_buf[1], read_buf[1];
+	struct i2c_msg msgs[2];
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0x07;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	if (read_buf[0] != 0x41)
+		return -EIO;
+
+	return 0;
+}
+
+static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = {
+	{0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
+	{0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
+	{0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
+	{0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = {
+	{0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a},
+	{0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89},
+	{0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04},
+	{0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
+{
+	const u8 (*bufs)[2];
+	int size;
+	u8 addr;
+	u8 buf[2];
+	struct i2c_msg msg;
+	int i;
+
+	switch (state->config->frequency) {
+	case VA1J5JF8007S_20MHZ:
+		bufs = va1j5jf8007s_20mhz_prepare_bufs;
+		size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs);
+		break;
+	case VA1J5JF8007S_25MHZ:
+		bufs = va1j5jf8007s_25mhz_prepare_bufs;
+		size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	addr = state->config->demod_address;
+
+	msg.addr = addr;
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = buf;
+	for (i = 0; i < size; i++) {
+		memcpy(buf, bufs[i], sizeof(buf));
+		if (i2c_transfer(state->adap, &msg, 1) != 1)
+			return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+/* must be called after va1j5jf8007t_attach */
+int va1j5jf8007s_prepare(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007s_state *state;
+	int ret;
+
+	state = fe->demodulator_priv;
+
+	ret = va1j5jf8007s_prepare_1(state);
+	if (ret < 0)
+		return ret;
+
+	ret = va1j5jf8007s_prepare_2(state);
+	if (ret < 0)
+		return ret;
+
+	return va1j5jf8007s_init_frequency(state);
+}
+
+struct dvb_frontend *
+va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
+		    struct i2c_adapter *adap)
+{
+	struct va1j5jf8007s_state *state;
+	struct dvb_frontend *fe;
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	state->config = config;
+	state->adap = adap;
+
+	fe = &state->fe;
+	memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
+	fe->demodulator_priv = state;
+
+	buf[0] = 0x01;
+	buf[1] = 0x80;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1) {
+		kfree(state);
+		return NULL;
+	}
+
+	return fe;
+}
diff --git a/drivers/media/pci/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h
new file mode 100644
index 000000000000..b7d6f05a0e02
--- /dev/null
+++ b/drivers/media/pci/pt1/va1j5jf8007s.h
@@ -0,0 +1,46 @@
+/*
+ * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * 	by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * 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 VA1J5JF8007S_H
+#define VA1J5JF8007S_H
+
+enum va1j5jf8007s_frequency {
+	VA1J5JF8007S_20MHZ,
+	VA1J5JF8007S_25MHZ,
+};
+
+struct va1j5jf8007s_config {
+	u8 demod_address;
+	enum va1j5jf8007s_frequency frequency;
+};
+
+struct i2c_adapter;
+
+struct dvb_frontend *
+va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
+		    struct i2c_adapter *adap);
+
+/* must be called after va1j5jf8007t_attach */
+int va1j5jf8007s_prepare(struct dvb_frontend *fe);
+
+#endif
diff --git a/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c
new file mode 100644
index 000000000000..2db15159d514
--- /dev/null
+++ b/drivers/media/pci/pt1/va1j5jf8007t.c
@@ -0,0 +1,536 @@
+/*
+ * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * 	by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * 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/slab.h>
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "va1j5jf8007t.h"
+
+enum va1j5jf8007t_tune_state {
+	VA1J5JF8007T_IDLE,
+	VA1J5JF8007T_SET_FREQUENCY,
+	VA1J5JF8007T_CHECK_FREQUENCY,
+	VA1J5JF8007T_SET_MODULATION,
+	VA1J5JF8007T_CHECK_MODULATION,
+	VA1J5JF8007T_TRACK,
+	VA1J5JF8007T_ABORT,
+};
+
+struct va1j5jf8007t_state {
+	const struct va1j5jf8007t_config *config;
+	struct i2c_adapter *adap;
+	struct dvb_frontend fe;
+	enum va1j5jf8007t_tune_state tune_state;
+};
+
+static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct va1j5jf8007t_state *state;
+	u8 addr;
+	int i;
+	u8 write_buf[1], read_buf[1];
+	struct i2c_msg msgs[2];
+	s32 word, x, y;
+
+	state = fe->demodulator_priv;
+	addr = state->config->demod_address;
+
+	word = 0;
+	for (i = 0; i < 3; i++) {
+		write_buf[0] = 0x8b + i;
+
+		msgs[0].addr = addr;
+		msgs[0].flags = 0;
+		msgs[0].len = sizeof(write_buf);
+		msgs[0].buf = write_buf;
+
+		msgs[1].addr = addr;
+		msgs[1].flags = I2C_M_RD;
+		msgs[1].len = sizeof(read_buf);
+		msgs[1].buf = read_buf;
+
+		if (i2c_transfer(state->adap, msgs, 2) != 2)
+			return -EREMOTEIO;
+
+		word <<= 8;
+		word |= read_buf[0];
+	}
+
+	if (!word)
+		return -EIO;
+
+	x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24));
+	y = (24ll << 46) / 1000000;
+	y = ((s64)y * x >> 30) - (16ll << 40) / 10000;
+	y = ((s64)y * x >> 29) + (398ll << 35) / 10000;
+	y = ((s64)y * x >> 30) + (5491ll << 29) / 10000;
+	y = ((s64)y * x >> 30) + (30965ll << 23) / 10000;
+	*snr = y >> 15;
+	return 0;
+}
+
+static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+
+static int
+va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct va1j5jf8007t_state *state;
+
+	state = fe->demodulator_priv;
+
+	switch (state->tune_state) {
+	case VA1J5JF8007T_IDLE:
+	case VA1J5JF8007T_SET_FREQUENCY:
+	case VA1J5JF8007T_CHECK_FREQUENCY:
+		*status = 0;
+		return 0;
+
+
+	case VA1J5JF8007T_SET_MODULATION:
+	case VA1J5JF8007T_CHECK_MODULATION:
+	case VA1J5JF8007T_ABORT:
+		*status |= FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007T_TRACK:
+		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+		return 0;
+	}
+
+	BUG();
+}
+
+struct va1j5jf8007t_cb_map {
+	u32 frequency;
+	u8 cb;
+};
+
+static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = {
+	{  90000000, 0x80 },
+	{ 140000000, 0x81 },
+	{ 170000000, 0xa1 },
+	{ 220000000, 0x62 },
+	{ 330000000, 0xa2 },
+	{ 402000000, 0xe2 },
+	{ 450000000, 0x64 },
+	{ 550000000, 0x84 },
+	{ 600000000, 0xa4 },
+	{ 700000000, 0xc4 },
+};
+
+static u8 va1j5jf8007t_lookup_cb(u32 frequency)
+{
+	int i;
+	const struct va1j5jf8007t_cb_map *map;
+
+	for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) {
+		map = &va1j5jf8007t_cb_maps[i];
+		if (frequency < map->frequency)
+			return map->cb;
+	}
+	return 0xe4;
+}
+
+static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state)
+{
+	u32 frequency;
+	u16 word;
+	u8 buf[6];
+	struct i2c_msg msg;
+
+	frequency = state->fe.dtv_property_cache.frequency;
+
+	word = (frequency + 71428) / 142857 + 399;
+	buf[0] = 0xfe;
+	buf[1] = 0xc2;
+	buf[2] = word >> 8;
+	buf[3] = word;
+	buf[4] = 0x80;
+	buf[5] = va1j5jf8007t_lookup_cb(frequency);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int
+va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock)
+{
+	u8 addr;
+	u8 write_buf[2], read_buf[1];
+	struct i2c_msg msgs[2];
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0xfe;
+	write_buf[1] = 0xc3;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	*lock = read_buf[0] & 0x40;
+	return 0;
+}
+
+static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state)
+{
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	buf[0] = 0x01;
+	buf[1] = 0x40;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state,
+					 int *lock, int *retry)
+{
+	u8 addr;
+	u8 write_buf[1], read_buf[1];
+	struct i2c_msg msgs[2];
+
+	addr = state->config->demod_address;
+
+	write_buf[0] = 0x80;
+
+	msgs[0].addr = addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(write_buf);
+	msgs[0].buf = write_buf;
+
+	msgs[1].addr = addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(read_buf);
+	msgs[1].buf = read_buf;
+
+	if (i2c_transfer(state->adap, msgs, 2) != 2)
+		return -EREMOTEIO;
+
+	*lock = !(read_buf[0] & 0x10);
+	*retry = read_buf[0] & 0x80;
+	return 0;
+}
+
+static int
+va1j5jf8007t_tune(struct dvb_frontend *fe,
+		  bool re_tune,
+		  unsigned int mode_flags,  unsigned int *delay,
+		  fe_status_t *status)
+{
+	struct va1j5jf8007t_state *state;
+	int ret;
+	int lock = 0, retry = 0;
+
+	state = fe->demodulator_priv;
+
+	if (re_tune)
+		state->tune_state = VA1J5JF8007T_SET_FREQUENCY;
+
+	switch (state->tune_state) {
+	case VA1J5JF8007T_IDLE:
+		*delay = 3 * HZ;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007T_SET_FREQUENCY:
+		ret = va1j5jf8007t_set_frequency(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY;
+		*delay = 0;
+		*status = 0;
+		return 0;
+
+	case VA1J5JF8007T_CHECK_FREQUENCY:
+		ret = va1j5jf8007t_check_frequency(state, &lock);
+		if (ret < 0)
+			return ret;
+
+		if (!lock)  {
+			*delay = (HZ + 999) / 1000;
+			*status = 0;
+			return 0;
+		}
+
+		state->tune_state = VA1J5JF8007T_SET_MODULATION;
+		*delay = 0;
+		*status = FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007T_SET_MODULATION:
+		ret = va1j5jf8007t_set_modulation(state);
+		if (ret < 0)
+			return ret;
+
+		state->tune_state = VA1J5JF8007T_CHECK_MODULATION;
+		*delay = 0;
+		*status = FE_HAS_SIGNAL;
+		return 0;
+
+	case VA1J5JF8007T_CHECK_MODULATION:
+		ret = va1j5jf8007t_check_modulation(state, &lock, &retry);
+		if (ret < 0)
+			return ret;
+
+		if (!lock)  {
+			if (!retry)  {
+				state->tune_state = VA1J5JF8007T_ABORT;
+				*delay = 3 * HZ;
+				*status = FE_HAS_SIGNAL;
+				return 0;
+			}
+			*delay = (HZ + 999) / 1000;
+			*status = FE_HAS_SIGNAL;
+			return 0;
+		}
+
+		state->tune_state = VA1J5JF8007T_TRACK;
+		/* fall through */
+
+	case VA1J5JF8007T_TRACK:
+		*delay = 3 * HZ;
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+		return 0;
+
+	case VA1J5JF8007T_ABORT:
+		*delay = 3 * HZ;
+		*status = FE_HAS_SIGNAL;
+		return 0;
+	}
+
+	BUG();
+}
+
+static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state)
+{
+	u8 buf[7];
+	struct i2c_msg msg;
+
+	buf[0] = 0xfe;
+	buf[1] = 0xc2;
+	buf[2] = 0x01;
+	buf[3] = 0x8f;
+	buf[4] = 0xc1;
+	buf[5] = 0x80;
+	buf[6] = 0x80;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep)
+{
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	buf[0] = 0x03;
+	buf[1] = sleep ? 0x90 : 0x80;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int va1j5jf8007t_sleep(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007t_state *state;
+	int ret;
+
+	state = fe->demodulator_priv;
+
+	ret = va1j5jf8007t_init_frequency(state);
+	if (ret < 0)
+		return ret;
+
+	return va1j5jf8007t_set_sleep(state, 1);
+}
+
+static int va1j5jf8007t_init(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007t_state *state;
+
+	state = fe->demodulator_priv;
+	state->tune_state = VA1J5JF8007T_IDLE;
+
+	return va1j5jf8007t_set_sleep(state, 0);
+}
+
+static void va1j5jf8007t_release(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007t_state *state;
+	state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops va1j5jf8007t_ops = {
+	.delsys = { SYS_ISDBT },
+	.info = {
+		.name = "VA1J5JF8007/VA1J5JF8011 ISDB-T",
+		.frequency_min = 90000000,
+		.frequency_max = 770000000,
+		.frequency_stepsize = 142857,
+		.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
+			FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.read_snr = va1j5jf8007t_read_snr,
+	.get_frontend_algo = va1j5jf8007t_get_frontend_algo,
+	.read_status = va1j5jf8007t_read_status,
+	.tune = va1j5jf8007t_tune,
+	.sleep = va1j5jf8007t_sleep,
+	.init = va1j5jf8007t_init,
+	.release = va1j5jf8007t_release,
+};
+
+static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = {
+	{0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2},
+	{0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00},
+	{0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03},
+	{0xef, 0x01}
+};
+
+static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = {
+	{0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83},
+	{0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c},
+	{0x77, 0x03}, {0xef, 0x01}
+};
+
+int va1j5jf8007t_prepare(struct dvb_frontend *fe)
+{
+	struct va1j5jf8007t_state *state;
+	const u8 (*bufs)[2];
+	int size;
+	u8 buf[2];
+	struct i2c_msg msg;
+	int i;
+
+	state = fe->demodulator_priv;
+
+	switch (state->config->frequency) {
+	case VA1J5JF8007T_20MHZ:
+		bufs = va1j5jf8007t_20mhz_prepare_bufs;
+		size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs);
+		break;
+	case VA1J5JF8007T_25MHZ:
+		bufs = va1j5jf8007t_25mhz_prepare_bufs;
+		size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	for (i = 0; i < size; i++) {
+		memcpy(buf, bufs[i], sizeof(buf));
+		if (i2c_transfer(state->adap, &msg, 1) != 1)
+			return -EREMOTEIO;
+	}
+
+	return va1j5jf8007t_init_frequency(state);
+}
+
+struct dvb_frontend *
+va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
+		    struct i2c_adapter *adap)
+{
+	struct va1j5jf8007t_state *state;
+	struct dvb_frontend *fe;
+	u8 buf[2];
+	struct i2c_msg msg;
+
+	state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	state->config = config;
+	state->adap = adap;
+
+	fe = &state->fe;
+	memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops));
+	fe->demodulator_priv = state;
+
+	buf[0] = 0x01;
+	buf[1] = 0x80;
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.len = sizeof(buf);
+	msg.buf = buf;
+
+	if (i2c_transfer(state->adap, &msg, 1) != 1) {
+		kfree(state);
+		return NULL;
+	}
+
+	return fe;
+}
diff --git a/drivers/media/pci/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h
new file mode 100644
index 000000000000..2903be519ef5
--- /dev/null
+++ b/drivers/media/pci/pt1/va1j5jf8007t.h
@@ -0,0 +1,46 @@
+/*
+ * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * 	by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * 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 VA1J5JF8007T_H
+#define VA1J5JF8007T_H
+
+enum va1j5jf8007t_frequency {
+	VA1J5JF8007T_20MHZ,
+	VA1J5JF8007T_25MHZ,
+};
+
+struct va1j5jf8007t_config {
+	u8 demod_address;
+	enum va1j5jf8007t_frequency frequency;
+};
+
+struct i2c_adapter;
+
+struct dvb_frontend *
+va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
+		    struct i2c_adapter *adap);
+
+/* must be called after va1j5jf8007s_attach */
+int va1j5jf8007t_prepare(struct dvb_frontend *fe);
+
+#endif
diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig
new file mode 100644
index 000000000000..15b90d6e9130
--- /dev/null
+++ b/drivers/media/pci/saa7134/Kconfig
@@ -0,0 +1,64 @@
+config VIDEO_SAA7134
+	tristate "Philips SAA7134 support"
+	depends on VIDEO_DEV && PCI && I2C
+	select VIDEOBUF_DMA_SG
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select CRC32
+	select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for Philips SAA713x based
+	  TV cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called saa7134.
+
+config VIDEO_SAA7134_ALSA
+	tristate "Philips SAA7134 DMA audio support"
+	depends on VIDEO_SAA7134 && SND
+	select SND_PCM
+	---help---
+	  This is a video4linux driver for direct (DMA) audio in
+	  Philips SAA713x based TV cards using ALSA
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called saa7134-alsa.
+
+config VIDEO_SAA7134_RC
+	bool "Philips SAA7134 Remote Controller support"
+	depends on RC_CORE
+	depends on VIDEO_SAA7134
+	depends on !(RC_CORE=m && VIDEO_SAA7134=y)
+	default y
+	---help---
+	  Enables Remote Controller support on saa7134 driver.
+
+config VIDEO_SAA7134_DVB
+	tristate "DVB/ATSC Support for saa7134 based TV cards"
+	depends on VIDEO_SAA7134 && DVB_CORE
+	select VIDEOBUF_DVB
+	select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ISL6405 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10036 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This adds support for DVB cards based on the
+	  Philips saa7134 chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called saa7134-dvb.
diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile
new file mode 100644
index 000000000000..35375480ed4d
--- /dev/null
+++ b/drivers/media/pci/saa7134/Makefile
@@ -0,0 +1,16 @@
+
+saa7134-y +=	saa7134-cards.o saa7134-core.o saa7134-i2c.o
+saa7134-y +=	saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
+saa7134-y +=	saa7134-video.o
+saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
+
+obj-$(CONFIG_VIDEO_SAA7134) +=  saa6752hs.o saa7134.o saa7134-empress.o
+
+obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
+
+obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c
new file mode 100644
index 000000000000..f147b05bd860
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa6752hs.c
@@ -0,0 +1,1012 @@
+ /*
+    saa6752hs - i2c-driver for the saa6752hs by Philips
+
+    Copyright (C) 2004 Andrew de Quincey
+
+    AC-3 support:
+
+    Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA.
+  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#define MPEG_VIDEO_TARGET_BITRATE_MAX  27000
+#define MPEG_VIDEO_MAX_BITRATE_MAX     27000
+#define MPEG_TOTAL_TARGET_BITRATE_MAX  27000
+#define MPEG_PID_MAX ((1 << 14) - 1)
+
+
+MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+enum saa6752hs_videoformat {
+	SAA6752HS_VF_D1 = 0,    /* standard D1 video format: 720x576 */
+	SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */
+	SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */
+	SAA6752HS_VF_SIF = 3,   /* SIF video format: 352x288 */
+	SAA6752HS_VF_UNKNOWN,
+};
+
+struct saa6752hs_mpeg_params {
+	/* transport streams */
+	__u16				ts_pid_pmt;
+	__u16				ts_pid_audio;
+	__u16				ts_pid_video;
+	__u16				ts_pid_pcr;
+
+	/* audio */
+	enum v4l2_mpeg_audio_encoding    au_encoding;
+	enum v4l2_mpeg_audio_l2_bitrate  au_l2_bitrate;
+	enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate;
+
+	/* video */
+	enum v4l2_mpeg_video_aspect	vi_aspect;
+	enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode;
+	__u32 				vi_bitrate;
+	__u32 				vi_bitrate_peak;
+};
+
+static const struct v4l2_format v4l2_format_table[] =
+{
+	[SAA6752HS_VF_D1] =
+		{ .fmt = { .pix = { .width = 720, .height = 576 }}},
+	[SAA6752HS_VF_2_3_D1] =
+		{ .fmt = { .pix = { .width = 480, .height = 576 }}},
+	[SAA6752HS_VF_1_2_D1] =
+		{ .fmt = { .pix = { .width = 352, .height = 576 }}},
+	[SAA6752HS_VF_SIF] =
+		{ .fmt = { .pix = { .width = 352, .height = 288 }}},
+	[SAA6752HS_VF_UNKNOWN] =
+		{ .fmt = { .pix = { .width = 0, .height = 0}}},
+};
+
+struct saa6752hs_state {
+	struct v4l2_subdev            sd;
+	int 			      chip;
+	u32 			      revision;
+	int 			      has_ac3;
+	struct saa6752hs_mpeg_params  params;
+	enum saa6752hs_videoformat    video_format;
+	v4l2_std_id                   standard;
+};
+
+enum saa6752hs_command {
+	SAA6752HS_COMMAND_RESET = 0,
+	SAA6752HS_COMMAND_STOP = 1,
+	SAA6752HS_COMMAND_START = 2,
+	SAA6752HS_COMMAND_PAUSE = 3,
+	SAA6752HS_COMMAND_RECONFIGURE = 4,
+	SAA6752HS_COMMAND_SLEEP = 5,
+	SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6,
+
+	SAA6752HS_COMMAND_MAX
+};
+
+static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct saa6752hs_state, sd);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static u8 PAT[] = {
+	0xc2, /* i2c register */
+	0x00, /* table number for encoder */
+
+	0x47, /* sync */
+	0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */
+	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+	0x00, /* PSI pointer to start of table */
+
+	0x00, /* tid(0) */
+	0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */
+
+	0x00, 0x01, /* transport_stream_id(1) */
+
+	0xc1, /* version_number(0), current_next_indicator(1) */
+
+	0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+	0x00, 0x01, /* program_number(1) */
+
+	0xe0, 0x00, /* PMT PID */
+
+	0x00, 0x00, 0x00, 0x00 /* CRC32 */
+};
+
+static u8 PMT[] = {
+	0xc2, /* i2c register */
+	0x01, /* table number for encoder */
+
+	0x47, /* sync */
+	0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */
+	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+	0x00, /* PSI pointer to start of table */
+
+	0x02, /* tid(2) */
+	0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */
+
+	0x00, 0x01, /* program_number(1) */
+
+	0xc1, /* version_number(0), current_next_indicator(1) */
+
+	0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+	0xe0, 0x00, /* PCR_PID */
+
+	0xf0, 0x00, /* program_info_length(0) */
+
+	0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */
+	0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */
+
+	0x00, 0x00, 0x00, 0x00 /* CRC32 */
+};
+
+static u8 PMT_AC3[] = {
+	0xc2, /* i2c register */
+	0x01, /* table number for encoder(1) */
+	0x47, /* sync */
+
+	0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */
+	0x10, /* PMT PID (0x0010) */
+	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+	0x00, /* PSI pointer to start of table */
+
+	0x02, /* TID (2) */
+	0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */
+
+	0x00, 0x01, /* program_number(1) */
+
+	0xc1, /* version_number(0), current_next_indicator(1) */
+
+	0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+	0xe1, 0x04, /* PCR_PID (0x0104) */
+
+	0xf0, 0x00, /* program_info_length(0) */
+
+	0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */
+	0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */
+	0x6a, /* AC3 */
+	0x01, /* Descriptor_length(1) */
+	0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */
+
+	0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */
+};
+
+static struct saa6752hs_mpeg_params param_defaults =
+{
+	.ts_pid_pmt      = 16,
+	.ts_pid_video    = 260,
+	.ts_pid_audio    = 256,
+	.ts_pid_pcr      = 259,
+
+	.vi_aspect       = V4L2_MPEG_VIDEO_ASPECT_4x3,
+	.vi_bitrate      = 4000,
+	.vi_bitrate_peak = 6000,
+	.vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+
+	.au_encoding     = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+	.au_l2_bitrate   = V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+	.au_ac3_bitrate  = V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int saa6752hs_chip_command(struct i2c_client *client,
+				  enum saa6752hs_command command)
+{
+	unsigned char buf[3];
+	unsigned long timeout;
+	int status = 0;
+
+	/* execute the command */
+	switch(command) {
+	case SAA6752HS_COMMAND_RESET:
+		buf[0] = 0x00;
+		break;
+
+	case SAA6752HS_COMMAND_STOP:
+		buf[0] = 0x03;
+		break;
+
+	case SAA6752HS_COMMAND_START:
+		buf[0] = 0x02;
+		break;
+
+	case SAA6752HS_COMMAND_PAUSE:
+		buf[0] = 0x04;
+		break;
+
+	case SAA6752HS_COMMAND_RECONFIGURE:
+		buf[0] = 0x05;
+		break;
+
+	case SAA6752HS_COMMAND_SLEEP:
+		buf[0] = 0x06;
+		break;
+
+	case SAA6752HS_COMMAND_RECONFIGURE_FORCE:
+		buf[0] = 0x07;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* set it and wait for it to be so */
+	i2c_master_send(client, buf, 1);
+	timeout = jiffies + HZ * 3;
+	for (;;) {
+		/* get the current status */
+		buf[0] = 0x10;
+		i2c_master_send(client, buf, 1);
+		i2c_master_recv(client, buf, 1);
+
+		if (!(buf[0] & 0x20))
+			break;
+		if (time_after(jiffies,timeout)) {
+			status = -ETIMEDOUT;
+			break;
+		}
+
+		msleep(10);
+	}
+
+	/* delay a bit to let encoder settle */
+	msleep(50);
+
+	return status;
+}
+
+
+static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val)
+{
+	u8 buf[2];
+
+	buf[0] = reg;
+	buf[1] = val;
+	i2c_master_send(client, buf, 2);
+}
+
+static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val)
+{
+	u8 buf[3];
+
+	buf[0] = reg;
+	buf[1] = val >> 8;
+	buf[2] = val & 0xff;
+	i2c_master_send(client, buf, 3);
+}
+
+static int saa6752hs_set_bitrate(struct i2c_client *client,
+				 struct saa6752hs_state *h)
+{
+	struct saa6752hs_mpeg_params *params = &h->params;
+	int tot_bitrate;
+	int is_384k;
+
+	/* set the bitrate mode */
+	set_reg8(client, 0x71,
+		params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+
+	/* set the video bitrate */
+	if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
+		/* set the target bitrate */
+		set_reg16(client, 0x80, params->vi_bitrate);
+
+		/* set the max bitrate */
+		set_reg16(client, 0x81, params->vi_bitrate_peak);
+		tot_bitrate = params->vi_bitrate_peak;
+	} else {
+		/* set the target bitrate (no max bitrate for CBR) */
+		set_reg16(client, 0x81, params->vi_bitrate);
+		tot_bitrate = params->vi_bitrate;
+	}
+
+	/* set the audio encoding */
+	set_reg8(client, 0x93,
+			params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3);
+
+	/* set the audio bitrate */
+	if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3)
+		is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate;
+	else
+		is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate;
+	set_reg8(client, 0x94, is_384k);
+	tot_bitrate += is_384k ? 384 : 256;
+
+	/* Note: the total max bitrate is determined by adding the video and audio
+	   bitrates together and also adding an extra 768kbit/s to stay on the
+	   safe side. If more control should be required, then an extra MPEG control
+	   should be added. */
+	tot_bitrate += 768;
+	if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX)
+		tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX;
+
+	/* set the total bitrate */
+	set_reg16(client, 0xb1, tot_bitrate);
+	return 0;
+}
+
+
+static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
+		struct v4l2_ext_control *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_PMT:
+		ctrl->value = params->ts_pid_pmt;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+		ctrl->value = params->ts_pid_audio;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+		ctrl->value = params->ts_pid_video;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_PCR:
+		ctrl->value = params->ts_pid_pcr;
+		break;
+	case V4L2_CID_MPEG_AUDIO_ENCODING:
+		ctrl->value = params->au_encoding;
+		break;
+	case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+		ctrl->value = params->au_l2_bitrate;
+		break;
+	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+		if (!has_ac3)
+			return -EINVAL;
+		ctrl->value = params->au_ac3_bitrate;
+		break;
+	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+		ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ENCODING:
+		ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctrl->value = params->vi_aspect;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		ctrl->value = params->vi_bitrate * 1000;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		ctrl->value = params->vi_bitrate_peak * 1000;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		ctrl->value = params->vi_bitrate_mode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
+		struct v4l2_ext_control *ctrl, int set)
+{
+	int old = 0, new;
+
+	new = ctrl->value;
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+		if (set && new != old)
+			return -ERANGE;
+		new = old;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_PMT:
+		old = params->ts_pid_pmt;
+		if (set && new > MPEG_PID_MAX)
+			return -ERANGE;
+		if (new > MPEG_PID_MAX)
+			new = MPEG_PID_MAX;
+		params->ts_pid_pmt = new;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+		old = params->ts_pid_audio;
+		if (set && new > MPEG_PID_MAX)
+			return -ERANGE;
+		if (new > MPEG_PID_MAX)
+			new = MPEG_PID_MAX;
+		params->ts_pid_audio = new;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+		old = params->ts_pid_video;
+		if (set && new > MPEG_PID_MAX)
+			return -ERANGE;
+		if (new > MPEG_PID_MAX)
+			new = MPEG_PID_MAX;
+		params->ts_pid_video = new;
+		break;
+	case V4L2_CID_MPEG_STREAM_PID_PCR:
+		old = params->ts_pid_pcr;
+		if (set && new > MPEG_PID_MAX)
+			return -ERANGE;
+		if (new > MPEG_PID_MAX)
+			new = MPEG_PID_MAX;
+		params->ts_pid_pcr = new;
+		break;
+	case V4L2_CID_MPEG_AUDIO_ENCODING:
+		old = params->au_encoding;
+		if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+		    (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
+			return -ERANGE;
+		params->au_encoding = new;
+		break;
+	case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+		old = params->au_l2_bitrate;
+		if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
+			   new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
+			return -ERANGE;
+		if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
+			new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
+		else
+			new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
+		params->au_l2_bitrate = new;
+		break;
+	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+		if (!has_ac3)
+			return -EINVAL;
+		old = params->au_ac3_bitrate;
+		if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
+			   new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
+			return -ERANGE;
+		if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
+			new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
+		else
+			new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
+		params->au_ac3_bitrate = new;
+		break;
+	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+		old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+		if (set && new != old)
+			return -ERANGE;
+		new = old;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ENCODING:
+		old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+		if (set && new != old)
+			return -ERANGE;
+		new = old;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		old = params->vi_aspect;
+		if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
+			   new != V4L2_MPEG_VIDEO_ASPECT_4x3)
+			return -ERANGE;
+		if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
+			new = V4L2_MPEG_VIDEO_ASPECT_4x3;
+		params->vi_aspect = new;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		old = params->vi_bitrate * 1000;
+		new = 1000 * (new / 1000);
+		if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+			return -ERANGE;
+		if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+			new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+		params->vi_bitrate = new / 1000;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		old = params->vi_bitrate_peak * 1000;
+		new = 1000 * (new / 1000);
+		if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+			return -ERANGE;
+		if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+			new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+		params->vi_bitrate_peak = new / 1000;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		old = params->vi_bitrate_mode;
+		params->vi_bitrate_mode = new;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ctrl->value = new;
+	return 0;
+}
+
+
+static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+{
+	struct saa6752hs_state *h = to_state(sd);
+	struct saa6752hs_mpeg_params *params = &h->params;
+	int err;
+
+	switch (qctrl->id) {
+	case V4L2_CID_MPEG_AUDIO_ENCODING:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+				h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 :
+					V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+				1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+
+	case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+				V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+				V4L2_MPEG_AUDIO_L2_BITRATE_256K);
+
+	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+		if (!h->has_ac3)
+			return -EINVAL;
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1,
+				V4L2_MPEG_AUDIO_AC3_BITRATE_256K);
+
+	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+				V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1,
+				V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+	case V4L2_CID_MPEG_VIDEO_ENCODING:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+				V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+				V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_VIDEO_ASPECT_4x3,
+				V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
+				V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+		if (err == 0 &&
+		    params->vi_bitrate_mode ==
+				V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+			qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+		return err;
+
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+				V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1,
+				V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		return v4l2_ctrl_query_fill(qctrl,
+				V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+				V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+				V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+	case V4L2_CID_MPEG_STREAM_PID_PMT:
+		return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16);
+	case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+		return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260);
+	case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+		return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256);
+	case V4L2_CID_MPEG_STREAM_PID_PCR:
+		return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259);
+
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu)
+{
+	static const u32 mpeg_audio_encoding[] = {
+		V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+		V4L2_CTRL_MENU_IDS_END
+	};
+	static const u32 mpeg_audio_ac3_encoding[] = {
+		V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+		V4L2_MPEG_AUDIO_ENCODING_AC3,
+		V4L2_CTRL_MENU_IDS_END
+	};
+	static u32 mpeg_audio_l2_bitrate[] = {
+		V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+		V4L2_MPEG_AUDIO_L2_BITRATE_384K,
+		V4L2_CTRL_MENU_IDS_END
+	};
+	static u32 mpeg_audio_ac3_bitrate[] = {
+		V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+		V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+		V4L2_CTRL_MENU_IDS_END
+	};
+	struct saa6752hs_state *h = to_state(sd);
+	struct v4l2_queryctrl qctrl;
+	int err;
+
+	qctrl.id = qmenu->id;
+	err = saa6752hs_queryctrl(sd, &qctrl);
+	if (err)
+		return err;
+	switch (qmenu->id) {
+	case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+		return v4l2_ctrl_query_menu_valid_items(qmenu,
+				mpeg_audio_l2_bitrate);
+	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+		if (!h->has_ac3)
+			return -EINVAL;
+		return v4l2_ctrl_query_menu_valid_items(qmenu,
+				mpeg_audio_ac3_bitrate);
+	case V4L2_CID_MPEG_AUDIO_ENCODING:
+		return v4l2_ctrl_query_menu_valid_items(qmenu,
+			h->has_ac3 ? mpeg_audio_ac3_encoding :
+				mpeg_audio_encoding);
+	}
+	return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
+}
+
+static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes)
+{
+	unsigned char buf[9], buf2[4];
+	struct saa6752hs_state *h = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	unsigned size;
+	u32 crc;
+	unsigned char localPAT[256];
+	unsigned char localPMT[256];
+
+	/* Set video format - must be done first as it resets other settings */
+	set_reg8(client, 0x41, h->video_format);
+
+	/* Set number of lines in input signal */
+	set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0);
+
+	/* set bitrate */
+	saa6752hs_set_bitrate(client, h);
+
+	/* Set GOP structure {3, 13} */
+	set_reg16(client, 0x72, 0x030d);
+
+	/* Set minimum Q-scale {4} */
+	set_reg8(client, 0x82, 0x04);
+
+	/* Set maximum Q-scale {12} */
+	set_reg8(client, 0x83, 0x0c);
+
+	/* Set Output Protocol */
+	set_reg8(client, 0xd0, 0x81);
+
+	/* Set video output stream format {TS} */
+	set_reg8(client, 0xb0, 0x05);
+
+	/* Set leading null byte for TS */
+	set_reg16(client, 0xf6, leading_null_bytes);
+
+	/* compute PAT */
+	memcpy(localPAT, PAT, sizeof(PAT));
+	localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+	localPAT[18] = h->params.ts_pid_pmt & 0xff;
+	crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4);
+	localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF;
+	localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF;
+	localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF;
+	localPAT[sizeof(PAT) - 1] = crc & 0xFF;
+
+	/* compute PMT */
+	if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+		size = sizeof(PMT_AC3);
+		memcpy(localPMT, PMT_AC3, size);
+	} else {
+		size = sizeof(PMT);
+		memcpy(localPMT, PMT, size);
+	}
+	localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+	localPMT[4] = h->params.ts_pid_pmt & 0xff;
+	localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F);
+	localPMT[16] = h->params.ts_pid_pcr & 0xFF;
+	localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F);
+	localPMT[21] = h->params.ts_pid_video & 0xFF;
+	localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F);
+	localPMT[26] = h->params.ts_pid_audio & 0xFF;
+	crc = crc32_be(~0, &localPMT[7], size - 7 - 4);
+	localPMT[size - 4] = (crc >> 24) & 0xFF;
+	localPMT[size - 3] = (crc >> 16) & 0xFF;
+	localPMT[size - 2] = (crc >> 8) & 0xFF;
+	localPMT[size - 1] = crc & 0xFF;
+
+	/* Set Audio PID */
+	set_reg16(client, 0xc1, h->params.ts_pid_audio);
+
+	/* Set Video PID */
+	set_reg16(client, 0xc0, h->params.ts_pid_video);
+
+	/* Set PCR PID */
+	set_reg16(client, 0xc4, h->params.ts_pid_pcr);
+
+	/* Send SI tables */
+	i2c_master_send(client, localPAT, sizeof(PAT));
+	i2c_master_send(client, localPMT, size);
+
+	/* mute then unmute audio. This removes buzzing artefacts */
+	set_reg8(client, 0xa4, 1);
+	set_reg8(client, 0xa4, 0);
+
+	/* start it going */
+	saa6752hs_chip_command(client, SAA6752HS_COMMAND_START);
+
+	/* readout current state */
+	buf[0] = 0xE1;
+	buf[1] = 0xA7;
+	buf[2] = 0xFE;
+	buf[3] = 0x82;
+	buf[4] = 0xB0;
+	i2c_master_send(client, buf, 5);
+	i2c_master_recv(client, buf2, 4);
+
+	/* change aspect ratio */
+	buf[0] = 0xE0;
+	buf[1] = 0xA7;
+	buf[2] = 0xFE;
+	buf[3] = 0x82;
+	buf[4] = 0xB0;
+	buf[5] = buf2[0];
+	switch (h->params.vi_aspect) {
+	case V4L2_MPEG_VIDEO_ASPECT_16x9:
+		buf[6] = buf2[1] | 0x40;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_4x3:
+	default:
+		buf[6] = buf2[1] & 0xBF;
+		break;
+	}
+	buf[7] = buf2[2];
+	buf[8] = buf2[3];
+	i2c_master_send(client, buf, 9);
+
+	return 0;
+}
+
+static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set)
+{
+	struct saa6752hs_state *h = to_state(sd);
+	struct saa6752hs_mpeg_params params;
+	int i;
+
+	if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	params = h->params;
+	for (i = 0; i < ctrls->count; i++) {
+		int err = handle_ctrl(h->has_ac3, &params, ctrls->controls + i, set);
+
+		if (err) {
+			ctrls->error_idx = i;
+			return err;
+		}
+	}
+	if (set)
+		h->params = params;
+	return 0;
+}
+
+static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+	return saa6752hs_do_ext_ctrls(sd, ctrls, 1);
+}
+
+static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+	return saa6752hs_do_ext_ctrls(sd, ctrls, 0);
+}
+
+static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+	struct saa6752hs_state *h = to_state(sd);
+	int i;
+
+	if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	for (i = 0; i < ctrls->count; i++) {
+		int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i);
+
+		if (err) {
+			ctrls->error_idx = i;
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+	struct saa6752hs_state *h = to_state(sd);
+
+	if (h->video_format == SAA6752HS_VF_UNKNOWN)
+		h->video_format = SAA6752HS_VF_D1;
+	f->width = v4l2_format_table[h->video_format].fmt.pix.width;
+	f->height = v4l2_format_table[h->video_format].fmt.pix.height;
+	f->code = V4L2_MBUS_FMT_FIXED;
+	f->field = V4L2_FIELD_INTERLACED;
+	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	return 0;
+}
+
+static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+	struct saa6752hs_state *h = to_state(sd);
+	int dist_352, dist_480, dist_720;
+
+	if (f->code != V4L2_MBUS_FMT_FIXED)
+		return -EINVAL;
+
+	/*
+	  FIXME: translate and round width/height into EMPRESS
+	  subsample type:
+
+	  type   |   PAL   |  NTSC
+	  ---------------------------
+	  SIF    | 352x288 | 352x240
+	  1/2 D1 | 352x576 | 352x480
+	  2/3 D1 | 480x576 | 480x480
+	  D1     | 720x576 | 720x480
+	*/
+
+	dist_352 = abs(f->width - 352);
+	dist_480 = abs(f->width - 480);
+	dist_720 = abs(f->width - 720);
+	if (dist_720 < dist_480) {
+		f->width = 720;
+		f->height = 576;
+		h->video_format = SAA6752HS_VF_D1;
+	} else if (dist_480 < dist_352) {
+		f->width = 480;
+		f->height = 576;
+		h->video_format = SAA6752HS_VF_2_3_D1;
+	} else {
+		f->width = 352;
+		if (abs(f->height - 576) <
+		    abs(f->height - 288)) {
+			f->height = 576;
+			h->video_format = SAA6752HS_VF_1_2_D1;
+		} else {
+			f->height = 288;
+			h->video_format = SAA6752HS_VF_SIF;
+		}
+	}
+	f->field = V4L2_FIELD_INTERLACED;
+	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	return 0;
+}
+
+static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct saa6752hs_state *h = to_state(sd);
+
+	h->standard = std;
+	return 0;
+}
+
+static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct saa6752hs_state *h = to_state(sd);
+
+	return v4l2_chip_ident_i2c_client(client,
+			chip, h->chip, h->revision);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
+	.g_chip_ident = saa6752hs_g_chip_ident,
+	.init = saa6752hs_init,
+	.queryctrl = saa6752hs_queryctrl,
+	.querymenu = saa6752hs_querymenu,
+	.g_ext_ctrls = saa6752hs_g_ext_ctrls,
+	.s_ext_ctrls = saa6752hs_s_ext_ctrls,
+	.try_ext_ctrls = saa6752hs_try_ext_ctrls,
+	.s_std = saa6752hs_s_std,
+};
+
+static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
+	.s_mbus_fmt = saa6752hs_s_mbus_fmt,
+	.g_mbus_fmt = saa6752hs_g_mbus_fmt,
+};
+
+static const struct v4l2_subdev_ops saa6752hs_ops = {
+	.core = &saa6752hs_core_ops,
+	.video = &saa6752hs_video_ops,
+};
+
+static int saa6752hs_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
+	struct v4l2_subdev *sd;
+	u8 addr = 0x13;
+	u8 data[12];
+
+	v4l_info(client, "chip found @ 0x%x (%s)\n",
+			client->addr << 1, client->adapter->name);
+	if (h == NULL)
+		return -ENOMEM;
+	sd = &h->sd;
+	v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops);
+
+	i2c_master_send(client, &addr, 1);
+	i2c_master_recv(client, data, sizeof(data));
+	h->chip = V4L2_IDENT_SAA6752HS;
+	h->revision = (data[8] << 8) | data[9];
+	h->has_ac3 = 0;
+	if (h->revision == 0x0206) {
+		h->chip = V4L2_IDENT_SAA6752HS_AC3;
+		h->has_ac3 = 1;
+		v4l_info(client, "support AC-3\n");
+	}
+	h->params = param_defaults;
+	h->standard = 0; /* Assume 625 input lines */
+	return 0;
+}
+
+static int saa6752hs_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+	v4l2_device_unregister_subdev(sd);
+	kfree(to_state(sd));
+	return 0;
+}
+
+static const struct i2c_device_id saa6752hs_id[] = {
+	{ "saa6752hs", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
+
+static struct i2c_driver saa6752hs_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa6752hs",
+	},
+	.probe		= saa6752hs_probe,
+	.remove		= saa6752hs_remove,
+	.id_table	= saa6752hs_id,
+};
+
+module_i2c_driver(saa6752hs_driver);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
new file mode 100644
index 000000000000..10460fd3ce39
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -0,0 +1,1209 @@
+/*
+ *   SAA713x ALSA support for V4L
+ *
+ *   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, version 2
+ *
+ *   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/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <linux/interrupt.h>
+
+#include "saa7134.h"
+#include "saa7134-reg.h"
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [alsa]");
+
+/*
+ * Configuration macros
+ */
+
+/* defaults */
+#define MIXER_ADDR_UNSELECTED	-1
+#define MIXER_ADDR_TVTUNER	0
+#define MIXER_ADDR_LINE1	1
+#define MIXER_ADDR_LINE2	2
+#define MIXER_ADDR_LAST		2
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(index, int, NULL, 0444);
+module_param_array(enable, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s).");
+MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s).");
+
+#define dprintk(fmt, arg...)    if (debug) \
+	printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg)
+
+
+
+/*
+ * Main chip structure
+ */
+
+typedef struct snd_card_saa7134 {
+	struct snd_card *card;
+	spinlock_t mixer_lock;
+	int mixer_volume[MIXER_ADDR_LAST+1][2];
+	int capture_source_addr;
+	int capture_source[2];
+	struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1];
+	struct pci_dev *pci;
+	struct saa7134_dev *dev;
+
+	unsigned long iobase;
+	s16 irq;
+	u16 mute_was_on;
+
+	spinlock_t lock;
+} snd_card_saa7134_t;
+
+
+/*
+ * PCM structure
+ */
+
+typedef struct snd_card_saa7134_pcm {
+	struct saa7134_dev *dev;
+
+	spinlock_t lock;
+
+	struct snd_pcm_substream *substream;
+} snd_card_saa7134_pcm_t;
+
+static struct snd_card *snd_saa7134_cards[SNDRV_CARDS];
+
+
+/*
+ * saa7134 DMA audio stop
+ *
+ *   Called when the capture device is released or the buffer overflows
+ *
+ *   - Copied verbatim from saa7134-oss's dsp_dma_stop.
+ *
+ */
+
+static void saa7134_dma_stop(struct saa7134_dev *dev)
+{
+	dev->dmasound.dma_blk     = -1;
+	dev->dmasound.dma_running = 0;
+	saa7134_set_dmabits(dev);
+}
+
+/*
+ * saa7134 DMA audio start
+ *
+ *   Called when preparing the capture device for use
+ *
+ *   - Copied verbatim from saa7134-oss's dsp_dma_start.
+ *
+ */
+
+static void saa7134_dma_start(struct saa7134_dev *dev)
+{
+	dev->dmasound.dma_blk     = 0;
+	dev->dmasound.dma_running = 1;
+	saa7134_set_dmabits(dev);
+}
+
+/*
+ * saa7134 audio DMA IRQ handler
+ *
+ *   Called whenever we get an SAA7134_IRQ_REPORT_DONE_RA3 interrupt
+ *   Handles shifting between the 2 buffers, manages the read counters,
+ *  and notifies ALSA when periods elapse
+ *
+ *   - Mostly copied from saa7134-oss's saa7134_irq_oss_done.
+ *
+ */
+
+static void saa7134_irq_alsa_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] - Blocks in %d\n",dev->dmasound.read_count,
+			dev->dmasound.bufsize, dev->dmasound.blocks);
+		spin_unlock(&dev->slock);
+		snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN);
+		return;
+	}
+
+	/* 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, blocks=%u, size=%u, read=%u\n",
+			(status & 0x10000000) ? "even" : "odd ", next_blk,
+			next_blk * dev->dmasound.blksize, dev->dmasound.blocks, dev->dmasound.blksize, dev->dmasound.read_count);
+
+	/* update status & wake waiting readers */
+	dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks;
+	dev->dmasound.read_count += dev->dmasound.blksize;
+
+	dev->dmasound.recording_on = reg;
+
+	if (dev->dmasound.read_count >= snd_pcm_lib_period_bytes(dev->dmasound.substream)) {
+		spin_unlock(&dev->slock);
+		snd_pcm_period_elapsed(dev->dmasound.substream);
+		spin_lock(&dev->slock);
+	}
+
+ done:
+	spin_unlock(&dev->slock);
+
+}
+
+/*
+ * IRQ request handler
+ *
+ *   Runs along with saa7134's IRQ handler, discards anything that isn't
+ *   DMA sound
+ *
+ */
+
+static irqreturn_t saa7134_alsa_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,
+				   SAA7134_IRQ_REPORT_DONE_RA3);
+			saa7134_irq_alsa_done(dev, status);
+		} else {
+			goto out;
+		}
+	}
+
+	if (loop == 10) {
+		dprintk("error! looping IRQ!");
+	}
+
+out:
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * ALSA capture trigger
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called whenever a capture is started or stopped. Must be defined,
+ *   but there's nothing we want to do here
+ *
+ */
+
+static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream,
+					  int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+	struct saa7134_dev *dev=pcm->dev;
+	int err = 0;
+
+	spin_lock(&dev->slock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		/* start dma */
+		saa7134_dma_start(dev);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		/* stop dma */
+		saa7134_dma_stop(dev);
+	} else {
+		err = -EINVAL;
+	}
+	spin_unlock(&dev->slock);
+
+	return err;
+}
+
+/*
+ * DMA buffer initialization
+ *
+ *   Uses V4L functions to initialize the DMA. Shouldn't be necessary in
+ *  ALSA, but I was unable to use ALSA's own DMA, and had to force the
+ *  usage of V4L's
+ *
+ *   - Copied verbatim from saa7134-oss.
+ *
+ */
+
+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;
+}
+
+/*
+ * DMA buffer release
+ *
+ *   Called after closing the device, during snd_card_saa7134_capture_close
+ *
+ */
+
+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;
+}
+
+/*
+ * Setting the capture source and updating the ALSA controls
+ */
+static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol,
+				  int left, int right, bool force_notify)
+{
+	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	int change = 0, addr = kcontrol->private_value;
+	int active, old_addr;
+	u32 anabar, xbarin;
+	int analog_io, rate;
+	struct saa7134_dev *dev;
+
+	dev = chip->dev;
+
+	spin_lock_irq(&chip->mixer_lock);
+
+	active = left != 0 || right != 0;
+	old_addr = chip->capture_source_addr;
+
+	/* The active capture source cannot be deactivated */
+	if (active) {
+		change = old_addr != addr ||
+			 chip->capture_source[0] != left ||
+			 chip->capture_source[1] != right;
+
+		chip->capture_source[0] = left;
+		chip->capture_source[1] = right;
+		chip->capture_source_addr = addr;
+		dev->dmasound.input = addr;
+	}
+	spin_unlock_irq(&chip->mixer_lock);
+
+	if (change) {
+		switch (dev->pci->device) {
+
+		case PCI_DEVICE_ID_PHILIPS_SAA7134:
+			switch (addr) {
+			case MIXER_ADDR_TVTUNER:
+				saa_andorb(SAA7134_AUDIO_FORMAT_CTRL,
+					   0xc0, 0xc0);
+				saa_andorb(SAA7134_SIF_SAMPLE_FREQ,
+					   0x03, 0x00);
+				break;
+			case MIXER_ADDR_LINE1:
+			case MIXER_ADDR_LINE2:
+				analog_io = (MIXER_ADDR_LINE1 == addr) ?
+					     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;
+			}
+
+			break;
+		case PCI_DEVICE_ID_PHILIPS_SAA7133:
+		case PCI_DEVICE_ID_PHILIPS_SAA7135:
+			xbarin = 0x03; /* adc */
+			anabar = 0;
+			switch (addr) {
+			case MIXER_ADDR_TVTUNER:
+				xbarin = 0; /* Demodulator */
+				anabar = 2; /* DACs */
+				break;
+			case MIXER_ADDR_LINE1:
+				anabar = 0;  /* aux1, aux1 */
+				break;
+			case MIXER_ADDR_LINE2:
+				anabar = 9;  /* aux2, aux2 */
+				break;
+			}
+
+			/* output xbar always main channel */
+			saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1,
+				       0xbbbb10);
+
+			if (left || right) {
+				/* We've got data, turn the input on */
+				saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1,
+					       xbarin);
+				saa_writel(SAA7133_ANALOG_IO_SELECT, anabar);
+			} else {
+				saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1,
+					       0);
+				saa_writel(SAA7133_ANALOG_IO_SELECT, 0);
+			}
+			break;
+		}
+	}
+
+	if (change) {
+		if (force_notify)
+			snd_ctl_notify(chip->card,
+				       SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->capture_ctl[addr]->id);
+
+		if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr)
+			snd_ctl_notify(chip->card,
+				       SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->capture_ctl[old_addr]->id);
+	}
+
+	return change;
+}
+
+/*
+ * ALSA PCM preparation
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called right after the capture device is opened, this function configures
+ *  the buffer using the previously defined functions, allocates the memory,
+ *  sets up the hardware registers, and then starts the DMA. When this function
+ *  returns, the audio should be flowing.
+ *
+ */
+
+static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int bswap, sign;
+	u32 fmt, control;
+	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+	struct saa7134_dev *dev;
+	snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+
+	pcm->dev->dmasound.substream = substream;
+
+	dev = saa7134->dev;
+
+	if (snd_pcm_format_width(runtime->format) == 8)
+		fmt = 0x00;
+	else
+		fmt = 0x01;
+
+	if (snd_pcm_format_signed(runtime->format))
+		sign = 1;
+	else
+		sign = 0;
+
+	if (snd_pcm_format_big_endian(runtime->format))
+		bswap = 1;
+	else
+		bswap = 0;
+
+	switch (dev->pci->device) {
+	  case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		if (1 == runtime->channels)
+			fmt |= (1 << 3);
+		if (2 == runtime->channels)
+			fmt |= (3 << 3);
+		if (sign)
+			fmt |= 0x04;
+
+		fmt |= (MIXER_ADDR_TVTUNER == 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 == runtime->channels)
+			fmt |= (1 << 4);
+		if (2 == runtime->channels)
+			fmt |= (2 << 4);
+		if (!sign)
+			fmt |= 0x04;
+		saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1);
+		saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24));
+		break;
+	}
+
+	dprintk("rec_start: afmt=%d ch=%d  =>  fmt=0x%x swap=%c\n",
+		runtime->format, runtime->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);
+
+	dev->dmasound.rate = runtime->rate;
+
+	/* Setup and update the card/ALSA controls */
+	snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1,
+			       true);
+
+	return 0;
+
+}
+
+/*
+ * ALSA pointer fetching
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called whenever a period elapses, it must return the current hardware
+ *  position of the buffer.
+ *   Also resets the read counter used to prevent overruns
+ *
+ */
+
+static snd_pcm_uframes_t
+snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+	struct saa7134_dev *dev=pcm->dev;
+
+	if (dev->dmasound.read_count) {
+		dev->dmasound.read_count  -= snd_pcm_lib_period_bytes(substream);
+		dev->dmasound.read_offset += snd_pcm_lib_period_bytes(substream);
+		if (dev->dmasound.read_offset == dev->dmasound.bufsize)
+			dev->dmasound.read_offset = 0;
+	}
+
+	return bytes_to_frames(runtime, dev->dmasound.read_offset);
+}
+
+/*
+ * ALSA hardware capabilities definition
+ *
+ *  Report only 32kHz for ALSA:
+ *
+ *  - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz
+ *    only
+ *  - SAA7134 for TV mode uses DemDec mode (32kHz)
+ *  - Radio works in 32kHz only
+ *  - When recording 48kHz from Line1/Line2, switching of capture source to TV
+ *    means
+ *    switching to 32kHz without any frequency translation
+ */
+
+static struct snd_pcm_hardware snd_card_saa7134_capture =
+{
+	.info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | \
+				SNDRV_PCM_FMTBIT_S16_BE | \
+				SNDRV_PCM_FMTBIT_S8 | \
+				SNDRV_PCM_FMTBIT_U8 | \
+				SNDRV_PCM_FMTBIT_U16_LE | \
+				SNDRV_PCM_FMTBIT_U16_BE,
+	.rates =		SNDRV_PCM_RATE_32000,
+	.rate_min =		32000,
+	.rate_max =		32000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(256*1024),
+	.periods_min =		4,
+	.periods_max =		1024,
+};
+
+static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime)
+{
+	snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+
+	kfree(pcm);
+}
+
+
+/*
+ * ALSA hardware params
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called on initialization, right before the PCM preparation
+ *
+ */
+
+static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream,
+				      struct snd_pcm_hw_params * hw_params)
+{
+	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+	struct saa7134_dev *dev;
+	unsigned int period_size, periods;
+	int err;
+
+	period_size = params_period_bytes(hw_params);
+	periods = params_periods(hw_params);
+
+	if (period_size < 0x100 || period_size > 0x10000)
+		return -EINVAL;
+	if (periods < 4)
+		return -EINVAL;
+	if (period_size * periods > 1024 * 1024)
+		return -EINVAL;
+
+	dev = saa7134->dev;
+
+	if (dev->dmasound.blocks == periods &&
+	    dev->dmasound.blksize == period_size)
+		return 0;
+
+	/* release the old buffer */
+	if (substream->runtime->dma_area) {
+		saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+		videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+		dsp_buffer_free(dev);
+		substream->runtime->dma_area = NULL;
+	}
+	dev->dmasound.blocks  = periods;
+	dev->dmasound.blksize = period_size;
+	dev->dmasound.bufsize = period_size * periods;
+
+	err = dsp_buffer_init(dev);
+	if (0 != err) {
+		dev->dmasound.blocks  = 0;
+		dev->dmasound.blksize = 0;
+		dev->dmasound.bufsize = 0;
+		return err;
+	}
+
+	if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) {
+		dsp_buffer_free(dev);
+		return err;
+	}
+	if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) {
+		videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+		dsp_buffer_free(dev);
+		return err;
+	}
+	if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
+						dev->dmasound.dma.sglist,
+						dev->dmasound.dma.sglen,
+						0))) {
+		saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+		videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+		dsp_buffer_free(dev);
+		return err;
+	}
+
+	/* I should be able to use runtime->dma_addr in the control
+	   byte, but it doesn't work. So I allocate the DMA using the
+	   V4L functions, and force ALSA to use that as the DMA area */
+
+	substream->runtime->dma_area = dev->dmasound.dma.vaddr;
+	substream->runtime->dma_bytes = dev->dmasound.bufsize;
+	substream->runtime->dma_addr = 0;
+
+	return 0;
+
+}
+
+/*
+ * ALSA hardware release
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called after closing the device, but before snd_card_saa7134_capture_close
+ *   It stops the DMA audio and releases the buffers.
+ *
+ */
+
+static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream)
+{
+	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+	struct saa7134_dev *dev;
+
+	dev = saa7134->dev;
+
+	if (substream->runtime->dma_area) {
+		saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+		videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+		dsp_buffer_free(dev);
+		substream->runtime->dma_area = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * ALSA capture finish
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called after closing the device.
+ *
+ */
+
+static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream)
+{
+	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+	struct saa7134_dev *dev = saa7134->dev;
+
+	if (saa7134->mute_was_on) {
+		dev->ctl_mute = 1;
+		saa7134_tvaudio_setmute(dev);
+	}
+	return 0;
+}
+
+/*
+ * ALSA capture start
+ *
+ *   - One of the ALSA capture callbacks.
+ *
+ *   Called when opening the device. It creates and populates the PCM
+ *  structure
+ *
+ */
+
+static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_card_saa7134_pcm_t *pcm;
+	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+	struct saa7134_dev *dev;
+	int amux, err;
+
+	if (!saa7134) {
+		printk(KERN_ERR "BUG: saa7134 can't find device struct."
+				" Can't proceed with open\n");
+		return -ENODEV;
+	}
+	dev = saa7134->dev;
+	mutex_lock(&dev->dmasound.lock);
+
+	dev->dmasound.read_count  = 0;
+	dev->dmasound.read_offset = 0;
+
+	amux = dev->input->amux;
+	if ((amux < 1) || (amux > 3))
+		amux = 1;
+	dev->dmasound.input  =  amux - 1;
+
+	mutex_unlock(&dev->dmasound.lock);
+
+	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+	if (pcm == NULL)
+		return -ENOMEM;
+
+	pcm->dev=saa7134->dev;
+
+	spin_lock_init(&pcm->lock);
+
+	pcm->substream = substream;
+	runtime->private_data = pcm;
+	runtime->private_free = snd_card_saa7134_runtime_free;
+	runtime->hw = snd_card_saa7134_capture;
+
+	if (dev->ctl_mute != 0) {
+		saa7134->mute_was_on = 1;
+		dev->ctl_mute = 0;
+		saa7134_tvaudio_setmute(dev);
+	}
+
+	err = snd_pcm_hw_constraint_integer(runtime,
+						SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_step(runtime, 0,
+						SNDRV_PCM_HW_PARAM_PERIODS, 2);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+
+static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream,
+					unsigned long offset)
+{
+	void *pageptr = substream->runtime->dma_area + offset;
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * ALSA capture callbacks definition
+ */
+
+static struct snd_pcm_ops snd_card_saa7134_capture_ops = {
+	.open =			snd_card_saa7134_capture_open,
+	.close =		snd_card_saa7134_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_card_saa7134_hw_params,
+	.hw_free =		snd_card_saa7134_hw_free,
+	.prepare =		snd_card_saa7134_capture_prepare,
+	.trigger =		snd_card_saa7134_capture_trigger,
+	.pointer =		snd_card_saa7134_capture_pointer,
+	.page =			snd_card_saa7134_page,
+};
+
+/*
+ * ALSA PCM setup
+ *
+ *   Called when initializing the board. Sets up the name and hooks up
+ *  the callbacks
+ *
+ */
+
+static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_saa7134_capture_ops);
+	pcm->private_data = saa7134;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "SAA7134 PCM");
+	return 0;
+}
+
+#define SAA713x_VOLUME(xname, xindex, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_saa7134_volume_info, \
+  .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \
+  .private_value = addr }
+
+static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol,
+				   struct snd_ctl_elem_info * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 20;
+	return 0;
+}
+
+static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol,
+				  struct snd_ctl_elem_value * ucontrol)
+{
+	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	int addr = kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0];
+	ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1];
+	return 0;
+}
+
+static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
+				  struct snd_ctl_elem_value * ucontrol)
+{
+	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	struct saa7134_dev *dev = chip->dev;
+
+	int change, addr = kcontrol->private_value;
+	int left, right;
+
+	left = ucontrol->value.integer.value[0];
+	if (left < 0)
+		left = 0;
+	if (left > 20)
+		left = 20;
+	right = ucontrol->value.integer.value[1];
+	if (right < 0)
+		right = 0;
+	if (right > 20)
+		right = 20;
+	spin_lock_irq(&chip->mixer_lock);
+	change = 0;
+	if (chip->mixer_volume[addr][0] != left) {
+		change = 1;
+		right = left;
+	}
+	if (chip->mixer_volume[addr][1] != right) {
+		change = 1;
+		left = right;
+	}
+	if (change) {
+		switch (dev->pci->device) {
+			case PCI_DEVICE_ID_PHILIPS_SAA7134:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+			case PCI_DEVICE_ID_PHILIPS_SAA7133:
+			case PCI_DEVICE_ID_PHILIPS_SAA7135:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(0x0594,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(0x0594,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+		}
+		chip->mixer_volume[addr][0] = left;
+		chip->mixer_volume[addr][1] = right;
+	}
+	spin_unlock_irq(&chip->mixer_lock);
+	return change;
+}
+
+#define SAA713x_CAPSRC(xname, xindex, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_saa7134_capsrc_info, \
+  .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \
+  .private_value = addr }
+
+static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol,
+				   struct snd_ctl_elem_info * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol,
+				  struct snd_ctl_elem_value * ucontrol)
+{
+	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	int addr = kcontrol->private_value;
+
+	spin_lock_irq(&chip->mixer_lock);
+	if (chip->capture_source_addr == addr) {
+		ucontrol->value.integer.value[0] = chip->capture_source[0];
+		ucontrol->value.integer.value[1] = chip->capture_source[1];
+	} else {
+		ucontrol->value.integer.value[0] = 0;
+		ucontrol->value.integer.value[1] = 0;
+	}
+	spin_unlock_irq(&chip->mixer_lock);
+
+	return 0;
+}
+
+static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol,
+				  struct snd_ctl_elem_value * ucontrol)
+{
+	int left, right;
+	left = ucontrol->value.integer.value[0] & 1;
+	right = ucontrol->value.integer.value[1] & 1;
+
+	return snd_saa7134_capsrc_set(kcontrol, left, right, false);
+}
+
+static struct snd_kcontrol_new snd_saa7134_volume_controls[] = {
+SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER),
+SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1),
+SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2),
+};
+
+static struct snd_kcontrol_new snd_saa7134_capture_controls[] = {
+SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER),
+SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1),
+SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2),
+};
+
+/*
+ * ALSA mixer setup
+ *
+ *   Called when initializing the board. Sets up the name and hooks up
+ *  the callbacks
+ *
+ */
+
+static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip)
+{
+	struct snd_card *card = chip->card;
+	struct snd_kcontrol *kcontrol;
+	unsigned int idx;
+	int err, addr;
+
+	strcpy(card->mixername, "SAA7134 Mixer");
+
+	for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) {
+		kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx],
+					chip);
+		err = snd_ctl_add(card, kcontrol);
+		if (err < 0)
+			return err;
+	}
+
+	for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) {
+		kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx],
+					chip);
+		addr = snd_saa7134_capture_controls[idx].private_value;
+		chip->capture_ctl[addr] = kcontrol;
+		err = snd_ctl_add(card, kcontrol);
+		if (err < 0)
+			return err;
+	}
+
+	chip->capture_source_addr = MIXER_ADDR_UNSELECTED;
+	return 0;
+}
+
+static void snd_saa7134_free(struct snd_card * card)
+{
+	snd_card_saa7134_t *chip = card->private_data;
+
+	if (chip->dev->dmasound.priv_data == NULL)
+		return;
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, &chip->dev->dmasound);
+
+	chip->dev->dmasound.priv_data = NULL;
+
+}
+
+/*
+ * ALSA initialization
+ *
+ *   Called by the init routine, once for each saa7134 device present,
+ *  it creates the basic structures and registers the ALSA devices
+ *
+ */
+
+static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum)
+{
+
+	struct snd_card *card;
+	snd_card_saa7134_t *chip;
+	int err;
+
+
+	if (devnum >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[devnum])
+		return -ENODEV;
+
+	err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
+			      sizeof(snd_card_saa7134_t), &card);
+	if (err < 0)
+		return err;
+
+	strcpy(card->driver, "SAA7134");
+
+	/* Card "creation" */
+
+	card->private_free = snd_saa7134_free;
+	chip = card->private_data;
+
+	spin_lock_init(&chip->lock);
+	spin_lock_init(&chip->mixer_lock);
+
+	chip->dev = dev;
+
+	chip->card = card;
+
+	chip->pci = dev->pci;
+	chip->iobase = pci_resource_start(dev->pci, 0);
+
+
+	err = request_irq(dev->pci->irq, saa7134_alsa_irq,
+				IRQF_SHARED | IRQF_DISABLED, dev->name,
+				(void*) &dev->dmasound);
+
+	if (err < 0) {
+		printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n",
+			dev->name, dev->pci->irq);
+		goto __nodev;
+	}
+
+	chip->irq = dev->pci->irq;
+
+	mutex_init(&dev->dmasound.lock);
+
+	if ((err = snd_card_saa7134_new_mixer(chip)) < 0)
+		goto __nodev;
+
+	if ((err = snd_card_saa7134_pcm(chip, 0)) < 0)
+		goto __nodev;
+
+	snd_card_set_dev(card, &chip->pci->dev);
+
+	/* End of "creation" */
+
+	strcpy(card->shortname, "SAA7134");
+	sprintf(card->longname, "%s at 0x%lx irq %d",
+		chip->dev->name, chip->iobase, chip->irq);
+
+	printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]);
+
+	if ((err = snd_card_register(card)) == 0) {
+		snd_saa7134_cards[devnum] = card;
+		return 0;
+	}
+
+__nodev:
+	snd_card_free(card);
+	return err;
+}
+
+
+static int alsa_device_init(struct saa7134_dev *dev)
+{
+	dev->dmasound.priv_data = dev;
+	alsa_card_saa7134_create(dev,dev->nr);
+	return 1;
+}
+
+static int alsa_device_exit(struct saa7134_dev *dev)
+{
+
+	snd_card_free(snd_saa7134_cards[dev->nr]);
+	snd_saa7134_cards[dev->nr] = NULL;
+	return 1;
+}
+
+/*
+ * Module initializer
+ *
+ * Loops through present saa7134 cards, and assigns an ALSA device
+ * to each one
+ *
+ */
+
+static int saa7134_alsa_init(void)
+{
+	struct saa7134_dev *dev = NULL;
+	struct list_head *list;
+
+	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->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+			printk(KERN_INFO "%s/alsa: %s doesn't support digital audio\n",
+				dev->name, saa7134_boards[dev->board].name);
+		else
+			alsa_device_init(dev);
+	}
+
+	if (dev == NULL)
+		printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n");
+
+	return 0;
+
+}
+
+/*
+ * Module destructor
+ */
+
+static void saa7134_alsa_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		snd_card_free(snd_saa7134_cards[idx]);
+	}
+
+	saa7134_dmasound_init = NULL;
+	saa7134_dmasound_exit = NULL;
+	printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n");
+
+	return;
+}
+
+/* We initialize this late, to make sure the sound system is up and running */
+late_initcall(saa7134_alsa_init);
+module_exit(saa7134_alsa_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ricardo Cerqueira");
diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
new file mode 100644
index 000000000000..bc08f1dbc293
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-cards.c
@@ -0,0 +1,8026 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2001-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include "tuner-xc2028.h"
+#include <media/v4l2-common.h>
+#include <media/tveeprom.h>
+#include "tea5767.h"
+#include "tda18271.h"
+#include "xc5000.h"
+#include "s5h1411.h"
+
+/* commly used strings */
+static char name_mute[]    = "mute";
+static char name_radio[]   = "Radio";
+static char name_tv[]      = "Television";
+static char name_tv_mono[] = "TV (mono only)";
+static char name_comp[]    = "Composite";
+static char name_comp1[]   = "Composite1";
+static char name_comp2[]   = "Composite2";
+static char name_comp3[]   = "Composite3";
+static char name_comp4[]   = "Composite4";
+static char name_svideo[]  = "S-Video";
+
+/* ------------------------------------------------------------------ */
+/* board config info                                                  */
+
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
+struct saa7134_board saa7134_boards[] = {
+	[SAA7134_BOARD_UNKNOWN] = {
+		.name		= "UNKNOWN/GENERIC",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			.name = "default",
+			.vmux = 0,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_PROTEUS_PRO] = {
+		/* /me */
+		.name		= "Proteus Pro [philips reference design]",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_FLYVIDEO3000] = {
+		/* "Marco d'Itri" <md@Linux.IT> */
+		.name		= "LifeView FlyVIDEO3000",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.gpiomask       = 0xe000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x8000,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x2000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x8000,
+		},
+	},
+	[SAA7134_BOARD_FLYVIDEO2000] = {
+		/* "TC Wan" <tcwan@cs.usm.my> */
+		.name           = "LifeView/Typhoon FlyVIDEO2000",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.gpiomask       = 0xe000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x2000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x8000,
+		},
+	},
+	[SAA7134_BOARD_FLYTVPLATINUM_MINI] = {
+		/* "Arnaud Quette" <aquette@free.fr> */
+		.name           = "LifeView FlyTV Platinum Mini",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,     /* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_FLYTVPLATINUM_FM] = {
+		/* LifeView FlyTV Platinum FM (LR214WF) */
+		/* "Peter Missel <peter.missel@onlinehome.de> */
+		.name           = "LifeView FlyTV Platinum FM / Gold",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.gpiomask       = 0x1E000,	/* Set GP16 and unused 15,14,13 to Output */
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x10000,	/* GP16=1 selects TV input */
+			.tv   = 1,
+		},{
+/*			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+*/			.name = name_comp1,	/* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+/*			.gpio = 0x4000,         */
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+/*			.gpio = 0x4000,         */
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+/*			.gpio = 0x4000,         */
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x00000,	/* GP16=0 selects FM radio antenna */
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x10000,
+		},
+	},
+	[SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = {
+		/* RoverMedia TV Link Pro FM (LR138 REV:I) */
+		/* Eugene Yudin <Eugene.Yudin@gmail.com> */
+		.name		= "RoverMedia TV Link Pro FM",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0xe000,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x8000,
+			.tv   = 1,
+		}, {
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}, {
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x2000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x8000,
+		},
+	},
+	[SAA7134_BOARD_EMPRESS] = {
+		/* "Gert Vervoort" <gert.vervoort@philips.com> */
+		.name		= "EMPRESS",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.empress_addr 	= 0x20,
+
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+		.mpeg      = SAA7134_MPEG_EMPRESS,
+		.video_out = CCIR656,
+	},
+	[SAA7134_BOARD_MONSTERTV] = {
+		/* "K.Ohta" <alpha292@bremen.or.jp> */
+		.name           = "SKNet Monster TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_MD9717] = {
+		.name		= "Tevion MD 9717",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			/* workaround for problems with normal TV sound */
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	       .mute = {
+		       .name = name_mute,
+		       .amux = TV,
+	       },
+	},
+	[SAA7134_BOARD_TVSTATION_RDS] = {
+		/* Typhoon TV Tuner RDS: Art.Nr. 50694 */
+		.name		= "KNC One TV-Station RDS / Typhoon TV Tuner 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 = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux   = LINE2,
+			.tv   = 1,
+		},{
+
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+
+			.name = "CVid over SVid",
+			.vmux = 0,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_TVSTATION_DVR] = {
+		.name		= "KNC One TV-Station DVR",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.empress_addr 	= 0x20,
+		.tda9887_conf	= TDA9887_PRESENT,
+		.gpiomask	= 0x820000,
+		.inputs		= {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x20000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x20000,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x20000,
+		}},
+		.radio		= {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x20000,
+		},
+		.mpeg           = SAA7134_MPEG_EMPRESS,
+		.video_out	= CCIR656,
+	},
+	[SAA7134_BOARD_CINERGY400] = {
+		.name           = "Terratec Cinergy 400 TV",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp2, /* CVideo over SVideo Connector */
+			.vmux = 0,
+			.amux = LINE1,
+		}}
+	},
+	[SAA7134_BOARD_MD5044] = {
+		.name           = "Medion 5044",
+		.audio_clock    = 0x00187de7, /* was: 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 = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			/* workaround for problems with normal TV sound */
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_KWORLD] = {
+		.name           = "Kworld/KuroutoShikou SAA7130-TVPCI",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_CINERGY600] = {
+		.name           = "Terratec Cinergy 600 TV",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp2, /* CVideo over SVideo Connector */
+			.vmux = 0,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_MD7134] = {
+		.name           = "Medion 7134",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+	       },
+	       .mute = {
+		       .name = name_mute,
+		       .amux = TV,
+		},
+	},
+	[SAA7134_BOARD_TYPHOON_90031] = {
+		/* aka Typhoon "TV+Radio", Art.Nr 90031 */
+		/* Tom Zoerner <tomzo at users sourceforge net> */
+		.name           = "Typhoon TV+Radio 90031",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
+	[SAA7134_BOARD_ELSA] = {
+		.name           = "ELSA EX-VISION 300TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_HITACHI_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 4,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_ELSA_500TV] = {
+		.name           = "ELSA EX-VISION 500TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_HITACHI_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 7,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 8,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 8,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_ELSA_700TV] = {
+		.name           = "ELSA EX-VISION 700TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_HITACHI_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 4,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 6,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 7,
+			.amux = LINE1,
+		}},
+		.mute           = {
+			.name = name_mute,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_ASUSTeK_TVFM7134] = {
+		.name           = "ASUS TV-FM 7134",
+		.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 = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_ASUSTeK_TVFM7135] = {
+		.name           = "ASUS TV-FM 7135",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE2,
+			.gpio = 0x0000,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+			.gpio = 0x0000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x200000,
+		},
+		.mute  = {
+			.name = name_mute,
+			.gpio = 0x0000,
+		},
+
+	},
+	[SAA7134_BOARD_VA1000POWER] = {
+		.name           = "AOPEN VA1000 POWER",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_10MOONSTVMASTER] = {
+		/* "lilicheng" <llc@linuxfans.org> */
+		.name           = "10MOONS PCI TV CAPTURE CARD",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0xe000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x2000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x8000,
+		},
+	},
+	[SAA7134_BOARD_BMK_MPEX_NOTUNER] = {
+		/* "Andrew de Quincey" <adq@lidskialf.net> */
+		.name		= "BMK MPEX No Tuner",
+		.audio_clock	= 0x200000,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.empress_addr 	= 0x20,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE1,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_comp3,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_comp4,
+			.vmux = 1,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.mpeg      = SAA7134_MPEG_EMPRESS,
+		.video_out = CCIR656,
+	},
+	[SAA7134_BOARD_VIDEOMATE_TV] = {
+		.name           = "Compro VideoMate TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = {
+		.name           = "Compro VideoMate TV Gold+",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.gpiomask       = 0x800c0000,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x06c00012,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x0ac20012,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x08c20012,
+			.tv   = 1,
+		}},				/* radio and probably mute is missing */
+	},
+	[SAA7134_BOARD_CRONOS_PLUS] = {
+		/*
+		gpio pins:
+			0  .. 3   BASE_ID
+			4  .. 7   PROTECT_ID
+			8  .. 11  USER_OUT
+			12 .. 13  USER_IN
+			14 .. 15  VIDIN_SEL
+		*/
+		.name           = "Matrox CronosPlus",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0xcf00,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 0,
+			.gpio = 2 << 14,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.gpio = 1 << 14,
+		},{
+			.name = name_comp3,
+			.vmux = 0,
+			.gpio = 0 << 14,
+		},{
+			.name = name_comp4,
+			.vmux = 0,
+			.gpio = 3 << 14,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.gpio = 2 << 14,
+		}},
+	},
+	[SAA7134_BOARD_MD2819] = {
+		.name           = "AverMedia M156 / Medion 2819",
+		.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	= 0x03,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x02,
+		}, {
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE1,
+			.gpio = 0x02,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x02,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+			.gpio = 0x01,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x00,
+		},
+	},
+	[SAA7134_BOARD_BMK_MPEX_TUNER] = {
+		/* "Greg Wickham <greg.wickham@grangenet.net> */
+		.name           = "BMK MPEX Tuner",
+		.audio_clock    = 0x200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.empress_addr 	= 0x20,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+		}},
+		.mpeg      = SAA7134_MPEG_EMPRESS,
+		.video_out = CCIR656,
+	},
+	[SAA7134_BOARD_ASUSTEK_TVFM7133] = {
+		.name           = "ASUS TV-FM 7133",
+		.audio_clock    = 0x00187de7,
+		/* probably wrong, the 7133 one is the NTSC version ...
+		* .tuner_type  = TUNER_PHILIPS_FM1236_MK3 */
+		.tuner_type     = TUNER_LG_NTSC_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_PINNACLE_PCTV_STEREO] = {
+		.name           = "Pinnacle PCTV Stereo (saa7134)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_MT2032,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 1,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_MANLI_MTV002] = {
+		/* Ognjen Nastic <ognjen@logosoft.ba> */
+		.name           = "Manli MuchTV M-TV002",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.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_MANLI_MTV001] = {
+		/* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */
+		.name           = "Manli MuchTV M-TV001",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.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_TG3000TV] = {
+		/* TransGear 3000TV */
+		.name           = "Nagase Sangyo TransGear 3000TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_ECS_TVP3XP] = {
+		.name           = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ",
+		.audio_clock    = 0x187de7,  /* xtal 32.1 MHz */
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_tv_mono,
+			.vmux   = 1,
+			.amux   = LINE2,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		},{
+			.name   = "CVid over SVid",
+			.vmux   = 0,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
+	[SAA7134_BOARD_ECS_TVP3XP_4CB5] = {
+		.name           = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_tv_mono,
+			.vmux   = 1,
+			.amux   = LINE2,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		},{
+			.name   = "CVid over SVid",
+			.vmux   = 0,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
+    [SAA7134_BOARD_ECS_TVP3XP_4CB6] = {
+		/* Barry Scott <barry.scott@onelan.co.uk> */
+		.name		= "Elitegroup ECS TVP3XP FM1246 Tuner Card (PAL,FM)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL_I,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_tv_mono,
+			.vmux   = 1,
+			.amux   = LINE2,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		},{
+			.name   = "CVid over SVid",
+			.vmux   = 0,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
+	[SAA7134_BOARD_AVACSSMARTTV] = {
+		/* Roman Pszonczenko <romka@kolos.math.uni.lodz.pl> */
+		.name           = "AVACS SmartTV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = {
+		/* Michael Smith <msmith@cbnco.com> */
+		.name           = "AVerMedia DVD EZMaker",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 3,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M103] = {
+		/* Massimo Piccioni <dafastidio@libero.it> */
+		.name           = "AVerMedia MiniPCI DVB-T Hybrid M103",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		 .mpeg           = SAA7134_MPEG_DVB,
+		 .inputs         = {{
+			 .name = name_tv,
+			 .vmux = 1,
+			 .amux = TV,
+			 .tv   = 1,
+		 } },
+	},
+	[SAA7134_BOARD_NOVAC_PRIMETV7133] = {
+		/* toshii@netbsd.org */
+		.name           = "Noval Prime TV 7133",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ALPS_TSBH1_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 3,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_305] = {
+		.name           = "AverMedia AverTV Studio 305",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1256_IH3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_505] = {
+		/* Vasiliy Temnikov <vaka@newmail.ru> */
+		.name           = "AverMedia AverTV Studio 505",
+		.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 = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		}, {
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_UPMOST_PURPLE_TV] = {
+		.name           = "UPMOST PURPLE TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1236_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 7,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_svideo,
+			.vmux = 7,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_ITEMS_MTV005] = {
+		/* Norman Jonas <normanjonas@arcor.de> */
+		.name           = "Items MuchTV Plus / IT-005",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.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_CINERGY200] = {
+		.name           = "Terratec Cinergy 200 TV",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp2, /* CVideo over SVideo Connector */
+			.vmux = 0,
+			.amux = LINE1,
+		}},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_TV_PVR] = {
+		/* Alain St-Denis <alain@topaze.homeip.net> */
+		.name           = "Compro VideoMate TV PVR/FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x808c0080,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x00080,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x00080,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2_LEFT,
+			.tv   = 1,
+			.gpio = 0x00080,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x80000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x40000,
+		},
+	},
+	[SAA7134_BOARD_SABRENT_SBTTVFM] = {
+		/* Michael Rodriguez-Torrent <mrtorrent@asu.edu> */
+		.name           = "Sabrent SBT-TVFM (saa7130)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC_M,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
+	[SAA7134_BOARD_ZOLID_XPERT_TV7134] = {
+		/* Helge Jensen <helge.jensen@slog.dk> */
+		.name           = ":Zolid Xpert TV7134",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = {
+		/* "Matteo Az" <matte.az@nospam.libero.it> ;-) */
+		.name           = "Empire PCI TV-Radio LE",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x4000,
+		.inputs         = {{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x8000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x8000,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+			.gpio = 0x8000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+			.gpio = 0x8000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio =0x8000,
+		}
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_307] = {
+		/*
+		Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+		Lots of thanks to Andrey Zolotarev <zolotarev_andrey@mail.ru>
+		*/
+		.name           = "Avermedia AVerTV Studio 307",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1256_IH3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x03,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00,
+		},{
+			.name = name_comp,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x02,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x02,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+			.gpio = 0x01,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = LINE1,
+			.gpio = 0x00,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_GO_007_FM] = {
+		.name           = "Avermedia AVerTV GO 007 FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x00300003,
+		/* .gpiomask       = 0x8c240003, */
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x01,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+			.gpio = 0x02,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+			.gpio = 0x02,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x00300001,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x01,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_CARDBUS] = {
+		/* Kees.Blom@cwi.nl */
+		.name           = "AVerMedia Cardbus TV/Radio (E500)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_CARDBUS_501] = {
+		/* Oldrich Jedlicka <oldium.pro@seznam.cz> */
+		.name           = "AVerMedia Cardbus TV/Radio (E501R)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_ALPS_TSBE5_PAL,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr	= 0x61,
+		.radio_addr	= 0x60,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x08000000,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x08000000,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x08000000,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x08000000,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x00000000,
+		},
+	},
+	[SAA7134_BOARD_CINERGY400_CARDBUS] = {
+		.name           = "Terratec Cinergy 400 mobile",
+		.audio_clock    = 0x187de7,
+		.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 = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_CINERGY600_MK3] = {
+		.name           = "Terratec Cinergy 600 TV MK3",
+		.audio_clock    = 0x00200000,
+		.tuner_type	= TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 4,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name = name_comp2, /* CVideo over SVideo Connector */
+			.vmux = 0,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_GOLD_PLUS] = {
+		/* Dylan Walkden <dylan_walkden@hotmail.com> */
+		.name		= "Compro VideoMate Gold+ Pal",
+		.audio_clock	= 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x1ce780,
+		.inputs		= {{
+			.name = name_svideo,
+			.vmux = 0,		/* CVideo over SVideo Connector - ok? */
+			.amux = LINE1,
+			.gpio = 0x008080,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x008080,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x008080,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x80000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x0c8000,
+		},
+	},
+	[SAA7134_BOARD_PINNACLE_300I_DVBT_PAL] = {
+		.name           = "Pinnacle PCTV 300i DVB-T + PAL",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_MT2032,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 1,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_PROVIDEO_PV952] = {
+		/* andreas.kretschmer@web.de */
+		.name		= "ProVideo PV952",
+		.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_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_305] = {
+		/* much like the "studio" version but without radio
+		* and another tuner (sirspiritus@yandex.ru) */
+		.name           = "AverMedia AverTV/305",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_FLYDVBTDUO] = {
+		/* LifeView FlyDVB-T DUO */
+		/* "Nico Sabbi <nsabbi@tiscali.it>  Hartmut Hackmann hartmut.hackmann@t-online.de*/
+		.name           = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x00200000,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x200000,	/* GPIO21=High for TV input */
+			.tv   = 1,
+		},{
+			.name = name_comp1,	/* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x000000,	/* GPIO21=Low for FM radio antenna */
+		},
+	},
+	[SAA7134_BOARD_PHILIPS_TOUGH] = {
+		.name           = "Philips TOUGH DVB-T reference design",
+		.tuner_type	= TUNER_ABSENT,
+		.audio_clock    = 0x00187de7,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_307] = {
+		/*
+		Davydov Vladimir <vladimir@iqmedia.com>
+		*/
+		.name           = "Avermedia AVerTV 307",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_ADS_INSTANT_TV] = {
+		.name           = "ADS Tech Instant TV (saa7135)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_KWORLD_VSTREAM_XPERT] = {
+		.name           = "Kworld/Tevion V-Stream Xpert TV PVR7134",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_PAL_I,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x0700,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x000,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+			.gpio   = 0x200,		/* gpio by DScaler */
+		},{
+			.name   = name_svideo,
+			.vmux   = 0,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE1,
+			.gpio   = 0x100,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x000,
+		},
+	},
+	[SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = {
+		.name		= "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask	= 0x00200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x200000,	/* GPIO21=High for TV input */
+			.tv   = 1,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+		},{
+			.name = name_comp1,	/* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x000000,	/* GPIO21=Low for FM radio antenna */
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII] = {
+		.name           = "Compro VideoMate TV Gold+II",
+		.audio_clock    = 0x002187de7,
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr     = 0x63,
+		.radio_addr     = 0x60,
+		.gpiomask       = 0x8c1880,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 0,
+			.amux = LINE1,
+			.gpio = 0x800800,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x801000,
+		},{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x800000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x880000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x840000,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_XPERT] = {
+		/*
+		FIXME:
+		- Remote control doesn't initialize properly.
+		- Audio volume starts muted,
+		then gradually increases after channel change.
+		- Overlay scaling problems (application error?)
+		- Composite S-Video untested.
+		From: Konrad Rzepecki <hannibal@megapolis.pl>
+		*/
+		.name           = "Kworld Xpert TV PVR7134",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_TENA_9533_DI,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr	= 0x61,
+		.radio_addr	= 0x60,
+		.gpiomask	= 0x0700,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x000,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+			.gpio   = 0x200,		/* gpio by DScaler */
+		},{
+			.name   = name_svideo,
+			.vmux   = 0,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE1,
+			.gpio   = 0x100,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x000,
+		},
+	},
+	[SAA7134_BOARD_FLYTV_DIGIMATRIX] = {
+		.name		= "FlyTV mini Asus Digimatrix",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_LG_TALN,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,		/* radio unconfirmed */
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_TERMINATOR] = {
+		/* Kworld V-Stream Studio TV Terminator */
+		/* "James Webb <jrwebb@qwest.net> */
+		.name           = "V-Stream Studio TV Terminator",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 1 << 21,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x0000000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,     /* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x0000000,
+		},{
+			.name = name_svideo,    /* S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x0000000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_YUAN_TUN900] = {
+		/* FIXME:
+		 * S-Video and composite sources untested.
+		 * Radio not working.
+		 * Remote control not yet implemented.
+		 * From : codemaster@webgeeks.be */
+		.name           = "Yuan TUN-900 (saa7135)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr= ADDR_UNSET,
+		.radio_addr= ADDR_UNSET,
+		.gpiomask       = 0x00010003,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x01,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x02,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+			.gpio = 0x02,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+			.gpio = 0x00010003,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x01,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_409FM] = {
+		/* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 409 FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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_GOTVIEW_7135] = {
+		/* Mike Baikov <mike@baikov.com> */
+		/* Andrey Cvetcov <ays14@yandex.ru> */
+		.name            = "GoTView 7135 PCI",
+		.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        = 0x00200003,
+		.inputs          = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00200003,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x00200003,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x00200003,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x00200003,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x00200003,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x00200003,
+		},
+	},
+	[SAA7134_BOARD_PHILIPS_EUROPA] = {
+		.name           = "Philips EUROPA V3 reference design",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TD1316,
+		.radio_type     = UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_VIDEOMATE_DVBT_300] = {
+		.name           = "Compro Videomate DVB-T300",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TD1316,
+		.radio_type     = UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_VIDEOMATE_DVBT_200] = {
+		.name           = "Compro Videomate DVB-T200",
+		.tuner_type	= TUNER_ABSENT,
+		.audio_clock    = 0x00187de7,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_RTD_VFG7350] = {
+		.name		= "RTD Embedded Technologies VFG7350",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.empress_addr 	= 0x21,
+		.inputs		= {{
+			.name   = "Composite 0",
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = "Composite 1",
+			.vmux   = 1,
+			.amux   = LINE2,
+		},{
+			.name   = "Composite 2",
+			.vmux   = 2,
+			.amux   = LINE1,
+		},{
+			.name   = "Composite 3",
+			.vmux   = 3,
+			.amux   = LINE2,
+		},{
+			.name   = "S-Video 0",
+			.vmux   = 8,
+			.amux   = LINE1,
+		},{
+			.name   = "S-Video 1",
+			.vmux   = 9,
+			.amux   = LINE2,
+		}},
+		.mpeg           = SAA7134_MPEG_EMPRESS,
+		.video_out      = CCIR656,
+		.vid_port_opts  = ( SET_T_CODE_POLARITY_NON_INVERTED |
+				    SET_CLOCK_NOT_DELAYED |
+				    SET_CLOCK_INVERTED |
+				    SET_VSYNC_OFF ),
+	},
+	[SAA7134_BOARD_RTD_VFG7330] = {
+		.name		= "RTD Embedded Technologies VFG7330",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs		= {{
+			.name   = "Composite 0",
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = "Composite 1",
+			.vmux   = 1,
+			.amux   = LINE2,
+		},{
+			.name   = "Composite 2",
+			.vmux   = 2,
+			.amux   = LINE1,
+		},{
+			.name   = "Composite 3",
+			.vmux   = 3,
+			.amux   = LINE2,
+		},{
+			.name   = "S-Video 0",
+			.vmux   = 8,
+			.amux   = LINE1,
+		},{
+			.name   = "S-Video 1",
+			.vmux   = 9,
+			.amux   = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_FLYTVPLATINUM_MINI2] = {
+		.name           = "LifeView FlyTV Platinum Mini2",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,     /* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180] = {
+		/* Michael Krufky <mkrufky@m1k.net>
+		 * Uses Alps Electric TDHU2, containing NXT2004 ATSC Decoder
+		 * AFAIK, there is no analog demod, thus,
+		 * no support for analog television.
+		 */
+		.name           = "AVerMedia AVerTVHD MCE A180",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_MONSTERTV_MOBILE] = {
+		.name           = "SKNet MonsterTV Mobile",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+
+		.inputs         = {{
+			  .name = name_tv,
+			  .vmux = 1,
+			  .amux = TV,
+			  .tv   = 1,
+		},{
+			  .name = name_comp1,
+			  .vmux = 3,
+			  .amux = LINE1,
+		},{
+			  .name = name_svideo,
+			  .vmux = 6,
+			  .amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_PINNACLE_PCTV_110i] = {
+	       .name           = "Pinnacle PCTV 40i/50i/110i (saa7133)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0x080200000,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 4,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE2,
+		}, {
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_ASUSTeK_P7131_DUAL] = {
+		.name           = "ASUSTeK P7131 Dual",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 1 << 21,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000000,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_SEDNA_PC_TV_CARDBUS] = {
+		/* Paul Tom Zalac <pzalac@gmail.com> */
+		/* Pavel Mihaylov <bin@bash.info> */
+		.name           = "Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)",
+				/* Sedna/MuchTV (OEM) Cardbus TV Tuner */
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0xe880c0,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+	},
+	[SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV] = {
+		/* "Cyril Lacoux (Yack)" <clacoux@ifeelgood.org> */
+		.name           = "ASUS Digimatrix TV",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_PHILIPS_TIGER] = {
+		.name           = "Philips Tiger reference design",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 0,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = {
+		.name           = "MSI TV@Anywhere plus",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 1 << 21,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE2,	/* unconfirmed, taken from Philips driver */
+		},{
+			.name   = name_comp2,
+			.vmux   = 0,		/* untested, Composite over S-Video */
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_CINERGY250PCI] = {
+		/* remote-control does not work. The signal about a
+		   key press comes in via gpio, but the key code
+		   doesn't. Neither does it have an i2c remote control
+		   interface. */
+		.name           = "Terratec Cinergy 250 PCI TV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x80200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_svideo,  /* NOT tested */
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_FLYDVB_TRIO] = {
+		/* LifeView LR319 FlyDVB Trio */
+		/* Peter Missel <peter.missel@onlinehome.de> */
+		.name           = "LifeView FlyDVB Trio",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x00200000,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,	/* Analog broadcast/cable TV */
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x200000,	/* GPIO21=High for TV input */
+			.tv   = 1,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+		},{
+			.name = name_comp1,	/* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x000000,	/* GPIO21=Low for FM radio antenna */
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_777] = {
+		.name           = "AverTV DVB-T 777",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_FLYDVBT_LR301] = {
+		/* LifeView FlyDVB-T */
+		/* Giampiero Giancipoli <gianci@libero.it> */
+		.name           = "LifeView FlyDVB-T / Genius VideoWonder DVB-T",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_comp1,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331] = {
+		.name           = "ADS Instant TV Duo Cardbus PTV331",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x00200000,
+		}},
+	},
+	[SAA7134_BOARD_TEVION_DVBT_220RF] = {
+		.name           = "Tevion/KWorld DVB-T 220RF",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 1 << 21,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_comp2,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_DVBT_210] = {
+		.name           = "KWorld DVB-T 210",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 1 << 21,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_ATSC110] = {
+		.name           = "Kworld ATSC110/115",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TUV1236D,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_A169_B] = {
+		/* AVerMedia A169  */
+		/* Rickard Osser <ricky@osser.se>  */
+		/* This card has two saa7134 chips on it,
+		   but only one of them is currently working. */
+		.name		= "AVerMedia A169 B",
+		.audio_clock    = 0x02187de7,
+		.tuner_type	= TUNER_LG_TALN,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x0a60000,
+	},
+	[SAA7134_BOARD_AVERMEDIA_A169_B1] = {
+		/* AVerMedia A169 */
+		/* Rickard Osser <ricky@osser.se> */
+		.name		= "AVerMedia A169 B1",
+		.audio_clock    = 0x02187de7,
+		.tuner_type	= TUNER_LG_TALN,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0xca60000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 4,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x04a61000,
+		},{
+			.name = name_comp2,  /*  Composite SVIDEO (B/W if signal is carried with SVIDEO) */
+			.vmux = 1,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 9,           /* 9 is correct as S-VIDEO1 according to a169.inf! */
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_MD7134_BRIDGE_2] = {
+		/* The second saa7134 on this card only serves as DVB-S host bridge */
+		.name           = "Medion 7134 Bridge #2",
+		.audio_clock    = 0x00187de7,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+	},
+	[SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = {
+		.name		= "LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x200000,	/* GPIO21=High for TV input */
+			.tv   = 1,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE2,
+		},{
+			.name = name_comp1,	/* Composite signal on S-Video input */
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x000000,	/* GPIO21=Low for FM radio antenna */
+		},
+	},
+	[SAA7134_BOARD_FLYVIDEO3000_NTSC] = {
+		/* "Zac Bowling" <zac@zacbowling.com> */
+		.name           = "LifeView FlyVIDEO3000 (NTSC)",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_NTSC,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+
+		.gpiomask       = 0xe000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.gpio = 0x8000,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x4000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x2000,
+		},
+			.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x8000,
+		},
+	},
+	[SAA7134_BOARD_MEDION_MD8800_QUADRO] = {
+		.name           = "Medion Md8800 Quadro",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_FLYDVBS_LR300] = {
+		/* LifeView FlyDVB-s */
+		/* Igor M. Liplianin <liplianin@tut.by> */
+		.name           = "LifeView FlyDVB-S /Acorp TV134DS",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_comp1,	/* Composite input */
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,	/* S-Video signal on S-Video input */
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_PROTEUS_2309] = {
+		.name           = "Proteus Pro 2309",
+		.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 = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_A16AR] = {
+		/* Petr Baudis <pasky@ucw.cz> */
+		.name           = "AVerMedia TV Hybrid A16AR",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_PHILIPS_TD1316, /* untested */
+		.radio_type     = TUNER_TEA5767, /* untested */
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = 0x60,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+		},
+	},
+	[SAA7134_BOARD_ASUS_EUROPA2_HYBRID] = {
+		.name           = "Asus Europa2 OEM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT| TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 4,
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE1,
+		},
+	},
+	[SAA7134_BOARD_PINNACLE_PCTV_310i] = {
+		.name           = "Pinnacle PCTV 310i",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 1,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x000200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 4,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_507] = {
+		/* Mikhail Fedotov <mo_fedotov@mail.ru> */
+		.name           = "Avermedia AVerTV Studio 507",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1256_IH3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x03,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x00,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x00,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x00,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x01,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = LINE1,
+			.gpio = 0x00,
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_DVBT_200A] = {
+		/* Francis Barber <fedora@barber-family.id.au> */
+		.name           = "Compro Videomate DVB-T200A",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
+		/* Thomas Genty <tomlohave@gmail.com> */
+		/* David Bentham <db260179@hotmail.com> */
+		.name           = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 1,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200100,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000100,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200100,
+		},
+	},
+	[SAA7134_BOARD_HAUPPAUGE_HVR1150] = {
+		.name           = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 3,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_SERIAL,
+		.ts_force_val   = 1,
+		.gpiomask       = 0x0800100, /* GPIO 21 is an INPUT */
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000100,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0800100, /* GPIO 23 HI for FM */
+		},
+	},
+	[SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
+		.name           = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 3,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_SERIAL,
+		.gpiomask       = 0x0800100, /* GPIO 21 is an INPUT */
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000100,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0800100, /* GPIO 23 HI for FM */
+		},
+	},
+	[SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
+		.name           = "Terratec Cinergy HT PCMCIA",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 6,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_ENCORE_ENLTV] = {
+	/* Steven Walter <stevenrwalter@gmail.com>
+	   Juan Pablo Sormani <sorman@gmail.com> */
+		.name           = "Encore ENLTV",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_TNF_5335MF,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = 3,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 7,
+			.amux = 4,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = 2,
+		},{
+			.name = name_svideo,
+			.vmux = 0,
+			.amux = 2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+/*			.gpio = 0x00300001,*/
+			.gpio = 0x20000,
+
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = 0,
+		},
+	},
+	[SAA7134_BOARD_ENCORE_ENLTV_FM] = {
+  /*	Juan Pablo Sormani <sorman@gmail.com> */
+		.name           = "Encore ENLTV-FM",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_FCV1236D,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = 3,
+			.tv   = 1,
+		},{
+			.name = name_tv_mono,
+			.vmux = 7,
+			.amux = 4,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = 2,
+		},{
+			.name = name_svideo,
+			.vmux = 0,
+			.amux = 2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x20000,
+
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = 0,
+		},
+	},
+	[SAA7134_BOARD_ENCORE_ENLTV_FM53] = {
+		.name           = "Encore ENLTV-FM v5.3",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_TNF_5335MF,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x7000,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = 1,
+			.tv   = 1,
+			.gpio = 0x50000,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = 2,
+			.gpio = 0x2000,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = 2,
+			.gpio = 0x2000,
+		} },
+		.radio = {
+			.name = name_radio,
+			.vmux = 1,
+			.amux = 1,
+		},
+		.mute = {
+			.name = name_mute,
+			.gpio = 0xf000,
+			.amux = 0,
+		},
+	},
+	[SAA7134_BOARD_ENCORE_ENLTV_FM3] = {
+		.name           = "Encore ENLTV-FM 3",
+		.audio_clock    = 0x02187de7,
+		.tuner_type     = TUNER_TENA_TNF_5337,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr	= 0x61,
+		.radio_addr	= 0x60,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.vmux = 1,
+			.amux = LINE1,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE1,
+			.gpio = 0x43000,
+		},
+	},
+	[SAA7134_BOARD_CINERGY_HT_PCI] = {
+		.name           = "Terratec Cinergy HT PCI",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 6,
+			.amux   = LINE1,
+		}},
+	},
+	[SAA7134_BOARD_PHILIPS_TIGER_S] = {
+		.name           = "Philips Tiger - S Reference design",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M102] = {
+		.name           = "Avermedia M102",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 1<<21,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_ASUS_P7131_4871] = {
+		.name           = "ASUS P7131 4871",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x0200000,
+		}},
+	},
+	[SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = {
+		.name           = "ASUSTeK P7131 Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.gpiomask	= 1 << 21,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x0000000,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x0200000,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = {
+	       .name           = "ASUSTeK P7131 Analog",
+	       .audio_clock    = 0x00187de7,
+	       .tuner_type     = TUNER_PHILIPS_TDA8290,
+	       .radio_type     = UNSET,
+	       .tuner_addr     = ADDR_UNSET,
+	       .radio_addr     = ADDR_UNSET,
+	       .gpiomask       = 1 << 21,
+	       .inputs         = {{
+		       .name = name_tv,
+		       .vmux = 1,
+		       .amux = TV,
+		       .tv   = 1,
+		       .gpio = 0x0000000,
+	       }, {
+		       .name = name_comp1,
+		       .vmux = 3,
+		       .amux = LINE2,
+	       }, {
+		       .name = name_comp2,
+		       .vmux = 0,
+		       .amux = LINE2,
+	       }, {
+		       .name = name_svideo,
+		       .vmux = 8,
+		       .amux = LINE2,
+	       } },
+	       .radio = {
+		       .name = name_radio,
+		       .amux = TV,
+		       .gpio = 0x0200000,
+	       },
+	},
+	[SAA7134_BOARD_SABRENT_TV_PCB05] = {
+		.name           = "Sabrent PCMCIA TV-PCB05",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE1,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_10MOONSTVMASTER3] = {
+		/* Tony Wan <aloha_cn@hotmail.com> */
+		.name           = "10MOONS TM300 TV Card",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0x7000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x2000,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x2000,
+		}},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x3000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_SUPER_007] = {
+		.name           = "Avermedia Super 007",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 0,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv, /* FIXME: analog tv untested */
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		}},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M135A] = {
+		.name           = "Avermedia PCI pure analog (M135A)",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 2,
+		.gpiomask       = 0x020200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x00200000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x01,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M733A] = {
+		.name		= "Avermedia PCI M733A",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_PHILIPS_TDA8290,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config	= 0,
+		.gpiomask	= 0x020200000,
+		.inputs		= {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x00200000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x01,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_401] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 401",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x00008000,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 403",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x00008000,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE1,
+		},{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = LINE2,
+			.tv   = 1,
+		}},
+	},
+	[SAA7134_BOARD_BEHOLD_403FM] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 403 FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FQ1216ME,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x00008000,
+		.inputs         = {{
+			.name = name_svideo,
+			.vmux = 8,
+			.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.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,
+		.gpiomask       = 0x00008000,
+		.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> */
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.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,
+		.gpiomask       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.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       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.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       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.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,
+		.gpiomask       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 505 FM",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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_505RDS_MK5] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 505 RDS",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 507 FM / 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,
+		.gpiomask       = 0x00008000,
+		.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_507RDS_MK5] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 507 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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_507RDS_MK3] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 507 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/* Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV Columbus TV/FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ALPS_TSBE5_PAL,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr     = 0xc2 >> 1,
+		.radio_addr     = 0xc0 >> 1,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x000A8004,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x000A8004,
+		}, {
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE1,
+			.gpio = 0x000A8000,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x000A8000,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x000A8000,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_607FM_MK3] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 607 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_609FM_MK3] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 609 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_607FM_MK5] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 607 FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.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_609FM_MK5] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 609 FM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.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_607RDS_MK3] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 607 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.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_609RDS_MK3] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 609 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.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_607RDS_MK5] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 607 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.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_609RDS_MK5] = {
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		.name           = "Beholder BeholdTV 609 RDS",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.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> */
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		/* Alexey Osipov <lion-simba@pridelands.ru> */
+		.name           = "Beholder BeholdTV M6",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.empress_addr 	= 0x20,
+		.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,
+		.video_out = CCIR656,
+		.vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+					SET_CLOCK_NOT_DELAYED |
+					SET_CLOCK_INVERTED |
+					SET_VSYNC_OFF),
+	},
+	[SAA7134_BOARD_BEHOLD_M63] = {
+		/* Igor Kuznetsov <igk@igk.ru> */
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV M63",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.empress_addr 	= 0x20,
+		.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,
+		.video_out = CCIR656,
+		.vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+					SET_CLOCK_NOT_DELAYED |
+					SET_CLOCK_INVERTED |
+					SET_VSYNC_OFF),
+	},
+	[SAA7134_BOARD_BEHOLD_M6_EXTRA] = {
+		/* Igor Kuznetsov <igk@igk.ru> */
+		/* Andrey Melnikoff <temnota@kmv.ru> */
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		/* Alexey Osipov <lion-simba@pridelands.ru> */
+		.name           = "Beholder BeholdTV M6 Extra",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216MK5,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.empress_addr 	= 0x20,
+		.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,
+		.video_out = CCIR656,
+		.vid_port_opts  = (SET_T_CODE_POLARITY_NON_INVERTED |
+					SET_CLOCK_NOT_DELAYED |
+					SET_CLOCK_INVERTED |
+					SET_VSYNC_OFF),
+	},
+	[SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = {
+		.name           = "Twinhan Hybrid DTV-DVB 3056 PCI",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,		/* untested */
+			.amux   = LINE1,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_GENIUS_TVGO_A11MCE] = {
+		/* Adrian Pardini <pardo.bsso@gmail.com> */
+		.name		= "Genius TVGO AM11MCE",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_TNF_5335MF,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0xf000,
+		.inputs         = {{
+			.name = name_tv_mono,
+			.vmux = 1,
+			.amux = LINE2,
+			.gpio = 0x0000,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x2000,
+			.tv = 1
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x2000,
+	} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x1000,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = LINE2,
+			.gpio = 0x6000,
+		},
+	},
+	[SAA7134_BOARD_PHILIPS_SNAKE] = {
+		.name           = "NXP Snake DVB-S reference design",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		} },
+	},
+	[SAA7134_BOARD_CREATIX_CTX953] = {
+		.name         = "Medion/Creatix CTX953 Hybrid",
+		.audio_clock  = 0x00187de7,
+		.tuner_type   = TUNER_PHILIPS_TDA8290,
+		.radio_type   = UNSET,
+		.tuner_addr   = ADDR_UNSET,
+		.radio_addr   = ADDR_UNSET,
+		.tuner_config = 0,
+		.mpeg         = SAA7134_MPEG_DVB,
+		.inputs       = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+	},
+	[SAA7134_BOARD_MSI_TVANYWHERE_AD11] = {
+		.name           = "MSI TV@nywhere A/D v1.1",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 2,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_CARDBUS_506] = {
+		.name           = "AVerMedia Cardbus TV/Radio (E506R)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		 .mpeg           = SAA7134_MPEG_DVB,
+		 .inputs         = {{
+			 .name = name_tv,
+			 .vmux = 1,
+			 .amux = TV,
+			 .tv   = 1,
+		 }, {
+			 .name = name_comp1,
+			 .vmux = 3,
+			 .amux = LINE1,
+		 }, {
+			 .name = name_svideo,
+			 .vmux = 8,
+			 .amux = LINE2,
+		 } },
+		 .radio = {
+			 .name = name_radio,
+			 .amux = TV,
+		 },
+	},
+	[SAA7134_BOARD_AVERMEDIA_A16D] = {
+		.name           = "AVerMedia Hybrid TV/Radio (A16D)",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		}, {
+			.name = name_comp,
+			.vmux = 0,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_M115] = {
+		.name           = "Avermedia M115",
+		.audio_clock    = 0x187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+	},
+	[SAA7134_BOARD_VIDEOMATE_T750] = {
+		/* John Newbigin <jn@it.swin.edu.au> */
+		.name           = "Compro VideoMate T750",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE2,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		}
+	},
+	[SAA7134_BOARD_AVERMEDIA_A700_PRO] = {
+		/* Matthias Schwarzott <zzam@gentoo.org> */
+		.name           = "Avermedia DVB-S Pro A700",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = { {
+			.name = name_comp,
+			.vmux = 1,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+		} },
+	},
+	[SAA7134_BOARD_AVERMEDIA_A700_HYBRID] = {
+		/* Matthias Schwarzott <zzam@gentoo.org> */
+		.name           = "Avermedia DVB-S Hybrid+FM A700",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = { {
+			.name   = name_tv,
+			.vmux   = 4,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name = name_comp,
+			.vmux = 1,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_H6] = {
+		/* Igor Kuznetsov <igk@igk.ru> */
+		.name           = "Beholder BeholdTV H6",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FMD1216MEX_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.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_ASUSTeK_TIGER_3IN1] = {
+		.name           = "Asus Tiger 3in1",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 2,
+		.gpiomask       = 1 << 21,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp,
+			.vmux = 0,
+			.amux = LINE2,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_ASUSTeK_PS3_100] = {
+		.name           = "Asus My Cinema PS3-100",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 2,
+		.gpiomask       = 1 << 21,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp,
+			.vmux = 0,
+			.amux = LINE2,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_REAL_ANGEL_220] = {
+		.name           = "Zogis Real Angel 220",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_TNF_5335MF,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0x801a8087,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = LINE2,
+			.tv     = 1,
+			.gpio   = 0x624000,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE1,
+			.gpio   = 0x624000,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 1,
+			.amux   = LINE1,
+			.gpio   = 0x624000,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+			.gpio   = 0x624001,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = {
+		.name           = "ADS Tech Instant HDTV",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TUV1236D,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp,
+			.vmux = 4,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+	},
+	[SAA7134_BOARD_ASUSTeK_TIGER] = {
+		.name           = "Asus Tiger Rev:1.00",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tuner_config   = 0,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x0200000,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE2,
+		}, {
+			.name   = name_comp2,
+			.vmux   = 0,
+			.amux   = LINE2,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = {
+		.name           = "Kworld Plus TV Analog Lite PCI",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_YMEC_TVF_5533MF,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = 0x60,
+		.gpiomask       = 0x80000700,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = LINE2,
+			.tv     = 1,
+			.gpio   = 0x100,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.vmux   = 1,
+			.amux   = LINE1,
+			.gpio   = 0x100,
+		},
+		.mute = {
+			.name = name_mute,
+			.vmux = 8,
+			.amux = 2,
+		},
+	},
+	[SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = {
+		.name           = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_type     = UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x8e054000,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_PARALLEL,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+#if 0	/* FIXME */
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+			.gpio   = 0x200,
+#endif
+		} },
+#if 0
+		.radio = {
+			.name   = name_radio,
+			.vmux   = 1,
+			.amux   = LINE1,
+			.gpio   = 0x100,
+		},
+#endif
+		.mute = {
+			.name = name_mute,
+			.vmux = 0,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS] = {
+		.name           = "Avermedia AVerTV GO 007 FM Plus",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask       = 0x00300003,
+		/* .gpiomask       = 0x8c240003, */
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x01,
+		}, {
+			.name = name_svideo,
+			.vmux = 6,
+			.amux = LINE1,
+			.gpio = 0x02,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+			.gpio = 0x00300001,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+			.gpio = 0x01,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_507UA] = {
+		/* Andy Shevchenko <andy@smile.org.ua> */
+		.name           = "Avermedia AVerTV Studio 507UA",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3, /* Should be MK5 */
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x03,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x00,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+			.gpio = 0x00,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x01,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = LINE1,
+			.gpio = 0x00,
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_S350] = {
+		/* Jan D. Louw <jd.louw@mweb.co.za */
+		.name		= "Compro VideoMate S350/S300",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg		= SAA7134_MPEG_DVB,
+		.inputs = { {
+			.name	= name_comp1,
+			.vmux	= 0,
+			.amux	= LINE1,
+		}, {
+			.name	= name_svideo,
+			.vmux	= 8, /* Not tested */
+			.amux	= LINE1
+		} },
+	},
+	[SAA7134_BOARD_BEHOLD_X7] = {
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV X7",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_XC5000,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 2,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 9,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_ZOLID_HYBRID_PCI] = {
+		.name           = "Zolid Hybrid TV Tuner PCI",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tuner_config   = 0,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_PARALLEL,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		} },
+		.radio = {	/* untested */
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_ASUS_EUROPA_HYBRID] = {
+		.name           = "Asus Europa Hybrid OEM",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TD1316,
+		.radio_type     = UNSET,
+		.tuner_addr	= 0x61,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 4,
+			.amux   = LINE2,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		} },
+	},
+	[SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = {
+		.name           = "Leadtek Winfast DTV1000S",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = { {
+			.name = name_comp1,
+			.vmux = 3,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+		} },
+	},
+	[SAA7134_BOARD_BEHOLD_505RDS_MK3] = {
+		/*       Beholder Intl. Ltd. 2008      */
+		/*Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 505 RDS",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.rds_addr 	= 0x10,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x00008000,
+		.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_HAWELL_HW_404M7] = {
+		/* Hawell HW-404M7 & Hawell HW-808M7  */
+		/* Bogoslovskiy Viktor <bogovic@bk.ru> */
+		.name         = "Hawell HW-404M7",
+		.audio_clock   = 0x00200000,
+		.tuner_type    = UNSET,
+		.radio_type    = UNSET,
+		.tuner_addr   = ADDR_UNSET,
+		.radio_addr   = ADDR_UNSET,
+		.gpiomask      = 0x389c00,
+		.inputs       = {{
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE1,
+			.gpio = 0x01fc00,
+		} },
+	},
+	[SAA7134_BOARD_BEHOLD_H7] = {
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV H7",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_XC5000,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_PARALLEL,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 2,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 9,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_A7] = {
+		/* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV A7",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_XC5000,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 2,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 9,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = {
+		.name           = "TechoTrend TT-budget T-3000",
+		.tuner_type     = TUNER_PHILIPS_TD1316,
+		.audio_clock    = 0x00187de7,
+		.radio_type     = UNSET,
+		.tuner_addr     = 0x63,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE2,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		} },
+	},
+	[SAA7134_BOARD_VIDEOMATE_M1F] = {
+		/* Pavel Osnova <pvosnova@gmail.com> */
+		.name           = "Compro VideoMate Vista M1F",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = TUNER_TEA5767,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = 0x60,
+		.inputs         = { {
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 3,
+			.amux = LINE2,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE1,
+		},
+		.mute = {
+			.name = name_mute,
+			.amux = TV,
+		},
+	},
+	[SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2] = {
+		/* Timothy Lee <timothy.lee@siriushk.com> */
+		.name		= "MagicPro ProHDTV Pro2 DMB-TH/Hybrid",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_PHILIPS_TDA8290,
+		.radio_type	= UNSET,
+		.tuner_config	= 3,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.gpiomask	= 0x02050000,
+		.mpeg		= SAA7134_MPEG_DVB,
+		.ts_type	= SAA7134_MPEG_TS_PARALLEL,
+		.inputs		= { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+			.gpio   = 0x00050000,
+		}, {
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE1,
+			.gpio   = 0x00050000,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+			.gpio   = 0x00050000,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio   = 0x00050000,
+		},
+		.mute = {
+			.name   = name_mute,
+			.vmux   = 0,
+			.amux   = TV,
+			.gpio   = 0x00050000,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_501] = {
+		/*       Beholder Intl. Ltd. 2010       */
+		/* Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 501",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0x00008000,
+		.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,
+		},
+	},
+	[SAA7134_BOARD_BEHOLD_503FM] = {
+		/*       Beholder Intl. Ltd. 2010       */
+		/* Dmitry Belimov <d.belimov@gmail.com> */
+		.name           = "Beholder BeholdTV 503 FM",
+		.audio_clock    = 0x00200000,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.gpiomask       = 0x00008000,
+		.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,
+		},
+	},
+	[SAA7134_BOARD_SENSORAY811_911] = {
+		.name		= "Sensoray 811/911",
+		.audio_clock	= 0x00200000,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.inputs		= {{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		}, {
+			.name   = name_comp3,
+			.vmux   = 2,
+			.amux   = LINE1,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		} },
+	},
+	[SAA7134_BOARD_KWORLD_PC150U] = {
+		.name           = "Kworld PC150-U",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 1 << 21,
+		.ts_type	= SAA7134_MPEG_TS_PARALLEL,
+		.inputs = { {
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		}, {
+			.name   = name_comp,
+			.vmux   = 3,
+			.amux   = LINE1,
+		}, {
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		} },
+		.radio = {
+			.name   = name_radio,
+			.amux   = TV,
+			.gpio	= 0x0000000,
+		},
+	},
+
+};
+
+const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI ids + subsystem IDs                                            */
+
+struct pci_device_id saa7134_pci_tbl[] = {
+	{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2001,
+		.driver_data  = SAA7134_BOARD_PROTEUS_PRO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2001,
+		.driver_data  = SAA7134_BOARD_PROTEUS_PRO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x6752,
+		.driver_data  = SAA7134_BOARD_EMPRESS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1131,
+		.subdevice    = 0x4e85,
+		.driver_data  = SAA7134_BOARD_MONSTERTV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1142,
+		.driver_data  = SAA7134_BOARD_CINERGY400,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1143,
+		.driver_data  = SAA7134_BOARD_CINERGY600,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1158,
+		.driver_data  = SAA7134_BOARD_CINERGY600_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1162,
+		.driver_data  = SAA7134_BOARD_CINERGY400_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5169,
+		.subdevice    = 0x0138,
+		.driver_data  = SAA7134_BOARD_FLYVIDEO3000_NTSC,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0138,
+		.driver_data  = SAA7134_BOARD_FLYVIDEO3000,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x4e42,				/* "Typhoon PCI Capture TV Card" Art.No. 50673 */
+		.subdevice    = 0x0138,
+		.driver_data  = SAA7134_BOARD_FLYVIDEO3000,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0138,
+		.driver_data  = SAA7134_BOARD_FLYVIDEO2000,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x4e42,		/* Typhoon */
+		.subdevice    = 0x0138,		/* LifeView FlyTV Prime30 OEM */
+		.driver_data  = SAA7134_BOARD_FLYVIDEO2000,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0212, /* minipci, LR212 */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x14c0,
+		.subdevice    = 0x1212, /* minipci, LR1212 */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_MINI2,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x4e42,
+		.subdevice    = 0x0212, /* OEM minipci, LR212 */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,	/* Animation Technologies (LifeView) */
+		.subdevice    = 0x0214, /* Standard PCI, LR214 Rev E and earlier (SAA7135) */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_FM,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,	/* Animation Technologies (LifeView) */
+		.subdevice    = 0x5214, /* Standard PCI, LR214 Rev F onwards (SAA7131) */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_FM,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1489, /* KYE */
+		.subdevice    = 0x0214, /* Genius VideoWonder ProTV */
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x0003,
+		.driver_data  = SAA7134_BOARD_MD7134,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */
+		.subdevice    = 0x5000, /* only analog TV and DVB-T for now */
+		.driver_data  = SAA7134_BOARD_MD7134,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1048,
+		.subdevice    = 0x226b,
+		.driver_data  = SAA7134_BOARD_ELSA,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1048,
+		.subdevice    = 0x226a,
+		.driver_data  = SAA7134_BOARD_ELSA_500TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1048,
+		.subdevice    = 0x226c,
+		.driver_data  = SAA7134_BOARD_ELSA_700TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_ASUSTEK,
+		.subdevice    = 0x4842,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TVFM7134,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_VENDOR_ID_ASUSTEK,
+		.subdevice    = 0x4845,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TVFM7135,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_ASUSTEK,
+		.subdevice    = 0x4830,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TVFM7134,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_VENDOR_ID_ASUSTEK,
+		.subdevice    = 0x4843,
+		.driver_data  = SAA7134_BOARD_ASUSTEK_TVFM7133,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_ASUSTEK,
+		.subdevice    = 0x4840,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TVFM7134,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0xfe01,
+		.driver_data  = SAA7134_BOARD_TVSTATION_RDS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1894,
+		.subdevice    = 0xfe01,
+		.driver_data  = SAA7134_BOARD_TVSTATION_RDS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1894,
+		.subdevice    = 0xa006,
+		.driver_data  = SAA7134_BOARD_TVSTATION_DVR,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1131,
+		.subdevice    = 0x7133,
+		.driver_data  = SAA7134_BOARD_VA1000POWER,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2001,
+		.driver_data  = SAA7134_BOARD_10MOONSTVMASTER,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc100,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc100,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_MATROX,
+		.subdevice    = 0x48d0,
+		.driver_data  = SAA7134_BOARD_CRONOS_PLUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa70b,
+		.driver_data  = SAA7134_BOARD_MD2819,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa7a1,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A700_PRO,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa7a2,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A700_HYBRID,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x2115,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_305,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa115,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_505,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x2108,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_305,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x10ff,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER,
+	},{
+		/* AVerMedia CardBus */
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xd6ee,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_CARDBUS,
+	},{
+		/* AVerMedia CardBus */
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xb7e9,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_CARDBUS_501,
+	}, {
+		/* TransGear 3000TV */
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x050c,
+		.driver_data  = SAA7134_BOARD_TG3000TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x11bd,
+		.subdevice    = 0x002b,
+		.driver_data  = SAA7134_BOARD_PINNACLE_PCTV_STEREO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x11bd,
+		.subdevice    = 0x002d,
+		.driver_data  = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1019,
+		.subdevice    = 0x4cb4,
+		.driver_data  = SAA7134_BOARD_ECS_TVP3XP,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1019,
+		.subdevice    = 0x4cb5,
+		.driver_data  = SAA7134_BOARD_ECS_TVP3XP_4CB5,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1019,
+		.subdevice    = 0x4cb6,
+		.driver_data  = SAA7134_BOARD_ECS_TVP3XP_4CB6,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x12ab,
+		.subdevice    = 0x0800,
+		.driver_data  = SAA7134_BOARD_UPMOST_PURPLE_TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1152,
+		.driver_data  = SAA7134_BOARD_CINERGY200,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc100,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_TV_PVR,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x9715,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_307,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa70a,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_307,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc200,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1540,
+		.subdevice    = 0x9524,
+		.driver_data  = SAA7134_BOARD_PROVIDEO_PV952,
+
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0502,                /* Cardbus version */
+		.driver_data  = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0306,                /* PCI version */
+		.driver_data  = SAA7134_BOARD_FLYDVBTDUO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf31f,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_GO_007_FM,
+
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf11d,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M135A,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x4155,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M733A,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x4255,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M733A,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2004,
+		.driver_data  = SAA7134_BOARD_PHILIPS_TOUGH,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1421,
+		.subdevice    = 0x0350,		/* PCI version */
+		.driver_data  = SAA7134_BOARD_ADS_INSTANT_TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1421,
+		.subdevice    = 0x0351,		/* PCI version, new revision */
+		.driver_data  = SAA7134_BOARD_ADS_INSTANT_TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1421,
+		.subdevice    = 0x0370,		/* cardbus version */
+		.driver_data  = SAA7134_BOARD_ADS_INSTANT_TV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1421,
+		.subdevice    = 0x1370,        /* cardbus version */
+		.driver_data  = SAA7134_BOARD_ADS_INSTANT_TV,
+
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x4e42,		/* Typhoon */
+		.subdevice    = 0x0502,		/* LifeView LR502 OEM */
+		.driver_data  = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x0210,		/* mini pci NTSC version */
+		.driver_data  = SAA7134_BOARD_FLYTV_DIGIMATRIX,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x0210,		/* mini pci PAL/SECAM version */
+		.driver_data  = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV,
+
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0000, /* It shouldn't break anything, since subdevice id seems unique */
+		.subdevice    = 0x4091,
+		.driver_data  = SAA7134_BOARD_BEHOLD_409FM,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5456, /* GoTView */
+		.subdevice    = 0x7135,
+		.driver_data  = SAA7134_BOARD_GOTVIEW_7135,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2004,
+		.driver_data  = SAA7134_BOARD_PHILIPS_EUROPA,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc900,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_DVBT_300,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc901,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_DVBT_200,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1435,
+		.subdevice    = 0x7350,
+		.driver_data  = SAA7134_BOARD_RTD_VFG7350,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1435,
+		.subdevice    = 0x7330,
+		.driver_data  = SAA7134_BOARD_RTD_VFG7330,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x1044,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1131,
+		.subdevice    = 0x4ee9,
+		.driver_data  = SAA7134_BOARD_MONSTERTV_MOBILE,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x11bd,
+		.subdevice    = 0x002e,
+		.driver_data  = SAA7134_BOARD_PINNACLE_PCTV_110i,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4862,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2018,
+		.driver_data  = SAA7134_BOARD_PHILIPS_TIGER,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1462,
+		.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,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1160,
+		.driver_data  = SAA7134_BOARD_CINERGY250PCI,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,	/* SAA 7131E */
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0319,
+		.driver_data  = SAA7134_BOARD_FLYDVB_TRIO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x2c05,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_777,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0301,
+		.driver_data  = SAA7134_BOARD_FLYDVBT_LR301,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0331,
+		.subdevice    = 0x1421,
+		.driver_data  = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x17de,
+		.subdevice    = 0x7201,
+		.driver_data  = SAA7134_BOARD_TEVION_DVBT_220RF,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x17de,
+		.subdevice    = 0x7250,
+		.driver_data  = SAA7134_BOARD_KWORLD_DVBT_210,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+		.subvendor    = 0x17de,
+		.subdevice    = 0x7350,
+		.driver_data  = SAA7134_BOARD_KWORLD_ATSC110,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+		.subvendor    = 0x17de,
+		.subdevice    = 0x7352,
+		.driver_data  = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+		.subvendor    = 0x17de,
+		.subdevice    = 0xa134,
+		.driver_data  = SAA7134_BOARD_KWORLD_PC150U,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x7360,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A169_B,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x6360,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A169_B1,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x0005,
+		.driver_data  = SAA7134_BOARD_MD7134_BRIDGE_2,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x0300,
+		.driver_data  = SAA7134_BOARD_FLYDVBS_LR300,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x4e42,
+		.subdevice    = 0x0300,/* LR300 */
+		.driver_data  = SAA7134_BOARD_FLYDVBS_LR300,
+	},{
+		.vendor = PCI_VENDOR_ID_PHILIPS,
+		.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor = 0x1489,
+		.subdevice = 0x0301,
+		.driver_data = SAA7134_BOARD_FLYDVBT_LR301,
+	},{
+		.vendor = PCI_VENDOR_ID_PHILIPS,
+		.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor = 0x5168, /* Animation Technologies (LifeView) */
+		.subdevice = 0x0304,
+		.driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x3306,
+		.driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x3502,  /* whats the difference to 0x3306 ?*/
+		.driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5168,
+		.subdevice    = 0x3307, /* FlyDVB-T Hybrid Mini PCI */
+		.driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x0007,
+		.driver_data  = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x0008,
+		.driver_data  = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x000d, /* triple CTX948_V1.1.1 */
+		.driver_data  = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x2c05,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_777,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1489,
+		.subdevice    = 0x0502,                /* Cardbus version */
+		.driver_data  = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x0919, /* Philips Proteus PRO 2309 */
+		.subdevice    = 0x2003,
+		.driver_data  = SAA7134_BOARD_PROTEUS_2309,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461,
+		.subdevice    = 0x2c00,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A16AR,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4860,
+		.driver_data  = SAA7134_BOARD_ASUS_EUROPA2_HYBRID,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x11bd,
+		.subdevice    = 0x002f,
+		.driver_data  = SAA7134_BOARD_PINNACLE_PCTV_310i,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x9715,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_507,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa11b,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4876,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA,
+	},{
+		.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    = 0x0070,
+		.subdevice    = 0x6706,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1150,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0070,
+		.subdevice    = 0x6707,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0070,
+		.subdevice    = 0x6708,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1150,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0070,
+		.subdevice    = 0x6709,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0070,
+		.subdevice    = 0x670a,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1172,
+		.driver_data  = SAA7134_BOARD_CINERGY_HT_PCMCIA,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2342,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1131,
+		.subdevice    = 0x2341,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x3016,
+		.subdevice    = 0x2344,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1131,
+		.subdevice    = 0x230f,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV_FM,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x1a7f,
+		.subdevice    = 0x2008,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV_FM53,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1a7f,
+		.subdevice    = 0x2108,
+		.driver_data  = SAA7134_BOARD_ENCORE_ENLTV_FM3,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1175,
+		.driver_data  = SAA7134_BOARD_CINERGY_HT_PCI,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf31e,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M102,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x4E42,         /* MSI */
+		.subdevice    = 0x0306,         /* TV@nywhere DUO */
+		.driver_data  = SAA7134_BOARD_FLYDVBTDUO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4871,
+		.driver_data  = SAA7134_BOARD_ASUS_P7131_4871,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4857,		/* REV:1.00 */
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TIGER,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x0919, /* SinoVideo PCI 2309 Proteus (7134) */
+		.subdevice    = 0x2003, /* OEM cardbus */
+		.driver_data  = SAA7134_BOARD_SABRENT_TV_PCB05,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2304,
+		.driver_data  = SAA7134_BOARD_10MOONSTVMASTER3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf01d, /* AVerTV DVB-T Super 007 */
+		.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    = 0x505B,
+		.driver_data  = SAA7134_BOARD_BEHOLD_505RDS_MK5,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x0000,
+		.subdevice    = 0x5051,
+		.driver_data  = SAA7134_BOARD_BEHOLD_505RDS_MK3,
+	},{
+		.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_507RDS_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0000,
+		.subdevice    = 0x507B,
+		.driver_data  = SAA7134_BOARD_BEHOLD_507RDS_MK5,
+	},{
+		.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_607FM_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6071,
+		.driver_data  = SAA7134_BOARD_BEHOLD_607FM_MK5,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6072,
+		.driver_data  = SAA7134_BOARD_BEHOLD_607RDS_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6073,
+		.driver_data  = SAA7134_BOARD_BEHOLD_607RDS_MK5,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6090,
+		.driver_data  = SAA7134_BOARD_BEHOLD_609FM_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6091,
+		.driver_data  = SAA7134_BOARD_BEHOLD_609FM_MK5,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6092,
+		.driver_data  = SAA7134_BOARD_BEHOLD_609RDS_MK3,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6093,
+		.driver_data  = SAA7134_BOARD_BEHOLD_609RDS_MK5,
+	},{
+		.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_EXTRA,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6191,
+		.driver_data  = SAA7134_BOARD_BEHOLD_M63,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x4e42,
+		.subdevice    = 0x3502,
+		.driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1822, /*Twinhan Technology Co. Ltd*/
+		.subdevice    = 0x0022,
+		.driver_data  = SAA7134_BOARD_TWINHAN_DTV_DVB_3056,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x16be,
+		.subdevice    = 0x0010, /* Medion version CTX953_V.1.4.3 */
+		.driver_data  = SAA7134_BOARD_CREATIX_CTX953,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1462, /* MSI */
+		.subdevice    = 0x8625, /* TV@nywhere A/D v1.1 */
+		.driver_data  = SAA7134_BOARD_MSI_TVANYWHERE_AD11,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf436,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_CARDBUS_506,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf936,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_A16D,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xa836,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M115,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc900,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_T750,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+		.subvendor    = 0x1421,
+		.subdevice    = 0x0380,
+		.driver_data  = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5169,
+		.subdevice    = 0x1502,
+		.driver_data  = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6290,
+		.driver_data  = SAA7134_BOARD_BEHOLD_H6,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf636,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M103,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf736,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_M103,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4878, /* REV:1.02G */
+		.driver_data  = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x48cd,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_PS3_100,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x17de,
+		.subdevice    = 0x7128,
+		.driver_data  = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x17de,
+		.subdevice    = 0xb136,
+		.driver_data  = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0xf31d,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc900,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_S350,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace, /* Beholder Intl. Ltd. */
+		.subdevice    = 0x7595,
+		.driver_data  = SAA7134_BOARD_BEHOLD_X7,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x19d1, /* RoverMedia */
+		.subdevice    = 0x0138, /* LifeView FlyTV Prime30 OEM */
+		.driver_data  = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0x2004,
+		.driver_data  = SAA7134_BOARD_ZOLID_HYBRID_PCI,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4847,
+		.driver_data  = SAA7134_BOARD_ASUS_EUROPA_HYBRID,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x107d,
+		.subdevice    = 0x6655,
+		.driver_data  = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x13c2,
+		.subdevice    = 0x2804,
+		.driver_data  = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace, /* Beholder Intl. Ltd. */
+		.subdevice    = 0x7190,
+		.driver_data  = SAA7134_BOARD_BEHOLD_H7,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace, /* Beholder Intl. Ltd. */
+		.subdevice    = 0x7090,
+		.driver_data  = SAA7134_BOARD_BEHOLD_A7,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7135,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc900,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_M1F,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x5030,
+		.driver_data  = SAA7134_BOARD_BEHOLD_503FM,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x5010,
+		.driver_data  = SAA7134_BOARD_BEHOLD_501,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = 0x17de,
+		.subdevice    = 0xd136,
+		.driver_data  = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x6000,
+		.subdevice    = 0x0811,
+		.driver_data  = SAA7134_BOARD_SENSORAY811_911,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x6000,
+		.subdevice    = 0x0911,
+		.driver_data  = SAA7134_BOARD_SENSORAY811_911,
+	}, {
+		/* --- boards without eeprom + subsystem ID --- */
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0,
+		.driver_data  = SAA7134_BOARD_NOAUTO,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_VENDOR_ID_PHILIPS,
+		.subdevice    = 0,
+		.driver_data  = SAA7134_BOARD_NOAUTO,
+	},{
+		/* --- default catch --- */
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+		.driver_data  = SAA7134_BOARD_UNKNOWN,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+		.driver_data  = SAA7134_BOARD_UNKNOWN,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+		.driver_data  = SAA7134_BOARD_UNKNOWN,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7135,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+		.driver_data  = SAA7134_BOARD_UNKNOWN,
+	},{
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl);
+
+/* ----------------------------------------------------------- */
+/* flyvideo tweaks                                             */
+
+
+static void board_flyvideo(struct saa7134_dev *dev)
+{
+	printk("%s: there are different flyvideo cards with different tuners\n"
+	       "%s: out there, you might have to use the tuner=<nr> insmod\n"
+	       "%s: option to override the default value.\n",
+	       dev->name, dev->name, dev->name);
+}
+
+static int saa7134_xc2028_callback(struct saa7134_dev *dev,
+				   int command, int arg)
+{
+	switch (command) {
+	case XC2028_TUNER_RESET:
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+		switch (dev->board) {
+		case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+		case SAA7134_BOARD_AVERMEDIA_M103:
+			saa7134_set_gpio(dev, 23, 0);
+			msleep(10);
+			saa7134_set_gpio(dev, 23, 1);
+		break;
+		case SAA7134_BOARD_AVERMEDIA_A16D:
+			saa7134_set_gpio(dev, 21, 0);
+			msleep(10);
+			saa7134_set_gpio(dev, 21, 1);
+		break;
+		case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+			saa7134_set_gpio(dev, 18, 0);
+			msleep(10);
+			saa7134_set_gpio(dev, 18, 1);
+		break;
+		case SAA7134_BOARD_VIDEOMATE_T750:
+			saa7134_set_gpio(dev, 20, 0);
+			msleep(10);
+			saa7134_set_gpio(dev, 20, 1);
+		break;
+		}
+	return 0;
+	}
+	return -EINVAL;
+}
+
+static int saa7134_xc5000_callback(struct saa7134_dev *dev,
+				   int command, int arg)
+{
+	switch (dev->board) {
+	case SAA7134_BOARD_BEHOLD_X7:
+	case SAA7134_BOARD_BEHOLD_H7:
+	case SAA7134_BOARD_BEHOLD_A7:
+		if (command == XC5000_TUNER_RESET) {
+		/* Down and UP pheripherial RESET pin for reset all chips */
+			saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+			msleep(10);
+			saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+			msleep(10);
+		}
+		break;
+	default:
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000);
+		saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02);
+		saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81);
+		saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7);
+		saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03);
+		saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2,
+			   0x0001e000, 0x0001e000);
+		break;
+	}
+	return 0;
+}
+
+static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev,
+					 int command, int arg)
+{
+	u8 sync_control;
+
+	switch (command) {
+	case 0: /* switch LNA gain through GPIO 22*/
+		saa7134_set_gpio(dev, 22, arg) ;
+		break;
+	case 1: /* vsync output at GPIO22. 50 / 60Hz */
+		saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80);
+		saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03);
+		if (arg == 1)
+			sync_control = 11;
+		else
+			sync_control = 17;
+		saa_writeb(SAA7134_VGATE_START, sync_control);
+		saa_writeb(SAA7134_VGATE_STOP, sync_control + 1);
+		saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev,
+						      enum tda18271_mode mode)
+{
+	/* toggle AGC switch through GPIO 26 */
+	switch (mode) {
+	case TDA18271_ANALOG:
+		saa7134_set_gpio(dev, 26, 0);
+		break;
+	case TDA18271_DIGITAL:
+		saa7134_set_gpio(dev, 26, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev,
+						  enum tda18271_mode mode)
+{
+	/* toggle AGC switch through GPIO 27 */
+	switch (mode) {
+	case TDA18271_ANALOG:
+		saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000);
+		saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000);
+		msleep(20);
+		break;
+	case TDA18271_DIGITAL:
+		saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000);
+		saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000);
+		msleep(20);
+		saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000);
+		saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000);
+		msleep(30);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev,
+					    enum tda18271_mode mode)
+{
+	switch (mode) {
+	case TDA18271_ANALOG:
+		saa7134_set_gpio(dev, 18, 0);
+		break;
+	case TDA18271_DIGITAL:
+		saa7134_set_gpio(dev, 18, 1);
+		msleep(30);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
+					  int command, int arg)
+{
+	int ret = 0;
+
+	switch (command) {
+	case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */
+		switch (dev->board) {
+		case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+		case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+		case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+			ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg);
+			break;
+		case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+			ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg);
+			break;
+		case SAA7134_BOARD_KWORLD_PC150U:
+			ret = saa7134_kworld_pc150u_toggle_agc(dev, arg);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int saa7134_tda8290_callback(struct saa7134_dev *dev,
+				    int command, int arg)
+{
+	int ret;
+
+	switch (dev->board) {
+	case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+	case SAA7134_BOARD_AVERMEDIA_M733A:
+	case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+	case SAA7134_BOARD_KWORLD_PC150U:
+	case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+		/* tda8290 + tda18271 */
+		ret = saa7134_tda8290_18271_callback(dev, command, arg);
+		break;
+	default:
+		/* tda8290 + tda827x */
+		ret = saa7134_tda8290_827x_callback(dev, command, arg);
+		break;
+	}
+	return ret;
+}
+
+int saa7134_tuner_callback(void *priv, int component, int command, int arg)
+{
+	struct saa7134_dev *dev = priv;
+
+	if (dev != NULL) {
+		switch (dev->tuner_type) {
+		case TUNER_PHILIPS_TDA8290:
+			return saa7134_tda8290_callback(dev, command, arg);
+		case TUNER_XC2028:
+			return saa7134_xc2028_callback(dev, command, arg);
+		case TUNER_XC5000:
+			return saa7134_xc5000_callback(dev, command, arg);
+		}
+	} else {
+		printk(KERN_ERR "saa7134: Error - device struct undefined.\n");
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+EXPORT_SYMBOL(saa7134_tuner_callback);
+
+/* ----------------------------------------------------------- */
+
+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 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+	case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
+	case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, 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) */
+	case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+	case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, 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. */
+	saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0);
+	dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+	printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value);
+
+	switch (dev->board) {
+	case SAA7134_BOARD_FLYVIDEO2000:
+	case SAA7134_BOARD_FLYVIDEO3000:
+	case SAA7134_BOARD_FLYVIDEO3000_NTSC:
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		board_flyvideo(dev);
+		break;
+	case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
+	case SAA7134_BOARD_FLYTVPLATINUM_FM:
+	case SAA7134_BOARD_CINERGY400:
+	case SAA7134_BOARD_CINERGY600:
+	case SAA7134_BOARD_CINERGY600_MK3:
+	case SAA7134_BOARD_ECS_TVP3XP:
+	case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+	case SAA7134_BOARD_ECS_TVP3XP_4CB6:
+	case SAA7134_BOARD_MD2819:
+	case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+	case SAA7134_BOARD_KWORLD_XPERT:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+	case SAA7134_BOARD_AVERMEDIA_305:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+	case SAA7134_BOARD_AVERMEDIA_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+	case SAA7134_BOARD_AVERMEDIA_777:
+	case SAA7134_BOARD_AVERMEDIA_M135A:
+/*      case SAA7134_BOARD_SABRENT_SBTTVFM:  */ /* not finished yet */
+	case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+	case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
+	case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
+	case SAA7134_BOARD_VIDEOMATE_M1F:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+	case SAA7134_BOARD_MANLI_MTV001:
+	case SAA7134_BOARD_MANLI_MTV002:
+	case SAA7134_BOARD_BEHOLD_409FM:
+	case SAA7134_BOARD_AVACSSMARTTV:
+	case SAA7134_BOARD_GOTVIEW_7135:
+	case SAA7134_BOARD_KWORLD_TERMINATOR:
+	case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
+	case SAA7134_BOARD_FLYDVBT_LR301:
+	case SAA7134_BOARD_ASUSTeK_PS3_100:
+	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+	case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+	case SAA7134_BOARD_FLYDVBTDUO:
+	case SAA7134_BOARD_PROTEUS_2309:
+	case SAA7134_BOARD_AVERMEDIA_A16AR:
+	case SAA7134_BOARD_ENCORE_ENLTV:
+	case SAA7134_BOARD_ENCORE_ENLTV_FM:
+	case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+	case SAA7134_BOARD_ENCORE_ENLTV_FM3:
+	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_505RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_505RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_507_9FM:
+	case SAA7134_BOARD_BEHOLD_507RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_507RDS_MK5:
+	case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+	case SAA7134_BOARD_REAL_ANGEL_220:
+	case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+	case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+	case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
+	case SAA7134_BOARD_FLYDVBS_LR300:
+		saa_writeb(SAA7134_GPIO_GPMODE3, 0x80);
+		saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40);
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
+	case SAA7134_BOARD_MD5044:
+		printk("%s: seems there are two different versions of the MD5044\n"
+		       "%s: (with the same ID) out there.  If sound doesn't work for\n"
+		       "%s: you try the audio_clock_override=0x200000 insmod option.\n",
+		       dev->name,dev->name,dev->name);
+		break;
+	case SAA7134_BOARD_CINERGY400_CARDBUS:
+		/* power-up tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x00040000, 0x00040000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000);
+		break;
+	case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+		/* this turns the remote control chip off to work around a bug in it */
+		saa_writeb(SAA7134_GPIO_GPMODE1, 0x80);
+		saa_writeb(SAA7134_GPIO_GPSTATUS1, 0x80);
+		break;
+	case SAA7134_BOARD_MONSTERTV_MOBILE:
+		/* power-up tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x00040000, 0x00040000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000004);
+		break;
+	case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
+		/* turn the fan on */
+		saa_writeb(SAA7134_GPIO_GPMODE3, 0x08);
+		saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06);
+		break;
+	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS:
+	case SAA7134_BOARD_AVERMEDIA_M115:
+		/* power-down tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0xffffffff, 0);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0);
+		msleep(10);
+		/* power-up tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0xffffffff, 0xffffffff);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff);
+		msleep(10);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+		/* power-down tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x08400000, 0x08400000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0);
+		msleep(10);
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x08400000, 0x08400000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0x08400000);
+		msleep(10);
+		dev->has_remote = SAA7134_REMOTE_I2C;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+		saa7134_set_gpio(dev, 23, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 23, 1);
+		dev->has_remote = SAA7134_REMOTE_I2C;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_M103:
+		saa7134_set_gpio(dev, 23, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 23, 1);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A16D:
+		saa7134_set_gpio(dev, 21, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 21, 1);
+		msleep(1);
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
+	case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+		/* power-down tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x000A8004, 0x000A8004);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0);
+		msleep(10);
+		/* power-up tuner chip */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x000A8004, 0x000A8004);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004);
+		msleep(10);
+		/* remote via GPIO */
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
+	case SAA7134_BOARD_RTD_VFG7350:
+
+		/*
+		 * Make sure Production Test Register at offset 0x1D1 is cleared
+		 * to take chip out of test mode.  Clearing bit 4 (TST_EN_AOUT)
+		 * prevents pin 105 from remaining low; keeping pin 105 low
+		 * continually resets the SAA6752 chip.
+		 */
+
+		saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00);
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		/* GPIO 26 high for digital, low for analog */
+		saa7134_set_gpio(dev, 26, 0);
+		msleep(1);
+
+		saa7134_set_gpio(dev, 22, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 22, 1);
+		break;
+	/* i2c remotes */
+	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
+	case SAA7134_BOARD_UPMOST_PURPLE_TV:
+	case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+	case SAA7134_BOARD_BEHOLD_607FM_MK3:
+	case SAA7134_BOARD_BEHOLD_607FM_MK5:
+	case SAA7134_BOARD_BEHOLD_609FM_MK3:
+	case SAA7134_BOARD_BEHOLD_609FM_MK5:
+	case SAA7134_BOARD_BEHOLD_607RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_607RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_609RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_609RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_M6:
+	case SAA7134_BOARD_BEHOLD_M63:
+	case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+	case SAA7134_BOARD_BEHOLD_H6:
+	case SAA7134_BOARD_BEHOLD_X7:
+	case SAA7134_BOARD_BEHOLD_H7:
+	case SAA7134_BOARD_BEHOLD_A7:
+	case SAA7134_BOARD_KWORLD_PC150U:
+		dev->has_remote = SAA7134_REMOTE_I2C;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A169_B:
+		printk("%s: %s: dual saa713x broadcast decoders\n"
+		       "%s: Sorry, none of the inputs to this chip are supported yet.\n"
+		       "%s: Dual decoder functionality is disabled for now, use the other chip.\n",
+		       dev->name,card(dev).name,dev->name,dev->name);
+		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;
+	case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+	case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+		/* write windows gpio values */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x80040100, 0x80040100);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100);
+		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x0000C000, 0x0000C000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_M733A:
+		saa7134_set_gpio(dev, 1, 1);
+		msleep(10);
+		saa7134_set_gpio(dev, 1, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 1, 1);
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
+	case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+		/* enable LGS-8G75 */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x0e050000, 0x0c050000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000);
+		break;
+	case SAA7134_BOARD_VIDEOMATE_T750:
+		/* enable the analog tuner */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x00008000, 0x00008000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+		break;
+	}
+	return 0;
+}
+
+static void saa7134_tuner_setup(struct saa7134_dev *dev)
+{
+	struct tuner_setup tun_setup;
+	unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
+
+	memset(&tun_setup, 0, sizeof(tun_setup));
+	tun_setup.tuner_callback = saa7134_tuner_callback;
+
+	if (saa7134_boards[dev->board].radio_type != UNSET) {
+		tun_setup.type = saa7134_boards[dev->board].radio_type;
+		tun_setup.addr = saa7134_boards[dev->board].radio_addr;
+
+		tun_setup.mode_mask = T_RADIO;
+
+		saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+		mode_mask &= ~T_RADIO;
+	}
+
+	if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) {
+		tun_setup.type = dev->tuner_type;
+		tun_setup.addr = dev->tuner_addr;
+		tun_setup.config = saa7134_boards[dev->board].tuner_config;
+		tun_setup.tuner_callback = saa7134_tuner_callback;
+
+		tun_setup.mode_mask = mode_mask;
+
+		saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+	}
+
+	if (dev->tda9887_conf) {
+		struct v4l2_priv_tun_config tda9887_cfg;
+
+		tda9887_cfg.tuner = TUNER_TDA9887;
+		tda9887_cfg.priv = &dev->tda9887_conf;
+
+		saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+	}
+
+	if (dev->tuner_type == TUNER_XC2028) {
+		struct v4l2_priv_tun_config  xc2028_cfg;
+		struct xc2028_ctrl           ctl;
+
+		memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+		memset(&ctl, 0, sizeof(ctl));
+
+		ctl.fname   = XC2028_DEFAULT_FIRMWARE;
+		ctl.max_len = 64;
+
+		switch (dev->board) {
+		case SAA7134_BOARD_AVERMEDIA_A16D:
+		case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+		case SAA7134_BOARD_AVERMEDIA_M103:
+		case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+			ctl.demod = XC3028_FE_ZARLINK456;
+			break;
+		default:
+			ctl.demod = XC3028_FE_OREN538;
+			ctl.mts = 1;
+		}
+
+		xc2028_cfg.tuner = TUNER_XC2028;
+		xc2028_cfg.priv  = &ctl;
+
+		saa_call_all(dev, tuner, s_config, &xc2028_cfg);
+	}
+}
+
+/* stuff which needs working i2c */
+int saa7134_board_init2(struct saa7134_dev *dev)
+{
+	unsigned char buf;
+	int board;
+
+	/* Put here the code that enables the chips that are needed
+	   for analog mode and doesn't depend on the tuner attachment.
+	   It is also a good idea to get tuner type from eeprom, etc before
+	   initializing tuner, since we can avoid loading tuner driver
+	   on devices that has TUNER_ABSENT
+	 */
+	switch (dev->board) {
+	case SAA7134_BOARD_BMK_MPEX_NOTUNER:
+	case SAA7134_BOARD_BMK_MPEX_TUNER:
+		/* Checks if the device has a tuner at 0x60 addr
+		   If the device doesn't have a tuner, TUNER_ABSENT
+		   will be used at tuner_type, avoiding loading tuner
+		   without needing it
+		 */
+		dev->i2c_client.addr = 0x60;
+		board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0)
+			? SAA7134_BOARD_BMK_MPEX_NOTUNER
+			: SAA7134_BOARD_BMK_MPEX_TUNER;
+		if (board == dev->board)
+			break;
+		dev->board = board;
+		printk("%s: board type fixup: %s\n", dev->name,
+		saa7134_boards[dev->board].name);
+		dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+
+		break;
+	case SAA7134_BOARD_MD7134:
+	{
+		u8 subaddr;
+		u8 data[3];
+		int ret, tuner_t;
+		struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1},
+					{.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}};
+
+		subaddr= 0x14;
+		tuner_t = 0;
+
+		/* Retrieve device data from eeprom, checking for the
+		   proper tuner_type.
+		 */
+		ret = i2c_transfer(&dev->i2c_adap, msg, 2);
+		if (ret != 2) {
+			printk(KERN_ERR "EEPROM read failure\n");
+		} else if ((data[0] != 0) && (data[0] != 0xff)) {
+			/* old config structure */
+			subaddr = data[0] + 2;
+			msg[1].len = 2;
+			i2c_transfer(&dev->i2c_adap, msg, 2);
+			tuner_t = (data[0] << 8) + data[1];
+			switch (tuner_t){
+			case 0x0103:
+				dev->tuner_type = TUNER_PHILIPS_PAL;
+				break;
+			case 0x010C:
+				dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+				break;
+			default:
+				printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t);
+			}
+		} else if ((data[1] != 0) && (data[1] != 0xff)) {
+			/* new config structure */
+			subaddr = data[1] + 1;
+			msg[1].len = 1;
+			i2c_transfer(&dev->i2c_adap, msg, 2);
+			subaddr = data[0] + 1;
+			msg[1].len = 2;
+			i2c_transfer(&dev->i2c_adap, msg, 2);
+			tuner_t = (data[1] << 8) + data[0];
+			switch (tuner_t) {
+			case 0x0005:
+				dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+				break;
+			case 0x001d:
+				dev->tuner_type = TUNER_PHILIPS_FMD1216ME_MK3;
+					printk(KERN_INFO "%s Board has DVB-T\n", dev->name);
+				break;
+			default:
+				printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t);
+			}
+		} else {
+			printk(KERN_ERR "%s unexpected config structure\n", dev->name);
+		}
+
+		printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type);
+		break;
+	}
+	case SAA7134_BOARD_PHILIPS_EUROPA:
+		if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) {
+			/* Reconfigure board as Snake reference design */
+			dev->board = SAA7134_BOARD_PHILIPS_SNAKE;
+			dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+			printk(KERN_INFO "%s: Reconfigured board as %s\n",
+				dev->name, saa7134_boards[dev->board].name);
+			break;
+		}
+		/* break intentionally omitted */
+	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+	case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+	case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
+	case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
+	{
+
+		/* The Philips EUROPA based hybrid boards have the tuner
+		   connected through the channel decoder. We have to make it
+		   transparent to find it
+		 */
+		u8 data[] = { 0x07, 0x02};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+		break;
+	}
+	case SAA7134_BOARD_PHILIPS_TIGER:
+	case SAA7134_BOARD_PHILIPS_TIGER_S:
+	{
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		if (dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+			dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
+			printk(KERN_INFO "%s: Reconfigured board as %s\n",
+				dev->name, saa7134_boards[dev->board].name);
+		}
+		if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+			dev->tuner_type = TUNER_PHILIPS_TDA8290;
+
+			data[2] = 0x68;
+			i2c_transfer(&dev->i2c_adap, &msg, 1);
+			break;
+		}
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_ASUSTeK_TVFM7135:
+	/* The card below is detected as card=53, but is different */
+	       if (dev->autodetected && (dev->eedata[0x27] == 0x03)) {
+		       dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG;
+		       printk(KERN_INFO "%s: P7131 analog only, using "
+						       "entry of %s\n",
+		       dev->name, saa7134_boards[dev->board].name);
+
+			/* IR init has already happened for other cards, so
+			 * we have to catch up. */
+			dev->has_remote = SAA7134_REMOTE_GPIO;
+			saa7134_input_init1(dev);
+	       }
+	       break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+		hauppauge_eeprom(dev, dev->eedata+0x80);
+		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_TIGER:
+	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+	case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+	case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
+	case SAA7134_BOARD_CREATIX_CTX953:
+	{
+		/* this is a hybrid board, initialize to analog mode
+		 * and configure firmware eeprom address
+		 */
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+	{
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+							.len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_ASUSTeK_PS3_100:
+	{
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+						       .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_FLYDVB_TRIO:
+	{
+		u8 temp = 0;
+		int rc;
+		u8 data[] = { 0x3c, 0x33, 0x62};
+		struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+		/*
+		 * send weak up message to pic16C505 chip
+		 * @ LifeView FlyDVB Trio
+		 */
+		msg.buf = &temp;
+		msg.addr = 0x0b;
+		msg.len = 1;
+		if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) {
+			printk(KERN_WARNING "%s: send wake up byte to pic16C505"
+					"(IR chip) failed\n", dev->name);
+		} else {
+			msg.flags = I2C_M_RD;
+			rc = i2c_transfer(&dev->i2c_adap, &msg, 1);
+			printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n",
+				   dev->name, msg.addr,
+				   (1 == rc) ? "yes" : "no");
+			if (rc == 1)
+				dev->has_remote = SAA7134_REMOTE_I2C;
+		}
+		break;
+	}
+	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+	{
+		/* initialize analog mode  */
+		u8 data[] = { 0x3c, 0x33, 0x6a};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+	case SAA7134_BOARD_CINERGY_HT_PCI:
+	{
+		/* initialize analog mode */
+		u8 data[] = { 0x3c, 0x33, 0x68};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		break;
+	}
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+		/* The T200 and the T200A share the same pci id.  Consequently,
+		 * we are going to query eeprom to try to find out which one we
+		 * are actually looking at. */
+
+		/* Don't do this if the board was specifically selected with an
+		 * insmod option or if we have the default configuration T200*/
+		if (!dev->autodetected || (dev->eedata[0x41] == 0xd0))
+			break;
+		if (dev->eedata[0x41] == 0x02) {
+			/* Reconfigure board  as T200A */
+			dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A;
+			dev->tuner_type   = saa7134_boards[dev->board].tuner_type;
+			dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+			printk(KERN_INFO "%s: Reconfigured board as %s\n",
+				dev->name, saa7134_boards[dev->board].name);
+		} else {
+			printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n",
+				dev->name, dev->eedata[0x41]);
+			break;
+		}
+		break;
+	case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+	case SAA7134_BOARD_KWORLD_ATSC110:
+	{
+		struct i2c_msg msg = { .addr = 0x0a, .flags = 0 };
+		int i;
+		static u8 buffer[][2] = {
+			{ 0x10, 0x12 },
+			{ 0x13, 0x04 },
+			{ 0x16, 0x00 },
+			{ 0x14, 0x04 },
+			{ 0x17, 0x00 },
+		};
+
+		for (i = 0; i < ARRAY_SIZE(buffer); i++) {
+			msg.buf = &buffer[i][0];
+			msg.len = ARRAY_SIZE(buffer[0]);
+			if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+				printk(KERN_WARNING
+				       "%s: Unable to enable tuner(%i).\n",
+				       dev->name, i);
+		}
+		break;
+	}
+	case SAA7134_BOARD_BEHOLD_H6:
+	{
+		u8 data[] = { 0x09, 0x9f, 0x86, 0x11};
+		struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data,
+							.len = sizeof(data)};
+
+		/* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware    */
+		/* start has disabled IF and enabled DVB-T. When saa7134    */
+		/* scan I2C devices it not detect IF tda9887 and can`t      */
+		/* watch TV without software reboot. For solve this problem */
+		/* switch the tuner to analog TV mode manually.             */
+		if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+				printk(KERN_WARNING
+				      "%s: Unable to enable IF of the tuner.\n",
+				       dev->name);
+		break;
+	}
+	case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+		saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000);
+		saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000);
+
+		saa7134_set_gpio(dev, 27, 0);
+		break;
+	} /* switch() */
+
+	/* initialize tuner */
+	if (TUNER_ABSENT != dev->tuner_type) {
+		int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+
+		/* Note: radio tuner address is always filled in,
+		   so we do not need to probe for a radio tuner device. */
+		if (dev->radio_type != UNSET)
+			v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_adap, "tuner",
+				dev->radio_addr, NULL);
+		if (has_demod)
+			v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+		if (dev->tuner_addr == ADDR_UNSET) {
+			enum v4l2_i2c_tuner_type type =
+				has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+
+			v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_adap, "tuner",
+				0, v4l2_i2c_tuner_addrs(type));
+		} else {
+			v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_adap, "tuner",
+				dev->tuner_addr, NULL);
+		}
+	}
+
+	saa7134_tuner_setup(dev);
+
+	switch (dev->board) {
+	case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+	{
+		struct v4l2_priv_tun_config tea5767_cfg;
+		struct tea5767_ctrl ctl;
+
+		dev->i2c_client.addr = 0xC0;
+		/* set TEA5767(analog FM) defines */
+		memset(&ctl, 0, sizeof(ctl));
+		ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+		tea5767_cfg.tuner = TUNER_TEA5767;
+		tea5767_cfg.priv  = &ctl;
+		saa_call_all(dev, tuner, s_config, &tea5767_cfg);
+		break;
+	}
+	} /* switch() */
+
+	return 0;
+}
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
new file mode 100644
index 000000000000..f2b37e05b964
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -0,0 +1,1368 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * driver core
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SAA7134_VERSION);
+
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int gpio_tracking;
+module_param(gpio_tracking, int, 0644);
+MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
+
+static unsigned int alsa = 1;
+module_param(alsa, int, 0644);
+MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]");
+
+static unsigned int latency = UNSET;
+module_param(latency, int, 0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+int saa7134_no_overlay=-1;
+module_param_named(no_overlay, saa7134_no_overlay, int, 0444);
+MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+		" [some VIA/SIS chipsets are known to have problem with overlay]");
+
+static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[]   = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int tuner[]    = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[]     = {[0 ... (SAA7134_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_param_array(tuner,    int, NULL, 0444);
+module_param_array(card,     int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device number");
+MODULE_PARM_DESC(vbi_nr,   "vbi device number");
+MODULE_PARM_DESC(radio_nr, "radio device number");
+MODULE_PARM_DESC(tuner,    "tuner type");
+MODULE_PARM_DESC(card,     "card type");
+
+DEFINE_MUTEX(saa7134_devlist_lock);
+EXPORT_SYMBOL(saa7134_devlist_lock);
+LIST_HEAD(saa7134_devlist);
+EXPORT_SYMBOL(saa7134_devlist);
+static LIST_HEAD(mops_list);
+static unsigned int saa7134_devcount;
+
+int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+
+#define dprintk(fmt, arg...)	if (core_debug) \
+	printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
+{
+	unsigned long mode,status;
+
+	if (!gpio_tracking)
+		return;
+	/* rising SAA7134_GPIO_GPRESCAN reads the status */
+	saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0);
+	saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN);
+	mode   = saa_readl(SAA7134_GPIO_GPMODE0   >> 2) & 0xfffffff;
+	status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff;
+	printk(KERN_DEBUG
+	       "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n",
+	       dev->name, mode, (~mode) & status, mode & status, msg);
+}
+
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
+{
+	u32 index, bitval;
+
+	index = 1 << bit_no;
+	switch (value) {
+	case 0: /* static value */
+	case 1:	dprintk("setting GPIO%d to static %d\n", bit_no, value);
+		/* turn sync mode off if necessary */
+		if (index & 0x00c00000)
+			saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00);
+		if (value)
+			bitval = index;
+		else
+			bitval = 0;
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval);
+		break;
+	case 3:	/* tristate */
+		dprintk("setting GPIO%d to tristate\n", bit_no);
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0);
+		break;
+	}
+}
+
+/* ------------------------------------------------------------------ */
+
+
+/* ----------------------------------------------------------- */
+/* delayed request_module                                      */
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+
+static void request_module_async(struct work_struct *work){
+	struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
+	if (card_is_empress(dev))
+		request_module("saa7134-empress");
+	if (card_is_dvb(dev))
+		request_module("saa7134-dvb");
+	if (alsa) {
+		if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130)
+			request_module("saa7134-alsa");
+	}
+}
+
+static void request_submodules(struct saa7134_dev *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_submodules(struct saa7134_dev *dev)
+{
+	flush_work(&dev->request_module_wk);
+}
+
+#else
+#define request_submodules(dev)
+#define flush_request_submodules(dev)
+#endif /* CONFIG_MODULES */
+
+/* ------------------------------------------------------------------ */
+
+/* nr of (saa7134-)pages for the given buffer size */
+static int saa7134_buffer_pages(int size)
+{
+	size  = PAGE_ALIGN(size);
+	size += PAGE_SIZE; /* for non-page-aligned buffers */
+	size /= 4096;
+	return size;
+}
+
+/* calc max # of buffers from size (must not exceed the 4MB virtual
+ * address space per DMA channel) */
+int saa7134_buffer_count(unsigned int size, unsigned int count)
+{
+	unsigned int maxcount;
+
+	maxcount = 1024 / saa7134_buffer_pages(size);
+	if (count > maxcount)
+		count = maxcount;
+	return count;
+}
+
+int saa7134_buffer_startpage(struct saa7134_buf *buf)
+{
+	return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;
+}
+
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf)
+{
+	unsigned long base;
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+	base  = saa7134_buffer_startpage(buf) * 4096;
+	base += dma->sglist[0].offset;
+	return base;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+	__le32       *cpu;
+	dma_addr_t   dma_addr = 0;
+
+	cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
+	if (NULL == cpu)
+		return -ENOMEM;
+	pt->size = SAA7134_PGTABLE_SIZE;
+	pt->cpu  = cpu;
+	pt->dma  = dma_addr;
+	return 0;
+}
+
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+			  struct scatterlist *list, unsigned int length,
+			  unsigned int startpage)
+{
+	__le32        *ptr;
+	unsigned int  i,p;
+
+	BUG_ON(NULL == pt || NULL == pt->cpu);
+
+	ptr = pt->cpu + startpage;
+	for (i = 0; i < length; i++, list++)
+		for (p = 0; p * 4096 < list->length; p++, ptr++)
+			*ptr = cpu_to_le32(sg_dma_address(list) - list->offset);
+	return 0;
+}
+
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+	if (NULL == pt->cpu)
+		return;
+	pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+	pt->cpu = NULL;
+}
+
+/* ------------------------------------------------------------------ */
+
+void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf)
+{
+	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+	BUG_ON(in_interrupt());
+
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_dma_unmap(q->dev, dma);
+	videobuf_dma_free(dma);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_buffer_queue(struct saa7134_dev *dev,
+			 struct saa7134_dmaqueue *q,
+			 struct saa7134_buf *buf)
+{
+	struct saa7134_buf *next = NULL;
+
+	assert_spin_locked(&dev->slock);
+	dprintk("buffer_queue %p\n",buf);
+	if (NULL == q->curr) {
+		if (!q->need_two) {
+			q->curr = buf;
+			buf->activate(dev,buf,NULL);
+		} else if (list_empty(&q->queue)) {
+			list_add_tail(&buf->vb.queue,&q->queue);
+			buf->vb.state = VIDEOBUF_QUEUED;
+		} else {
+			next = list_entry(q->queue.next,struct saa7134_buf,
+					  vb.queue);
+			q->curr = buf;
+			buf->activate(dev,buf,next);
+		}
+	} else {
+		list_add_tail(&buf->vb.queue,&q->queue);
+		buf->vb.state = VIDEOBUF_QUEUED;
+	}
+	return 0;
+}
+
+void saa7134_buffer_finish(struct saa7134_dev *dev,
+			   struct saa7134_dmaqueue *q,
+			   unsigned int state)
+{
+	assert_spin_locked(&dev->slock);
+	dprintk("buffer_finish %p\n",q->curr);
+
+	/* finish current buffer */
+	q->curr->vb.state = state;
+	do_gettimeofday(&q->curr->vb.ts);
+	wake_up(&q->curr->vb.done);
+	q->curr = NULL;
+}
+
+void saa7134_buffer_next(struct saa7134_dev *dev,
+			 struct saa7134_dmaqueue *q)
+{
+	struct saa7134_buf *buf,*next = NULL;
+
+	assert_spin_locked(&dev->slock);
+	BUG_ON(NULL != q->curr);
+
+	if (!list_empty(&q->queue)) {
+		/* activate next one from queue */
+		buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue);
+		dprintk("buffer_next %p [prev=%p/next=%p]\n",
+			buf,q->queue.prev,q->queue.next);
+		list_del(&buf->vb.queue);
+		if (!list_empty(&q->queue))
+			next = list_entry(q->queue.next,struct saa7134_buf,
+					  vb.queue);
+		q->curr = buf;
+		buf->activate(dev,buf,next);
+		dprintk("buffer_next #2 prev=%p/next=%p\n",
+			q->queue.prev,q->queue.next);
+	} else {
+		/* nothing to do -- just stop DMA */
+		dprintk("buffer_next %p\n",NULL);
+		saa7134_set_dmabits(dev);
+		del_timer(&q->timeout);
+
+		if (card_has_mpeg(dev))
+			if (dev->ts_started)
+				saa7134_ts_stop(dev);
+	}
+}
+
+void saa7134_buffer_timeout(unsigned long data)
+{
+	struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data;
+	struct saa7134_dev *dev = q->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->slock,flags);
+
+	/* try to reset the hardware (SWRST) */
+	saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+	saa_writeb(SAA7134_REGION_ENABLE, 0x80);
+	saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+
+	/* flag current buffer as failed,
+	   try to start over with the next one. */
+	if (q->curr) {
+		dprintk("timeout on %p\n",q->curr);
+		saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR);
+	}
+	saa7134_buffer_next(dev,q);
+	spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_set_dmabits(struct saa7134_dev *dev)
+{
+	u32 split, task=0, ctrl=0, irq=0;
+	enum v4l2_field cap = V4L2_FIELD_ANY;
+	enum v4l2_field ov  = V4L2_FIELD_ANY;
+
+	assert_spin_locked(&dev->slock);
+
+	if (dev->insuspend)
+		return 0;
+
+	/* video capture -- dma 0 + video task A */
+	if (dev->video_q.curr) {
+		task |= 0x01;
+		ctrl |= SAA7134_MAIN_CTRL_TE0;
+		irq  |= SAA7134_IRQ1_INTE_RA0_1 |
+			SAA7134_IRQ1_INTE_RA0_0;
+		cap = dev->video_q.curr->vb.field;
+	}
+
+	/* video capture -- dma 1+2 (planar modes) */
+	if (dev->video_q.curr &&
+	    dev->video_q.curr->fmt->planar) {
+		ctrl |= SAA7134_MAIN_CTRL_TE4 |
+			SAA7134_MAIN_CTRL_TE5;
+	}
+
+	/* screen overlay -- dma 0 + video task B */
+	if (dev->ovenable) {
+		task |= 0x10;
+		ctrl |= SAA7134_MAIN_CTRL_TE1;
+		ov = dev->ovfield;
+	}
+
+	/* vbi capture -- dma 0 + vbi task A+B */
+	if (dev->vbi_q.curr) {
+		task |= 0x22;
+		ctrl |= SAA7134_MAIN_CTRL_TE2 |
+			SAA7134_MAIN_CTRL_TE3;
+		irq  |= SAA7134_IRQ1_INTE_RA0_7 |
+			SAA7134_IRQ1_INTE_RA0_6 |
+			SAA7134_IRQ1_INTE_RA0_5 |
+			SAA7134_IRQ1_INTE_RA0_4;
+	}
+
+	/* audio capture -- dma 3 */
+	if (dev->dmasound.dma_running) {
+		ctrl |= SAA7134_MAIN_CTRL_TE6;
+		irq  |= SAA7134_IRQ1_INTE_RA3_1 |
+			SAA7134_IRQ1_INTE_RA3_0;
+	}
+
+	/* TS capture -- dma 5 */
+	if (dev->ts_q.curr) {
+		ctrl |= SAA7134_MAIN_CTRL_TE5;
+		irq  |= SAA7134_IRQ1_INTE_RA2_1 |
+			SAA7134_IRQ1_INTE_RA2_0;
+	}
+
+	/* set task conditions + field handling */
+	if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) {
+		/* default config -- use full frames */
+		saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+		saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+		saa_writeb(SAA7134_FIELD_HANDLING(TASK_A),  0x02);
+		saa_writeb(SAA7134_FIELD_HANDLING(TASK_B),  0x02);
+		split = 0;
+	} else {
+		/* split fields between tasks */
+		if (V4L2_FIELD_TOP == cap) {
+			/* odd A, even B, repeat */
+			saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+			saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e);
+		} else {
+			/* odd B, even A, repeat */
+			saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e);
+			saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+		}
+		saa_writeb(SAA7134_FIELD_HANDLING(TASK_A),  0x01);
+		saa_writeb(SAA7134_FIELD_HANDLING(TASK_B),  0x01);
+		split = 1;
+	}
+
+	/* irqs */
+	saa_writeb(SAA7134_REGION_ENABLE, task);
+	saa_writel(SAA7134_IRQ1,          irq);
+	saa_andorl(SAA7134_MAIN_CTRL,
+		   SAA7134_MAIN_CTRL_TE0 |
+		   SAA7134_MAIN_CTRL_TE1 |
+		   SAA7134_MAIN_CTRL_TE2 |
+		   SAA7134_MAIN_CTRL_TE3 |
+		   SAA7134_MAIN_CTRL_TE4 |
+		   SAA7134_MAIN_CTRL_TE5 |
+		   SAA7134_MAIN_CTRL_TE6,
+		   ctrl);
+	dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n",
+		task, ctrl, irq, split ? "no" : "yes");
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* IRQ handler + helpers                                              */
+
+static char *irqbits[] = {
+	"DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3",
+	"AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC",
+	"TRIG_ERR", "CONF_ERR", "LOAD_ERR",
+	"GPIO16", "GPIO18", "GPIO22", "GPIO23"
+};
+#define IRQBITS ARRAY_SIZE(irqbits)
+
+static void print_irqstatus(struct saa7134_dev *dev, int loop,
+			    unsigned long report, unsigned long status)
+{
+	unsigned int i;
+
+	printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx",
+	       dev->name,loop,jiffies,report,status);
+	for (i = 0; i < IRQBITS; i++) {
+		if (!(report & (1 << i)))
+			continue;
+		printk(" %s",irqbits[i]);
+	}
+	if (report & SAA7134_IRQ_REPORT_DONE_RA0) {
+		printk(" | RA0=%s,%s,%s,%ld",
+		       (status & 0x40) ? "vbi"  : "video",
+		       (status & 0x20) ? "b"    : "a",
+		       (status & 0x10) ? "odd"  : "even",
+		       (status & 0x0f));
+	}
+	printk("\n");
+}
+
+static irqreturn_t saa7134_irq(int irq, void *dev_id)
+{
+	struct saa7134_dev *dev = (struct saa7134_dev*) dev_id;
+	unsigned long report,status;
+	int loop, handled = 0;
+
+	if (dev->insuspend)
+		goto out;
+
+	for (loop = 0; loop < 10; loop++) {
+		report = saa_readl(SAA7134_IRQ_REPORT);
+		status = saa_readl(SAA7134_IRQ_STATUS);
+
+		/* If dmasound support is active and we get a sound report,
+		 * mask out the report and let the saa7134-alsa module deal
+		 * with it */
+		if ((report & SAA7134_IRQ_REPORT_DONE_RA3) &&
+			(dev->dmasound.priv_data != NULL) )
+		{
+			if (irq_debug > 1)
+				printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n",
+				       dev->name);
+			report &= ~SAA7134_IRQ_REPORT_DONE_RA3;
+		}
+
+		if (0 == report) {
+			if (irq_debug > 1)
+				printk(KERN_DEBUG "%s/irq: no (more) work\n",
+				       dev->name);
+			goto out;
+		}
+
+		handled = 1;
+		saa_writel(SAA7134_IRQ_REPORT,report);
+		if (irq_debug)
+			print_irqstatus(dev,loop,report,status);
+
+
+		if ((report & SAA7134_IRQ_REPORT_RDCAP) ||
+			(report & SAA7134_IRQ_REPORT_INTL))
+				saa7134_irq_video_signalchange(dev);
+
+
+		if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+		    (status & 0x60) == 0)
+			saa7134_irq_video_done(dev,status);
+
+		if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+		    (status & 0x40) == 0x40)
+			saa7134_irq_vbi_done(dev,status);
+
+		if ((report & SAA7134_IRQ_REPORT_DONE_RA2) &&
+		    card_has_mpeg(dev))
+			saa7134_irq_ts_done(dev,status);
+
+		if (report & SAA7134_IRQ_REPORT_GPIO16) {
+			switch (dev->has_remote) {
+				case SAA7134_REMOTE_GPIO:
+					if (!dev->remote)
+						break;
+					if  (dev->remote->mask_keydown & 0x10000) {
+						saa7134_input_irq(dev);
+					}
+					break;
+
+				case SAA7134_REMOTE_I2C:
+					break;			/* FIXME: invoke I2C get_key() */
+
+				default:			/* GPIO16 not used by IR remote */
+					break;
+			}
+		}
+
+		if (report & SAA7134_IRQ_REPORT_GPIO18) {
+			switch (dev->has_remote) {
+				case SAA7134_REMOTE_GPIO:
+					if (!dev->remote)
+						break;
+					if ((dev->remote->mask_keydown & 0x40000) ||
+					    (dev->remote->mask_keyup & 0x40000)) {
+						saa7134_input_irq(dev);
+					}
+					break;
+
+				case SAA7134_REMOTE_I2C:
+					break;			/* FIXME: invoke I2C get_key() */
+
+				default:			/* GPIO18 not used by IR remote */
+					break;
+			}
+		}
+	}
+
+	if (10 == loop) {
+		print_irqstatus(dev,loop,report,status);
+		if (report & SAA7134_IRQ_REPORT_PE) {
+			/* disable all parity error */
+			printk(KERN_WARNING "%s/irq: looping -- "
+			       "clearing PE (parity error!) enable bit\n",dev->name);
+			saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE);
+		} else if (report & SAA7134_IRQ_REPORT_GPIO16) {
+			/* disable gpio16 IRQ */
+			printk(KERN_WARNING "%s/irq: looping -- "
+			       "clearing GPIO16 enable bit\n",dev->name);
+			saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P);
+			saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N);
+		} else if (report & SAA7134_IRQ_REPORT_GPIO18) {
+			/* disable gpio18 IRQs */
+			printk(KERN_WARNING "%s/irq: looping -- "
+			       "clearing GPIO18 enable bit\n",dev->name);
+			saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P);
+			saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N);
+		} else {
+			/* disable all irqs */
+			printk(KERN_WARNING "%s/irq: looping -- "
+			       "clearing all enable bits\n",dev->name);
+			saa_writel(SAA7134_IRQ1,0);
+			saa_writel(SAA7134_IRQ2,0);
+		}
+	}
+
+ out:
+	return IRQ_RETVAL(handled);
+}
+
+/* ------------------------------------------------------------------ */
+
+/* early init (no i2c, no irq) */
+
+static int saa7134_hw_enable1(struct saa7134_dev *dev)
+{
+	/* RAM FIFO config */
+	saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
+	saa_writel(SAA7134_THRESHOULD, 0x02020202);
+
+	/* enable audio + video processing */
+	saa_writel(SAA7134_MAIN_CTRL,
+			SAA7134_MAIN_CTRL_VPLLE |
+			SAA7134_MAIN_CTRL_APLLE |
+			SAA7134_MAIN_CTRL_EXOSC |
+			SAA7134_MAIN_CTRL_EVFE1 |
+			SAA7134_MAIN_CTRL_EVFE2 |
+			SAA7134_MAIN_CTRL_ESFE  |
+			SAA7134_MAIN_CTRL_EBDAC);
+
+	/*
+	* Initialize OSS _after_ enabling audio clock PLL and audio processing.
+	* OSS initialization writes to registers via the audio DSP; these
+	* writes will fail unless the audio clock has been started.  At worst,
+	* audio will not work.
+	*/
+
+	/* enable peripheral devices */
+	saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+
+	/* set vertical line numbering start (vbi needs this) */
+	saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+
+	return 0;
+}
+
+static int saa7134_hwinit1(struct saa7134_dev *dev)
+{
+	dprintk("hwinit1\n");
+
+	saa_writel(SAA7134_IRQ1, 0);
+	saa_writel(SAA7134_IRQ2, 0);
+
+	/* Clear any stale IRQ reports */
+	saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
+
+	mutex_init(&dev->lock);
+	spin_lock_init(&dev->slock);
+
+	saa7134_track_gpio(dev,"pre-init");
+	saa7134_video_init1(dev);
+	saa7134_vbi_init1(dev);
+	if (card_has_mpeg(dev))
+		saa7134_ts_init1(dev);
+	saa7134_input_init1(dev);
+
+	saa7134_hw_enable1(dev);
+
+	return 0;
+}
+
+/* late init (with i2c + irq) */
+static int saa7134_hw_enable2(struct saa7134_dev *dev)
+{
+
+	unsigned int irq2_mask;
+
+	/* enable IRQ's */
+	irq2_mask =
+		SAA7134_IRQ2_INTE_DEC3    |
+		SAA7134_IRQ2_INTE_DEC2    |
+		SAA7134_IRQ2_INTE_DEC1    |
+		SAA7134_IRQ2_INTE_DEC0    |
+		SAA7134_IRQ2_INTE_PE      |
+		SAA7134_IRQ2_INTE_AR;
+
+	if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) {
+		if (dev->remote->mask_keydown & 0x10000)
+			irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N;
+		else {		/* Allow enabling both IRQ edge triggers */
+			if (dev->remote->mask_keydown & 0x40000)
+				irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P;
+			if (dev->remote->mask_keyup & 0x40000)
+				irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N;
+		}
+	}
+
+	if (dev->has_remote == SAA7134_REMOTE_I2C) {
+		request_module("ir-kbd-i2c");
+	}
+
+	saa_writel(SAA7134_IRQ1, 0);
+	saa_writel(SAA7134_IRQ2, irq2_mask);
+
+	return 0;
+}
+
+static int saa7134_hwinit2(struct saa7134_dev *dev)
+{
+
+	dprintk("hwinit2\n");
+
+	saa7134_video_init2(dev);
+	saa7134_tvaudio_init2(dev);
+
+	saa7134_hw_enable2(dev);
+
+	return 0;
+}
+
+
+/* shutdown */
+static int saa7134_hwfini(struct saa7134_dev *dev)
+{
+	dprintk("hwfini\n");
+
+	if (card_has_mpeg(dev))
+		saa7134_ts_fini(dev);
+	saa7134_input_fini(dev);
+	saa7134_vbi_fini(dev);
+	saa7134_tvaudio_fini(dev);
+	return 0;
+}
+
+static void __devinit must_configure_manually(int has_eeprom)
+{
+	unsigned int i,p;
+
+	if (!has_eeprom)
+		printk(KERN_WARNING
+		       "saa7134: <rant>\n"
+		       "saa7134:  Congratulations!  Your TV card vendor saved a few\n"
+		       "saa7134:  cents for a eeprom, thus your pci board has no\n"
+		       "saa7134:  subsystem ID and I can't identify it automatically\n"
+		       "saa7134: </rant>\n"
+		       "saa7134: I feel better now.  Ok, here are the good news:\n"
+		       "saa7134: You can use the card=<nr> insmod option to specify\n"
+		       "saa7134: which board do you have.  The list:\n");
+	else
+		printk(KERN_WARNING
+		       "saa7134: Board is currently unknown. You might try to use the card=<nr>\n"
+		       "saa7134: insmod option to specify which board do you have, but this is\n"
+		       "saa7134: somewhat risky, as might damage your card. It is better to ask\n"
+		       "saa7134: for support at linux-media@vger.kernel.org.\n"
+		       "saa7134: The supported cards are:\n");
+
+	for (i = 0; i < saa7134_bcount; i++) {
+		printk(KERN_WARNING "saa7134:   card=%d -> %-40.40s",
+		       i,saa7134_boards[i].name);
+		for (p = 0; saa7134_pci_tbl[p].driver_data; p++) {
+			if (saa7134_pci_tbl[p].driver_data != i)
+				continue;
+			printk(" %04x:%04x",
+			       saa7134_pci_tbl[p].subvendor,
+			       saa7134_pci_tbl[p].subdevice);
+		}
+		printk("\n");
+	}
+}
+
+static struct video_device *vdev_init(struct saa7134_dev *dev,
+				      struct video_device *template,
+				      char *type)
+{
+	struct video_device *vfd;
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->v4l2_dev  = &dev->v4l2_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);
+	video_set_drvdata(vfd, dev);
+	return vfd;
+}
+
+static void saa7134_unregister_video(struct saa7134_dev *dev)
+{
+	if (dev->video_dev) {
+		if (video_is_registered(dev->video_dev))
+			video_unregister_device(dev->video_dev);
+		else
+			video_device_release(dev->video_dev);
+		dev->video_dev = NULL;
+	}
+	if (dev->vbi_dev) {
+		if (video_is_registered(dev->vbi_dev))
+			video_unregister_device(dev->vbi_dev);
+		else
+			video_device_release(dev->vbi_dev);
+		dev->vbi_dev = NULL;
+	}
+	if (dev->radio_dev) {
+		if (video_is_registered(dev->radio_dev))
+			video_unregister_device(dev->radio_dev);
+		else
+			video_device_release(dev->radio_dev);
+		dev->radio_dev = NULL;
+	}
+}
+
+static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops,
+			    struct saa7134_dev *dev)
+{
+	int err;
+
+	if (NULL != dev->mops)
+		return;
+	if (saa7134_boards[dev->board].mpeg != ops->type)
+		return;
+	err = ops->init(dev);
+	if (0 != err)
+		return;
+	dev->mops = ops;
+}
+
+static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops,
+			    struct saa7134_dev *dev)
+{
+	if (NULL == dev->mops)
+		return;
+	if (dev->mops != ops)
+		return;
+	dev->mops->fini(dev);
+	dev->mops = NULL;
+}
+
+static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+				     const struct pci_device_id *pci_id)
+{
+	struct saa7134_dev *dev;
+	struct saa7134_mpeg_ops *mops;
+	int err;
+
+	if (saa7134_devcount == SAA7134_MAXBOARDS)
+		return -ENOMEM;
+
+	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+
+	err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+	if (err)
+		goto fail0;
+
+	/* pci init */
+	dev->pci = pci_dev;
+	if (pci_enable_device(pci_dev)) {
+		err = -EIO;
+		goto fail1;
+	}
+
+	dev->nr = saa7134_devcount;
+	sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr);
+
+	/* pci quirks */
+	if (pci_pci_problems) {
+		if (pci_pci_problems & PCIPCI_TRITON)
+			printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name);
+		if (pci_pci_problems & PCIPCI_NATOMA)
+			printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name);
+		if (pci_pci_problems & PCIPCI_VIAETBF)
+			printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name);
+		if (pci_pci_problems & PCIPCI_VSFX)
+			printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name);
+#ifdef PCIPCI_ALIMAGIK
+		if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+			printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+			       dev->name);
+			latency = 0x0A;
+		}
+#endif
+		if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) {
+			printk(KERN_INFO "%s: quirk: this driver and your "
+					"chipset may not work together"
+					" in overlay mode.\n",dev->name);
+			if (!saa7134_no_overlay) {
+				printk(KERN_INFO "%s: quirk: overlay "
+						"mode will be disabled.\n",
+						dev->name);
+				saa7134_no_overlay = 1;
+			} else {
+				printk(KERN_INFO "%s: quirk: overlay "
+						"mode will be forced. Use this"
+						" option at your own risk.\n",
+						dev->name);
+			}
+		}
+	}
+	if (UNSET != latency) {
+		printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+		       dev->name,latency);
+		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+	}
+
+	/* print pci info */
+	dev->pci_rev = pci_dev->revision;
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+	printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", dev->name,
+	       pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+	       dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+	pci_set_master(pci_dev);
+	if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+		printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
+		err = -EIO;
+		goto fail1;
+	}
+
+	/* board config */
+	dev->board = pci_id->driver_data;
+	if (card[dev->nr] >= 0 &&
+	    card[dev->nr] < saa7134_bcount)
+		dev->board = card[dev->nr];
+	if (SAA7134_BOARD_UNKNOWN == dev->board)
+		must_configure_manually(0);
+	else if (SAA7134_BOARD_NOAUTO == dev->board) {
+		must_configure_manually(1);
+		dev->board = SAA7134_BOARD_UNKNOWN;
+	}
+	dev->autodetected = card[dev->nr] != dev->board;
+	dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+	dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
+	dev->radio_type = saa7134_boards[dev->board].radio_type;
+	dev->radio_addr = saa7134_boards[dev->board].radio_addr;
+	dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+	if (UNSET != tuner[dev->nr])
+		dev->tuner_type = tuner[dev->nr];
+	printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+		dev->name,pci_dev->subsystem_vendor,
+		pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+		dev->board, dev->autodetected ?
+		"autodetected" : "insmod option");
+
+	/* get mmio */
+	if (!request_mem_region(pci_resource_start(pci_dev,0),
+				pci_resource_len(pci_dev,0),
+				dev->name)) {
+		err = -EBUSY;
+		printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+		       dev->name,(unsigned long long)pci_resource_start(pci_dev,0));
+		goto fail1;
+	}
+	dev->lmmio = ioremap(pci_resource_start(pci_dev, 0),
+			     pci_resource_len(pci_dev, 0));
+	dev->bmmio = (__u8 __iomem *)dev->lmmio;
+	if (NULL == dev->lmmio) {
+		err = -EIO;
+		printk(KERN_ERR "%s: can't ioremap() MMIO memory\n",
+		       dev->name);
+		goto fail2;
+	}
+
+	/* initialize hardware #1 */
+	saa7134_board_init1(dev);
+	saa7134_hwinit1(dev);
+
+	/* get irq */
+	err = request_irq(pci_dev->irq, saa7134_irq,
+			  IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+	if (err < 0) {
+		printk(KERN_ERR "%s: can't get IRQ %d\n",
+		       dev->name,pci_dev->irq);
+		goto fail3;
+	}
+
+	/* wait a bit, register i2c bus */
+	msleep(100);
+	saa7134_i2c_register(dev);
+	saa7134_board_init2(dev);
+
+	saa7134_hwinit2(dev);
+
+	/* load i2c helpers */
+	if (card_is_empress(dev)) {
+		struct v4l2_subdev *sd =
+			v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+				"saa6752hs",
+				saa7134_boards[dev->board].empress_addr, NULL);
+
+		if (sd)
+			sd->grp_id = GRP_EMPRESS;
+	}
+
+	if (saa7134_boards[dev->board].rds_addr) {
+		struct v4l2_subdev *sd;
+
+		sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+				&dev->i2c_adap, "saa6588",
+				0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr));
+		if (sd) {
+			printk(KERN_INFO "%s: found RDS decoder\n", dev->name);
+			dev->has_rds = 1;
+		}
+	}
+
+	v4l2_prio_init(&dev->prio);
+
+	mutex_lock(&saa7134_devlist_lock);
+	list_for_each_entry(mops, &mops_list, next)
+		mpeg_ops_attach(mops, dev);
+	list_add_tail(&dev->devlist, &saa7134_devlist);
+	mutex_unlock(&saa7134_devlist_lock);
+
+	/* check for signal */
+	saa7134_irq_video_signalchange(dev);
+
+	if (TUNER_ABSENT != dev->tuner_type)
+		saa_call_all(dev, core, s_power, 0);
+
+	/* register v4l devices */
+	if (saa7134_no_overlay > 0)
+		printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name);
+
+	dev->video_dev = vdev_init(dev,&saa7134_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 fail4;
+	}
+	printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+	       dev->name, video_device_node_name(dev->video_dev));
+
+	dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
+
+	err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+				    vbi_nr[dev->nr]);
+	if (err < 0)
+		goto fail4;
+	printk(KERN_INFO "%s: registered device %s\n",
+	       dev->name, video_device_node_name(dev->vbi_dev));
+
+	if (card_has_radio(dev)) {
+		dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
+		err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+					    radio_nr[dev->nr]);
+		if (err < 0)
+			goto fail4;
+		printk(KERN_INFO "%s: registered device %s\n",
+		       dev->name, video_device_node_name(dev->radio_dev));
+	}
+
+	/* everything worked */
+	saa7134_devcount++;
+
+	if (saa7134_dmasound_init && !dev->dmasound.priv_data)
+		saa7134_dmasound_init(dev);
+
+	request_submodules(dev);
+	return 0;
+
+ fail4:
+	saa7134_unregister_video(dev);
+	saa7134_i2c_unregister(dev);
+	free_irq(pci_dev->irq, dev);
+ fail3:
+	saa7134_hwfini(dev);
+	iounmap(dev->lmmio);
+ fail2:
+	release_mem_region(pci_resource_start(pci_dev,0),
+			   pci_resource_len(pci_dev,0));
+ fail1:
+	v4l2_device_unregister(&dev->v4l2_dev);
+ fail0:
+	kfree(dev);
+	return err;
+}
+
+static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+	struct saa7134_mpeg_ops *mops;
+
+	flush_request_submodules(dev);
+
+	/* Release DMA sound modules if present */
+	if (saa7134_dmasound_exit && dev->dmasound.priv_data) {
+		saa7134_dmasound_exit(dev);
+	}
+
+	/* debugging ... */
+	if (irq_debug) {
+		u32 report = saa_readl(SAA7134_IRQ_REPORT);
+		u32 status = saa_readl(SAA7134_IRQ_STATUS);
+		print_irqstatus(dev,42,report,status);
+	}
+
+	/* disable peripheral devices */
+	saa_writeb(SAA7134_SPECIAL_MODE,0);
+
+	/* shutdown hardware */
+	saa_writel(SAA7134_IRQ1,0);
+	saa_writel(SAA7134_IRQ2,0);
+	saa_writel(SAA7134_MAIN_CTRL,0);
+
+	/* shutdown subsystems */
+	saa7134_hwfini(dev);
+
+	/* unregister */
+	mutex_lock(&saa7134_devlist_lock);
+	list_del(&dev->devlist);
+	list_for_each_entry(mops, &mops_list, next)
+		mpeg_ops_detach(mops, dev);
+	mutex_unlock(&saa7134_devlist_lock);
+	saa7134_devcount--;
+
+	saa7134_i2c_unregister(dev);
+	saa7134_unregister_video(dev);
+
+
+	/* the DMA sound modules should be unloaded before reaching
+	   this, but just in case they are still present... */
+	if (dev->dmasound.priv_data != NULL) {
+		free_irq(pci_dev->irq, &dev->dmasound);
+		dev->dmasound.priv_data = NULL;
+	}
+
+
+	/* release resources */
+	free_irq(pci_dev->irq, dev);
+	iounmap(dev->lmmio);
+	release_mem_region(pci_resource_start(pci_dev,0),
+			   pci_resource_len(pci_dev,0));
+
+
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	/* free memory */
+	kfree(dev);
+}
+
+#ifdef CONFIG_PM
+
+/* resends a current buffer in queue after resume */
+static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+				  struct saa7134_dmaqueue *q)
+{
+	struct saa7134_buf *buf, *next;
+
+	assert_spin_locked(&dev->slock);
+
+	buf  = q->curr;
+	next = buf;
+	dprintk("buffer_requeue\n");
+
+	if (!buf)
+		return 0;
+
+	dprintk("buffer_requeue : resending active buffers \n");
+
+	if (!list_empty(&q->queue))
+		next = list_entry(q->queue.next, struct saa7134_buf,
+					  vb.queue);
+	buf->activate(dev, buf, next);
+
+	return 0;
+}
+
+static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+
+	/* disable overlay - apps should enable it explicitly on resume*/
+	dev->ovenable = 0;
+
+	/* Disable interrupts, DMA, and rest of the chip*/
+	saa_writel(SAA7134_IRQ1, 0);
+	saa_writel(SAA7134_IRQ2, 0);
+	saa_writel(SAA7134_MAIN_CTRL, 0);
+
+	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*/
+
+	del_timer(&dev->video_q.timeout);
+	del_timer(&dev->vbi_q.timeout);
+	del_timer(&dev->ts_q.timeout);
+
+	if (dev->remote)
+		saa7134_ir_stop(dev);
+
+	pci_save_state(pci_dev);
+	pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+
+	return 0;
+}
+
+static int saa7134_resume(struct pci_dev *pci_dev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+	struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+	unsigned long flags;
+
+	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.*/
+
+	saa7134_board_init1(dev);
+
+	/* saa7134_hwinit1 */
+	if (saa7134_boards[dev->board].video_out)
+		saa7134_videoport_init(dev);
+	if (card_has_mpeg(dev))
+		saa7134_ts_init_hw(dev);
+	if (dev->remote)
+		saa7134_ir_start(dev);
+	saa7134_hw_enable1(dev);
+
+	msleep(100);
+
+	saa7134_board_init2(dev);
+
+	/*saa7134_hwinit2*/
+	saa7134_set_tvnorm_hw(dev);
+	saa7134_tvaudio_setmute(dev);
+	saa7134_tvaudio_setvolume(dev, dev->ctl_volume);
+	saa7134_tvaudio_init(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);
+	saa7134_buffer_requeue(dev, &dev->vbi_q);
+	saa7134_buffer_requeue(dev, &dev->ts_q);
+
+	/* FIXME: Disable DMA audio sound - temporary till proper support
+		  is implemented*/
+
+	dev->dmasound.dma_running = 0;
+
+	/* start DMA now*/
+	dev->insuspend = 0;
+	smp_wmb();
+	saa7134_set_dmabits(dev);
+	spin_unlock_irqrestore(&dev->slock, flags);
+
+	return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
+{
+	struct saa7134_dev *dev;
+
+	mutex_lock(&saa7134_devlist_lock);
+	list_for_each_entry(dev, &saa7134_devlist, devlist)
+		mpeg_ops_attach(ops, dev);
+	list_add_tail(&ops->next,&mops_list);
+	mutex_unlock(&saa7134_devlist_lock);
+	return 0;
+}
+
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops)
+{
+	struct saa7134_dev *dev;
+
+	mutex_lock(&saa7134_devlist_lock);
+	list_del(&ops->next);
+	list_for_each_entry(dev, &saa7134_devlist, devlist)
+		mpeg_ops_detach(ops, dev);
+	mutex_unlock(&saa7134_devlist_lock);
+}
+
+EXPORT_SYMBOL(saa7134_ts_register);
+EXPORT_SYMBOL(saa7134_ts_unregister);
+
+/* ----------------------------------------------------------- */
+
+static struct pci_driver saa7134_pci_driver = {
+	.name     = "saa7134",
+	.id_table = saa7134_pci_tbl,
+	.probe    = saa7134_initdev,
+	.remove   = __devexit_p(saa7134_finidev),
+#ifdef CONFIG_PM
+	.suspend  = saa7134_suspend,
+	.resume   = saa7134_resume
+#endif
+};
+
+static int __init saa7134_init(void)
+{
+	INIT_LIST_HEAD(&saa7134_devlist);
+	printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n",
+	       SAA7134_VERSION);
+	return pci_register_driver(&saa7134_pci_driver);
+}
+
+static void __exit saa7134_fini(void)
+{
+	pci_unregister_driver(&saa7134_pci_driver);
+}
+
+module_init(saa7134_init);
+module_exit(saa7134_fini);
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(saa7134_set_gpio);
+EXPORT_SYMBOL(saa7134_boards);
+
+/* ----------------- for the DMA sound modules --------------- */
+
+EXPORT_SYMBOL(saa7134_dmasound_init);
+EXPORT_SYMBOL(saa7134_dmasound_exit);
+EXPORT_SYMBOL(saa7134_pgtable_free);
+EXPORT_SYMBOL(saa7134_pgtable_build);
+EXPORT_SYMBOL(saa7134_pgtable_alloc);
+EXPORT_SYMBOL(saa7134_set_dmabits);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c
new file mode 100644
index 000000000000..b209de40a4f8
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-dvb.c
@@ -0,0 +1,1936 @@
+/*
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  Extended 3 / 2005 by Hartmut Hackmann to support various
+ *  cards with the tda10046 DVB-T channel decoder
+ *
+ *  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/delay.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include "dvb-pll.h"
+#include <dvb_frontend.h>
+
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1004x.h"
+#include "nxt200x.h"
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+#include "tda10086.h"
+#include "tda826x.h"
+#include "tda827x.h"
+#include "isl6421.h"
+#include "isl6405.h"
+#include "lnbp21.h"
+#include "tuner-simple.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "lgdt3305.h"
+#include "tda8290.h"
+#include "mb86a20s.h"
+#include "lgs8gxx.h"
+
+#include "zl10353.h"
+#include "qt1010.h"
+
+#include "zl10036.h"
+#include "zl10039.h"
+#include "mt312.h"
+#include "s5h1411.h"
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int antenna_pwr;
+
+module_param(antenna_pwr, int, 0444);
+MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
+
+static int use_frontend;
+module_param(use_frontend, int, 0644);
+MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(fmt, arg...)	do { if (debug) \
+	printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
+
+/* Print a warning */
+#define wprintk(fmt, arg...) \
+	printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------
+ * mt352 based DVB-T cards
+ */
+
+static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
+{
+	u32 ok;
+
+	if (!on) {
+		saa_setl(SAA7134_GPIO_GPMODE0 >> 2,     (1 << 26));
+		saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+		return 0;
+	}
+
+	saa_setl(SAA7134_GPIO_GPMODE0 >> 2,     (1 << 26));
+	saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2,   (1 << 26));
+	udelay(10);
+
+	saa_setl(SAA7134_GPIO_GPMODE0 >> 2,     (1 << 28));
+	saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+	udelay(10);
+	saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2,   (1 << 28));
+	udelay(10);
+	ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
+	dprintk("%s %s\n", __func__, ok ? "on" : "off");
+
+	if (!ok)
+		saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2,   (1 << 26));
+	return ok;
+}
+
+static int mt352_pinnacle_init(struct dvb_frontend* fe)
+{
+	static u8 clock_config []  = { CLOCK_CTL,  0x3d, 0x28 };
+	static u8 reset []         = { RESET,      0x80 };
+	static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static u8 agc_cfg []       = { AGC_TARGET, 0x28, 0xa0 };
+	static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 };
+	static u8 fsm_ctl_cfg[]    = { 0x7b,       0x04 };
+	static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x0f };
+	static u8 scan_ctl_cfg []  = { SCAN_CTL,   0x0d };
+	static u8 irq_cfg []       = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
+	struct saa7134_dev *dev= fe->dvb->priv;
+
+	dprintk("%s called\n", __func__);
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(200);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+	mt352_write(fe, gpp_ctl_cfg,    sizeof(gpp_ctl_cfg));
+
+	mt352_write(fe, fsm_ctl_cfg,    sizeof(fsm_ctl_cfg));
+	mt352_write(fe, scan_ctl_cfg,   sizeof(scan_ctl_cfg));
+	mt352_write(fe, irq_cfg,        sizeof(irq_cfg));
+
+	return 0;
+}
+
+static int mt352_aver777_init(struct dvb_frontend* fe)
+{
+	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x2d };
+	static u8 reset []         = { RESET,      0x80 };
+	static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static u8 agc_cfg []       = { AGC_TARGET, 0x28, 0xa0 };
+	static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(200);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+	return 0;
+}
+
+static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe)
+{
+	static u8 clock_config []  = { CLOCK_CTL, 0x38, 0x2d };
+	static u8 reset []         = { RESET, 0x80 };
+	static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+	static u8 agc_cfg []       = { AGC_TARGET, 0xe };
+	static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+	mt352_write(fe, clock_config,   sizeof(clock_config));
+	udelay(200);
+	mt352_write(fe, reset,          sizeof(reset));
+	mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+	mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+	mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+	return 0;
+}
+
+static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u8 off[] = { 0x00, 0xf1};
+	u8 on[]  = { 0x00, 0x71};
+	struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)};
+
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct v4l2_frequency f;
+
+	/* set frequency (mt2050) */
+	f.tuner     = 0;
+	f.type      = V4L2_TUNER_DIGITAL_TV;
+	f.frequency = c->frequency / 1000 * 16 / 1000;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(&dev->i2c_adap, &msg, 1);
+	saa_call_all(dev, tuner, s_frequency, &f);
+	msg.buf = on;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+	pinnacle_antenna_pwr(dev, antenna_pwr);
+
+	/* mt352 setup */
+	return mt352_pinnacle_init(fe);
+}
+
+static struct mt352_config pinnacle_300i = {
+	.demod_address = 0x3c >> 1,
+	.adc_clock     = 20333,
+	.if2           = 36150,
+	.no_tuner      = 1,
+	.demod_init    = mt352_pinnacle_init,
+};
+
+static struct mt352_config avermedia_777 = {
+	.demod_address = 0xf,
+	.demod_init    = mt352_aver777_init,
+};
+
+static struct mt352_config avermedia_xc3028_mt352_dev = {
+	.demod_address   = (0x1e >> 1),
+	.no_tuner        = 1,
+	.demod_init      = mt352_avermedia_xc3028_init,
+};
+
+static struct tda18271_std_map mb86a20s_tda18271_std_map = {
+	.dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+		      .if_lvl = 7, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config kworld_tda18271_config = {
+	.std_map = &mb86a20s_tda18271_std_map,
+	.gate    = TDA18271_GATE_DIGITAL,
+	.config  = 3,	/* Use tuner callback for AGC */
+
+};
+
+static const struct mb86a20s_config kworld_mb86a20s_config = {
+	.demod_address = 0x10,
+};
+
+static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+
+	unsigned char initmsg[] = {0x45, 0x97};
+	unsigned char msg_enable[] = {0x45, 0xc1};
+	unsigned char msg_disable[] = {0x45, 0x81};
+	struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2};
+
+	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+		wprintk("could not access the I2C gate\n");
+		return -EIO;
+	}
+	if (enable)
+		msg.buf = msg_enable;
+	else
+		msg.buf = msg_disable;
+	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+		wprintk("could not access the I2C gate\n");
+		return -EIO;
+	}
+	msleep(20);
+	return 0;
+}
+
+/* ==================================================================
+ * tda1004x based DVB-T cards, helper functions
+ */
+
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
+					   const struct firmware **fw, char *name)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	return request_firmware(fw, name, &dev->pci->dev);
+}
+
+/* ------------------------------------------------------------------
+ * these tuners are tu1216, td1316(a)
+ */
+
+static int philips_tda6651_pll_set(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
+			sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	/* determine charge pump */
+	tuner_frequency = c->frequency + 36166000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	/* determine band */
+	if (c->frequency < 49000000)
+		return -EINVAL;
+	else if (c->frequency < 161000000)
+		band = 1;
+	else if (c->frequency < 444000000)
+		band = 2;
+	else if (c->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	/* setup PLL filter */
+	switch (c->bandwidth_hz) {
+	case 6000000:
+		filter = 0;
+		break;
+
+	case 7000000:
+		filter = 0;
+		break;
+
+	case 8000000:
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* calculate divisor
+	 * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+	 */
+	tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
+
+	/* setup tuner buffer */
+	tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+		wprintk("could not write to tuner at addr: 0x%02x\n",
+			addr << 1);
+		return -EIO;
+	}
+	msleep(1);
+	return 0;
+}
+
+static int philips_tu1216_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
+	static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+	/* setup PLL configuration */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config philips_tu1216_60_config = {
+	.demod_address = 0x8,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_4M,
+	.agc_config    = TDA10046_AGC_DEFAULT,
+	.if_freq       = TDA10046_FREQ_3617,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tu1216_61_config = {
+
+	.demod_address = 0x8,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_4M,
+	.agc_config    = TDA10046_AGC_DEFAULT,
+	.if_freq       = TDA10046_FREQ_3617,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------ */
+
+static int philips_td1316_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
+	static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
+	struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+	/* setup PLL configuration */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static int philips_td1316_tuner_set_params(struct dvb_frontend *fe)
+{
+	return philips_tda6651_pll_set(fe);
+}
+
+static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+	u8 addr = state->config->tuner_address;
+	static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
+	struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+	/* switch the tuner to analog mode */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int philips_europa_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 msg[] = { 0x00, 0x40};
+	struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+
+	if (philips_td1316_tuner_init(fe))
+		return -EIO;
+	msleep(1);
+	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+
+	static u8 msg[] = { 0x00, 0x14 };
+	struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+	if (philips_td1316_tuner_sleep(fe))
+		return -EIO;
+
+	/* switch the board to analog mode */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
+	return 0;
+}
+
+static int philips_europa_demod_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+
+	if (dev->original_demod_sleep)
+		dev->original_demod_sleep(fe);
+	fe->ops.i2c_gate_ctrl(fe, 1);
+	return 0;
+}
+
+static struct tda1004x_config philips_europa_config = {
+
+	.demod_address = 0x8,
+	.invert        = 0,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_4M,
+	.agc_config    = TDA10046_AGC_IFO_AUTO_POS,
+	.if_freq       = TDA10046_FREQ_052,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config medion_cardbus = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_IFO_AUTO_NEG,
+	.if_freq       = TDA10046_FREQ_3613,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config technotrend_budget_t3000_config = {
+	.demod_address = 0x8,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_4M,
+	.agc_config    = TDA10046_AGC_DEFAULT,
+	.if_freq       = TDA10046_FREQ_3617,
+	.tuner_address = 0x63,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * tda 1004x based cards with philips silicon tuner
+ */
+
+static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
+{
+	struct tda1004x_state *state = fe->demodulator_priv;
+
+	u8 addr = state->config->i2c_gate;
+	static u8 tda8290_close[] = { 0x21, 0xc0};
+	static u8 tda8290_open[]  = { 0x21, 0x80};
+	struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
+	if (enable) {
+		tda8290_msg.buf = tda8290_close;
+	} else {
+		tda8290_msg.buf = tda8290_open;
+	}
+	if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
+		struct saa7134_dev *dev = fe->dvb->priv;
+		wprintk("could not access tda8290 I2C gate\n");
+		return -EIO;
+	}
+	msleep(20);
+	return 0;
+}
+
+static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+
+	switch (state->config->antenna_switch) {
+	case 0: break;
+	case 1:	dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+		saa7134_set_gpio(dev, 21, 0);
+		break;
+	case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+		saa7134_set_gpio(dev, 21, 1);
+		break;
+	}
+	return 0;
+}
+
+static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	struct tda1004x_state *state = fe->demodulator_priv;
+
+	switch (state->config->antenna_switch) {
+	case 0: break;
+	case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+		saa7134_set_gpio(dev, 21, 1);
+		break;
+	case 2:	dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+		saa7134_set_gpio(dev, 21, 0);
+		break;
+	}
+	return 0;
+}
+
+static int configure_tda827x_fe(struct saa7134_dev *dev,
+				struct tda1004x_config *cdec_conf,
+				struct tda827x_config *tuner_conf)
+{
+	struct videobuf_dvb_frontend *fe0;
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+
+	fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap);
+	if (fe0->dvb.frontend) {
+		if (cdec_conf->i2c_gate)
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+		if (dvb_attach(tda827x_attach, fe0->dvb.frontend,
+			       cdec_conf->tuner_address,
+			       &dev->i2c_adap, tuner_conf))
+			return 0;
+
+		wprintk("no tda827x tuner found at addr: %02x\n",
+				cdec_conf->tuner_address);
+	}
+	return -EINVAL;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda827x_config tda827x_cfg_0 = {
+	.init = philips_tda827x_tuner_init,
+	.sleep = philips_tda827x_tuner_sleep,
+	.config = 0,
+	.switch_addr = 0
+};
+
+static struct tda827x_config tda827x_cfg_1 = {
+	.init = philips_tda827x_tuner_init,
+	.sleep = philips_tda827x_tuner_sleep,
+	.config = 1,
+	.switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2 = {
+	.init = philips_tda827x_tuner_init,
+	.sleep = philips_tda827x_tuner_sleep,
+	.config = 2,
+	.switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2_sw42 = {
+	.init = philips_tda827x_tuner_init,
+	.sleep = philips_tda827x_tuner_sleep,
+	.config = 2,
+	.switch_addr = 0x42
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config tda827x_lifeview_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_pci_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_s_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config hauppauge_hvr_1110_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_dual_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config lifeview_trio_config = {
+	.demod_address = 0x09,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP00_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config tevion_dvbt220rf_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config md8800_dvbt_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x60,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_4871_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_hybrid_lna_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 2,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config kworld_dvb_t_210_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config avermedia_super_007_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x60,
+	.antenna_switch= 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config twinhan_dtv_dvb_3056_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP01_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x42,
+	.tuner_address = 0x61,
+	.antenna_switch = 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_tiger_3in1_config = {
+	.demod_address = 0x0b,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch = 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_ps3_100_config = {
+	.demod_address = 0x0b,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP11_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.i2c_gate      = 0x4b,
+	.tuner_address = 0x61,
+	.antenna_switch = 1,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * special case: this card uses saa713x GPIO22 for the mode switch
+ */
+
+static int ads_duo_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	philips_tda827x_tuner_init(fe);
+	/* route TDA8275a AGC input to the channel decoder */
+	saa7134_set_gpio(dev, 22, 1);
+	return 0;
+}
+
+static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	/* route TDA8275a AGC input to the analog IF chip*/
+	saa7134_set_gpio(dev, 22, 0);
+	philips_tda827x_tuner_sleep(fe);
+	return 0;
+}
+
+static struct tda827x_config ads_duo_cfg = {
+	.init = ads_duo_tuner_init,
+	.sleep = ads_duo_tuner_sleep,
+	.config = 0
+};
+
+static struct tda1004x_config ads_tech_duo_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X,
+	.gpio_config   = TDA10046_GP00_I,
+	.if_freq       = TDA10046_FREQ_045,
+	.tuner_address = 0x61,
+	.request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct zl10353_config behold_h6_config = {
+	.demod_address = 0x1e>>1,
+	.no_tuner      = 1,
+	.parallel_ts   = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static struct xc5000_config behold_x7_tunerconfig = {
+	.i2c_address      = 0xc2>>1,
+	.if_khz           = 4560,
+	.radio_input      = XC5000_RADIO_FM1,
+};
+
+static struct zl10353_config behold_x7_config = {
+	.demod_address = 0x1e>>1,
+	.if2           = 45600,
+	.no_tuner      = 1,
+	.parallel_ts   = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static struct zl10353_config videomate_t750_zl10353_config = {
+	.demod_address         = 0x0f,
+	.no_tuner              = 1,
+	.parallel_ts           = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static struct qt1010_config videomate_t750_qt1010_config = {
+	.i2c_address = 0x62
+};
+
+
+/* ==================================================================
+ * tda10086 based DVB-S cards, helper functions
+ */
+
+static struct tda10086_config flydvbs = {
+	.demod_address = 0x0e,
+	.invert = 0,
+	.diseqc_tone = 0,
+	.xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct tda10086_config sd1878_4m = {
+	.demod_address = 0x0e,
+	.invert = 0,
+	.diseqc_tone = 0,
+	.xtal_freq = TDA10086_XTAL_4M,
+};
+
+/* ------------------------------------------------------------------
+ * special case: lnb supply is connected to the gated i2c
+ */
+
+static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	int res = -EIO;
+	struct saa7134_dev *dev = fe->dvb->priv;
+	if (fe->ops.i2c_gate_ctrl) {
+		fe->ops.i2c_gate_ctrl(fe, 1);
+		if (dev->original_set_voltage)
+			res = dev->original_set_voltage(fe, voltage);
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+	return res;
+};
+
+static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg)
+{
+	int res = -EIO;
+	struct saa7134_dev *dev = fe->dvb->priv;
+	if (fe->ops.i2c_gate_ctrl) {
+		fe->ops.i2c_gate_ctrl(fe, 1);
+		if (dev->original_set_high_voltage)
+			res = dev->original_set_high_voltage(fe, arg);
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+	return res;
+};
+
+static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	u8 wbuf[2] = { 0x1f, 00 };
+	u8 rbuf;
+	struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 },
+				 { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } };
+
+	if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2)
+		return -EIO;
+	/* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */
+	if (voltage == SEC_VOLTAGE_18)
+		wbuf[1] = rbuf | 0x10;
+	else
+		wbuf[1] = rbuf & 0xef;
+	msg[0].len = 2;
+	i2c_transfer(&dev->i2c_adap, msg, 1);
+	return 0;
+}
+
+static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__);
+	return -EIO;
+}
+
+/* ==================================================================
+ * nxt200x based ATSC cards, helper functions
+ */
+
+static struct nxt200x_config avertvhda180 = {
+	.demod_address    = 0x0a,
+};
+
+static struct nxt200x_config kworldatsc110 = {
+	.demod_address    = 0x0a,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct mt312_config avertv_a700_mt312 = {
+	.demod_address = 0x0e,
+	.voltage_inverted = 1,
+};
+
+static struct zl10036_config avertv_a700_tuner = {
+	.tuner_address = 0x60,
+};
+
+static struct mt312_config zl10313_compro_s350_config = {
+	.demod_address = 0x0e,
+};
+
+static struct lgdt3305_config hcw_lgdt3305_config = {
+	.i2c_addr           = 0x0e,
+	.mpeg_mode          = LGDT3305_MPEG_SERIAL,
+	.tpclk_edge         = LGDT3305_TPCLK_RISING_EDGE,
+	.tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
+	.deny_i2c_rptr      = 1,
+	.spectral_inversion = 1,
+	.qam_if_khz         = 4000,
+	.vsb_if_khz         = 3250,
+};
+
+static struct tda10048_config hcw_tda10048_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_SERIAL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3500,
+	.dtv8_if_freq_khz = TDA10048_IF_4000,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+	.disable_gate_access = 1,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x58, },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x58, },
+};
+
+static struct tda18271_config hcw_tda18271_config = {
+	.std_map = &hauppauge_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+	.config  = 3,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+	.probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda10048_config zolid_tda10048_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_PARALLEL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3500,
+	.dtv8_if_freq_khz = TDA10048_IF_4000,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+	.disable_gate_access = 1,
+};
+
+static struct tda18271_config zolid_tda18271_config = {
+	.gate    = TDA18271_GATE_ANALOG,
+};
+
+static struct tda10048_config dtv1000s_tda10048_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_PARALLEL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3800,
+	.dtv8_if_freq_khz = TDA10048_IF_4300,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+	.disable_gate_access = 1,
+};
+
+static struct tda18271_std_map dtv1000s_tda18271_std_map = {
+	.dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_7   = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_8   = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config dtv1000s_tda18271_config = {
+	.std_map = &dtv1000s_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+};
+
+static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = {
+	.prod = LGS8GXX_PROD_LGS8G75,
+	.demod_address = 0x1d,
+	.serial_ts = 0,
+	.ts_clk_pol = 1,
+	.ts_clk_gated = 0,
+	.if_clk_freq = 30400, /* 30.4 MHz */
+	.if_freq = 4000, /* 4.00 MHz */
+	.if_neg_center = 0,
+	.ext_adc = 0,
+	.adc_signed = 1,
+	.adc_vpp = 3, /* 2.0 Vpp */
+	.if_neg_edge = 1,
+};
+
+static struct tda18271_config prohdtv_pro2_tda18271_config = {
+	.gate = TDA18271_GATE_ANALOG,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map kworld_tda18271_std_map = {
+	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config kworld_pc150u_tda18271_config = {
+	.std_map = &kworld_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+	.config  = 3,	/* Use tuner callback for AGC */
+	.rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config kworld_s5h1411_config = {
+	.output_mode   = S5H1411_PARALLEL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.qam_if        = S5H1411_IF_4000,
+	.vsb_if        = S5H1411_IF_3250,
+	.inversion     = S5H1411_INVERSION_ON,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   =
+		S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+
+/* ==================================================================
+ * Core code
+ */
+
+static int dvb_init(struct saa7134_dev *dev)
+{
+	int ret;
+	int attach_xc3028 = 0;
+	struct videobuf_dvb_frontend *fe0;
+
+	/* FIXME: add support for multi-frontend */
+	mutex_init(&dev->frontends.lock);
+	INIT_LIST_HEAD(&dev->frontends.felist);
+
+	printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
+	fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+	if (!fe0) {
+		printk(KERN_ERR "%s() failed to alloc\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* init struct videobuf_dvb */
+	dev->ts.nr_bufs    = 32;
+	dev->ts.nr_packets = 32*4;
+	fe0->dvb.name = dev->name;
+	videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_ALTERNATE,
+			    sizeof(struct saa7134_buf),
+			    dev, NULL);
+
+	switch (dev->board) {
+	case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+		dprintk("pinnacle 300i dvb setup\n");
+		fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_AVERMEDIA_777:
+	case SAA7134_BOARD_AVERMEDIA_A16AR:
+		dprintk("avertv 777 dvb setup\n");
+		fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x61,
+				   TUNER_PHILIPS_TD1316);
+		}
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A16D:
+		dprintk("AverMedia A16D dvb setup\n");
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+						&avermedia_xc3028_mt352_dev,
+						&dev->i2c_adap);
+		attach_xc3028 = 1;
+		break;
+	case SAA7134_BOARD_MD7134:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &medion_cardbus,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, medion_cardbus.tuner_address,
+				   TUNER_PHILIPS_FMD1216ME_MK3);
+		}
+		break;
+	case SAA7134_BOARD_PHILIPS_TOUGH:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &philips_tu1216_60_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+			fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+		}
+		break;
+	case SAA7134_BOARD_FLYDVBTDUO:
+	case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
+		if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_PHILIPS_EUROPA:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+	case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &philips_europa_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+			fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+			fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+			fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+			fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &technotrend_budget_t3000_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+			fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+			fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+			fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+			fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &philips_tu1216_61_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+			fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+		}
+		break;
+	case SAA7134_BOARD_KWORLD_DVBT_210:
+		if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
+					 &tda827x_cfg_2) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
+					       &hcw_tda10048_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &hcw_tda18271_config);
+		}
+		break;
+	case SAA7134_BOARD_PHILIPS_TIGER:
+		if (configure_tda827x_fe(dev, &philips_tiger_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
+		if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
+					 &tda827x_cfg_1) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+		if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
+					 &tda827x_cfg_1) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+		fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+					       &hcw_lgdt3305_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &hcw_tda18271_config);
+		}
+		break;
+	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+		if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_FLYDVBT_LR301:
+		if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_FLYDVB_TRIO:
+		if (!use_frontend) {	/* terrestrial */
+			if (configure_tda827x_fe(dev, &lifeview_trio_config,
+						 &tda827x_cfg_0) < 0)
+				goto detach_frontend;
+		} else {  		/* satellite */
+			fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
+			if (fe0->dvb.frontend) {
+				if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63,
+									&dev->i2c_adap, 0) == NULL) {
+					wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__);
+					goto detach_frontend;
+				}
+				if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
+										0x08, 0, 0) == NULL) {
+					wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
+					goto detach_frontend;
+				}
+			}
+		}
+		break;
+	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &ads_tech_duo_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (dvb_attach(tda827x_attach,fe0->dvb.frontend,
+				   ads_tech_duo_config.tuner_address, &dev->i2c_adap,
+								&ads_duo_cfg) == NULL) {
+				wprintk("no tda827x tuner found at addr: %02x\n",
+					ads_tech_duo_config.tuner_address);
+				goto detach_frontend;
+			}
+		} else
+			wprintk("failed to attach tda10046\n");
+		break;
+	case SAA7134_BOARD_TEVION_DVBT_220RF:
+		if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+		if (!use_frontend) {     /* terrestrial */
+			if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+						 &tda827x_cfg_0) < 0)
+				goto detach_frontend;
+		} else {        /* satellite */
+			fe0->dvb.frontend = dvb_attach(tda10086_attach,
+							&flydvbs, &dev->i2c_adap);
+			if (fe0->dvb.frontend) {
+				struct dvb_frontend *fe = fe0->dvb.frontend;
+				u8 dev_id = dev->eedata[2];
+				u8 data = 0xc4;
+				struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1};
+
+				if (dvb_attach(tda826x_attach, fe0->dvb.frontend,
+						0x60, &dev->i2c_adap, 0) == NULL) {
+					wprintk("%s: Medion Quadro, no tda826x "
+						"found !\n", __func__);
+					goto detach_frontend;
+				}
+				if (dev_id != 0x08) {
+					/* we need to open the i2c gate (we know it exists) */
+					fe->ops.i2c_gate_ctrl(fe, 1);
+					if (dvb_attach(isl6405_attach, fe,
+							&dev->i2c_adap, 0x08, 0, 0) == NULL) {
+						wprintk("%s: Medion Quadro, no ISL6405 "
+							"found !\n", __func__);
+						goto detach_frontend;
+					}
+					if (dev_id == 0x07) {
+						/* fire up the 2nd section of the LNB supply since
+						   we can't do this from the other section */
+						msg.buf = &data;
+						i2c_transfer(&dev->i2c_adap, &msg, 1);
+					}
+					fe->ops.i2c_gate_ctrl(fe, 0);
+					dev->original_set_voltage = fe->ops.set_voltage;
+					fe->ops.set_voltage = md8800_set_voltage;
+					dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+					fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+				} else {
+					fe->ops.set_voltage = md8800_set_voltage2;
+					fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2;
+				}
+			}
+		}
+		break;
+	case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
+		fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend)
+			dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61,
+				   NULL, DVB_PLL_TDHU2);
+		break;
+	case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+	case SAA7134_BOARD_KWORLD_ATSC110:
+		fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend)
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x61,
+				   TUNER_PHILIPS_TUV1236D);
+		break;
+	case SAA7134_BOARD_KWORLD_PC150U:
+		saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
+		saa7134_tuner_callback(dev, 0,
+				       TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+					       &kworld_s5h1411_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &kworld_pc150u_tda18271_config);
+		}
+		break;
+	case SAA7134_BOARD_FLYDVBS_LR300:
+		fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+				       &dev->i2c_adap, 0) == NULL) {
+				wprintk("%s: No tda826x found!\n", __func__);
+				goto detach_frontend;
+			}
+			if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
+				       &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+				wprintk("%s: No ISL6421 found!\n", __func__);
+				goto detach_frontend;
+			}
+		}
+		break;
+	case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+					       &medion_cardbus,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+			fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, medion_cardbus.tuner_address,
+				   TUNER_PHILIPS_FMD1216ME_MK3);
+		}
+		break;
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+		fe0->dvb.frontend = dvb_attach(tda10046_attach,
+				&philips_europa_config,
+				&dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
+			fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+		if (configure_tda827x_fe(dev, &cinergy_ht_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_CINERGY_HT_PCI:
+		if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_PHILIPS_TIGER_S:
+		if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+					 &tda827x_cfg_2) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_ASUS_P7131_4871:
+		if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
+					 &tda827x_cfg_2) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+		if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
+					 &tda827x_cfg_2) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+		if (configure_tda827x_fe(dev, &avermedia_super_007_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
+		if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
+					 &tda827x_cfg_2_sw42) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_PHILIPS_SNAKE:
+		fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+					&dev->i2c_adap, 0) == NULL) {
+				wprintk("%s: No tda826x found!\n", __func__);
+				goto detach_frontend;
+			}
+			if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+					&dev->i2c_adap, 0, 0) == NULL) {
+				wprintk("%s: No lnbp21 found!\n", __func__);
+				goto detach_frontend;
+			}
+		}
+		break;
+	case SAA7134_BOARD_CREATIX_CTX953:
+		if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
+		if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+					 &tda827x_cfg_2) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+		dprintk("AverMedia E506R dvb setup\n");
+		saa7134_set_gpio(dev, 25, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 25, 1);
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+						&avermedia_xc3028_mt352_dev,
+						&dev->i2c_adap);
+		attach_xc3028 = 1;
+		break;
+	case SAA7134_BOARD_MD7134_BRIDGE_2:
+		fe0->dvb.frontend = dvb_attach(tda10086_attach,
+						&sd1878_4m, &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			struct dvb_frontend *fe;
+			if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+				  &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
+				wprintk("%s: MD7134 DVB-S, no SD1878 "
+					"found !\n", __func__);
+				goto detach_frontend;
+			}
+			/* we need to open the i2c gate (we know it exists) */
+			fe = fe0->dvb.frontend;
+			fe->ops.i2c_gate_ctrl(fe, 1);
+			if (dvb_attach(isl6405_attach, fe,
+					&dev->i2c_adap, 0x08, 0, 0) == NULL) {
+				wprintk("%s: MD7134 DVB-S, no ISL6405 "
+					"found !\n", __func__);
+				goto detach_frontend;
+			}
+			fe->ops.i2c_gate_ctrl(fe, 0);
+			dev->original_set_voltage = fe->ops.set_voltage;
+			fe->ops.set_voltage = md8800_set_voltage;
+			dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+			fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+		}
+		break;
+	case SAA7134_BOARD_AVERMEDIA_M103:
+		saa7134_set_gpio(dev, 25, 0);
+		msleep(10);
+		saa7134_set_gpio(dev, 25, 1);
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
+						&avermedia_xc3028_mt352_dev,
+						&dev->i2c_adap);
+		attach_xc3028 = 1;
+		break;
+	case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+		if (!use_frontend) {     /* terrestrial */
+			if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
+							&tda827x_cfg_2) < 0)
+				goto detach_frontend;
+		} else {  		/* satellite */
+			fe0->dvb.frontend = dvb_attach(tda10086_attach,
+						&flydvbs, &dev->i2c_adap);
+			if (fe0->dvb.frontend) {
+				if (dvb_attach(tda826x_attach,
+						fe0->dvb.frontend, 0x60,
+						&dev->i2c_adap, 0) == NULL) {
+					wprintk("%s: Asus Tiger 3in1, no "
+						"tda826x found!\n", __func__);
+					goto detach_frontend;
+				}
+				if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+						&dev->i2c_adap, 0, 0) == NULL) {
+					wprintk("%s: Asus Tiger 3in1, no lnbp21"
+						" found!\n", __func__);
+				       goto detach_frontend;
+			       }
+		       }
+	       }
+	       break;
+	case SAA7134_BOARD_ASUSTeK_PS3_100:
+		if (!use_frontend) {     /* terrestrial */
+			if (configure_tda827x_fe(dev, &asus_ps3_100_config,
+						 &tda827x_cfg_2) < 0)
+				goto detach_frontend;
+	       } else {                /* satellite */
+			fe0->dvb.frontend = dvb_attach(tda10086_attach,
+						       &flydvbs, &dev->i2c_adap);
+			if (fe0->dvb.frontend) {
+				if (dvb_attach(tda826x_attach,
+					       fe0->dvb.frontend, 0x60,
+					       &dev->i2c_adap, 0) == NULL) {
+					wprintk("%s: Asus My Cinema PS3-100, no "
+						"tda826x found!\n", __func__);
+					goto detach_frontend;
+				}
+				if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+					       &dev->i2c_adap, 0, 0) == NULL) {
+					wprintk("%s: Asus My Cinema PS3-100, no lnbp21"
+						" found!\n", __func__);
+					goto detach_frontend;
+				}
+			}
+		}
+		break;
+	case SAA7134_BOARD_ASUSTeK_TIGER:
+		if (configure_tda827x_fe(dev, &philips_tiger_config,
+					 &tda827x_cfg_0) < 0)
+			goto detach_frontend;
+		break;
+	case SAA7134_BOARD_BEHOLD_H6:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+						&behold_h6_config,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x61,
+				   TUNER_PHILIPS_FMD1216MEX_MK3);
+		}
+		break;
+	case SAA7134_BOARD_BEHOLD_X7:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+						&behold_x7_config,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, &behold_x7_tunerconfig);
+		}
+		break;
+	case SAA7134_BOARD_BEHOLD_H7:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+						&behold_x7_config,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, &behold_x7_tunerconfig);
+		}
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+	case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+		/* Zarlink ZL10313 */
+		fe0->dvb.frontend = dvb_attach(mt312_attach,
+			&avertv_a700_mt312, &dev->i2c_adap);
+		if (fe0->dvb.frontend) {
+			if (dvb_attach(zl10036_attach, fe0->dvb.frontend,
+					&avertv_a700_tuner, &dev->i2c_adap) == NULL) {
+				wprintk("%s: No zl10036 found!\n",
+					__func__);
+			}
+		}
+		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		fe0->dvb.frontend = dvb_attach(mt312_attach,
+				&zl10313_compro_s350_config, &dev->i2c_adap);
+		if (fe0->dvb.frontend)
+			if (dvb_attach(zl10039_attach, fe0->dvb.frontend,
+					0x60, &dev->i2c_adap) == NULL)
+				wprintk("%s: No zl10039 found!\n",
+					__func__);
+
+		break;
+	case SAA7134_BOARD_VIDEOMATE_T750:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+						&videomate_t750_zl10353_config,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (dvb_attach(qt1010_attach,
+					fe0->dvb.frontend,
+					&dev->i2c_adap,
+					&videomate_t750_qt1010_config) == NULL)
+				wprintk("error attaching QT1010\n");
+		}
+		break;
+	case SAA7134_BOARD_ZOLID_HYBRID_PCI:
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
+					       &zolid_tda10048_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &zolid_tda18271_config);
+		}
+		break;
+	case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
+					       &dtv1000s_tda10048_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &dtv1000s_tda18271_config);
+		}
+		break;
+	case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+		/* Switch to digital mode */
+		saa7134_tuner_callback(dev, 0,
+				       TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+		fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
+					       &kworld_mb86a20s_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl;
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &kworld_tda18271_config);
+		}
+
+		/* mb86a20s need to use the I2C gateway */
+		break;
+	case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+		fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+					       &prohdtv_pro2_lgs8g75_config,
+					       &dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
+				   &dev->i2c_adap, 0x4b,
+				   &tda829x_no_probe);
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
+				   0x60, &dev->i2c_adap,
+				   &prohdtv_pro2_tda18271_config);
+		}
+		break;
+	default:
+		wprintk("Huh? unknown DVB card?\n");
+		break;
+	}
+
+	if (attach_xc3028) {
+		struct dvb_frontend *fe;
+		struct xc2028_config cfg = {
+			.i2c_adap  = &dev->i2c_adap,
+			.i2c_addr  = 0x61,
+		};
+
+		if (!fe0->dvb.frontend)
+			goto detach_frontend;
+
+		fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+		if (!fe) {
+			printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+			       dev->name);
+			goto detach_frontend;
+		}
+	}
+
+	if (NULL == fe0->dvb.frontend) {
+		printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
+		goto detach_frontend;
+	}
+	/* define general-purpose callback pointer */
+	fe0->dvb.frontend->callback = saa7134_tuner_callback;
+
+	/* register everything else */
+	ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+					&dev->pci->dev, adapter_nr, 0);
+
+	/* this sequence is necessary to make the tda1004x load its firmware
+	 * and to enter analog mode of hybrid boards
+	 */
+	if (!ret) {
+		if (fe0->dvb.frontend->ops.init)
+			fe0->dvb.frontend->ops.init(fe0->dvb.frontend);
+		if (fe0->dvb.frontend->ops.sleep)
+			fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend);
+		if (fe0->dvb.frontend->ops.tuner_ops.sleep)
+			fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend);
+	}
+	return ret;
+
+detach_frontend:
+	videobuf_dvb_dealloc_frontends(&dev->frontends);
+	return -EINVAL;
+}
+
+static int dvb_fini(struct saa7134_dev *dev)
+{
+	struct videobuf_dvb_frontend *fe0;
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	/* 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;
+
+		/* otherwise we don't detect the tuner on next insmod */
+		saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+	} else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) {
+		if ((dev->eedata[2] == 0x07) && use_frontend) {
+			/* turn off the 2nd lnb supply */
+			u8 data = 0x80;
+			struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1};
+			struct dvb_frontend *fe;
+			fe = fe0->dvb.frontend;
+			if (fe->ops.i2c_gate_ctrl) {
+				fe->ops.i2c_gate_ctrl(fe, 1);
+				i2c_transfer(&dev->i2c_adap, &msg, 1);
+				fe->ops.i2c_gate_ctrl(fe, 0);
+			}
+		}
+	}
+	videobuf_dvb_unregister_bus(&dev->frontends);
+	return 0;
+}
+
+static struct saa7134_mpeg_ops dvb_ops = {
+	.type          = SAA7134_MPEG_DVB,
+	.init          = dvb_init,
+	.fini          = dvb_fini,
+};
+
+static int __init dvb_register(void)
+{
+	return saa7134_ts_register(&dvb_ops);
+}
+
+static void __exit dvb_unregister(void)
+{
+	saa7134_ts_unregister(&dvb_ops);
+}
+
+module_init(dvb_register);
+module_exit(dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
new file mode 100644
index 000000000000..4df79c656909
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -0,0 +1,590 @@
+/*
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#include <media/saa6752hs.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(empress_nr, int, NULL, 0444);
+MODULE_PARM_DESC(empress_nr,"ts device number");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...)	if (debug)			\
+	printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void ts_reset_encoder(struct saa7134_dev* dev)
+{
+	if (!dev->empress_started)
+		return;
+
+	saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+	msleep(10);
+	saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+	msleep(100);
+	dev->empress_started = 0;
+}
+
+static int ts_init_encoder(struct saa7134_dev* dev)
+{
+	u32 leading_null_bytes = 0;
+
+	/* If more cards start to need this, then this
+	   should probably be added to the card definitions. */
+	switch (dev->board) {
+	case SAA7134_BOARD_BEHOLD_M6:
+	case SAA7134_BOARD_BEHOLD_M63:
+	case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+		leading_null_bytes = 1;
+		break;
+	}
+	ts_reset_encoder(dev);
+	saa_call_all(dev, core, init, leading_null_bytes);
+	dev->empress_started = 1;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int ts_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct saa7134_dev *dev = video_drvdata(file);
+	int err;
+
+	dprintk("open dev=%s\n", video_device_node_name(vdev));
+	err = -EBUSY;
+	if (!mutex_trylock(&dev->empress_tsq.vb_lock))
+		return err;
+	if (atomic_read(&dev->empress_users))
+		goto done;
+
+	/* Unmute audio */
+	saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+		saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
+
+	atomic_inc(&dev->empress_users);
+	file->private_data = dev;
+	err = 0;
+
+done:
+	mutex_unlock(&dev->empress_tsq.vb_lock);
+	return err;
+}
+
+static int ts_release(struct file *file)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	videobuf_stop(&dev->empress_tsq);
+	videobuf_mmap_free(&dev->empress_tsq);
+
+	/* stop the encoder */
+	ts_reset_encoder(dev);
+
+	/* Mute audio */
+	saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+		saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+
+	atomic_dec(&dev->empress_users);
+
+	return 0;
+}
+
+static ssize_t
+ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	if (!dev->empress_started)
+		ts_init_encoder(dev);
+
+	return videobuf_read_stream(&dev->empress_tsq,
+				    data, count, ppos, 0,
+				    file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+ts_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+}
+
+
+static int
+ts_mmap(struct file *file, struct vm_area_struct * vma)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_mmap_mapper(&dev->empress_tsq, vma);
+}
+
+/*
+ * 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 empress_querycap(struct file *file, void  *priv,
+					struct v4l2_capability *cap)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	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->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE |
+		V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int empress_enum_input(struct file *file, void *priv,
+					struct v4l2_input *i)
+{
+	if (i->index != 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strcpy(i->name, "CCIR656");
+
+	return 0;
+}
+
+static int empress_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int empress_s_input(struct file *file, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int empress_enum_fmt_vid_cap(struct file *file, void  *priv,
+					struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG TS", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int empress_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7134_dev *dev = file->private_data;
+	struct v4l2_mbus_framefmt mbus_fmt;
+
+	saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt);
+
+	v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+	return 0;
+}
+
+static int empress_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7134_dev *dev = file->private_data;
+	struct v4l2_mbus_framefmt mbus_fmt;
+
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+	saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+	v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+	return 0;
+}
+
+static int empress_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+	return 0;
+}
+
+static int empress_reqbufs(struct file *file, void *priv,
+					struct v4l2_requestbuffers *p)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_reqbufs(&dev->empress_tsq, p);
+}
+
+static int empress_querybuf(struct file *file, void *priv,
+					struct v4l2_buffer *b)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_querybuf(&dev->empress_tsq, b);
+}
+
+static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_qbuf(&dev->empress_tsq, b);
+}
+
+static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	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_dev *dev = file->private_data;
+
+	return videobuf_streamon(&dev->empress_tsq);
+}
+
+static int empress_streamoff(struct file *file, void *priv,
+					enum v4l2_buf_type type)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return videobuf_streamoff(&dev->empress_tsq);
+}
+
+static int empress_s_ext_ctrls(struct file *file, void *priv,
+			       struct v4l2_ext_controls *ctrls)
+{
+	struct saa7134_dev *dev = file->private_data;
+	int err;
+
+	/* 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;
+
+	err = saa_call_empress(dev, core, s_ext_ctrls, ctrls);
+	ts_init_encoder(dev);
+
+	return err;
+}
+
+static int empress_g_ext_ctrls(struct file *file, void *priv,
+			       struct v4l2_ext_controls *ctrls)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+	return saa_call_empress(dev, core, g_ext_ctrls, ctrls);
+}
+
+static int empress_g_ctrl(struct file *file, void *priv,
+					struct v4l2_control *c)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return saa7134_g_ctrl_internal(dev, NULL, c);
+}
+
+static int empress_s_ctrl(struct file *file, void *priv,
+					struct v4l2_control *c)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return saa7134_s_ctrl_internal(dev, NULL, c);
+}
+
+static int empress_queryctrl(struct file *file, void *priv,
+					struct v4l2_queryctrl *c)
+{
+	/* Must be sorted from low to high control ID! */
+	static const u32 user_ctrls[] = {
+		V4L2_CID_USER_CLASS,
+		V4L2_CID_BRIGHTNESS,
+		V4L2_CID_CONTRAST,
+		V4L2_CID_SATURATION,
+		V4L2_CID_HUE,
+		V4L2_CID_AUDIO_VOLUME,
+		V4L2_CID_AUDIO_MUTE,
+		V4L2_CID_HFLIP,
+		0
+	};
+
+	/* Must be sorted from low to high control ID! */
+	static const u32 mpeg_ctrls[] = {
+		V4L2_CID_MPEG_CLASS,
+		V4L2_CID_MPEG_STREAM_TYPE,
+		V4L2_CID_MPEG_STREAM_PID_PMT,
+		V4L2_CID_MPEG_STREAM_PID_AUDIO,
+		V4L2_CID_MPEG_STREAM_PID_VIDEO,
+		V4L2_CID_MPEG_STREAM_PID_PCR,
+		V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+		V4L2_CID_MPEG_AUDIO_ENCODING,
+		V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+		V4L2_CID_MPEG_VIDEO_ENCODING,
+		V4L2_CID_MPEG_VIDEO_ASPECT,
+		V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+		V4L2_CID_MPEG_VIDEO_BITRATE,
+		V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+		0
+	};
+	static const u32 *ctrl_classes[] = {
+		user_ctrls,
+		mpeg_ctrls,
+		NULL
+	};
+	struct saa7134_dev *dev = file->private_data;
+
+	c->id = v4l2_ctrl_next(ctrl_classes, c->id);
+	if (c->id == 0)
+		return -EINVAL;
+	if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
+		return v4l2_ctrl_query_fill(c, 0, 0, 0, 0);
+	if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+		return saa7134_queryctrl(file, priv, c);
+	return saa_call_empress(dev, core, queryctrl, c);
+}
+
+static int empress_querymenu(struct file *file, void *priv,
+					struct v4l2_querymenu *c)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+	return saa_call_empress(dev, core, querymenu, c);
+}
+
+static int empress_g_chip_ident(struct file *file, void *fh,
+	       struct v4l2_dbg_chip_ident *chip)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER &&
+	    !strcmp(chip->match.name, "saa6752hs"))
+		return saa_call_empress(dev, core, g_chip_ident, chip);
+	if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR)
+		return saa_call_empress(dev, core, g_chip_ident, chip);
+	return -EINVAL;
+}
+
+static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	return saa7134_s_std_internal(dev, NULL, id);
+}
+
+static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7134_dev *dev = file->private_data;
+
+	*id = dev->tvnorm->id;
+	return 0;
+}
+
+static const struct v4l2_file_operations ts_fops =
+{
+	.owner	  = THIS_MODULE,
+	.open	  = ts_open,
+	.release  = ts_release,
+	.read	  = ts_read,
+	.poll	  = ts_poll,
+	.mmap	  = ts_mmap,
+	.ioctl	  = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops ts_ioctl_ops = {
+	.vidioc_querycap		= empress_querycap,
+	.vidioc_enum_fmt_vid_cap	= empress_enum_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		= empress_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		= empress_s_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		= empress_g_fmt_vid_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		= empress_queryctrl,
+	.vidioc_querymenu		= empress_querymenu,
+	.vidioc_g_ctrl			= empress_g_ctrl,
+	.vidioc_s_ctrl			= empress_s_ctrl,
+	.vidioc_g_chip_ident 		= empress_g_chip_ident,
+	.vidioc_s_std			= empress_s_std,
+	.vidioc_g_std			= empress_g_std,
+};
+
+/* ----------------------------------------------------------- */
+
+static struct video_device saa7134_empress_template = {
+	.name          = "saa7134-empress",
+	.fops          = &ts_fops,
+	.ioctl_ops     = &ts_ioctl_ops,
+
+	.tvnorms			= SAA7134_NORMS,
+	.current_norm			= V4L2_STD_PAL,
+};
+
+static void empress_signal_update(struct work_struct *work)
+{
+	struct saa7134_dev* dev =
+		container_of(work, struct saa7134_dev, empress_workqueue);
+
+	if (dev->nosignal) {
+		dprintk("no video signal\n");
+	} else {
+		dprintk("video signal acquired\n");
+	}
+}
+
+static void empress_signal_change(struct saa7134_dev *dev)
+{
+	schedule_work(&dev->empress_workqueue);
+}
+
+
+static int empress_init(struct saa7134_dev *dev)
+{
+	int err;
+
+	dprintk("%s: %s\n",dev->name,__func__);
+	dev->empress_dev = video_device_alloc();
+	if (NULL == dev->empress_dev)
+		return -ENOMEM;
+	*(dev->empress_dev) = saa7134_empress_template;
+	dev->empress_dev->parent  = &dev->pci->dev;
+	dev->empress_dev->release = video_device_release;
+	snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
+		 "%s empress (%s)", dev->name,
+		 saa7134_boards[dev->board].name);
+
+	INIT_WORK(&dev->empress_workqueue, empress_signal_update);
+
+	video_set_drvdata(dev->empress_dev, dev);
+	err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
+				    empress_nr[dev->nr]);
+	if (err < 0) {
+		printk(KERN_INFO "%s: can't register video device\n",
+		       dev->name);
+		video_device_release(dev->empress_dev);
+		dev->empress_dev = NULL;
+		return err;
+	}
+	printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+	       dev->name, video_device_node_name(dev->empress_dev));
+
+	videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_ALTERNATE,
+			    sizeof(struct saa7134_buf),
+			    dev, NULL);
+
+	empress_signal_update(&dev->empress_workqueue);
+	return 0;
+}
+
+static int empress_fini(struct saa7134_dev *dev)
+{
+	dprintk("%s: %s\n",dev->name,__func__);
+
+	if (NULL == dev->empress_dev)
+		return 0;
+	flush_work(&dev->empress_workqueue);
+	video_unregister_device(dev->empress_dev);
+	dev->empress_dev = NULL;
+	return 0;
+}
+
+static struct saa7134_mpeg_ops empress_ops = {
+	.type          = SAA7134_MPEG_EMPRESS,
+	.init          = empress_init,
+	.fini          = empress_fini,
+	.signal_change = empress_signal_change,
+};
+
+static int __init empress_register(void)
+{
+	return saa7134_ts_register(&empress_ops);
+}
+
+static void __exit empress_unregister(void)
+{
+	saa7134_ts_unregister(&empress_ops);
+}
+
+module_init(empress_register);
+module_exit(empress_unregister);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c
new file mode 100644
index 000000000000..a176ec3285e0
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-i2c.c
@@ -0,0 +1,435 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * i2c interface support
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+#define d1printk if (1 == i2c_debug) printk
+#define d2printk if (2 == i2c_debug) printk
+
+#define I2C_WAIT_DELAY  32
+#define I2C_WAIT_RETRY  16
+
+/* ----------------------------------------------------------- */
+
+static char *str_i2c_status[] = {
+	"IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE",
+	"DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE",
+	"NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR"
+};
+
+enum i2c_status {
+	IDLE          = 0,  // no I2C command pending
+	DONE_STOP     = 1,  // I2C command done and STOP executed
+	BUSY          = 2,  // executing I2C command
+	TO_SCL        = 3,  // executing I2C command, time out on clock stretching
+	TO_ARB        = 4,  // time out on arbitration trial, still trying
+	DONE_WRITE    = 5,  // I2C command done and awaiting next write command
+	DONE_READ     = 6,  // I2C command done and awaiting next read command
+	DONE_WRITE_TO = 7,  // see 5, and time out on status echo
+	DONE_READ_TO  = 8,  // see 6, and time out on status echo
+	NO_DEVICE     = 9,  // no acknowledge on device slave address
+	NO_ACKN       = 10, // no acknowledge after data byte transfer
+	BUS_ERR       = 11, // bus error
+	ARB_LOST      = 12, // arbitration lost during transfer
+	SEQ_ERR       = 13, // erroneous programming sequence
+	ST_ERR        = 14, // wrong status echoing
+	SW_ERR        = 15  // software error
+};
+
+static char *str_i2c_attr[] = {
+	"NOP", "STOP", "CONTINUE", "START"
+};
+
+enum i2c_attr {
+	NOP           = 0,  // no operation on I2C bus
+	STOP          = 1,  // stop condition, no associated byte transfer
+	CONTINUE      = 2,  // continue with byte transfer
+	START         = 3   // start condition with byte transfer
+};
+
+static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev)
+{
+	enum i2c_status status;
+
+	status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f;
+	d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name,
+		 str_i2c_status[status]);
+	return status;
+}
+
+static inline void i2c_set_status(struct saa7134_dev *dev,
+				  enum i2c_status status)
+{
+	d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name,
+		 str_i2c_status[status]);
+	saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status);
+}
+
+static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr)
+{
+	d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name,
+		 str_i2c_attr[attr]);
+	saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6);
+}
+
+static inline int i2c_is_error(enum i2c_status status)
+{
+	switch (status) {
+	case NO_DEVICE:
+	case NO_ACKN:
+	case BUS_ERR:
+	case ARB_LOST:
+	case SEQ_ERR:
+	case ST_ERR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline int i2c_is_idle(enum i2c_status status)
+{
+	switch (status) {
+	case IDLE:
+	case DONE_STOP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline int i2c_is_busy(enum i2c_status status)
+{
+	switch (status) {
+	case BUSY:
+	case TO_SCL:
+	case TO_ARB:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int i2c_is_busy_wait(struct saa7134_dev *dev)
+{
+	enum i2c_status status;
+	int count;
+
+	for (count = 0; count < I2C_WAIT_RETRY; count++) {
+		status = i2c_get_status(dev);
+		if (!i2c_is_busy(status))
+			break;
+		saa_wait(I2C_WAIT_DELAY);
+	}
+	if (I2C_WAIT_RETRY == count)
+		return false;
+	return true;
+}
+
+static int i2c_reset(struct saa7134_dev *dev)
+{
+	enum i2c_status status;
+	int count;
+
+	d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name);
+	status = i2c_get_status(dev);
+	if (!i2c_is_error(status))
+		return true;
+	i2c_set_status(dev,status);
+
+	for (count = 0; count < I2C_WAIT_RETRY; count++) {
+		status = i2c_get_status(dev);
+		if (!i2c_is_error(status))
+			break;
+		udelay(I2C_WAIT_DELAY);
+	}
+	if (I2C_WAIT_RETRY == count)
+		return false;
+
+	if (!i2c_is_idle(status))
+		return false;
+
+	i2c_set_attr(dev,NOP);
+	return true;
+}
+
+static inline int i2c_send_byte(struct saa7134_dev *dev,
+				enum i2c_attr attr,
+				unsigned char data)
+{
+	enum i2c_status status;
+	__u32 dword;
+
+	/* have to write both attr + data in one 32bit word */
+	dword  = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2);
+	dword &= 0x0f;
+	dword |= (attr << 6);
+	dword |= ((__u32)data << 8);
+	dword |= 0x00 << 16;  /* 100 kHz */
+//	dword |= 0x40 << 16;  /* 400 kHz */
+	dword |= 0xf0 << 24;
+	saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword);
+	d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data);
+
+	if (!i2c_is_busy_wait(dev))
+		return -EIO;
+	status = i2c_get_status(dev);
+	if (i2c_is_error(status))
+		return -EIO;
+	return 0;
+}
+
+static inline int i2c_recv_byte(struct saa7134_dev *dev)
+{
+	enum i2c_status status;
+	unsigned char data;
+
+	i2c_set_attr(dev,CONTINUE);
+	if (!i2c_is_busy_wait(dev))
+		return -EIO;
+	status = i2c_get_status(dev);
+	if (i2c_is_error(status))
+		return -EIO;
+	data = saa_readb(SAA7134_I2C_DATA);
+	d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data);
+	return data;
+}
+
+static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
+			    struct i2c_msg *msgs, int num)
+{
+	struct saa7134_dev *dev = i2c_adap->algo_data;
+	enum i2c_status status;
+	unsigned char data;
+	int addr,rc,i,byte;
+
+	status = i2c_get_status(dev);
+	if (!i2c_is_idle(status))
+		if (!i2c_reset(dev))
+			return -EIO;
+
+	d2printk("start xfer\n");
+	d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name);
+	for (i = 0; i < num; i++) {
+		if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) {
+			/* send address */
+			d2printk("send address\n");
+			addr  = msgs[i].addr << 1;
+			if (msgs[i].flags & I2C_M_RD)
+				addr |= 1;
+			if (i > 0 && msgs[i].flags &
+			    I2C_M_RD && msgs[i].addr != 0x40 &&
+			    msgs[i].addr != 0x19) {
+				/* workaround for a saa7134 i2c bug
+				 * needed to talk to the mt352 demux
+				 * thanks to pinnacle for the hint */
+				int quirk = 0xfe;
+				d1printk(" [%02x quirk]",quirk);
+				i2c_send_byte(dev,START,quirk);
+				i2c_recv_byte(dev);
+			}
+			d1printk(" < %02x", addr);
+			rc = i2c_send_byte(dev,START,addr);
+			if (rc < 0)
+				 goto err;
+		}
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read bytes */
+			d2printk("read bytes\n");
+			for (byte = 0; byte < msgs[i].len; byte++) {
+				d1printk(" =");
+				rc = i2c_recv_byte(dev);
+				if (rc < 0)
+					goto err;
+				d1printk("%02x", rc);
+				msgs[i].buf[byte] = rc;
+			}
+			/* discard mysterious extra byte when reading
+			   from Samsung S5H1411.  i2c bus gets error
+			   if we do not. */
+			if (0x19 == msgs[i].addr) {
+				d1printk(" ?");
+				rc = i2c_recv_byte(dev);
+				if (rc < 0)
+					goto err;
+				d1printk("%02x", rc);
+			}
+		} else {
+			/* write bytes */
+			d2printk("write bytes\n");
+			for (byte = 0; byte < msgs[i].len; byte++) {
+				data = msgs[i].buf[byte];
+				d1printk(" %02x", data);
+				rc = i2c_send_byte(dev,CONTINUE,data);
+				if (rc < 0)
+					goto err;
+			}
+		}
+	}
+	d2printk("xfer done\n");
+	d1printk(" >");
+	i2c_set_attr(dev,STOP);
+	rc = -EIO;
+	if (!i2c_is_busy_wait(dev))
+		goto err;
+	status = i2c_get_status(dev);
+	if (i2c_is_error(status))
+		goto err;
+	/* ensure that the bus is idle for at least one bit slot */
+	msleep(1);
+
+	d1printk("\n");
+	return num;
+ err:
+	if (1 == i2c_debug) {
+		status = i2c_get_status(dev);
+		printk(" ERROR: %s\n",str_i2c_status[status]);
+	}
+	return rc;
+}
+
+/* ----------------------------------------------------------- */
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm saa7134_algo = {
+	.master_xfer   = saa7134_i2c_xfer,
+	.functionality = functionality,
+};
+
+static struct i2c_adapter saa7134_adap_template = {
+	.owner         = THIS_MODULE,
+	.name          = "saa7134",
+	.algo          = &saa7134_algo,
+};
+
+static struct i2c_client saa7134_client_template = {
+	.name	= "saa7134 internal",
+};
+
+/* ----------------------------------------------------------- */
+
+static int
+saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len)
+{
+	unsigned char buf;
+	int i,err;
+
+	dev->i2c_client.addr = 0xa0 >> 1;
+	buf = 0;
+	if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) {
+		printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
+		       dev->name,err);
+		return -1;
+	}
+	if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) {
+		printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n",
+		       dev->name,err);
+		return -1;
+	}
+	for (i = 0; i < len; i++) {
+		if (0 == (i % 16))
+			printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i);
+		printk(" %02x",eedata[i]);
+		if (15 == (i % 16))
+			printk("\n");
+	}
+	return 0;
+}
+
+static char *i2c_devs[128] = {
+	[ 0x20      ] = "mpeg encoder (saa6752hs)",
+	[ 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)
+{
+	unsigned char buf;
+	int i,rc;
+
+	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+		c->addr = i;
+		rc = i2c_master_recv(c,&buf,0);
+		if (rc < 0)
+			continue;
+		printk("%s: i2c scan: found device @ 0x%x  [%s]\n",
+		       name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+	}
+}
+
+int saa7134_i2c_register(struct saa7134_dev *dev)
+{
+	dev->i2c_adap = saa7134_adap_template;
+	dev->i2c_adap.dev.parent = &dev->pci->dev;
+	strcpy(dev->i2c_adap.name,dev->name);
+	dev->i2c_adap.algo_data = dev;
+	i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+	i2c_add_adapter(&dev->i2c_adap);
+
+	dev->i2c_client = saa7134_client_template;
+	dev->i2c_client.adapter = &dev->i2c_adap;
+
+	saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata));
+	if (i2c_scan)
+		do_i2c_scan(dev->name,&dev->i2c_client);
+
+	/* Instantiate the IR receiver device, if present */
+	saa7134_probe_i2c_ir(dev);
+	return 0;
+}
+
+int saa7134_i2c_unregister(struct saa7134_dev *dev)
+{
+	i2c_del_adapter(&dev->i2c_adap);
+	return 0;
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
new file mode 100644
index 000000000000..0f78f5e537e2
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -0,0 +1,1041 @@
+/*
+ *
+ * handle saa7134 IR remotes via linux kernel input layer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#define MODULE_NAME "saa7134"
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
+
+static int pinnacle_remote;
+module_param(pinnacle_remote, int, 0644);    /* Choose Pinnacle PCTV remote */
+MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)");
+
+#define dprintk(fmt, arg...)	if (ir_debug) \
+	printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
+#define i2cdprintk(fmt, arg...)    if (ir_debug) \
+	printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg)
+
+/* Helper function for raw decoding at GPIO16 or GPIO18 */
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev);
+
+/* -------------------- GPIO generic keycode builder -------------------- */
+
+static int build_key(struct saa7134_dev *dev)
+{
+	struct saa7134_card_ir *ir = dev->remote;
+	u32 gpio, data;
+
+	/* here comes the additional handshake steps for some cards */
+	switch (dev->board) {
+	case SAA7134_BOARD_GOTVIEW_7135:
+		saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80);
+		saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80);
+		break;
+	}
+	/* 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 (ir->polling) {
+		if (ir->last_gpio == gpio)
+			return 0;
+		ir->last_gpio = gpio;
+	}
+
+	data = ir_extract_bits(gpio, ir->mask_keycode);
+	dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
+		gpio, ir->mask_keycode, data);
+
+	switch (dev->board) {
+	case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+		if (data == ir->mask_keycode)
+			rc_keyup(ir->dev);
+		else
+			rc_keydown_notimeout(ir->dev, data, 0);
+		return 0;
+	}
+
+	if (ir->polling) {
+		if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
+		    (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
+			rc_keydown_notimeout(ir->dev, data, 0);
+		} else {
+			rc_keyup(ir->dev);
+		}
+	}
+	else {	/* IRQ driven mode - handle key press and release in one go */
+		if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
+		    (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
+			rc_keydown_notimeout(ir->dev, data, 0);
+			rc_keyup(ir->dev);
+		}
+	}
+
+	return 0;
+}
+
+/* --------------------- Chip specific I2C key builders ----------------- */
+
+static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	int gpio;
+	int attempt = 0;
+	unsigned char b;
+
+	/* We need this to access GPI Used by the saa_readl macro. */
+	struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+	if (dev == NULL) {
+		i2cdprintk("get_key_flydvb_trio: "
+			   "ir->c->adapter->algo_data is NULL!\n");
+		return -EIO;
+	}
+
+	/* rising SAA7134_GPIGPRESCAN 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 (0x40000 & ~gpio)
+		return 0; /* No button press */
+
+	/* No button press - only before first key pressed */
+	if (b == 0xFF)
+		return 0;
+
+	/* poll IR chip */
+	/* weak up the IR chip */
+	b = 0;
+
+	while (1 != i2c_master_send(ir->c, &b, 1)) {
+		if ((attempt++) < 10) {
+			/*
+			 * wait a bit for next attempt -
+			 * I don't know how make it better
+			 */
+			msleep(10);
+			continue;
+		}
+		i2cdprintk("send wake up byte to pic16C505 (IR chip)"
+			   "failed %dx\n", attempt);
+		return -EIO;
+	}
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		i2cdprintk("read error\n");
+		return -EIO;
+	}
+
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
+				       u32 *ir_raw)
+{
+	unsigned char b;
+	int gpio;
+
+	/* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+	struct saa7134_dev *dev = ir->c->adapter->algo_data;
+	if (dev == NULL) {
+		i2cdprintk("get_key_msi_tvanywhere_plus: "
+			   "ir->c->adapter->algo_data is NULL!\n");
+		return -EIO;
+	}
+
+	/* 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);
+
+	/* GPIO&0x40 is pulsed low when a button is pressed. Don't do
+	   I2C receive if gpio&0x40 is not low. */
+
+	if (gpio & 0x40)
+		return 0;       /* No button press */
+
+	/* GPIO says there is a button press. Get it. */
+
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		i2cdprintk("read error\n");
+		return -EIO;
+	}
+
+	/* No button press */
+
+	if (b == 0xff)
+		return 0;
+
+	/* Button pressed */
+
+	dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+/* copied and modified from get_key_msi_tvanywhere_plus() */
+static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
+					u32 *ir_raw)
+{
+	unsigned char b;
+	unsigned int gpio;
+
+	/* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+	struct saa7134_dev *dev = ir->c->adapter->algo_data;
+	if (dev == NULL) {
+		i2cdprintk("get_key_kworld_pc150u: "
+			   "ir->c->adapter->algo_data is NULL!\n");
+		return -EIO;
+	}
+
+	/* 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);
+
+	/* GPIO&0x100 is pulsed low when a button is pressed. Don't do
+	   I2C receive if gpio&0x100 is not low. */
+
+	if (gpio & 0x100)
+		return 0;       /* No button press */
+
+	/* GPIO says there is a button press. Get it. */
+
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		i2cdprintk("read error\n");
+		return -EIO;
+	}
+
+	/* No button press */
+
+	if (b == 0xff)
+		return 0;
+
+	/* Button pressed */
+
+	dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char b;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		i2cdprintk("read error\n");
+		return -EIO;
+	}
+
+	/* no button press */
+	if (b==0)
+		return 0;
+
+	/* repeating */
+	if (b & 0x80)
+		return 1;
+
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char buf[5];
+
+	/* poll IR chip */
+	if (5 != i2c_master_recv(ir->c, buf, 5))
+		return -EIO;
+
+	/* Check if some key were pressed */
+	if (!(buf[0] & 0x80))
+		return 0;
+
+	/*
+	 * buf[3] & 0x80 is always high.
+	 * buf[3] & 0x40 is a parity bit. A repeat event is marked
+	 * by preserving it into two separate readings
+	 * buf[4] bits 0 and 1, and buf[1] and buf[2] are always
+	 * zero.
+	 */
+	*ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2));
+	*ir_raw = *ir_key;
+	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;
+	}
+
+	if (data[9] != (unsigned char)(~data[8]))
+		return 0;
+
+	*ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0));
+	*ir_key = *ir_raw;
+
+	return 1;
+}
+
+/* Common (grey or coloured) pinnacle PCTV remote handling
+ *
+ */
+static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
+			    int parity_offset, int marker, int code_modulo)
+{
+	unsigned char b[4];
+	unsigned int start = 0,parity = 0,code = 0;
+
+	/* poll IR chip */
+	if (4 != i2c_master_recv(ir->c, b, 4)) {
+		i2cdprintk("read error\n");
+		return -EIO;
+	}
+
+	for (start = 0; start < ARRAY_SIZE(b); start++) {
+		if (b[start] == marker) {
+			code=b[(start+parity_offset + 1) % 4];
+			parity=b[(start+parity_offset) % 4];
+		}
+	}
+
+	/* Empty Request */
+	if (parity == 0)
+		return 0;
+
+	/* Repeating... */
+	if (ir->old == parity)
+		return 0;
+
+	ir->old = parity;
+
+	/* drop special codes when a key is held down a long time for the grey controller
+	   In this case, the second bit of the code is asserted */
+	if (marker == 0xfe && (code & 0x40))
+		return 0;
+
+	code %= code_modulo;
+
+	*ir_raw = code;
+	*ir_key = code;
+
+	i2cdprintk("Pinnacle PCTV key %02x\n", code);
+
+	return 1;
+}
+
+/* The grey pinnacle PCTV remote
+ *
+ *  There are one issue with this remote:
+ *   - I2c packet does not change when the same key is pressed quickly. The workaround
+ *     is to hold down each key for about half a second, so that another code is generated
+ *     in the i2c packet, and the function can distinguish key presses.
+ *
+ * Sylvain Pasche <sylvain.pasche@gmail.com>
+ */
+static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+
+	return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
+}
+
+
+/* The new pinnacle PCTV remote (with the colored buttons)
+ *
+ * Ricardo Cerqueira <v4l@cerqueira.org>
+ */
+static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	/* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
+	 *
+	 * this is the only value that results in 42 unique
+	 * codes < 128
+	 */
+
+	return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
+}
+
+void saa7134_input_irq(struct saa7134_dev *dev)
+{
+	struct saa7134_card_ir *ir;
+
+	if (!dev || !dev->remote)
+		return;
+
+	ir = dev->remote;
+	if (!ir->running)
+		return;
+
+	if (!ir->polling && !ir->raw_decode) {
+		build_key(dev);
+	} else if (ir->raw_decode) {
+		saa7134_raw_decode_irq(dev);
+	}
+}
+
+static void saa7134_input_timer(unsigned long data)
+{
+	struct saa7134_dev *dev = (struct saa7134_dev *)data;
+	struct saa7134_card_ir *ir = dev->remote;
+
+	build_key(dev);
+	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+static void ir_raw_decode_timer_end(unsigned long data)
+{
+	struct saa7134_dev *dev = (struct saa7134_dev *)data;
+
+	ir_raw_event_handle(dev->remote->dev);
+}
+
+static int __saa7134_ir_start(void *priv)
+{
+	struct saa7134_dev *dev = priv;
+	struct saa7134_card_ir *ir;
+
+	if (!dev || !dev->remote)
+		return -EINVAL;
+
+	ir  = dev->remote;
+	if (ir->running)
+		return 0;
+
+	/* Moved here from saa7134_input_init1() because the latter
+	 * is not called on device resume */
+	switch (dev->board) {
+	case SAA7134_BOARD_MD2819:
+	case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+	case SAA7134_BOARD_AVERMEDIA_305:
+	case SAA7134_BOARD_AVERMEDIA_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+	case SAA7134_BOARD_AVERMEDIA_M102:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+		/* Without this we won't receive key up events */
+		saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
+		saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_777:
+	case SAA7134_BOARD_AVERMEDIA_A16AR:
+		/* Without this we won't receive key up events */
+		saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+		saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A16D:
+		/* Without this we won't receive key up events */
+		saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+		saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+		break;
+	case SAA7134_BOARD_GOTVIEW_7135:
+		saa_setb(SAA7134_GPIO_GPMODE1, 0x80);
+		break;
+	}
+
+	ir->running = true;
+
+	if (ir->polling) {
+		setup_timer(&ir->timer, saa7134_input_timer,
+			    (unsigned long)dev);
+		ir->timer.expires = jiffies + HZ;
+		add_timer(&ir->timer);
+	} else if (ir->raw_decode) {
+		/* set timer_end for code completion */
+		setup_timer(&ir->timer, ir_raw_decode_timer_end,
+			    (unsigned long)dev);
+	}
+
+	return 0;
+}
+
+static void __saa7134_ir_stop(void *priv)
+{
+	struct saa7134_dev *dev = priv;
+	struct saa7134_card_ir *ir;
+
+	if (!dev || !dev->remote)
+		return;
+
+	ir  = dev->remote;
+	if (!ir->running)
+		return;
+
+	if (ir->polling || ir->raw_decode)
+		del_timer_sync(&ir->timer);
+
+	ir->running = false;
+
+	return;
+}
+
+int saa7134_ir_start(struct saa7134_dev *dev)
+{
+	if (dev->remote->users)
+		return __saa7134_ir_start(dev);
+
+	return 0;
+}
+
+void saa7134_ir_stop(struct saa7134_dev *dev)
+{
+	if (dev->remote->users)
+		__saa7134_ir_stop(dev);
+}
+
+static int saa7134_ir_open(struct rc_dev *rc)
+{
+	struct saa7134_dev *dev = rc->priv;
+
+	dev->remote->users++;
+	return __saa7134_ir_start(dev);
+}
+
+static void saa7134_ir_close(struct rc_dev *rc)
+{
+	struct saa7134_dev *dev = rc->priv;
+
+	dev->remote->users--;
+	if (!dev->remote->users)
+		__saa7134_ir_stop(dev);
+}
+
+int saa7134_input_init1(struct saa7134_dev *dev)
+{
+	struct saa7134_card_ir *ir;
+	struct rc_dev *rc;
+	char *ir_codes = NULL;
+	u32 mask_keycode = 0;
+	u32 mask_keydown = 0;
+	u32 mask_keyup   = 0;
+	unsigned polling = 0;
+	bool raw_decode  = false;
+	int err;
+
+	if (dev->has_remote != SAA7134_REMOTE_GPIO)
+		return -ENODEV;
+	if (disable_ir)
+		return -ENODEV;
+
+	/* detect & configure */
+	switch (dev->board) {
+	case SAA7134_BOARD_FLYVIDEO2000:
+	case SAA7134_BOARD_FLYVIDEO3000:
+	case SAA7134_BOARD_FLYTVPLATINUM_FM:
+	case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
+	case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+		ir_codes     = RC_MAP_FLYVIDEO;
+		mask_keycode = 0xEC00000;
+		mask_keydown = 0x0040000;
+		break;
+	case SAA7134_BOARD_CINERGY400:
+	case SAA7134_BOARD_CINERGY600:
+	case SAA7134_BOARD_CINERGY600_MK3:
+		ir_codes     = RC_MAP_CINERGY;
+		mask_keycode = 0x00003f;
+		mask_keyup   = 0x040000;
+		break;
+	case SAA7134_BOARD_ECS_TVP3XP:
+	case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+		ir_codes     = RC_MAP_EZTV;
+		mask_keycode = 0x00017c;
+		mask_keyup   = 0x000002;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_KWORLD_XPERT:
+	case SAA7134_BOARD_AVACSSMARTTV:
+		ir_codes     = RC_MAP_PIXELVIEW;
+		mask_keycode = 0x00001F;
+		mask_keyup   = 0x000020;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_MD2819:
+	case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+	case SAA7134_BOARD_AVERMEDIA_305:
+	case SAA7134_BOARD_AVERMEDIA_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+	case SAA7134_BOARD_AVERMEDIA_M102:
+	case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+		ir_codes     = RC_MAP_AVERMEDIA;
+		mask_keycode = 0x0007C8;
+		mask_keydown = 0x000010;
+		polling      = 50; // ms
+		/* GPIO stuff moved to __saa7134_ir_start() */
+		break;
+	case SAA7134_BOARD_AVERMEDIA_M135A:
+		ir_codes     = RC_MAP_AVERMEDIA_M135A;
+		mask_keydown = 0x0040000;	/* Enable GPIO18 line on both edges */
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_M733A:
+		ir_codes     = RC_MAP_AVERMEDIA_M733A_RM_K6;
+		mask_keydown = 0x0040000;
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_777:
+	case SAA7134_BOARD_AVERMEDIA_A16AR:
+		ir_codes     = RC_MAP_AVERMEDIA;
+		mask_keycode = 0x02F200;
+		mask_keydown = 0x000400;
+		polling      = 50; // ms
+		/* GPIO stuff moved to __saa7134_ir_start() */
+		break;
+	case SAA7134_BOARD_AVERMEDIA_A16D:
+		ir_codes     = RC_MAP_AVERMEDIA_A16D;
+		mask_keycode = 0x02F200;
+		mask_keydown = 0x000400;
+		polling      = 50; /* ms */
+		/* GPIO stuff moved to __saa7134_ir_start() */
+		break;
+	case SAA7134_BOARD_KWORLD_TERMINATOR:
+		ir_codes     = RC_MAP_PIXELVIEW;
+		mask_keycode = 0x00001f;
+		mask_keyup   = 0x000060;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_MANLI_MTV001:
+	case SAA7134_BOARD_MANLI_MTV002:
+		ir_codes     = RC_MAP_MANLI;
+		mask_keycode = 0x001f00;
+		mask_keyup   = 0x004000;
+		polling      = 50; /* ms */
+		break;
+	case SAA7134_BOARD_BEHOLD_409FM:
+	case SAA7134_BOARD_BEHOLD_401:
+	case SAA7134_BOARD_BEHOLD_403:
+	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_505RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_505RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_507_9FM:
+	case SAA7134_BOARD_BEHOLD_507RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_507RDS_MK5:
+		ir_codes     = RC_MAP_MANLI;
+		mask_keycode = 0x003f00;
+		mask_keyup   = 0x004000;
+		polling      = 50; /* ms */
+		break;
+	case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+		ir_codes     = RC_MAP_BEHOLD_COLUMBUS;
+		mask_keycode = 0x003f00;
+		mask_keyup   = 0x004000;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
+		ir_codes     = RC_MAP_PCTV_SEDNA;
+		mask_keycode = 0x001f00;
+		mask_keyup   = 0x004000;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_GOTVIEW_7135:
+		ir_codes     = RC_MAP_GOTVIEW7135;
+		mask_keycode = 0x0003CC;
+		mask_keydown = 0x000010;
+		polling	     = 5; /* ms */
+		/* GPIO stuff moved to __saa7134_ir_start() */
+		break;
+	case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+	case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
+	case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
+		ir_codes     = RC_MAP_VIDEOMATE_TV_PVR;
+		mask_keycode = 0x00003F;
+		mask_keyup   = 0x400000;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_PROTEUS_2309:
+		ir_codes     = RC_MAP_PROTEUS_2309;
+		mask_keycode = 0x00007F;
+		mask_keyup   = 0x000080;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+		ir_codes     = RC_MAP_VIDEOMATE_TV_PVR;
+		mask_keycode = 0x003F00;
+		mask_keyup   = 0x040000;
+		break;
+	case SAA7134_BOARD_FLYDVBS_LR300:
+	case SAA7134_BOARD_FLYDVBT_LR301:
+	case SAA7134_BOARD_FLYDVBTDUO:
+		ir_codes     = RC_MAP_FLYDVB;
+		mask_keycode = 0x0001F00;
+		mask_keydown = 0x0040000;
+		break;
+	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+	case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+		ir_codes     = RC_MAP_ASUS_PC39;
+		mask_keydown = 0x0040000;	/* Enable GPIO18 line on both edges */
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	case SAA7134_BOARD_ASUSTeK_PS3_100:
+		ir_codes     = RC_MAP_ASUS_PS3_100;
+		mask_keydown = 0x0040000;
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	case SAA7134_BOARD_ENCORE_ENLTV:
+	case SAA7134_BOARD_ENCORE_ENLTV_FM:
+		ir_codes     = RC_MAP_ENCORE_ENLTV;
+		mask_keycode = 0x00007f;
+		mask_keyup   = 0x040000;
+		polling      = 50; // ms
+		break;
+	case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+	case SAA7134_BOARD_ENCORE_ENLTV_FM3:
+		ir_codes     = RC_MAP_ENCORE_ENLTV_FM53;
+		mask_keydown = 0x0040000;	/* Enable GPIO18 line on both edges */
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	case SAA7134_BOARD_10MOONSTVMASTER3:
+		ir_codes     = RC_MAP_ENCORE_ENLTV;
+		mask_keycode = 0x5f80000;
+		mask_keyup   = 0x8000000;
+		polling      = 50; //ms
+		break;
+	case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+		ir_codes     = RC_MAP_GENIUS_TVGO_A11MCE;
+		mask_keycode = 0xff;
+		mask_keydown = 0xf00000;
+		polling = 50; /* ms */
+		break;
+	case SAA7134_BOARD_REAL_ANGEL_220:
+		ir_codes     = RC_MAP_REAL_AUDIO_220_32_KEYS;
+		mask_keycode = 0x3f00;
+		mask_keyup   = 0x4000;
+		polling = 50; /* ms */
+		break;
+	case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+		ir_codes     = RC_MAP_KWORLD_PLUS_TV_ANALOG;
+		mask_keycode = 0x7f;
+		polling = 40; /* ms */
+		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		ir_codes     = RC_MAP_VIDEOMATE_S350;
+		mask_keycode = 0x003f00;
+		mask_keydown = 0x040000;
+		break;
+	case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+		ir_codes     = RC_MAP_WINFAST;
+		mask_keycode = 0x5f00;
+		mask_keyup   = 0x020000;
+		polling      = 50; /* ms */
+		break;
+	case SAA7134_BOARD_VIDEOMATE_M1F:
+		ir_codes     = RC_MAP_VIDEOMATE_K100;
+		mask_keycode = 0x0ff00;
+		mask_keyup   = 0x040000;
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+		ir_codes     = RC_MAP_HAUPPAUGE;
+		mask_keydown = 0x0040000;	/* Enable GPIO18 line on both edges */
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = true;
+		break;
+	}
+	if (NULL == ir_codes) {
+		printk("%s: Oops: IR config error [card=%d]\n",
+		       dev->name, dev->board);
+		return -ENODEV;
+	}
+
+	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+	rc = rc_allocate_device();
+	if (!ir || !rc) {
+		err = -ENOMEM;
+		goto err_out_free;
+	}
+
+	ir->dev = rc;
+	dev->remote = ir;
+
+	/* init hardware-specific stuff */
+	ir->mask_keycode = mask_keycode;
+	ir->mask_keydown = mask_keydown;
+	ir->mask_keyup   = mask_keyup;
+	ir->polling      = polling;
+	ir->raw_decode	 = raw_decode;
+
+	/* init input device */
+	snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
+		 saa7134_boards[dev->board].name);
+	snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+		 pci_name(dev->pci));
+
+	rc->priv = dev;
+	rc->open = saa7134_ir_open;
+	rc->close = saa7134_ir_close;
+	if (raw_decode)
+		rc->driver_type = RC_DRIVER_IR_RAW;
+
+	rc->input_name = ir->name;
+	rc->input_phys = ir->phys;
+	rc->input_id.bustype = BUS_PCI;
+	rc->input_id.version = 1;
+	if (dev->pci->subsystem_vendor) {
+		rc->input_id.vendor  = dev->pci->subsystem_vendor;
+		rc->input_id.product = dev->pci->subsystem_device;
+	} else {
+		rc->input_id.vendor  = dev->pci->vendor;
+		rc->input_id.product = dev->pci->device;
+	}
+	rc->dev.parent = &dev->pci->dev;
+	rc->map_name = ir_codes;
+	rc->driver_name = MODULE_NAME;
+
+	err = rc_register_device(rc);
+	if (err)
+		goto err_out_free;
+
+	return 0;
+
+err_out_free:
+	rc_free_device(rc);
+	dev->remote = NULL;
+	kfree(ir);
+	return err;
+}
+
+void saa7134_input_fini(struct saa7134_dev *dev)
+{
+	if (NULL == dev->remote)
+		return;
+
+	saa7134_ir_stop(dev);
+	rc_unregister_device(dev->remote->dev);
+	kfree(dev->remote);
+	dev->remote = NULL;
+}
+
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
+{
+	struct i2c_board_info info;
+	struct i2c_msg msg_msi = {
+		.addr = 0x50,
+		.flags = I2C_M_RD,
+		.len = 0,
+		.buf = NULL,
+	};
+	int rc;
+
+	if (disable_ir) {
+		dprintk("IR has been disabled, not probing for i2c remote\n");
+		return;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	memset(&dev->init_data, 0, sizeof(dev->init_data));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+	switch (dev->board) {
+	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
+		dev->init_data.name = "Pinnacle PCTV";
+		if (pinnacle_remote == 0) {
+			dev->init_data.get_key = get_key_pinnacle_color;
+			dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR;
+			info.addr = 0x47;
+		} else {
+			dev->init_data.get_key = get_key_pinnacle_grey;
+			dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
+			info.addr = 0x47;
+		}
+		break;
+	case SAA7134_BOARD_UPMOST_PURPLE_TV:
+		dev->init_data.name = "Purple TV";
+		dev->init_data.get_key = get_key_purpletv;
+		dev->init_data.ir_codes = RC_MAP_PURPLETV;
+		info.addr = 0x7a;
+		break;
+	case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+		dev->init_data.name = "MSI TV@nywhere Plus";
+		dev->init_data.get_key = get_key_msi_tvanywhere_plus;
+		dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
+		/*
+		 * MSI TV@nyware Plus requires more frequent polling
+		 * otherwise it will miss some keypresses
+		 */
+		dev->init_data.polling_interval = 50;
+		info.addr = 0x30;
+		/* MSI TV@nywhere Plus controller doesn't seem to
+		   respond to probes unless we read something from
+		   an existing device. Weird...
+		   REVISIT: might no longer be needed */
+		rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+		dprintk("probe 0x%02x @ %s: %s\n",
+			msg_msi.addr, dev->i2c_adap.name,
+			(1 == rc) ? "yes" : "no");
+		break;
+	case SAA7134_BOARD_KWORLD_PC150U:
+		/* copied and modified from MSI TV@nywhere Plus */
+		dev->init_data.name = "Kworld PC150-U";
+		dev->init_data.get_key = get_key_kworld_pc150u;
+		dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
+		info.addr = 0x30;
+		/* MSI TV@nywhere Plus controller doesn't seem to
+		   respond to probes unless we read something from
+		   an existing device. Weird...
+		   REVISIT: might no longer be needed */
+		rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+		dprintk("probe 0x%02x @ %s: %s\n",
+			msg_msi.addr, dev->i2c_adap.name,
+			(1 == rc) ? "yes" : "no");
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+		dev->init_data.name = "HVR 1110";
+		dev->init_data.get_key = get_key_hvr1110;
+		dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+		info.addr = 0x71;
+		break;
+	case SAA7134_BOARD_BEHOLD_607FM_MK3:
+	case SAA7134_BOARD_BEHOLD_607FM_MK5:
+	case SAA7134_BOARD_BEHOLD_609FM_MK3:
+	case SAA7134_BOARD_BEHOLD_609FM_MK5:
+	case SAA7134_BOARD_BEHOLD_607RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_607RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_609RDS_MK3:
+	case SAA7134_BOARD_BEHOLD_609RDS_MK5:
+	case SAA7134_BOARD_BEHOLD_M6:
+	case SAA7134_BOARD_BEHOLD_M63:
+	case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+	case SAA7134_BOARD_BEHOLD_H6:
+	case SAA7134_BOARD_BEHOLD_X7:
+	case SAA7134_BOARD_BEHOLD_H7:
+	case SAA7134_BOARD_BEHOLD_A7:
+		dev->init_data.name = "BeholdTV";
+		dev->init_data.get_key = get_key_beholdm6xx;
+		dev->init_data.ir_codes = RC_MAP_BEHOLD;
+		dev->init_data.type = RC_TYPE_NEC;
+		info.addr = 0x2d;
+		break;
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+	case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+		info.addr = 0x40;
+		break;
+	case SAA7134_BOARD_FLYDVB_TRIO:
+		dev->init_data.name = "FlyDVB Trio";
+		dev->init_data.get_key = get_key_flydvb_trio;
+		dev->init_data.ir_codes = RC_MAP_FLYDVB;
+		info.addr = 0x0b;
+		break;
+	default:
+		dprintk("No I2C IR support for board %x\n", dev->board);
+		return;
+	}
+
+	if (dev->init_data.name)
+		info.platform_data = &dev->init_data;
+	i2c_new_device(&dev->i2c_adap, &info);
+}
+
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev)
+{
+	struct saa7134_card_ir *ir = dev->remote;
+	unsigned long timeout;
+	int space;
+
+	/* Generate initial event */
+	saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
+	ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE);
+
+	/*
+	 * Wait 15 ms from the start of the first IR event before processing
+	 * the event. This time is enough for NEC protocol. May need adjustments
+	 * to work with other protocols.
+	 */
+	smp_mb();
+
+	if (!timer_pending(&ir->timer)) {
+		timeout = jiffies + msecs_to_jiffies(15);
+		mod_timer(&ir->timer, timeout);
+	}
+
+	return 1;
+}
diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h
new file mode 100644
index 000000000000..e7e0af101fa7
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-reg.h
@@ -0,0 +1,378 @@
+/*
+ *
+ * philips saa7134 registers
+ */
+
+/* ------------------------------------------------------------------ */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130
+# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133
+# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134
+# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135
+# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135
+#endif
+
+/* ------------------------------------------------------------------ */
+/*
+ *  registers -- 32 bit
+ */
+
+/* DMA channels, n = 0 ... 6 */
+#define SAA7134_RS_BA1(n)			((0x200 >> 2) + 4*n)
+#define SAA7134_RS_BA2(n)			((0x204 >> 2) + 4*n)
+#define SAA7134_RS_PITCH(n)			((0x208 >> 2) + 4*n)
+#define SAA7134_RS_CONTROL(n)			((0x20c >> 2) + 4*n)
+#define   SAA7134_RS_CONTROL_WSWAP		(0x01 << 25)
+#define   SAA7134_RS_CONTROL_BSWAP		(0x01 << 24)
+#define   SAA7134_RS_CONTROL_BURST_2		(0x01 << 21)
+#define   SAA7134_RS_CONTROL_BURST_4		(0x02 << 21)
+#define   SAA7134_RS_CONTROL_BURST_8		(0x03 << 21)
+#define   SAA7134_RS_CONTROL_BURST_16		(0x04 << 21)
+#define   SAA7134_RS_CONTROL_BURST_32		(0x05 << 21)
+#define   SAA7134_RS_CONTROL_BURST_64		(0x06 << 21)
+#define   SAA7134_RS_CONTROL_BURST_MAX		(0x07 << 21)
+#define   SAA7134_RS_CONTROL_ME			(0x01 << 20)
+#define SAA7134_FIFO_SIZE                       (0x2a0 >> 2)
+#define SAA7134_THRESHOULD                      (0x2a4 >> 2)
+
+#define SAA7133_NUM_SAMPLES			(0x588 >> 2)
+#define SAA7133_AUDIO_CHANNEL			(0x58c >> 2)
+#define SAA7133_AUDIO_FORMAT			(0x58f >> 2)
+#define SAA7133_DIGITAL_OUTPUT_SEL1		(0x46c >> 2)
+#define SAA7133_DIGITAL_OUTPUT_SEL2		(0x470 >> 2)
+#define SAA7133_DIGITAL_INPUT_XBAR1		(0x464 >> 2)
+#define SAA7133_ANALOG_IO_SELECT                (0x594 >> 2)
+
+/* main control */
+#define SAA7134_MAIN_CTRL                       (0x2a8 >> 2)
+#define   SAA7134_MAIN_CTRL_VPLLE		(1 << 15)
+#define   SAA7134_MAIN_CTRL_APLLE		(1 << 14)
+#define   SAA7134_MAIN_CTRL_EXOSC		(1 << 13)
+#define   SAA7134_MAIN_CTRL_EVFE1		(1 << 12)
+#define   SAA7134_MAIN_CTRL_EVFE2		(1 << 11)
+#define   SAA7134_MAIN_CTRL_ESFE		(1 << 10)
+#define   SAA7134_MAIN_CTRL_EBADC		(1 << 9)
+#define   SAA7134_MAIN_CTRL_EBDAC		(1 << 8)
+#define   SAA7134_MAIN_CTRL_TE6			(1 << 6)
+#define   SAA7134_MAIN_CTRL_TE5			(1 << 5)
+#define   SAA7134_MAIN_CTRL_TE4			(1 << 4)
+#define   SAA7134_MAIN_CTRL_TE3			(1 << 3)
+#define   SAA7134_MAIN_CTRL_TE2			(1 << 2)
+#define   SAA7134_MAIN_CTRL_TE1			(1 << 1)
+#define   SAA7134_MAIN_CTRL_TE0			(1 << 0)
+
+/* DMA status */
+#define SAA7134_DMA_STATUS                      (0x2ac >> 2)
+
+/* audio / video status */
+#define SAA7134_AV_STATUS			(0x2c0 >> 2)
+#define   SAA7134_AV_STATUS_STEREO		(1 << 17)
+#define   SAA7134_AV_STATUS_DUAL                (1 << 16)
+#define   SAA7134_AV_STATUS_PILOT               (1 << 15)
+#define   SAA7134_AV_STATUS_SMB                 (1 << 14)
+#define   SAA7134_AV_STATUS_DMB                 (1 << 13)
+#define   SAA7134_AV_STATUS_VDSP                (1 << 12)
+#define   SAA7134_AV_STATUS_IIC_STATUS          (3 << 10)
+#define   SAA7134_AV_STATUS_MVM                 (7 << 7)
+#define   SAA7134_AV_STATUS_FIDT                (1 << 6)
+#define   SAA7134_AV_STATUS_INTL                (1 << 5)
+#define   SAA7134_AV_STATUS_RDCAP               (1 << 4)
+#define   SAA7134_AV_STATUS_PWR_ON              (1 << 3)
+#define   SAA7134_AV_STATUS_LOAD_ERR            (1 << 2)
+#define   SAA7134_AV_STATUS_TRIG_ERR            (1 << 1)
+#define   SAA7134_AV_STATUS_CONF_ERR            (1 << 0)
+
+/* interrupt */
+#define SAA7134_IRQ1                            (0x2c4 >> 2)
+#define   SAA7134_IRQ1_INTE_RA3_1               (1 << 25)
+#define   SAA7134_IRQ1_INTE_RA3_0               (1 << 24)
+#define   SAA7134_IRQ1_INTE_RA2_3               (1 << 19)
+#define   SAA7134_IRQ1_INTE_RA2_2               (1 << 18)
+#define   SAA7134_IRQ1_INTE_RA2_1               (1 << 17)
+#define   SAA7134_IRQ1_INTE_RA2_0               (1 << 16)
+#define   SAA7134_IRQ1_INTE_RA1_3               (1 << 11)
+#define   SAA7134_IRQ1_INTE_RA1_2               (1 << 10)
+#define   SAA7134_IRQ1_INTE_RA1_1               (1 <<  9)
+#define   SAA7134_IRQ1_INTE_RA1_0               (1 <<  8)
+#define   SAA7134_IRQ1_INTE_RA0_7               (1 <<  7)
+#define   SAA7134_IRQ1_INTE_RA0_6               (1 <<  6)
+#define   SAA7134_IRQ1_INTE_RA0_5               (1 <<  5)
+#define   SAA7134_IRQ1_INTE_RA0_4               (1 <<  4)
+#define   SAA7134_IRQ1_INTE_RA0_3               (1 <<  3)
+#define   SAA7134_IRQ1_INTE_RA0_2               (1 <<  2)
+#define   SAA7134_IRQ1_INTE_RA0_1               (1 <<  1)
+#define   SAA7134_IRQ1_INTE_RA0_0               (1 <<  0)
+
+#define SAA7134_IRQ2                            (0x2c8 >> 2)
+#define   SAA7134_IRQ2_INTE_GPIO23_N             (1 << 17)	/* negative edge */
+#define   SAA7134_IRQ2_INTE_GPIO23_P             (1 << 16)	/* positive edge */
+#define   SAA7134_IRQ2_INTE_GPIO22_N             (1 << 15)	/* negative edge */
+#define   SAA7134_IRQ2_INTE_GPIO22_P             (1 << 14)	/* positive edge */
+#define   SAA7134_IRQ2_INTE_GPIO18_N             (1 << 13)	/* negative edge */
+#define   SAA7134_IRQ2_INTE_GPIO18_P             (1 << 12)	/* positive edge */
+#define   SAA7134_IRQ2_INTE_GPIO16_N             (1 << 11)	/* negative edge */
+#define   SAA7134_IRQ2_INTE_GPIO16_P             (1 << 10)	/* positive edge */
+#define   SAA7134_IRQ2_INTE_SC2                 (1 <<  9)
+#define   SAA7134_IRQ2_INTE_SC1                 (1 <<  8)
+#define   SAA7134_IRQ2_INTE_SC0                 (1 <<  7)
+#define   SAA7134_IRQ2_INTE_DEC4                (1 <<  6)
+#define   SAA7134_IRQ2_INTE_DEC3                (1 <<  5)
+#define   SAA7134_IRQ2_INTE_DEC2                (1 <<  4)
+#define   SAA7134_IRQ2_INTE_DEC1                (1 <<  3)
+#define   SAA7134_IRQ2_INTE_DEC0                (1 <<  2)
+#define   SAA7134_IRQ2_INTE_PE                  (1 <<  1)
+#define   SAA7134_IRQ2_INTE_AR                  (1 <<  0)
+
+#define SAA7134_IRQ_REPORT                      (0x2cc >> 2)
+#define   SAA7134_IRQ_REPORT_GPIO23             (1 << 17)
+#define   SAA7134_IRQ_REPORT_GPIO22             (1 << 16)
+#define   SAA7134_IRQ_REPORT_GPIO18             (1 << 15)
+#define   SAA7134_IRQ_REPORT_GPIO16             (1 << 14)
+#define   SAA7134_IRQ_REPORT_LOAD_ERR           (1 << 13)
+#define   SAA7134_IRQ_REPORT_CONF_ERR           (1 << 12)
+#define   SAA7134_IRQ_REPORT_TRIG_ERR           (1 << 11)
+#define   SAA7134_IRQ_REPORT_MMC                (1 << 10)
+#define   SAA7134_IRQ_REPORT_FIDT               (1 <<  9)
+#define   SAA7134_IRQ_REPORT_INTL               (1 <<  8)
+#define   SAA7134_IRQ_REPORT_RDCAP              (1 <<  7)
+#define   SAA7134_IRQ_REPORT_PWR_ON             (1 <<  6)
+#define   SAA7134_IRQ_REPORT_PE                 (1 <<  5)
+#define   SAA7134_IRQ_REPORT_AR                 (1 <<  4)
+#define   SAA7134_IRQ_REPORT_DONE_RA3           (1 <<  3)
+#define   SAA7134_IRQ_REPORT_DONE_RA2           (1 <<  2)
+#define   SAA7134_IRQ_REPORT_DONE_RA1           (1 <<  1)
+#define   SAA7134_IRQ_REPORT_DONE_RA0           (1 <<  0)
+#define SAA7134_IRQ_STATUS                      (0x2d0 >> 2)
+
+
+/* ------------------------------------------------------------------ */
+/*
+ *  registers -- 8 bit
+ */
+
+/* video decoder */
+#define SAA7134_INCR_DELAY                      0x101
+#define SAA7134_ANALOG_IN_CTRL1                 0x102
+#define SAA7134_ANALOG_IN_CTRL2                 0x103
+#define SAA7134_ANALOG_IN_CTRL3                 0x104
+#define SAA7134_ANALOG_IN_CTRL4                 0x105
+#define SAA7134_HSYNC_START                     0x106
+#define SAA7134_HSYNC_STOP                      0x107
+#define SAA7134_SYNC_CTRL                       0x108
+#define SAA7134_LUMA_CTRL                       0x109
+#define SAA7134_DEC_LUMA_BRIGHT                 0x10a
+#define SAA7134_DEC_LUMA_CONTRAST               0x10b
+#define SAA7134_DEC_CHROMA_SATURATION           0x10c
+#define SAA7134_DEC_CHROMA_HUE                  0x10d
+#define SAA7134_CHROMA_CTRL1                    0x10e
+#define SAA7134_CHROMA_GAIN                     0x10f
+#define SAA7134_CHROMA_CTRL2                    0x110
+#define SAA7134_MODE_DELAY_CTRL                 0x111
+
+#define SAA7134_ANALOG_ADC                      0x114
+#define SAA7134_VGATE_START                     0x115
+#define SAA7134_VGATE_STOP                      0x116
+#define SAA7134_MISC_VGATE_MSB                  0x117
+#define SAA7134_RAW_DATA_GAIN                   0x118
+#define SAA7134_RAW_DATA_OFFSET                 0x119
+#define SAA7134_STATUS_VIDEO1                   0x11e
+#define SAA7134_STATUS_VIDEO2                   0x11f
+
+/* video scaler */
+#define SAA7134_SOURCE_TIMING1                  0x000
+#define SAA7134_SOURCE_TIMING2                  0x001
+#define SAA7134_REGION_ENABLE                   0x004
+#define SAA7134_SCALER_STATUS0                  0x006
+#define SAA7134_SCALER_STATUS1                  0x007
+#define SAA7134_START_GREEN                     0x00c
+#define SAA7134_START_BLUE                      0x00d
+#define SAA7134_START_RED                       0x00e
+#define SAA7134_GREEN_PATH(x)                   (0x010 +x)
+#define SAA7134_BLUE_PATH(x)                    (0x020 +x)
+#define SAA7134_RED_PATH(x)                     (0x030 +x)
+
+#define TASK_A                                  0x040
+#define TASK_B                                  0x080
+#define SAA7134_TASK_CONDITIONS(t)              (0x000 +t)
+#define SAA7134_FIELD_HANDLING(t)               (0x001 +t)
+#define SAA7134_DATA_PATH(t)                    (0x002 +t)
+#define SAA7134_VBI_H_START1(t)                 (0x004 +t)
+#define SAA7134_VBI_H_START2(t)                 (0x005 +t)
+#define SAA7134_VBI_H_STOP1(t)                  (0x006 +t)
+#define SAA7134_VBI_H_STOP2(t)                  (0x007 +t)
+#define SAA7134_VBI_V_START1(t)                 (0x008 +t)
+#define SAA7134_VBI_V_START2(t)                 (0x009 +t)
+#define SAA7134_VBI_V_STOP1(t)                  (0x00a +t)
+#define SAA7134_VBI_V_STOP2(t)                  (0x00b +t)
+#define SAA7134_VBI_H_LEN1(t)                   (0x00c +t)
+#define SAA7134_VBI_H_LEN2(t)                   (0x00d +t)
+#define SAA7134_VBI_V_LEN1(t)                   (0x00e +t)
+#define SAA7134_VBI_V_LEN2(t)                   (0x00f +t)
+
+#define SAA7134_VIDEO_H_START1(t)               (0x014 +t)
+#define SAA7134_VIDEO_H_START2(t)               (0x015 +t)
+#define SAA7134_VIDEO_H_STOP1(t)                (0x016 +t)
+#define SAA7134_VIDEO_H_STOP2(t)                (0x017 +t)
+#define SAA7134_VIDEO_V_START1(t)               (0x018 +t)
+#define SAA7134_VIDEO_V_START2(t)               (0x019 +t)
+#define SAA7134_VIDEO_V_STOP1(t)                (0x01a +t)
+#define SAA7134_VIDEO_V_STOP2(t)                (0x01b +t)
+#define SAA7134_VIDEO_PIXELS1(t)                (0x01c +t)
+#define SAA7134_VIDEO_PIXELS2(t)                (0x01d +t)
+#define SAA7134_VIDEO_LINES1(t)                 (0x01e +t)
+#define SAA7134_VIDEO_LINES2(t)                 (0x01f +t)
+
+#define SAA7134_H_PRESCALE(t)                   (0x020 +t)
+#define SAA7134_ACC_LENGTH(t)                   (0x021 +t)
+#define SAA7134_LEVEL_CTRL(t)                   (0x022 +t)
+#define SAA7134_FIR_PREFILTER_CTRL(t)           (0x023 +t)
+#define SAA7134_LUMA_BRIGHT(t)                  (0x024 +t)
+#define SAA7134_LUMA_CONTRAST(t)                (0x025 +t)
+#define SAA7134_CHROMA_SATURATION(t)            (0x026 +t)
+#define SAA7134_VBI_H_SCALE_INC1(t)             (0x028 +t)
+#define SAA7134_VBI_H_SCALE_INC2(t)             (0x029 +t)
+#define SAA7134_VBI_PHASE_OFFSET_LUMA(t)        (0x02a +t)
+#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t)      (0x02b +t)
+#define SAA7134_H_SCALE_INC1(t)                 (0x02c +t)
+#define SAA7134_H_SCALE_INC2(t)                 (0x02d +t)
+#define SAA7134_H_PHASE_OFF_LUMA(t)             (0x02e +t)
+#define SAA7134_H_PHASE_OFF_CHROMA(t)           (0x02f +t)
+#define SAA7134_V_SCALE_RATIO1(t)               (0x030 +t)
+#define SAA7134_V_SCALE_RATIO2(t)               (0x031 +t)
+#define SAA7134_V_FILTER(t)                     (0x032 +t)
+#define SAA7134_V_PHASE_OFFSET0(t)              (0x034 +t)
+#define SAA7134_V_PHASE_OFFSET1(t)              (0x035 +t)
+#define SAA7134_V_PHASE_OFFSET2(t)              (0x036 +t)
+#define SAA7134_V_PHASE_OFFSET3(t)              (0x037 +t)
+
+/* clipping & dma */
+#define SAA7134_OFMT_VIDEO_A                    0x300
+#define SAA7134_OFMT_DATA_A                     0x301
+#define SAA7134_OFMT_VIDEO_B                    0x302
+#define SAA7134_OFMT_DATA_B                     0x303
+#define SAA7134_ALPHA_NOCLIP                    0x304
+#define SAA7134_ALPHA_CLIP                      0x305
+#define SAA7134_UV_PIXEL                        0x308
+#define SAA7134_CLIP_RED                        0x309
+#define SAA7134_CLIP_GREEN                      0x30a
+#define SAA7134_CLIP_BLUE                       0x30b
+
+/* i2c bus */
+#define SAA7134_I2C_ATTR_STATUS                 0x180
+#define SAA7134_I2C_DATA                        0x181
+#define SAA7134_I2C_CLOCK_SELECT                0x182
+#define SAA7134_I2C_TIMER                       0x183
+
+/* audio */
+#define SAA7134_NICAM_ADD_DATA1                 0x140
+#define SAA7134_NICAM_ADD_DATA2                 0x141
+#define SAA7134_NICAM_STATUS                    0x142
+#define SAA7134_AUDIO_STATUS                    0x143
+#define SAA7134_NICAM_ERROR_COUNT               0x144
+#define SAA7134_IDENT_SIF                       0x145
+#define SAA7134_LEVEL_READOUT1                  0x146
+#define SAA7134_LEVEL_READOUT2                  0x147
+#define SAA7134_NICAM_ERROR_LOW                 0x148
+#define SAA7134_NICAM_ERROR_HIGH                0x149
+#define SAA7134_DCXO_IDENT_CTRL                 0x14a
+#define SAA7134_DEMODULATOR                     0x14b
+#define SAA7134_AGC_GAIN_SELECT                 0x14c
+#define SAA7134_CARRIER1_FREQ0                  0x150
+#define SAA7134_CARRIER1_FREQ1                  0x151
+#define SAA7134_CARRIER1_FREQ2                  0x152
+#define SAA7134_CARRIER2_FREQ0                  0x154
+#define SAA7134_CARRIER2_FREQ1                  0x155
+#define SAA7134_CARRIER2_FREQ2                  0x156
+#define SAA7134_NUM_SAMPLES0                    0x158
+#define SAA7134_NUM_SAMPLES1                    0x159
+#define SAA7134_NUM_SAMPLES2                    0x15a
+#define SAA7134_AUDIO_FORMAT_CTRL               0x15b
+#define SAA7134_MONITOR_SELECT                  0x160
+#define SAA7134_FM_DEEMPHASIS                   0x161
+#define SAA7134_FM_DEMATRIX                     0x162
+#define SAA7134_CHANNEL1_LEVEL                  0x163
+#define SAA7134_CHANNEL2_LEVEL                  0x164
+#define SAA7134_NICAM_CONFIG                    0x165
+#define SAA7134_NICAM_LEVEL_ADJUST              0x166
+#define SAA7134_STEREO_DAC_OUTPUT_SELECT        0x167
+#define SAA7134_I2S_OUTPUT_FORMAT               0x168
+#define SAA7134_I2S_OUTPUT_SELECT               0x169
+#define SAA7134_I2S_OUTPUT_LEVEL                0x16a
+#define SAA7134_DSP_OUTPUT_SELECT               0x16b
+#define SAA7134_AUDIO_MUTE_CTRL                 0x16c
+#define SAA7134_SIF_SAMPLE_FREQ                 0x16d
+#define SAA7134_ANALOG_IO_SELECT                0x16e
+#define SAA7134_AUDIO_CLOCK0                    0x170
+#define SAA7134_AUDIO_CLOCK1                    0x171
+#define SAA7134_AUDIO_CLOCK2                    0x172
+#define SAA7134_AUDIO_PLL_CTRL                  0x173
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD0         0x174
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD1         0x175
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD2         0x176
+
+/* video port output */
+#define SAA7134_VIDEO_PORT_CTRL0                0x190
+#define SAA7134_VIDEO_PORT_CTRL1                0x191
+#define SAA7134_VIDEO_PORT_CTRL2                0x192
+#define SAA7134_VIDEO_PORT_CTRL3                0x193
+#define SAA7134_VIDEO_PORT_CTRL4                0x194
+#define SAA7134_VIDEO_PORT_CTRL5                0x195
+#define SAA7134_VIDEO_PORT_CTRL6                0x196
+#define SAA7134_VIDEO_PORT_CTRL7                0x197
+#define SAA7134_VIDEO_PORT_CTRL8                0x198
+
+/* transport stream interface */
+#define SAA7134_TS_PARALLEL                     0x1a0
+#define SAA7134_TS_PARALLEL_SERIAL              0x1a1
+#define SAA7134_TS_SERIAL0                      0x1a2
+#define SAA7134_TS_SERIAL1                      0x1a3
+#define SAA7134_TS_DMA0                         0x1a4
+#define SAA7134_TS_DMA1                         0x1a5
+#define SAA7134_TS_DMA2                         0x1a6
+
+/* GPIO Controls */
+#define SAA7134_GPIO_GPRESCAN                   0x80
+#define SAA7134_GPIO_27_25                      0x0E
+
+#define SAA7134_GPIO_GPMODE0                    0x1B0
+#define SAA7134_GPIO_GPMODE1                    0x1B1
+#define SAA7134_GPIO_GPMODE2                    0x1B2
+#define SAA7134_GPIO_GPMODE3                    0x1B3
+#define SAA7134_GPIO_GPSTATUS0                  0x1B4
+#define SAA7134_GPIO_GPSTATUS1                  0x1B5
+#define SAA7134_GPIO_GPSTATUS2                  0x1B6
+#define SAA7134_GPIO_GPSTATUS3                  0x1B7
+
+/* I2S output */
+#define SAA7134_I2S_AUDIO_OUTPUT                0x1c0
+
+/* test modes */
+#define SAA7134_SPECIAL_MODE                    0x1d0
+#define SAA7134_PRODUCTION_TEST_MODE            0x1d1
+
+/* audio -- saa7133 + saa7135 only */
+#define SAA7135_DSP_RWSTATE                     0x580
+#define SAA7135_DSP_RWSTATE_ERR                 (1 << 3)
+#define SAA7135_DSP_RWSTATE_IDA                 (1 << 2)
+#define SAA7135_DSP_RWSTATE_RDB                 (1 << 1)
+#define SAA7135_DSP_RWSTATE_WRR                 (1 << 0)
+
+#define SAA7135_DSP_RWCLEAR			0x586
+#define SAA7135_DSP_RWCLEAR_RERR		    1
+
+#define SAA7133_I2S_AUDIO_CONTROL               0x591
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c
new file mode 100644
index 000000000000..2e3f4b412d8c
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-ts.c
@@ -0,0 +1,327 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int ts_debug;
+module_param(ts_debug, int, 0644);
+MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]");
+
+#define dprintk(fmt, arg...)	if (ts_debug) \
+	printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+			   struct saa7134_buf *buf,
+			   struct saa7134_buf *next)
+{
+
+	dprintk("buffer_activate [%p]",buf);
+	buf->vb.state = VIDEOBUF_ACTIVE;
+	buf->top_seen = 0;
+
+	if (NULL == next)
+		next = buf;
+	if (V4L2_FIELD_TOP == buf->vb.field) {
+		dprintk("- [top]     buf=%p next=%p\n",buf,next);
+		saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf));
+		saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next));
+	} else {
+		dprintk("- [bottom]  buf=%p next=%p\n",buf,next);
+		saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next));
+		saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf));
+	}
+
+	/* start DMA */
+	saa7134_set_dmabits(dev);
+
+	mod_timer(&dev->ts_q.timeout, jiffies+TS_BUFFER_TIMEOUT);
+
+	if (!dev->ts_started)
+		saa7134_ts_start(dev);
+
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+		enum v4l2_field field)
+{
+	struct saa7134_dev *dev = q->priv_data;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+	unsigned int lines, llength, size;
+	int err;
+
+	dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]);
+
+	llength = TS_PACKET_SIZE;
+	lines = dev->ts.nr_packets;
+
+	size = lines * llength;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	if (buf->vb.size != size) {
+		saa7134_dma_free(q,buf);
+	}
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+
+		struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+		dprintk("buffer_prepare: needs_init\n");
+
+		buf->vb.width  = llength;
+		buf->vb.height = lines;
+		buf->vb.size   = size;
+		buf->pt        = &dev->ts.pt_ts;
+
+		err = videobuf_iolock(q,&buf->vb,NULL);
+		if (err)
+			goto oops;
+		err = saa7134_pgtable_build(dev->pci,buf->pt,
+					    dma->sglist,
+					    dma->sglen,
+					    saa7134_buffer_startpage(buf));
+		if (err)
+			goto oops;
+	}
+
+	buf->vb.state = VIDEOBUF_PREPARED;
+	buf->activate = buffer_activate;
+	buf->vb.field = field;
+	return 0;
+
+ oops:
+	saa7134_dma_free(q,buf);
+	return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+	struct saa7134_dev *dev = q->priv_data;
+
+	*size = TS_PACKET_SIZE * dev->ts.nr_packets;
+	if (0 == *count)
+		*count = dev->ts.nr_bufs;
+	*count = saa7134_buffer_count(*size,*count);
+
+	return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_dev *dev = q->priv_data;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+	saa7134_buffer_queue(dev,&dev->ts_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+	struct saa7134_dev *dev = q->priv_data;
+
+	if (dev->ts_started)
+		saa7134_ts_stop(dev);
+
+	saa7134_dma_free(q,buf);
+}
+
+struct videobuf_queue_ops saa7134_ts_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+EXPORT_SYMBOL_GPL(saa7134_ts_qops);
+
+/* ----------------------------------------------------------- */
+/* exported stuff                                              */
+
+static unsigned int tsbufs = 8;
+module_param(tsbufs, int, 0444);
+MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32");
+
+static unsigned int ts_nr_packets = 64;
+module_param(ts_nr_packets, int, 0444);
+MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)");
+
+int saa7134_ts_init_hw(struct saa7134_dev *dev)
+{
+	/* deactivate TS softreset */
+	saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+	/* TSSOP high active, TSVAL high active, TSLOCK ignored */
+	saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
+	saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
+	saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
+	saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
+	/* TSNOPIT=0, TSCOLAP=0 */
+	saa_writeb(SAA7134_TS_DMA2,
+		((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00));
+
+	return 0;
+}
+
+int saa7134_ts_init1(struct saa7134_dev *dev)
+{
+	/* sanitycheck insmod options */
+	if (tsbufs < 2)
+		tsbufs = 2;
+	if (tsbufs > VIDEO_MAX_FRAME)
+		tsbufs = VIDEO_MAX_FRAME;
+	if (ts_nr_packets < 4)
+		ts_nr_packets = 4;
+	if (ts_nr_packets > 312)
+		ts_nr_packets = 312;
+	dev->ts.nr_bufs    = tsbufs;
+	dev->ts.nr_packets = ts_nr_packets;
+
+	INIT_LIST_HEAD(&dev->ts_q.queue);
+	init_timer(&dev->ts_q.timeout);
+	dev->ts_q.timeout.function = saa7134_buffer_timeout;
+	dev->ts_q.timeout.data     = (unsigned long)(&dev->ts_q);
+	dev->ts_q.dev              = dev;
+	dev->ts_q.need_two         = 1;
+	dev->ts_started            = 0;
+	saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts);
+
+	/* init TS hw */
+	saa7134_ts_init_hw(dev);
+
+	return 0;
+}
+
+/* Function for stop TS */
+int saa7134_ts_stop(struct saa7134_dev *dev)
+{
+	dprintk("TS stop\n");
+
+	BUG_ON(!dev->ts_started);
+
+	/* Stop TS stream */
+	switch (saa7134_boards[dev->board].ts_type) {
+	case SAA7134_MPEG_TS_PARALLEL:
+		saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
+		dev->ts_started = 0;
+		break;
+	case SAA7134_MPEG_TS_SERIAL:
+		saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+		dev->ts_started = 0;
+		break;
+	}
+	return 0;
+}
+
+/* Function for start TS */
+int saa7134_ts_start(struct saa7134_dev *dev)
+{
+	dprintk("TS start\n");
+
+	BUG_ON(dev->ts_started);
+
+	/* dma: setup channel 5 (= TS) */
+	saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff);
+	saa_writeb(SAA7134_TS_DMA1,
+		((dev->ts.nr_packets - 1) >> 8) & 0xff);
+	/* TSNOPIT=0, TSCOLAP=0 */
+	saa_writeb(SAA7134_TS_DMA2,
+		(((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00);
+	saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE);
+	saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 |
+					  SAA7134_RS_CONTROL_ME |
+					  (dev->ts.pt_ts.dma >> 12));
+
+	/* reset hardware TS buffers */
+	saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+	saa_writeb(SAA7134_TS_SERIAL1, 0x03);
+	saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+	saa_writeb(SAA7134_TS_SERIAL1, 0x01);
+
+	/* TS clock non-inverted */
+	saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+
+	/* Start TS stream */
+	switch (saa7134_boards[dev->board].ts_type) {
+	case SAA7134_MPEG_TS_PARALLEL:
+		saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+		saa_writeb(SAA7134_TS_PARALLEL, 0xec |
+			(saa7134_boards[dev->board].ts_force_val << 4));
+		break;
+	case SAA7134_MPEG_TS_SERIAL:
+		saa_writeb(SAA7134_TS_SERIAL0, 0xd8);
+		saa_writeb(SAA7134_TS_PARALLEL, 0x6c |
+			(saa7134_boards[dev->board].ts_force_val << 4));
+		saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc);
+		saa_writeb(SAA7134_TS_SERIAL1, 0x02);
+		break;
+	}
+
+	dev->ts_started = 1;
+
+	return 0;
+}
+
+int saa7134_ts_fini(struct saa7134_dev *dev)
+{
+	saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts);
+	return 0;
+}
+
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status)
+{
+	enum v4l2_field field;
+
+	spin_lock(&dev->slock);
+	if (dev->ts_q.curr) {
+		field = dev->ts_q.curr->vb.field;
+		if (field == V4L2_FIELD_TOP) {
+			if ((status & 0x100000) != 0x000000)
+				goto done;
+		} else {
+			if ((status & 0x100000) != 0x100000)
+				goto done;
+		}
+		saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE);
+	}
+	saa7134_buffer_next(dev,&dev->ts_q);
+
+ done:
+	spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c
new file mode 100644
index 000000000000..b7a99bee2f98
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c
@@ -0,0 +1,1087 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * tv audio decoder (fm stereo, nicam, ...)
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/kthread.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <asm/div64.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]");
+
+static unsigned int audio_ddep;
+module_param(audio_ddep, int, 0644);
+MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite");
+
+static int audio_clock_override = UNSET;
+module_param(audio_clock_override, int, 0644);
+
+static int audio_clock_tweak;
+module_param(audio_clock_tweak, int, 0644);
+MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])");
+
+#define dprintk(fmt, arg...)	if (audio_debug) \
+	printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg)
+#define d2printk(fmt, arg...)	if (audio_debug > 1) \
+	printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg)
+
+#define print_regb(reg) printk("%s:   reg 0x%03x [%-16s]: 0x%02x\n", \
+		dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg)))
+
+/* msecs */
+#define SCAN_INITIAL_DELAY     1000
+#define SCAN_SAMPLE_DELAY       200
+#define SCAN_SUBCARRIER_DELAY  2000
+
+/* ------------------------------------------------------------------ */
+/* saa7134 code                                                       */
+
+static struct mainscan {
+	char         *name;
+	v4l2_std_id  std;
+	int          carr;
+} mainscan[] = {
+	{
+		.name = "MN",
+		.std  = V4L2_STD_MN,
+		.carr = 4500,
+	},{
+		.name = "BGH",
+		.std  = V4L2_STD_B | V4L2_STD_GH,
+		.carr = 5500,
+	},{
+		.name = "I",
+		.std  = V4L2_STD_PAL_I,
+		.carr = 6000,
+	},{
+		.name = "DKL",
+		.std  = V4L2_STD_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC,
+		.carr = 6500,
+	}
+};
+
+static struct saa7134_tvaudio tvaudio[] = {
+	{
+		.name          = "PAL-B/G FM-stereo",
+		.std           = V4L2_STD_PAL_BG,
+		.mode          = TVAUDIO_FM_BG_STEREO,
+		.carr1         = 5500,
+		.carr2         = 5742,
+	},{
+		.name          = "PAL-D/K1 FM-stereo",
+		.std           = V4L2_STD_PAL_DK,
+		.carr1         = 6500,
+		.carr2         = 6258,
+		.mode          = TVAUDIO_FM_BG_STEREO,
+	},{
+		.name          = "PAL-D/K2 FM-stereo",
+		.std           = V4L2_STD_PAL_DK,
+		.carr1         = 6500,
+		.carr2         = 6742,
+		.mode          = TVAUDIO_FM_BG_STEREO,
+	},{
+		.name          = "PAL-D/K3 FM-stereo",
+		.std           = V4L2_STD_PAL_DK,
+		.carr1         = 6500,
+		.carr2         = 5742,
+		.mode          = TVAUDIO_FM_BG_STEREO,
+	},{
+		.name          = "PAL-B/G NICAM",
+		.std           = V4L2_STD_PAL_BG,
+		.carr1         = 5500,
+		.carr2         = 5850,
+		.mode          = TVAUDIO_NICAM_FM,
+	},{
+		.name          = "PAL-I NICAM",
+		.std           = V4L2_STD_PAL_I,
+		.carr1         = 6000,
+		.carr2         = 6552,
+		.mode          = TVAUDIO_NICAM_FM,
+	},{
+		.name          = "PAL-D/K NICAM",
+		.std           = V4L2_STD_PAL_DK,
+		.carr1         = 6500,
+		.carr2         = 5850,
+		.mode          = TVAUDIO_NICAM_FM,
+	},{
+		.name          = "SECAM-L NICAM",
+		.std           = V4L2_STD_SECAM_L,
+		.carr1         = 6500,
+		.carr2         = 5850,
+		.mode          = TVAUDIO_NICAM_AM,
+	},{
+		.name          = "SECAM-D/K NICAM",
+		.std           = V4L2_STD_SECAM_DK,
+		.carr1         = 6500,
+		.carr2         = 5850,
+		.mode          = TVAUDIO_NICAM_FM,
+	},{
+		.name          = "NTSC-A2 FM-stereo",
+		.std           = V4L2_STD_NTSC,
+		.carr1         = 4500,
+		.carr2         = 4724,
+		.mode          = TVAUDIO_FM_K_STEREO,
+	},{
+		.name          = "NTSC-M",
+		.std           = V4L2_STD_NTSC,
+		.carr1         = 4500,
+		.carr2         = -1,
+		.mode          = TVAUDIO_FM_MONO,
+	}
+};
+#define TVAUDIO ARRAY_SIZE(tvaudio)
+
+/* ------------------------------------------------------------------ */
+
+static u32 tvaudio_carr2reg(u32 carrier)
+{
+	u64 a = carrier;
+
+	a <<= 24;
+	do_div(a,12288);
+	return a;
+}
+
+static void tvaudio_setcarrier(struct saa7134_dev *dev,
+			       int primary, int secondary)
+{
+	if (-1 == secondary)
+		secondary = primary;
+	saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary));
+	saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary));
+}
+
+#define SAA7134_MUTE_MASK 0xbb
+#define SAA7134_MUTE_ANALOG 0x04
+#define SAA7134_MUTE_I2S 0x40
+
+static void mute_input_7134(struct saa7134_dev *dev)
+{
+	unsigned int mute;
+	struct saa7134_input *in;
+	int ausel=0, ics=0, ocs=0;
+	int mask;
+
+	/* look what is to do ... */
+	in   = dev->input;
+	mute = (dev->ctl_mute ||
+		(dev->automute  &&  (&card(dev).radio) != in));
+	if (card(dev).mute.name) {
+		/*
+		 * 7130 - we'll mute using some unconnected audio input
+		 * 7134 - we'll probably should switch external mux with gpio
+		 */
+		if (mute)
+			in = &card(dev).mute;
+	}
+
+	if (dev->hw_mute  == mute &&
+		dev->hw_input == in && !dev->insuspend) {
+		dprintk("mute/input: nothing to do [mute=%d,input=%s]\n",
+			mute,in->name);
+		return;
+	}
+
+	dprintk("ctl_mute=%d automute=%d input=%s  =>  mute=%d input=%s\n",
+		dev->ctl_mute,dev->automute,dev->input->name,mute,in->name);
+	dev->hw_mute  = mute;
+	dev->hw_input = in;
+
+	if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device)
+		/* 7134 mute */
+		saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ?
+						    SAA7134_MUTE_MASK |
+						    SAA7134_MUTE_ANALOG |
+						    SAA7134_MUTE_I2S :
+						    SAA7134_MUTE_MASK);
+
+	/* switch internal audio mux */
+	switch (in->amux) {
+	case TV:         ausel=0xc0; ics=0x00; ocs=0x02; break;
+	case LINE1:      ausel=0x80; ics=0x00; ocs=0x00; break;
+	case LINE2:      ausel=0x80; ics=0x08; ocs=0x01; break;
+	case LINE2_LEFT: ausel=0x80; ics=0x08; ocs=0x05; break;
+	}
+	saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel);
+	saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics);
+	saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs);
+	// for oss, we need to change the clock configuration
+	if (in->amux == TV)
+		saa_andorb(SAA7134_SIF_SAMPLE_FREQ,   0x03, 0x00);
+	else
+		saa_andorb(SAA7134_SIF_SAMPLE_FREQ,   0x03, 0x01);
+
+	/* switch gpio-connected external audio mux */
+	if (0 == card(dev).gpiomask)
+		return;
+
+	mask = card(dev).gpiomask;
+	saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   mask, mask);
+	saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
+	saa7134_track_gpio(dev,in->name);
+}
+
+static void tvaudio_setmode(struct saa7134_dev *dev,
+			    struct saa7134_tvaudio *audio,
+			    char *note)
+{
+	int acpf, tweak = 0;
+
+	if (dev->tvnorm->id == V4L2_STD_NTSC) {
+		acpf = 0x19066;
+	} else {
+		acpf = 0x1e000;
+	}
+	if (audio_clock_tweak > -1024 && audio_clock_tweak < 1024)
+		tweak = audio_clock_tweak;
+
+	if (note)
+		dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n",
+			note,audio->name,
+			audio->carr1 / 1000, audio->carr1 % 1000,
+			audio->carr2 / 1000, audio->carr2 % 1000,
+			acpf, tweak);
+
+	acpf += tweak;
+	saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, (acpf & 0x0000ff) >> 0);
+	saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, (acpf & 0x00ff00) >> 8);
+	saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, (acpf & 0x030000) >> 16);
+	tvaudio_setcarrier(dev,audio->carr1,audio->carr2);
+
+	switch (audio->mode) {
+	case TVAUDIO_FM_MONO:
+	case TVAUDIO_FM_BG_STEREO:
+		saa_writeb(SAA7134_DEMODULATOR,               0x00);
+		saa_writeb(SAA7134_DCXO_IDENT_CTRL,           0x00);
+		saa_writeb(SAA7134_FM_DEEMPHASIS,             0x22);
+		saa_writeb(SAA7134_FM_DEMATRIX,               0x80);
+		saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT,  0xa0);
+		break;
+	case TVAUDIO_FM_K_STEREO:
+		saa_writeb(SAA7134_DEMODULATOR,               0x00);
+		saa_writeb(SAA7134_DCXO_IDENT_CTRL,           0x01);
+		saa_writeb(SAA7134_FM_DEEMPHASIS,             0x22);
+		saa_writeb(SAA7134_FM_DEMATRIX,               0x80);
+		saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT,  0xa0);
+		break;
+	case TVAUDIO_NICAM_FM:
+		saa_writeb(SAA7134_DEMODULATOR,               0x10);
+		saa_writeb(SAA7134_DCXO_IDENT_CTRL,           0x00);
+		saa_writeb(SAA7134_FM_DEEMPHASIS,             0x44);
+		saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT,  0xa1);
+		saa_writeb(SAA7134_NICAM_CONFIG,              0x00);
+		break;
+	case TVAUDIO_NICAM_AM:
+		saa_writeb(SAA7134_DEMODULATOR,               0x12);
+		saa_writeb(SAA7134_DCXO_IDENT_CTRL,           0x00);
+		saa_writeb(SAA7134_FM_DEEMPHASIS,             0x44);
+		saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT,  0xa1);
+		saa_writeb(SAA7134_NICAM_CONFIG,              0x00);
+		break;
+	case TVAUDIO_FM_SAT_STEREO:
+		/* not implemented (yet) */
+		break;
+	}
+}
+
+static int tvaudio_sleep(struct saa7134_dev *dev, int timeout)
+{
+	if (dev->thread.scan1 == dev->thread.scan2 &&
+	    !kthread_should_stop()) {
+		if (timeout < 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		} else {
+			schedule_timeout_interruptible
+						(msecs_to_jiffies(timeout));
+		}
+	}
+	return dev->thread.scan1 != dev->thread.scan2;
+}
+
+static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan)
+{
+	__s32 left,right,value;
+
+	if (!(dev->tvnorm->id & scan->std)) {
+		value = 0;
+		dprintk("skipping %d.%03d MHz [%4s]\n",
+			scan->carr / 1000, scan->carr % 1000, scan->name);
+		return 0;
+	}
+
+	if (audio_debug > 1) {
+		int i;
+		dprintk("debug %d:",scan->carr);
+		for (i = -150; i <= 150; i += 30) {
+			tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i);
+			saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+			if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+				return -1;
+			value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+			if (0 == i)
+				printk("  #  %6d  # ",value >> 16);
+			else
+				printk(" %6d",value >> 16);
+		}
+		printk("\n");
+	}
+
+	tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90);
+	saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+	if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+		return -1;
+	left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+	tvaudio_setcarrier(dev,scan->carr+90,scan->carr+90);
+	saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+	if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+		return -1;
+	right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+	left >>= 16;
+	right >>= 16;
+	value = left > right ? left - right : right - left;
+	dprintk("scanning %d.%03d MHz [%4s] =>  dc is %5d [%d/%d]\n",
+		scan->carr / 1000, scan->carr % 1000,
+		scan->name, value, left, right);
+	return value;
+}
+
+
+static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio)
+{
+	__u32 idp, nicam, nicam_status;
+	int retval = -1;
+
+	switch (audio->mode) {
+	case TVAUDIO_FM_MONO:
+		return V4L2_TUNER_SUB_MONO;
+	case TVAUDIO_FM_K_STEREO:
+	case TVAUDIO_FM_BG_STEREO:
+		idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5;
+		dprintk("getstereo: fm/stereo: idp=0x%x\n",idp);
+		if (0x03 == (idp & 0x03))
+			retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+		else if (0x05 == (idp & 0x05))
+			retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+		else if (0x01 == (idp & 0x01))
+			retval = V4L2_TUNER_SUB_MONO;
+		break;
+	case TVAUDIO_FM_SAT_STEREO:
+		/* not implemented (yet) */
+		break;
+	case TVAUDIO_NICAM_FM:
+	case TVAUDIO_NICAM_AM:
+		nicam = saa_readb(SAA7134_AUDIO_STATUS);
+		dprintk("getstereo: nicam=0x%x\n",nicam);
+		if (nicam & 0x1) {
+			nicam_status = saa_readb(SAA7134_NICAM_STATUS);
+			dprintk("getstereo: nicam_status=0x%x\n", nicam_status);
+
+			switch (nicam_status & 0x03) {
+			    case 0x01:
+				retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+				break;
+			    case 0x02:
+				retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+				break;
+			    default:
+				retval = V4L2_TUNER_SUB_MONO;
+			}
+		} else {
+			/* No nicam detected */
+		}
+		break;
+	}
+	if (retval != -1)
+		dprintk("found audio subchannels:%s%s%s%s\n",
+			(retval & V4L2_TUNER_SUB_MONO)   ? " mono"   : "",
+			(retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
+			(retval & V4L2_TUNER_SUB_LANG1)  ? " lang1"  : "",
+			(retval & V4L2_TUNER_SUB_LANG2)  ? " lang2"  : "");
+	return retval;
+}
+
+static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio,
+			     u32 mode)
+{
+	static char *name[] = {
+		[ V4L2_TUNER_MODE_MONO   ] = "mono",
+		[ V4L2_TUNER_MODE_STEREO ] = "stereo",
+		[ V4L2_TUNER_MODE_LANG1  ] = "lang1",
+		[ V4L2_TUNER_MODE_LANG2  ] = "lang2",
+		[ V4L2_TUNER_MODE_LANG1_LANG2  ] = "lang1+lang2",
+	};
+	static u32 fm[] = {
+		[ V4L2_TUNER_MODE_MONO   ] = 0x00,  /* ch1  */
+		[ V4L2_TUNER_MODE_STEREO ] = 0x80,  /* auto */
+		[ V4L2_TUNER_MODE_LANG1  ] = 0x00,  /* ch1  */
+		[ V4L2_TUNER_MODE_LANG2  ] = 0x01,  /* ch2  */
+		[ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80,  /* auto */
+	};
+	u32 reg;
+
+	switch (audio->mode) {
+	case TVAUDIO_FM_MONO:
+		/* nothing to do ... */
+		break;
+	case TVAUDIO_FM_K_STEREO:
+	case TVAUDIO_FM_BG_STEREO:
+	case TVAUDIO_NICAM_AM:
+	case TVAUDIO_NICAM_FM:
+		dprintk("setstereo [fm] => %s\n",
+			name[ mode % ARRAY_SIZE(name) ]);
+		reg = fm[ mode % ARRAY_SIZE(fm) ];
+		saa_writeb(SAA7134_FM_DEMATRIX, reg);
+		break;
+	case TVAUDIO_FM_SAT_STEREO:
+		/* Not implemented */
+		break;
+	}
+	return 0;
+}
+
+static int tvaudio_thread(void *data)
+{
+	struct saa7134_dev *dev = data;
+	int carr_vals[ARRAY_SIZE(mainscan)];
+	unsigned int i, audio, nscan;
+	int max1,max2,carrier,rx,mode,lastmode,default_carrier;
+
+	set_freezable();
+
+	for (;;) {
+		tvaudio_sleep(dev,-1);
+		if (kthread_should_stop())
+			goto done;
+
+	restart:
+		try_to_freeze();
+
+		dev->thread.scan1 = dev->thread.scan2;
+		dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+		dev->tvaudio  = NULL;
+
+		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 */
+		if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY))
+			goto restart;
+
+		max1 = 0;
+		max2 = 0;
+		nscan = 0;
+		carrier = 0;
+		default_carrier = 0;
+		for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+			if (!(dev->tvnorm->id & mainscan[i].std))
+				continue;
+			if (!default_carrier)
+				default_carrier = mainscan[i].carr;
+			nscan++;
+		}
+
+		if (1 == nscan) {
+			/* only one candidate -- skip scan ;) */
+			dprintk("only one main carrier candidate - skipping scan\n");
+			max1 = 12345;
+			carrier = default_carrier;
+		} else {
+			/* scan for the main carrier */
+			saa_writeb(SAA7134_MONITOR_SELECT,0x00);
+			tvaudio_setmode(dev,&tvaudio[0],NULL);
+			for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+				carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i);
+				if (dev->thread.scan1 != dev->thread.scan2)
+					goto restart;
+			}
+			for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) {
+				if (max1 < carr_vals[i]) {
+					max2 = max1;
+					max1 = carr_vals[i];
+					carrier = mainscan[i].carr;
+				} else if (max2 < carr_vals[i]) {
+					max2 = carr_vals[i];
+				}
+			}
+		}
+
+		if (0 != carrier && max1 > 2000 && max1 > max2*3) {
+			/* found good carrier */
+			dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n",
+				dev->tvnorm->name, carrier/1000, carrier%1000,
+				max1, max2);
+			dev->last_carrier = carrier;
+			dev->automute = 0;
+
+		} else if (0 != dev->last_carrier) {
+			/* no carrier -- try last detected one as fallback */
+			carrier = dev->last_carrier;
+			dprintk("audio carrier scan failed, "
+				"using %d.%03d MHz [last detected]\n",
+				carrier/1000, carrier%1000);
+			dev->automute = 1;
+
+		} else {
+			/* no carrier + no fallback -- use default */
+			carrier = default_carrier;
+			dprintk("audio carrier scan failed, "
+				"using %d.%03d MHz [default]\n",
+				carrier/1000, carrier%1000);
+			dev->automute = 1;
+		}
+		tvaudio_setcarrier(dev,carrier,carrier);
+		saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00);
+		saa7134_tvaudio_setmute(dev);
+		/* find the exact tv audio norm */
+		for (audio = UNSET, i = 0; i < TVAUDIO; i++) {
+			if (dev->tvnorm->id != UNSET &&
+				!(dev->tvnorm->id & tvaudio[i].std))
+				continue;
+			if (tvaudio[i].carr1 != carrier)
+				continue;
+			/* Note: at least the primary carrier is right here */
+			if (UNSET == audio)
+				audio = i;
+			tvaudio_setmode(dev,&tvaudio[i],"trying");
+			if (tvaudio_sleep(dev,SCAN_SUBCARRIER_DELAY))
+				goto restart;
+			if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) {
+				audio = i;
+				break;
+			}
+		}
+		saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30);
+		if (UNSET == audio)
+			continue;
+		tvaudio_setmode(dev,&tvaudio[audio],"using");
+
+		tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO);
+		dev->tvaudio = &tvaudio[audio];
+
+		lastmode = 42;
+		for (;;) {
+
+			try_to_freeze();
+
+			if (tvaudio_sleep(dev,5000))
+				goto restart;
+			if (kthread_should_stop())
+				break;
+			if (UNSET == dev->thread.mode) {
+				rx = tvaudio_getstereo(dev, &tvaudio[audio]);
+				mode = saa7134_tvaudio_rx2mode(rx);
+			} else {
+				mode = dev->thread.mode;
+			}
+			if (lastmode != mode) {
+				tvaudio_setstereo(dev,&tvaudio[audio],mode);
+				lastmode = mode;
+			}
+		}
+	}
+
+ done:
+	dev->thread.stopped = 1;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* saa7133 / saa7135 code                                             */
+
+static char *stdres[0x20] = {
+	[0x00] = "no standard detected",
+	[0x01] = "B/G (in progress)",
+	[0x02] = "D/K (in progress)",
+	[0x03] = "M (in progress)",
+
+	[0x04] = "B/G A2",
+	[0x05] = "B/G NICAM",
+	[0x06] = "D/K A2 (1)",
+	[0x07] = "D/K A2 (2)",
+	[0x08] = "D/K A2 (3)",
+	[0x09] = "D/K NICAM",
+	[0x0a] = "L NICAM",
+	[0x0b] = "I NICAM",
+
+	[0x0c] = "M Korea",
+	[0x0d] = "M BTSC ",
+	[0x0e] = "M EIAJ",
+
+	[0x0f] = "FM radio / IF 10.7 / 50 deemp",
+	[0x10] = "FM radio / IF 10.7 / 75 deemp",
+	[0x11] = "FM radio / IF sel / 50 deemp",
+	[0x12] = "FM radio / IF sel / 75 deemp",
+
+	[0x13 ... 0x1e ] = "unknown",
+	[0x1f] = "??? [in progress]",
+};
+
+#define DSP_RETRY 32
+#define DSP_DELAY 16
+#define SAA7135_DSP_RWCLEAR_RERR 1
+
+static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev)
+{
+	int state = saa_readb(SAA7135_DSP_RWSTATE);
+	if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) {
+		d2printk("%s: resetting error bit\n", dev->name);
+		saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR);
+	}
+	return 0;
+}
+
+static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit)
+{
+	int state, count = DSP_RETRY;
+
+	state = saa_readb(SAA7135_DSP_RWSTATE);
+	if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) {
+		printk(KERN_WARNING "%s: dsp access error\n", dev->name);
+		saa_dsp_reset_error_bit(dev);
+		return -EIO;
+	}
+	while (0 == (state & bit)) {
+		if (unlikely(0 == count)) {
+			printk("%s: dsp access wait timeout [bit=%s]\n",
+			       dev->name,
+			       (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" :
+			       (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" :
+			       (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" :
+			       "???");
+			return -EIO;
+		}
+		saa_wait(DSP_DELAY);
+		state = saa_readb(SAA7135_DSP_RWSTATE);
+		count--;
+	}
+	return 0;
+}
+
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value)
+{
+	int err;
+
+	d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value);
+	err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+	if (err < 0)
+		return err;
+	saa_writel(reg,value);
+	err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int getstereo_7133(struct saa7134_dev *dev)
+{
+	int retval = V4L2_TUNER_SUB_MONO;
+	u32 value;
+
+	value = saa_readl(0x528 >> 2);
+	if (value & 0x20)
+		retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+	if (value & 0x40)
+		retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+	return retval;
+}
+
+static int mute_input_7133(struct saa7134_dev *dev)
+{
+	u32 reg = 0;
+	u32 xbarin, xbarout;
+	int mask;
+	struct saa7134_input *in;
+
+	xbarin = 0x03;
+	switch (dev->input->amux) {
+	case TV:
+		reg = 0x02;
+		xbarin = 0;
+		break;
+	case LINE1:
+		reg = 0x00;
+		break;
+	case LINE2:
+	case LINE2_LEFT:
+		reg = 0x09;
+		break;
+	}
+	saa_dsp_writel(dev, 0x464 >> 2, xbarin);
+	if (dev->ctl_mute) {
+		reg = 0x07;
+		xbarout = 0xbbbbbb;
+	} else
+		xbarout = 0xbbbb10;
+	saa_dsp_writel(dev, 0x46c >> 2, xbarout);
+
+	saa_writel(0x594 >> 2, reg);
+
+
+	/* switch gpio-connected external audio mux */
+	if (0 != card(dev).gpiomask) {
+		mask = card(dev).gpiomask;
+
+		if (card(dev).mute.name && dev->ctl_mute)
+			in = &card(dev).mute;
+		else
+			in = dev->input;
+
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   mask, mask);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
+		saa7134_track_gpio(dev,in->name);
+	}
+
+	return 0;
+}
+
+static int tvaudio_thread_ddep(void *data)
+{
+	struct saa7134_dev *dev = data;
+	u32 value, norms;
+
+	set_freezable();
+	for (;;) {
+		tvaudio_sleep(dev,-1);
+		if (kthread_should_stop())
+			goto done;
+	restart:
+		try_to_freeze();
+
+		dev->thread.scan1 = dev->thread.scan2;
+		dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+
+		if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) {
+			/* insmod option override */
+			norms = (audio_ddep << 2) | 0x01;
+			dprintk("ddep override: %s\n",stdres[audio_ddep]);
+		} else if (&card(dev).radio == dev->input) {
+			dprintk("FM Radio\n");
+			if (dev->tuner_type == TUNER_PHILIPS_TDA8290) {
+				norms = (0x11 << 2) | 0x01;
+				saa_dsp_writel(dev, 0x42c >> 2, 0x729555);
+			} else {
+				norms = (0x0f << 2) | 0x01;
+			}
+		} else {
+			/* (let chip) scan for sound carrier */
+			norms = 0;
+			if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH))
+				norms |= 0x04;
+			if (dev->tvnorm->id & V4L2_STD_PAL_I)
+				norms |= 0x20;
+			if (dev->tvnorm->id & V4L2_STD_DK)
+				norms |= 0x08;
+			if (dev->tvnorm->id & V4L2_STD_MN)
+				norms |= 0x40;
+			if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))
+				norms |= 0x10;
+			if (0 == norms)
+				norms = 0x7c; /* all */
+			dprintk("scanning:%s%s%s%s%s\n",
+				(norms & 0x04) ? " B/G"  : "",
+				(norms & 0x08) ? " D/K"  : "",
+				(norms & 0x10) ? " L/L'" : "",
+				(norms & 0x20) ? " I"    : "",
+				(norms & 0x40) ? " M"    : "");
+		}
+
+		/* kick automatic standard detection */
+		saa_dsp_writel(dev, 0x454 >> 2, 0);
+		saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80);
+
+		/* setup crossbars */
+		saa_dsp_writel(dev, 0x464 >> 2, 0x000000);
+		saa_dsp_writel(dev, 0x470 >> 2, 0x101010);
+
+		if (tvaudio_sleep(dev,3000))
+			goto restart;
+		value = saa_readl(0x528 >> 2) & 0xffffff;
+
+		dprintk("tvaudio thread status: 0x%x [%s%s%s]\n",
+			value, stdres[value & 0x1f],
+			(value & 0x000020) ? ",stereo" : "",
+			(value & 0x000040) ? ",dual"   : "");
+		dprintk("detailed status: "
+			"%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
+			(value & 0x000080) ? " A2/EIAJ pilot tone "     : "",
+			(value & 0x000100) ? " A2/EIAJ dual "           : "",
+			(value & 0x000200) ? " A2/EIAJ stereo "         : "",
+			(value & 0x000400) ? " A2/EIAJ noise mute "     : "",
+
+			(value & 0x000800) ? " BTSC/FM radio pilot "    : "",
+			(value & 0x001000) ? " SAP carrier "            : "",
+			(value & 0x002000) ? " BTSC stereo noise mute " : "",
+			(value & 0x004000) ? " SAP noise mute "         : "",
+			(value & 0x008000) ? " VDSP "                   : "",
+
+			(value & 0x010000) ? " NICST "                  : "",
+			(value & 0x020000) ? " NICDU "                  : "",
+			(value & 0x040000) ? " NICAM muted "            : "",
+			(value & 0x080000) ? " NICAM reserve sound "    : "",
+
+			(value & 0x100000) ? " init done "              : "");
+	}
+
+ done:
+	dev->thread.stopped = 1;
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* common stuff + external entry points                               */
+
+void saa7134_enable_i2s(struct saa7134_dev *dev)
+{
+	int i2s_format;
+
+	if (!card_is_empress(dev))
+		return;
+
+	if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+		return;
+
+	/* configure GPIO for out */
+	saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000);
+
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7133:
+	case PCI_DEVICE_ID_PHILIPS_SAA7135:
+	    /* Set I2S format (SONY)  */
+	    saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00);
+	    /* Start I2S */
+	    saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11);
+	    break;
+
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+	    i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
+
+	    /* enable I2S audio output for the mpeg encoder */
+	    saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
+	    saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
+	    saa_writeb(SAA7134_I2S_OUTPUT_LEVEL,  0x0F);
+	    saa_writeb(SAA7134_I2S_AUDIO_OUTPUT,  0x01);
+
+	default:
+	    break;
+	}
+}
+
+int saa7134_tvaudio_rx2mode(u32 rx)
+{
+	u32 mode;
+
+	mode = V4L2_TUNER_MODE_MONO;
+	if (rx & V4L2_TUNER_SUB_STEREO)
+		mode = V4L2_TUNER_MODE_STEREO;
+	else if (rx & V4L2_TUNER_SUB_LANG1)
+		mode = V4L2_TUNER_MODE_LANG1;
+	else if (rx & V4L2_TUNER_SUB_LANG2)
+		mode = V4L2_TUNER_MODE_LANG2;
+	return mode;
+}
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev)
+{
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7130:
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		mute_input_7134(dev);
+		break;
+	case PCI_DEVICE_ID_PHILIPS_SAA7133:
+	case PCI_DEVICE_ID_PHILIPS_SAA7135:
+		mute_input_7133(dev);
+		break;
+	}
+}
+
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+			      struct saa7134_input *in)
+{
+	dev->input = in;
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7130:
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		mute_input_7134(dev);
+		break;
+	case PCI_DEVICE_ID_PHILIPS_SAA7133:
+	case PCI_DEVICE_ID_PHILIPS_SAA7135:
+		mute_input_7133(dev);
+		break;
+	}
+	saa7134_enable_i2s(dev);
+}
+
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level)
+{
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		saa_writeb(SAA7134_CHANNEL1_LEVEL,     level & 0x1f);
+		saa_writeb(SAA7134_CHANNEL2_LEVEL,     level & 0x1f);
+		saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f);
+		break;
+	}
+}
+
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev)
+{
+	int retval = V4L2_TUNER_SUB_MONO;
+
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		if (dev->tvaudio)
+			retval = tvaudio_getstereo(dev,dev->tvaudio);
+		break;
+	case PCI_DEVICE_ID_PHILIPS_SAA7133:
+	case PCI_DEVICE_ID_PHILIPS_SAA7135:
+		retval = getstereo_7133(dev);
+		break;
+	}
+	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;
+
+	switch (dev->pci->device) {
+	case PCI_DEVICE_ID_PHILIPS_SAA7134:
+		my_thread = tvaudio_thread;
+		break;
+	case PCI_DEVICE_ID_PHILIPS_SAA7133:
+	case PCI_DEVICE_ID_PHILIPS_SAA7135:
+		my_thread = tvaudio_thread_ddep;
+		break;
+	}
+
+	dev->thread.thread = NULL;
+	dev->thread.scan1 = dev->thread.scan2 = 0;
+	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)) {
+			printk(KERN_WARNING "%s: kernel_thread() failed\n",
+			       dev->name);
+			/* XXX: missing error handling here */
+		}
+	}
+
+	saa7134_enable_i2s(dev);
+	return 0;
+}
+
+int saa7134_tvaudio_close(struct saa7134_dev *dev)
+{
+	dev->automute = 1;
+	/* anything else to undo? */
+	return 0;
+}
+
+int saa7134_tvaudio_fini(struct saa7134_dev *dev)
+{
+	/* shutdown tvaudio thread */
+	if (dev->thread.thread && !dev->thread.stopped)
+		kthread_stop(dev->thread.thread);
+
+	saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */
+	return 0;
+}
+
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
+{
+	if (dev->input->amux != TV) {
+		dprintk("sound IF not in use, skipping scan\n");
+		dev->automute = 0;
+		saa7134_tvaudio_setmute(dev);
+	} else if (dev->thread.thread) {
+		dev->thread.mode = UNSET;
+		dev->thread.scan2++;
+
+		if (!dev->insuspend && !dev->thread.stopped)
+			wake_up_process(dev->thread.thread);
+	} else {
+		dev->automute = 0;
+		saa7134_tvaudio_setmute(dev);
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(saa_dsp_writel);
+EXPORT_SYMBOL(saa7134_tvaudio_setmute);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
new file mode 100644
index 000000000000..e9aa94b807f1
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-vbi.c
@@ -0,0 +1,255 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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 "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0444);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+#define dprintk(fmt, arg...)	if (vbi_debug) \
+	printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+#define VBI_LINE_COUNT     16
+#define VBI_LINE_LENGTH  2048
+#define VBI_SCALE       0x200
+
+static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf,
+		      int task)
+{
+	struct saa7134_tvnorm *norm = dev->tvnorm;
+
+	/* setup video scaler */
+	saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start     &  0xff);
+	saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start     >> 8);
+	saa_writeb(SAA7134_VBI_H_STOP1(task),  norm->h_stop      &  0xff);
+	saa_writeb(SAA7134_VBI_H_STOP2(task),  norm->h_stop      >> 8);
+	saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start_0 &  0xff);
+	saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start_0 >> 8);
+	saa_writeb(SAA7134_VBI_V_STOP1(task),  norm->vbi_v_stop_0  &  0xff);
+	saa_writeb(SAA7134_VBI_V_STOP2(task),  norm->vbi_v_stop_0  >> 8);
+
+	saa_writeb(SAA7134_VBI_H_SCALE_INC1(task),        VBI_SCALE & 0xff);
+	saa_writeb(SAA7134_VBI_H_SCALE_INC2(task),        VBI_SCALE >> 8);
+	saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task),   0x00);
+	saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00);
+
+	saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width   & 0xff);
+	saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width   >> 8);
+	saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height  & 0xff);
+	saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height  >> 8);
+
+	saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+			   struct saa7134_buf *buf,
+			   struct saa7134_buf *next)
+{
+	unsigned long control,base;
+
+	dprintk("buffer_activate [%p]\n",buf);
+	buf->vb.state = VIDEOBUF_ACTIVE;
+	buf->top_seen = 0;
+
+	task_init(dev,buf,TASK_A);
+	task_init(dev,buf,TASK_B);
+	saa_writeb(SAA7134_OFMT_DATA_A, 0x06);
+	saa_writeb(SAA7134_OFMT_DATA_B, 0x06);
+
+	/* DMA: setup channel 2+3 (= VBI Task A+B) */
+	base    = saa7134_buffer_base(buf);
+	control = SAA7134_RS_CONTROL_BURST_16 |
+		SAA7134_RS_CONTROL_ME |
+		(buf->pt->dma >> 12);
+	saa_writel(SAA7134_RS_BA1(2),base);
+	saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2);
+	saa_writel(SAA7134_RS_PITCH(2),buf->vb.width);
+	saa_writel(SAA7134_RS_CONTROL(2),control);
+	saa_writel(SAA7134_RS_BA1(3),base);
+	saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2);
+	saa_writel(SAA7134_RS_PITCH(3),buf->vb.width);
+	saa_writel(SAA7134_RS_CONTROL(3),control);
+
+	/* start DMA */
+	saa7134_set_dmabits(dev);
+	mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+			  struct videobuf_buffer *vb,
+			  enum v4l2_field field)
+{
+	struct saa7134_fh *fh   = q->priv_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+	struct saa7134_tvnorm *norm = dev->tvnorm;
+	unsigned int lines, llength, size;
+	int err;
+
+	lines   = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
+	if (lines > VBI_LINE_COUNT)
+		lines = VBI_LINE_COUNT;
+	llength = VBI_LINE_LENGTH;
+	size = lines * llength * 2;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	if (buf->vb.size != size)
+		saa7134_dma_free(q,buf);
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+		buf->vb.width  = llength;
+		buf->vb.height = lines;
+		buf->vb.size   = size;
+		buf->pt        = &fh->pt_vbi;
+
+		err = videobuf_iolock(q,&buf->vb,NULL);
+		if (err)
+			goto oops;
+		err = saa7134_pgtable_build(dev->pci,buf->pt,
+					    dma->sglist,
+					    dma->sglen,
+					    saa7134_buffer_startpage(buf));
+		if (err)
+			goto oops;
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	buf->activate = buffer_activate;
+	buf->vb.field = field;
+	return 0;
+
+ oops:
+	saa7134_dma_free(q,buf);
+	return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+	struct saa7134_fh *fh   = q->priv_data;
+	struct saa7134_dev *dev = fh->dev;
+	int llength,lines;
+
+	lines   = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1;
+	llength = VBI_LINE_LENGTH;
+	*size = lines * llength * 2;
+	if (0 == *count)
+		*count = vbibufs;
+	*count = saa7134_buffer_count(*size,*count);
+	return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_fh *fh = q->priv_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+	saa7134_buffer_queue(dev,&dev->vbi_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+	saa7134_dma_free(q,buf);
+}
+
+struct videobuf_queue_ops saa7134_vbi_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_vbi_init1(struct saa7134_dev *dev)
+{
+	INIT_LIST_HEAD(&dev->vbi_q.queue);
+	init_timer(&dev->vbi_q.timeout);
+	dev->vbi_q.timeout.function = saa7134_buffer_timeout;
+	dev->vbi_q.timeout.data     = (unsigned long)(&dev->vbi_q);
+	dev->vbi_q.dev              = dev;
+
+	if (vbibufs < 2)
+		vbibufs = 2;
+	if (vbibufs > VIDEO_MAX_FRAME)
+		vbibufs = VIDEO_MAX_FRAME;
+	return 0;
+}
+
+int saa7134_vbi_fini(struct saa7134_dev *dev)
+{
+	/* nothing */
+	return 0;
+}
+
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
+{
+	spin_lock(&dev->slock);
+	if (dev->vbi_q.curr) {
+		dev->vbi_fieldcount++;
+		/* make sure we have seen both fields */
+		if ((status & 0x10) == 0x00) {
+			dev->vbi_q.curr->top_seen = 1;
+			goto done;
+		}
+		if (!dev->vbi_q.curr->top_seen)
+			goto done;
+
+		dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
+		saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE);
+	}
+	saa7134_buffer_next(dev,&dev->vbi_q);
+
+ done:
+	spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
new file mode 100644
index 000000000000..22f8758d047f
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -0,0 +1,2661 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, 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/slab.h>
+#include <linux/sort.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include <media/saa6588.h>
+
+/* ------------------------------------------------------------------ */
+
+unsigned int video_debug;
+static unsigned int gbuffers      = 8;
+static unsigned int noninterlaced; /* 0 */
+static unsigned int gbufsize      = 720*576*4;
+static unsigned int gbufsize_max  = 720*576*4;
+static char secam[] = "--";
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
+module_param(noninterlaced, int, 0644);
+MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
+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&0x04) \
+	printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x191            */
+
+/* Bit 0: VIP code T bit polarity */
+
+#define VP_T_CODE_P_NON_INVERTED	0x00
+#define VP_T_CODE_P_INVERTED		0x01
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x195            */
+
+/* Bit 2: Video output clock delay control */
+
+#define VP_CLK_CTRL2_NOT_DELAYED	0x00
+#define VP_CLK_CTRL2_DELAYED		0x04
+
+/* Bit 1: Video output clock invert control */
+
+#define VP_CLK_CTRL1_NON_INVERTED	0x00
+#define VP_CLK_CTRL1_INVERTED		0x02
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x196            */
+
+/* Bits 2 to 0: VSYNC pin video vertical sync type */
+
+#define VP_VS_TYPE_MASK			0x07
+
+#define VP_VS_TYPE_OFF			0x00
+#define VP_VS_TYPE_V123			0x01
+#define VP_VS_TYPE_V_ITU		0x02
+#define VP_VS_TYPE_VGATE_L		0x03
+#define VP_VS_TYPE_RESERVED1		0x04
+#define VP_VS_TYPE_RESERVED2		0x05
+#define VP_VS_TYPE_F_ITU		0x06
+#define VP_VS_TYPE_SC_FID		0x07
+
+/* ------------------------------------------------------------------ */
+/* data structs for video                                             */
+
+static int video_out[][9] = {
+	[CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
+};
+
+static struct saa7134_format formats[] = {
+	{
+		.name     = "8 bpp gray",
+		.fourcc   = V4L2_PIX_FMT_GREY,
+		.depth    = 8,
+		.pm       = 0x06,
+	},{
+		.name     = "15 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB555,
+		.depth    = 16,
+		.pm       = 0x13 | 0x80,
+	},{
+		.name     = "15 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB555X,
+		.depth    = 16,
+		.pm       = 0x13 | 0x80,
+		.bswap    = 1,
+	},{
+		.name     = "16 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_RGB565,
+		.depth    = 16,
+		.pm       = 0x10 | 0x80,
+	},{
+		.name     = "16 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB565X,
+		.depth    = 16,
+		.pm       = 0x10 | 0x80,
+		.bswap    = 1,
+	},{
+		.name     = "24 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR24,
+		.depth    = 24,
+		.pm       = 0x11,
+	},{
+		.name     = "24 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB24,
+		.depth    = 24,
+		.pm       = 0x11,
+		.bswap    = 1,
+	},{
+		.name     = "32 bpp RGB, le",
+		.fourcc   = V4L2_PIX_FMT_BGR32,
+		.depth    = 32,
+		.pm       = 0x12,
+	},{
+		.name     = "32 bpp RGB, be",
+		.fourcc   = V4L2_PIX_FMT_RGB32,
+		.depth    = 32,
+		.pm       = 0x12,
+		.bswap    = 1,
+		.wswap    = 1,
+	},{
+		.name     = "4:2:2 packed, YUYV",
+		.fourcc   = V4L2_PIX_FMT_YUYV,
+		.depth    = 16,
+		.pm       = 0x00,
+		.bswap    = 1,
+		.yuv      = 1,
+	},{
+		.name     = "4:2:2 packed, UYVY",
+		.fourcc   = V4L2_PIX_FMT_UYVY,
+		.depth    = 16,
+		.pm       = 0x00,
+		.yuv      = 1,
+	},{
+		.name     = "4:2:2 planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV422P,
+		.depth    = 16,
+		.pm       = 0x09,
+		.yuv      = 1,
+		.planar   = 1,
+		.hshift   = 1,
+		.vshift   = 0,
+	},{
+		.name     = "4:2:0 planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YUV420,
+		.depth    = 12,
+		.pm       = 0x0a,
+		.yuv      = 1,
+		.planar   = 1,
+		.hshift   = 1,
+		.vshift   = 1,
+	},{
+		.name     = "4:2:0 planar, Y-Cb-Cr",
+		.fourcc   = V4L2_PIX_FMT_YVU420,
+		.depth    = 12,
+		.pm       = 0x0a,
+		.yuv      = 1,
+		.planar   = 1,
+		.uvswap   = 1,
+		.hshift   = 1,
+		.vshift   = 1,
+	}
+};
+#define FORMATS ARRAY_SIZE(formats)
+
+#define NORM_625_50			\
+		.h_start       = 0,	\
+		.h_stop        = 719,	\
+		.video_v_start = 24,	\
+		.video_v_stop  = 311,	\
+		.vbi_v_start_0 = 7,	\
+		.vbi_v_stop_0  = 22,	\
+		.vbi_v_start_1 = 319,   \
+		.src_timing    = 4
+
+#define NORM_525_60			\
+		.h_start       = 0,	\
+		.h_stop        = 719,	\
+		.video_v_start = 23,	\
+		.video_v_stop  = 262,	\
+		.vbi_v_start_0 = 10,	\
+		.vbi_v_stop_0  = 21,	\
+		.vbi_v_start_1 = 273,	\
+		.src_timing    = 7
+
+static struct saa7134_tvnorm tvnorms[] = {
+	{
+		.name          = "PAL", /* autodetect */
+		.id            = V4L2_STD_PAL,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x81,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "PAL-BG",
+		.id            = V4L2_STD_PAL_BG,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x81,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "PAL-I",
+		.id            = V4L2_STD_PAL_I,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x81,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "PAL-DK",
+		.id            = V4L2_STD_PAL_DK,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x81,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "NTSC",
+		.id            = V4L2_STD_NTSC,
+		NORM_525_60,
+
+		.sync_control  = 0x59,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x89,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x0e,
+		.vgate_misc    = 0x18,
+
+	},{
+		.name          = "SECAM",
+		.id            = V4L2_STD_SECAM,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x1b,
+		.chroma_ctrl1  = 0xd1,
+		.chroma_gain   = 0x80,
+		.chroma_ctrl2  = 0x00,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "SECAM-DK",
+		.id            = V4L2_STD_SECAM_DK,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x1b,
+		.chroma_ctrl1  = 0xd1,
+		.chroma_gain   = 0x80,
+		.chroma_ctrl2  = 0x00,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "SECAM-L",
+		.id            = V4L2_STD_SECAM_L,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x1b,
+		.chroma_ctrl1  = 0xd1,
+		.chroma_gain   = 0x80,
+		.chroma_ctrl2  = 0x00,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "SECAM-Lc",
+		.id            = V4L2_STD_SECAM_LC,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x1b,
+		.chroma_ctrl1  = 0xd1,
+		.chroma_gain   = 0x80,
+		.chroma_ctrl2  = 0x00,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "PAL-M",
+		.id            = V4L2_STD_PAL_M,
+		NORM_525_60,
+
+		.sync_control  = 0x59,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0xb9,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x0e,
+		.vgate_misc    = 0x18,
+
+	},{
+		.name          = "PAL-Nc",
+		.id            = V4L2_STD_PAL_Nc,
+		NORM_625_50,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0xa1,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+
+	},{
+		.name          = "PAL-60",
+		.id            = V4L2_STD_PAL_60,
+
+		.h_start       = 0,
+		.h_stop        = 719,
+		.video_v_start = 23,
+		.video_v_stop  = 262,
+		.vbi_v_start_0 = 10,
+		.vbi_v_stop_0  = 21,
+		.vbi_v_start_1 = 273,
+		.src_timing    = 7,
+
+		.sync_control  = 0x18,
+		.luma_control  = 0x40,
+		.chroma_ctrl1  = 0x81,
+		.chroma_gain   = 0x2a,
+		.chroma_ctrl2  = 0x06,
+		.vgate_misc    = 0x1c,
+	}
+};
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+#define V4L2_CID_PRIVATE_INVERT      (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_Y_ODD       (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_Y_EVEN      (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_AUTOMUTE    (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 4)
+
+static const struct v4l2_queryctrl no_ctrl = {
+	.name  = "42",
+	.flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl video_ctrls[] = {
+	/* --- video --- */
+	{
+		.id            = V4L2_CID_BRIGHTNESS,
+		.name          = "Brightness",
+		.minimum       = 0,
+		.maximum       = 255,
+		.step          = 1,
+		.default_value = 128,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_CONTRAST,
+		.name          = "Contrast",
+		.minimum       = 0,
+		.maximum       = 127,
+		.step          = 1,
+		.default_value = 68,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_SATURATION,
+		.name          = "Saturation",
+		.minimum       = 0,
+		.maximum       = 127,
+		.step          = 1,
+		.default_value = 64,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_HUE,
+		.name          = "Hue",
+		.minimum       = -128,
+		.maximum       = 127,
+		.step          = 1,
+		.default_value = 0,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_HFLIP,
+		.name          = "Mirror",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},
+	/* --- audio --- */
+	{
+		.id            = V4L2_CID_AUDIO_MUTE,
+		.name          = "Mute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_AUDIO_VOLUME,
+		.name          = "Volume",
+		.minimum       = -15,
+		.maximum       = 15,
+		.step          = 1,
+		.default_value = 0,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},
+	/* --- private --- */
+	{
+		.id            = V4L2_CID_PRIVATE_INVERT,
+		.name          = "Invert",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_Y_ODD,
+		.name          = "y offset odd field",
+		.minimum       = 0,
+		.maximum       = 128,
+		.step          = 1,
+		.default_value = 0,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_Y_EVEN,
+		.name          = "y offset even field",
+		.minimum       = 0,
+		.maximum       = 128,
+		.step          = 1,
+		.default_value = 0,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_AUTOMUTE,
+		.name          = "automute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.default_value = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	}
+};
+static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
+
+static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
+{
+	unsigned int i;
+
+	for (i = 0; i < CTRLS; i++)
+		if (video_ctrls[i].id == id)
+			return video_ctrls+i;
+	return NULL;
+}
+
+static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < FORMATS; i++)
+		if (formats[i].fourcc == fourcc)
+			return formats+i;
+	return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management                                                     */
+
+static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
+{
+	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("res: get %d\n",bit);
+	mutex_unlock(&dev->lock);
+	return 1;
+}
+
+static int res_check(struct saa7134_fh *fh, unsigned int bit)
+{
+	return (fh->resources & bit);
+}
+
+static int res_locked(struct saa7134_dev *dev, unsigned int bit)
+{
+	return (dev->resources & bit);
+}
+
+static
+void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
+{
+	BUG_ON((fh->resources & bits) != bits);
+
+	mutex_lock(&dev->lock);
+	fh->resources  &= ~bits;
+	dev->resources &= ~bits;
+	dprintk("res: put %d\n",bits);
+	mutex_unlock(&dev->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+{
+	dprintk("set tv norm = %s\n",norm->name);
+	dev->tvnorm = norm;
+
+	/* setup cropping */
+	dev->crop_bounds.left    = norm->h_start;
+	dev->crop_defrect.left   = norm->h_start;
+	dev->crop_bounds.width   = norm->h_stop - norm->h_start +1;
+	dev->crop_defrect.width  = norm->h_stop - norm->h_start +1;
+
+	dev->crop_bounds.top     = (norm->vbi_v_stop_0+1)*2;
+	dev->crop_defrect.top    = norm->video_v_start*2;
+	dev->crop_bounds.height  = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
+		- dev->crop_bounds.top;
+	dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
+
+	dev->crop_current = dev->crop_defrect;
+
+	saa7134_set_tvnorm_hw(dev);
+}
+
+static void video_mux(struct saa7134_dev *dev, int input)
+{
+	dprintk("video input = %d [%s]\n", input, card_in(dev, input).name);
+	dev->ctl_input = input;
+	set_tvnorm(dev, dev->tvnorm);
+	saa7134_tvaudio_setinput(dev, &card_in(dev, input));
+}
+
+
+static void saa7134_set_decoder(struct saa7134_dev *dev)
+{
+	int luma_control, sync_control, mux;
+
+	struct saa7134_tvnorm *norm = dev->tvnorm;
+	mux = card_in(dev, dev->ctl_input).vmux;
+
+	luma_control = norm->luma_control;
+	sync_control = norm->sync_control;
+
+	if (mux > 5)
+		luma_control |= 0x80; /* svideo */
+	if (noninterlaced || dev->nosignal)
+		sync_control |= 0x20;
+
+	/* setup video decoder */
+	saa_writeb(SAA7134_INCR_DELAY,            0x08);
+	saa_writeb(SAA7134_ANALOG_IN_CTRL1,       0xc0 | mux);
+	saa_writeb(SAA7134_ANALOG_IN_CTRL2,       0x00);
+
+	saa_writeb(SAA7134_ANALOG_IN_CTRL3,       0x90);
+	saa_writeb(SAA7134_ANALOG_IN_CTRL4,       0x90);
+	saa_writeb(SAA7134_HSYNC_START,           0xeb);
+	saa_writeb(SAA7134_HSYNC_STOP,            0xe0);
+	saa_writeb(SAA7134_SOURCE_TIMING1,        norm->src_timing);
+
+	saa_writeb(SAA7134_SYNC_CTRL,             sync_control);
+	saa_writeb(SAA7134_LUMA_CTRL,             luma_control);
+	saa_writeb(SAA7134_DEC_LUMA_BRIGHT,       dev->ctl_bright);
+
+	saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+		dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+
+	saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+		dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+
+	saa_writeb(SAA7134_DEC_CHROMA_HUE,        dev->ctl_hue);
+	saa_writeb(SAA7134_CHROMA_CTRL1,          norm->chroma_ctrl1);
+	saa_writeb(SAA7134_CHROMA_GAIN,           norm->chroma_gain);
+
+	saa_writeb(SAA7134_CHROMA_CTRL2,          norm->chroma_ctrl2);
+	saa_writeb(SAA7134_MODE_DELAY_CTRL,       0x00);
+
+	saa_writeb(SAA7134_ANALOG_ADC,            0x01);
+	saa_writeb(SAA7134_VGATE_START,           0x11);
+	saa_writeb(SAA7134_VGATE_STOP,            0xfe);
+	saa_writeb(SAA7134_MISC_VGATE_MSB,        norm->vgate_misc);
+	saa_writeb(SAA7134_RAW_DATA_GAIN,         0x40);
+	saa_writeb(SAA7134_RAW_DATA_OFFSET,       0x80);
+}
+
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
+{
+	saa7134_set_decoder(dev);
+
+	if (card_in(dev, dev->ctl_input).tv)
+		saa_call_all(dev, core, s_std, dev->tvnorm->id);
+	/* Set the correct norm for the saa6752hs. This function
+	   does nothing if there is no saa6752hs. */
+	saa_call_empress(dev, core, s_std, dev->tvnorm->id);
+}
+
+static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
+{
+	static const struct {
+		int xpsc;
+		int xacl;
+		int xc2_1;
+		int xdcg;
+		int vpfy;
+	} vals[] = {
+		/* XPSC XACL XC2_1 XDCG VPFY */
+		{    1,   0,    0,    0,   0 },
+		{    2,   2,    1,    2,   2 },
+		{    3,   4,    1,    3,   2 },
+		{    4,   8,    1,    4,   2 },
+		{    5,   8,    1,    4,   2 },
+		{    6,   8,    1,    4,   3 },
+		{    7,   8,    1,    4,   3 },
+		{    8,  15,    0,    4,   3 },
+		{    9,  15,    0,    4,   3 },
+		{   10,  16,    1,    5,   3 },
+	};
+	static const int count = ARRAY_SIZE(vals);
+	int i;
+
+	for (i = 0; i < count; i++)
+		if (vals[i].xpsc == prescale)
+			break;
+	if (i == count)
+		return;
+
+	saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
+	saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
+	saa_writeb(SAA7134_LEVEL_CTRL(task),
+		   (vals[i].xc2_1 << 3) | (vals[i].xdcg));
+	saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
+		   (vals[i].vpfy << 2) | vals[i].vpfy);
+}
+
+static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
+{
+	int val,mirror;
+
+	saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale &  0xff);
+	saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
+
+	mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
+	if (yscale < 2048) {
+		/* LPI */
+		dprintk("yscale LPI yscale=%d\n",yscale);
+		saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
+		saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
+		saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
+	} else {
+		/* ACM */
+		val = 0x40 * 1024 / yscale;
+		dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val);
+		saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
+		saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
+		saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
+	}
+	saa_writeb(SAA7134_LUMA_BRIGHT(task),       0x80);
+}
+
+static void set_size(struct saa7134_dev *dev, int task,
+		     int width, int height, int interlace)
+{
+	int prescale,xscale,yscale,y_even,y_odd;
+	int h_start, h_stop, v_start, v_stop;
+	int div = interlace ? 2 : 1;
+
+	/* setup video scaler */
+	h_start = dev->crop_current.left;
+	v_start = dev->crop_current.top/2;
+	h_stop  = (dev->crop_current.left + dev->crop_current.width -1);
+	v_stop  = (dev->crop_current.top + dev->crop_current.height -1)/2;
+
+	saa_writeb(SAA7134_VIDEO_H_START1(task), h_start &  0xff);
+	saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
+	saa_writeb(SAA7134_VIDEO_H_STOP1(task),  h_stop  &  0xff);
+	saa_writeb(SAA7134_VIDEO_H_STOP2(task),  h_stop  >> 8);
+	saa_writeb(SAA7134_VIDEO_V_START1(task), v_start &  0xff);
+	saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
+	saa_writeb(SAA7134_VIDEO_V_STOP1(task),  v_stop  &  0xff);
+	saa_writeb(SAA7134_VIDEO_V_STOP2(task),  v_stop  >> 8);
+
+	prescale = dev->crop_current.width / width;
+	if (0 == prescale)
+		prescale = 1;
+	xscale = 1024 * dev->crop_current.width / prescale / width;
+	yscale = 512 * div * dev->crop_current.height / height;
+	dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale);
+	set_h_prescale(dev,task,prescale);
+	saa_writeb(SAA7134_H_SCALE_INC1(task),      xscale &  0xff);
+	saa_writeb(SAA7134_H_SCALE_INC2(task),      xscale >> 8);
+	set_v_scale(dev,task,yscale);
+
+	saa_writeb(SAA7134_VIDEO_PIXELS1(task),     width  & 0xff);
+	saa_writeb(SAA7134_VIDEO_PIXELS2(task),     width  >> 8);
+	saa_writeb(SAA7134_VIDEO_LINES1(task),      height/div & 0xff);
+	saa_writeb(SAA7134_VIDEO_LINES2(task),      height/div >> 8);
+
+	/* deinterlace y offsets */
+	y_odd  = dev->ctl_y_odd;
+	y_even = dev->ctl_y_even;
+	saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
+	saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
+	saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
+	saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct cliplist {
+	__u16 position;
+	__u8  enable;
+	__u8  disable;
+};
+
+static void set_cliplist(struct saa7134_dev *dev, int reg,
+			struct cliplist *cl, int entries, char *name)
+{
+	__u8 winbits = 0;
+	int i;
+
+	for (i = 0; i < entries; i++) {
+		winbits |= cl[i].enable;
+		winbits &= ~cl[i].disable;
+		if (i < 15 && cl[i].position == cl[i+1].position)
+			continue;
+		saa_writeb(reg + 0, winbits);
+		saa_writeb(reg + 2, cl[i].position & 0xff);
+		saa_writeb(reg + 3, cl[i].position >> 8);
+		dprintk("clip: %s winbits=%02x pos=%d\n",
+			name,winbits,cl[i].position);
+		reg += 8;
+	}
+	for (; reg < 0x400; reg += 8) {
+		saa_writeb(reg+ 0, 0);
+		saa_writeb(reg + 1, 0);
+		saa_writeb(reg + 2, 0);
+		saa_writeb(reg + 3, 0);
+	}
+}
+
+static int clip_range(int val)
+{
+	if (val < 0)
+		val = 0;
+	return val;
+}
+
+/* Sort into smallest position first order */
+static int cliplist_cmp(const void *a, const void *b)
+{
+	const struct cliplist *cla = a;
+	const struct cliplist *clb = b;
+	if (cla->position < clb->position)
+		return -1;
+	if (cla->position > clb->position)
+		return 1;
+	return 0;
+}
+
+static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
+			  int nclips, int interlace)
+{
+	struct cliplist col[16], row[16];
+	int cols = 0, rows = 0, i;
+	int div = interlace ? 2 : 1;
+
+	memset(col, 0, sizeof(col));
+	memset(row, 0, sizeof(row));
+	for (i = 0; i < nclips && i < 8; i++) {
+		col[cols].position = clip_range(clips[i].c.left);
+		col[cols].enable   = (1 << i);
+		cols++;
+		col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
+		col[cols].disable  = (1 << i);
+		cols++;
+		row[rows].position = clip_range(clips[i].c.top / div);
+		row[rows].enable   = (1 << i);
+		rows++;
+		row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
+						/ div);
+		row[rows].disable  = (1 << i);
+		rows++;
+	}
+	sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
+	sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
+	set_cliplist(dev,0x380,col,cols,"cols");
+	set_cliplist(dev,0x384,row,rows,"rows");
+	return 0;
+}
+
+static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
+{
+	enum v4l2_field field;
+	int maxw, maxh;
+
+	if (NULL == dev->ovbuf.base)
+		return -EINVAL;
+	if (NULL == dev->ovfmt)
+		return -EINVAL;
+	if (win->w.width < 48 || win->w.height <  32)
+		return -EINVAL;
+	if (win->clipcount > 2048)
+		return -EINVAL;
+
+	field = win->field;
+	maxw  = dev->crop_current.width;
+	maxh  = dev->crop_current.height;
+
+	if (V4L2_FIELD_ANY == field) {
+		field = (win->w.height > maxh/2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_TOP;
+	}
+	switch (field) {
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+		maxh = maxh / 2;
+		break;
+	case V4L2_FIELD_INTERLACED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	win->field = field;
+	if (win->w.width > maxw)
+		win->w.width = maxw;
+	if (win->w.height > maxh)
+		win->w.height = maxh;
+	return 0;
+}
+
+static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+	unsigned long base,control,bpl;
+	int err;
+
+	err = verify_preview(dev,&fh->win);
+	if (0 != err)
+		return err;
+
+	dev->ovfield = fh->win.field;
+	dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
+		fh->win.w.width,fh->win.w.height,
+		fh->win.w.left,fh->win.w.top,
+		dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
+
+	/* setup window + clipping */
+	set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
+		 V4L2_FIELD_HAS_BOTH(dev->ovfield));
+	setup_clipping(dev,fh->clips,fh->nclips,
+		       V4L2_FIELD_HAS_BOTH(dev->ovfield));
+	if (dev->ovfmt->yuv)
+		saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
+	else
+		saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
+	saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
+
+	/* dma: setup channel 1 (= Video Task B) */
+	base  = (unsigned long)dev->ovbuf.base;
+	base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
+	base += dev->ovfmt->depth/8         * fh->win.w.left;
+	bpl   = dev->ovbuf.fmt.bytesperline;
+	control = SAA7134_RS_CONTROL_BURST_16;
+	if (dev->ovfmt->bswap)
+		control |= SAA7134_RS_CONTROL_BSWAP;
+	if (dev->ovfmt->wswap)
+		control |= SAA7134_RS_CONTROL_WSWAP;
+	if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
+		saa_writel(SAA7134_RS_BA1(1),base);
+		saa_writel(SAA7134_RS_BA2(1),base+bpl);
+		saa_writel(SAA7134_RS_PITCH(1),bpl*2);
+		saa_writel(SAA7134_RS_CONTROL(1),control);
+	} else {
+		saa_writel(SAA7134_RS_BA1(1),base);
+		saa_writel(SAA7134_RS_BA2(1),base);
+		saa_writel(SAA7134_RS_PITCH(1),bpl);
+		saa_writel(SAA7134_RS_CONTROL(1),control);
+	}
+
+	/* start dma */
+	dev->ovenable = 1;
+	saa7134_set_dmabits(dev);
+
+	return 0;
+}
+
+static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+	dev->ovenable = 0;
+	saa7134_set_dmabits(dev);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+			   struct saa7134_buf *buf,
+			   struct saa7134_buf *next)
+{
+	unsigned long base,control,bpl;
+	unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
+
+	dprintk("buffer_activate buf=%p\n",buf);
+	buf->vb.state = VIDEOBUF_ACTIVE;
+	buf->top_seen = 0;
+
+	set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
+		 V4L2_FIELD_HAS_BOTH(buf->vb.field));
+	if (buf->fmt->yuv)
+		saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
+	else
+		saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
+	saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
+
+	/* DMA: setup channel 0 (= Video Task A0) */
+	base  = saa7134_buffer_base(buf);
+	if (buf->fmt->planar)
+		bpl = buf->vb.width;
+	else
+		bpl = (buf->vb.width * buf->fmt->depth) / 8;
+	control = SAA7134_RS_CONTROL_BURST_16 |
+		SAA7134_RS_CONTROL_ME |
+		(buf->pt->dma >> 12);
+	if (buf->fmt->bswap)
+		control |= SAA7134_RS_CONTROL_BSWAP;
+	if (buf->fmt->wswap)
+		control |= SAA7134_RS_CONTROL_WSWAP;
+	if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+		/* interlaced */
+		saa_writel(SAA7134_RS_BA1(0),base);
+		saa_writel(SAA7134_RS_BA2(0),base+bpl);
+		saa_writel(SAA7134_RS_PITCH(0),bpl*2);
+	} else {
+		/* non-interlaced */
+		saa_writel(SAA7134_RS_BA1(0),base);
+		saa_writel(SAA7134_RS_BA2(0),base);
+		saa_writel(SAA7134_RS_PITCH(0),bpl);
+	}
+	saa_writel(SAA7134_RS_CONTROL(0),control);
+
+	if (buf->fmt->planar) {
+		/* DMA: setup channel 4+5 (= planar task A) */
+		bpl_uv   = bpl >> buf->fmt->hshift;
+		lines_uv = buf->vb.height >> buf->fmt->vshift;
+		base2    = base + bpl * buf->vb.height;
+		base3    = base2 + bpl_uv * lines_uv;
+		if (buf->fmt->uvswap)
+			tmp = base2, base2 = base3, base3 = tmp;
+		dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
+			bpl_uv,lines_uv,base2,base3);
+		if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+			/* interlaced */
+			saa_writel(SAA7134_RS_BA1(4),base2);
+			saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
+			saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
+			saa_writel(SAA7134_RS_BA1(5),base3);
+			saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
+			saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
+		} else {
+			/* non-interlaced */
+			saa_writel(SAA7134_RS_BA1(4),base2);
+			saa_writel(SAA7134_RS_BA2(4),base2);
+			saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
+			saa_writel(SAA7134_RS_BA1(5),base3);
+			saa_writel(SAA7134_RS_BA2(5),base3);
+			saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
+		}
+		saa_writel(SAA7134_RS_CONTROL(4),control);
+		saa_writel(SAA7134_RS_CONTROL(5),control);
+	}
+
+	/* start DMA */
+	saa7134_set_dmabits(dev);
+	mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+			  struct videobuf_buffer *vb,
+			  enum v4l2_field field)
+{
+	struct saa7134_fh *fh = q->priv_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+	unsigned int size;
+	int err;
+
+	/* sanity checks */
+	if (NULL == fh->fmt)
+		return -EINVAL;
+	if (fh->width    < 48 ||
+	    fh->height   < 32 ||
+	    fh->width/4  > dev->crop_current.width  ||
+	    fh->height/4 > dev->crop_current.height ||
+	    fh->width    > dev->crop_bounds.width  ||
+	    fh->height   > dev->crop_bounds.height)
+		return -EINVAL;
+	size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
+		vb->i,fh->width,fh->height,size,v4l2_field_names[field],
+		fh->fmt->name);
+	if (buf->vb.width  != fh->width  ||
+	    buf->vb.height != fh->height ||
+	    buf->vb.size   != size       ||
+	    buf->vb.field  != field      ||
+	    buf->fmt       != fh->fmt) {
+		saa7134_dma_free(q,buf);
+	}
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+		buf->vb.width  = fh->width;
+		buf->vb.height = fh->height;
+		buf->vb.size   = size;
+		buf->vb.field  = field;
+		buf->fmt       = fh->fmt;
+		buf->pt        = &fh->pt_cap;
+		dev->video_q.curr = NULL;
+
+		err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
+		if (err)
+			goto oops;
+		err = saa7134_pgtable_build(dev->pci,buf->pt,
+					    dma->sglist,
+					    dma->sglen,
+					    saa7134_buffer_startpage(buf));
+		if (err)
+			goto oops;
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	buf->activate = buffer_activate;
+	return 0;
+
+ oops:
+	saa7134_dma_free(q,buf);
+	return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+	struct saa7134_fh *fh = q->priv_data;
+
+	*size = fh->fmt->depth * fh->width * fh->height >> 3;
+	if (0 == *count)
+		*count = gbuffers;
+	*count = saa7134_buffer_count(*size,*count);
+	return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_fh *fh = q->priv_data;
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+	saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+	saa7134_dma_free(q,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+{
+	const struct v4l2_queryctrl* ctrl;
+
+	ctrl = ctrl_by_id(c->id);
+	if (NULL == ctrl)
+		return -EINVAL;
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		c->value = dev->ctl_bright;
+		break;
+	case V4L2_CID_HUE:
+		c->value = dev->ctl_hue;
+		break;
+	case V4L2_CID_CONTRAST:
+		c->value = dev->ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		c->value = dev->ctl_saturation;
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		c->value = dev->ctl_mute;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		c->value = dev->ctl_volume;
+		break;
+	case V4L2_CID_PRIVATE_INVERT:
+		c->value = dev->ctl_invert;
+		break;
+	case V4L2_CID_HFLIP:
+		c->value = dev->ctl_mirror;
+		break;
+	case V4L2_CID_PRIVATE_Y_EVEN:
+		c->value = dev->ctl_y_even;
+		break;
+	case V4L2_CID_PRIVATE_Y_ODD:
+		c->value = dev->ctl_y_odd;
+		break;
+	case V4L2_CID_PRIVATE_AUTOMUTE:
+		c->value = dev->ctl_automute;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);
+
+static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
+{
+	struct saa7134_fh *fh = priv;
+
+	return saa7134_g_ctrl_internal(fh->dev, fh, c);
+}
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev,  struct saa7134_fh *fh, struct v4l2_control *c)
+{
+	const struct v4l2_queryctrl* ctrl;
+	unsigned long flags;
+	int restart_overlay = 0;
+	int err;
+
+	/* When called from the empress code fh == NULL.
+	   That needs to be fixed somehow, but for now this is
+	   good enough. */
+	if (fh) {
+		err = v4l2_prio_check(&dev->prio, fh->prio);
+		if (0 != err)
+			return err;
+	}
+	err = -EINVAL;
+
+	mutex_lock(&dev->lock);
+
+	ctrl = ctrl_by_id(c->id);
+	if (NULL == ctrl)
+		goto error;
+
+	dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_BOOLEAN:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_INTEGER:
+		if (c->value < ctrl->minimum)
+			c->value = ctrl->minimum;
+		if (c->value > ctrl->maximum)
+			c->value = ctrl->maximum;
+		break;
+	default:
+		/* nothing */;
+	};
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		dev->ctl_bright = c->value;
+		saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+		break;
+	case V4L2_CID_HUE:
+		dev->ctl_hue = c->value;
+		saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+		break;
+	case V4L2_CID_CONTRAST:
+		dev->ctl_contrast = c->value;
+		saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+			   dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+		break;
+	case V4L2_CID_SATURATION:
+		dev->ctl_saturation = c->value;
+		saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+			   dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		dev->ctl_mute = c->value;
+		saa7134_tvaudio_setmute(dev);
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		dev->ctl_volume = c->value;
+		saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+		break;
+	case V4L2_CID_PRIVATE_INVERT:
+		dev->ctl_invert = c->value;
+		saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+			   dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+		saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+			   dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+		break;
+	case V4L2_CID_HFLIP:
+		dev->ctl_mirror = c->value;
+		restart_overlay = 1;
+		break;
+	case V4L2_CID_PRIVATE_Y_EVEN:
+		dev->ctl_y_even = c->value;
+		restart_overlay = 1;
+		break;
+	case V4L2_CID_PRIVATE_Y_ODD:
+		dev->ctl_y_odd = c->value;
+		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;
+
+			saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+		}
+		break;
+	}
+	default:
+		goto error;
+	}
+	if (restart_overlay && fh && 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);
+	}
+	err = 0;
+
+error:
+	mutex_unlock(&dev->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);
+
+static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
+{
+	struct saa7134_fh *fh = f;
+
+	return saa7134_s_ctrl_internal(fh->dev, fh, c);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
+{
+	struct videobuf_queue* q = NULL;
+
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		q = &fh->cap;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		q = &fh->vbi;
+		break;
+	default:
+		BUG();
+	}
+	return q;
+}
+
+static int saa7134_resource(struct saa7134_fh *fh)
+{
+	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return RESOURCE_VIDEO;
+
+	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return RESOURCE_VBI;
+
+	BUG();
+	return 0;
+}
+
+static int video_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct saa7134_dev *dev = video_drvdata(file);
+	struct saa7134_fh *fh;
+	enum v4l2_buf_type type = 0;
+	int radio = 0;
+
+	switch (vdev->vfl_type) {
+	case VFL_TYPE_GRABBER:
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		break;
+	case VFL_TYPE_VBI:
+		type = V4L2_BUF_TYPE_VBI_CAPTURE;
+		break;
+	case VFL_TYPE_RADIO:
+		radio = 1;
+		break;
+	}
+
+	dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
+		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->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+	fh->width    = 720;
+	fh->height   = 576;
+	v4l2_prio_open(&dev->prio, &fh->prio);
+
+	videobuf_queue_sg_init(&fh->cap, &video_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct saa7134_buf),
+			    fh, NULL);
+	videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
+			    &dev->pci->dev, &dev->slock,
+			    V4L2_BUF_TYPE_VBI_CAPTURE,
+			    V4L2_FIELD_SEQ_TB,
+			    sizeof(struct saa7134_buf),
+			    fh, NULL);
+	saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
+	saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
+
+	if (fh->radio) {
+		/* switch to radio mode */
+		saa7134_tvaudio_setinput(dev,&card(dev).radio);
+		saa_call_all(dev, tuner, s_radio);
+	} else {
+		/* switch to video/vbi mode */
+		video_mux(dev,dev->ctl_input);
+	}
+	return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+	struct saa7134_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(saa7134_queue(fh),
+					 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(saa7134_queue(fh),
+					    data, count, ppos, 1,
+					    file->f_flags & O_NONBLOCK);
+		break;
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct saa7134_fh *fh = file->private_data;
+	struct videobuf_buffer *buf = NULL;
+	unsigned int rc = 0;
+
+	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+		return videobuf_poll_stream(file, &fh->vbi, wait);
+
+	if (res_check(fh,RESOURCE_VIDEO)) {
+		mutex_lock(&fh->cap.vb_lock);
+		if (!list_empty(&fh->cap.stream))
+			buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
+	} else {
+		mutex_lock(&fh->cap.vb_lock);
+		if (UNSET == fh->cap.read_off) {
+			/* need to capture a new frame */
+			if (res_locked(fh->dev,RESOURCE_VIDEO))
+				goto err;
+			if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field))
+				goto err;
+			fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+			fh->cap.read_off = 0;
+		}
+		buf = fh->cap.read_buf;
+	}
+
+	if (!buf)
+		goto err;
+
+	poll_wait(file, &buf->done, wait);
+	if (buf->state == VIDEOBUF_DONE ||
+	    buf->state == VIDEOBUF_ERROR)
+		rc = POLLIN|POLLRDNORM;
+	mutex_unlock(&fh->cap.vb_lock);
+	return rc;
+
+err:
+	mutex_unlock(&fh->cap.vb_lock);
+	return POLLERR;
+}
+
+static int video_release(struct file *file)
+{
+	struct saa7134_fh  *fh  = file->private_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa6588_command cmd;
+	unsigned long flags;
+
+	saa7134_tvaudio_close(dev);
+
+	/* turn off overlay */
+	if (res_check(fh, RESOURCE_OVERLAY)) {
+		spin_lock_irqsave(&dev->slock,flags);
+		stop_preview(dev,fh);
+		spin_unlock_irqrestore(&dev->slock,flags);
+		res_free(dev,fh,RESOURCE_OVERLAY);
+	}
+
+	/* stop video capture */
+	if (res_check(fh, RESOURCE_VIDEO)) {
+		videobuf_streamoff(&fh->cap);
+		res_free(dev,fh,RESOURCE_VIDEO);
+	}
+	if (fh->cap.read_buf) {
+		buffer_release(&fh->cap,fh->cap.read_buf);
+		kfree(fh->cap.read_buf);
+	}
+
+	/* stop vbi capture */
+	if (res_check(fh, RESOURCE_VBI)) {
+		videobuf_stop(&fh->vbi);
+		res_free(dev,fh,RESOURCE_VBI);
+	}
+
+	/* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
+	saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
+
+	saa_call_all(dev, core, s_power, 0);
+	if (fh->radio)
+		saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+	/* free stuff */
+	videobuf_mmap_free(&fh->cap);
+	videobuf_mmap_free(&fh->vbi);
+	saa7134_pgtable_free(dev->pci,&fh->pt_cap);
+	saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
+
+	v4l2_prio_close(&dev->prio, fh->prio);
+	file->private_data = NULL;
+	kfree(fh);
+	return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+	struct saa7134_fh *fh = file->private_data;
+
+	return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+			 size_t count, loff_t *ppos)
+{
+	struct saa7134_fh *fh = file->private_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa6588_command cmd;
+
+	cmd.block_count = count/3;
+	cmd.buffer = data;
+	cmd.instance = file;
+	cmd.result = -ENODEV;
+
+	saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+	return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+	struct saa7134_fh *fh = file->private_data;
+	struct saa7134_dev *dev = fh->dev;
+	struct saa6588_command cmd;
+
+	cmd.instance = file;
+	cmd.event_list = wait;
+	cmd.result = -ENODEV;
+	saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+	return cmd.result;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int saa7134_try_get_set_fmt_vbi_cap(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;
+	f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
+	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset = 64 * 4;
+	f->fmt.vbi.start[0] = norm->vbi_v_start_0;
+	f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
+	f->fmt.vbi.start[1] = norm->vbi_v_start_1;
+	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_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	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_g_fmt_vid_overlay(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7134_fh *fh = priv;
+
+	if (saa7134_no_overlay > 0) {
+		printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+		return -EINVAL;
+	}
+	f->fmt.win = fh->win;
+
+	return 0;
+}
+
+static int saa7134_try_fmt_vid_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;
+
+	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+	if (NULL == fmt)
+		return -EINVAL;
+
+	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);
+
+	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.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_try_fmt_vid_overlay(struct file *file, void *priv,
+						struct v4l2_format *f)
+{
+	struct saa7134_fh *fh = priv;
+	struct saa7134_dev *dev = fh->dev;
+
+	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);
+}
+
+static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct saa7134_fh *fh = priv;
+	int err;
+
+	err = saa7134_try_fmt_vid_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->cap.field = f->fmt.pix.field;
+	return 0;
+}
+
+static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
+					struct v4l2_format *f)
+{
+	struct saa7134_fh *fh = priv;
+	struct saa7134_dev *dev = fh->dev;
+	int err;
+	unsigned long flags;
+
+	if (saa7134_no_overlay > 0) {
+		printk(KERN_ERR "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;
+	}
+
+	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;
+}
+
+int saa7134_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) &&
+	    (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;
+	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;
+	int err;
+
+	err = v4l2_prio_check(&dev->prio, fh->prio);
+	if (0 != err)
+		return err;
+
+	if (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;
+}
+
+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->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_VBI_CAPTURE |
+		V4L2_CAP_READWRITE |
+		V4L2_CAP_STREAMING |
+		V4L2_CAP_TUNER;
+	if (dev->has_rds)
+		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+	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;
+}
+
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
+{
+	unsigned long flags;
+	unsigned int i;
+	v4l2_std_id fixup;
+	int err;
+
+	/* When called from the empress code fh == NULL.
+	   That needs to be fixed somehow, but for now this is
+	   good enough. */
+	if (fh) {
+		err = v4l2_prio_check(&dev->prio, fh->prio);
+		if (0 != err)
+			return err;
+	} else if (res_locked(dev, RESOURCE_OVERLAY)) {
+		/* Don't change the std from the mpeg device
+		   if overlay is active. */
+		return -EBUSY;
+	}
+
+	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;
+
+	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;
+	}
+
+	*id = tvnorms[i].id;
+
+	mutex_lock(&dev->lock);
+	if (fh && 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;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
+
+static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7134_fh *fh = priv;
+
+	return saa7134_s_std_internal(fh->dev, fh, id);
+}
+
+static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7134_fh *fh = priv;
+	struct saa7134_dev *dev = fh->dev;
+
+	*id = dev->tvnorm->id;
+	return 0;
+}
+
+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 (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;
+}
+
+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 (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+	crop->c = dev->crop_current;
+	return 0;
+}
+
+static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+{
+	struct saa7134_fh *fh = f;
+	struct saa7134_dev *dev = fh->dev;
+	struct v4l2_rect *b = &dev->crop_bounds;
+	struct v4l2_rect *c = &dev->crop_current;
+
+	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 (res_locked(fh->dev, RESOURCE_OVERLAY))
+		return -EBUSY;
+	if (res_locked(fh->dev, RESOURCE_VIDEO))
+		return -EBUSY;
+
+	*c = crop->c;
+	if (c->top < b->top)
+		c->top = b->top;
+	if (c->top > b->top + b->height)
+		c->top = b->top + b->height;
+	if (c->height > b->top - c->top + b->height)
+		c->height = b->top - c->top + b->height;
+
+	if (c->left < b->left)
+		c->left = b->left;
+	if (c->left > b->left + b->width)
+		c->left = b->left + b->width;
+	if (c->width > b->left - c->left + b->width)
+		c->width = b->left - c->left + b->width;
+	return 0;
+}
+
+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;
+
+	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 (n == SAA7134_INPUT_MAX)
+		return -EINVAL;
+	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;
+}
+
+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;
+
+	err = v4l2_prio_check(&dev->prio, fh->prio);
+	if (0 != err)
+		return err;
+
+	mode = dev->thread.mode;
+	if (UNSET == mode) {
+		rx   = saa7134_tvaudio_getstereo(dev);
+		mode = saa7134_tvaudio_rx2mode(rx);
+	}
+	if (mode != t->audmode)
+		dev->thread.mode = t->audmode;
+
+	return 0;
+}
+
+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;
+
+	f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+	f->frequency = dev->ctl_freq;
+
+	return 0;
+}
+
+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;
+
+	err = v4l2_prio_check(&dev->prio, fh->prio);
+	if (0 != err)
+		return err;
+
+	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;
+
+	saa_call_all(dev, tuner, 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, const 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_vid_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_vid_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;
+	}
+
+	if ((f->index >= FORMATS) || formats[f->index].planar)
+		return -EINVAL;
+
+	strlcpy(f->description, formats[f->index].name,
+		sizeof(f->description));
+
+	f->pixelformat = formats[f->index].fourcc;
+
+	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,
+					const 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;
+		}
+
+		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);
+	}
+	return 0;
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+
+	if (!res_get(dev, fh, res))
+		return -EBUSY;
+
+	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 saa7134_g_parm(struct file *file, void *fh,
+				struct v4l2_streamparm *parm)
+{
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *priv,
+			      struct v4l2_dbg_register *reg)
+{
+	struct saa7134_fh *fh = priv;
+	struct saa7134_dev *dev = fh->dev;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+	reg->val = saa_readb(reg->reg);
+	reg->size = 1;
+	return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *priv,
+				struct v4l2_dbg_register *reg)
+{
+	struct saa7134_fh *fh = priv;
+	struct saa7134_dev *dev = fh->dev;
+
+	if (!v4l2_chip_match_host(&reg->match))
+		return -EINVAL;
+	saa_writeb(reg->reg&0xffffff, reg->val);
+	return 0;
+}
+#endif
+
+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;
+
+	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->capabilities = V4L2_CAP_TUNER;
+	return 0;
+}
+
+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;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	memset(t, 0, sizeof(*t));
+	strcpy(t->name, "Radio");
+	t->type = V4L2_TUNER_RADIO;
+
+	saa_call_all(dev, tuner, 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;
+}
+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;
+
+	saa_call_all(dev, tuner, s_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_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	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_audio(struct file *file, void *priv,
+					const 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_ctrl;
+	return 0;
+}
+
+static const struct v4l2_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,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+	.vidioc_querycap		= saa7134_querycap,
+	.vidioc_enum_fmt_vid_cap	= saa7134_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		= saa7134_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		= saa7134_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		= saa7134_s_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_overlay	= saa7134_enum_fmt_vid_overlay,
+	.vidioc_g_fmt_vid_overlay	= saa7134_g_fmt_vid_overlay,
+	.vidioc_try_fmt_vid_overlay	= saa7134_try_fmt_vid_overlay,
+	.vidioc_s_fmt_vid_overlay	= saa7134_s_fmt_vid_overlay,
+	.vidioc_g_fmt_vbi_cap		= saa7134_try_get_set_fmt_vbi_cap,
+	.vidioc_try_fmt_vbi_cap		= saa7134_try_get_set_fmt_vbi_cap,
+	.vidioc_s_fmt_vbi_cap		= saa7134_try_get_set_fmt_vbi_cap,
+	.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_g_std			= saa7134_g_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,
+	.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,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register              = vidioc_g_register,
+	.vidioc_s_register              = vidioc_s_register,
+#endif
+};
+
+static const struct v4l2_file_operations radio_fops = {
+	.owner	  = THIS_MODULE,
+	.open	  = video_open,
+	.read     = radio_read,
+	.release  = video_release,
+	.ioctl	  = video_ioctl2,
+	.poll     = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+	.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,
+};
+
+/* ----------------------------------------------------------- */
+/* exported stuff                                              */
+
+struct video_device saa7134_video_template = {
+	.name				= "saa7134-video",
+	.fops				= &video_fops,
+	.ioctl_ops 			= &video_ioctl_ops,
+	.tvnorms			= SAA7134_NORMS,
+	.current_norm			= V4L2_STD_PAL,
+};
+
+struct video_device saa7134_radio_template = {
+	.name			= "saa7134-radio",
+	.fops			= &radio_fops,
+	.ioctl_ops 		= &radio_ioctl_ops,
+};
+
+int saa7134_video_init1(struct saa7134_dev *dev)
+{
+	/* sanitycheck insmod options */
+	if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+		gbuffers = 2;
+	if (gbufsize < 0 || gbufsize > gbufsize_max)
+		gbufsize = gbufsize_max;
+	gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+
+	/* put some sensible defaults into the data structures ... */
+	dev->ctl_bright     = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
+	dev->ctl_contrast   = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
+	dev->ctl_hue        = ctrl_by_id(V4L2_CID_HUE)->default_value;
+	dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
+	dev->ctl_volume     = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
+	dev->ctl_mute       = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
+	dev->ctl_invert     = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
+	dev->ctl_automute   = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
+
+	if (dev->tda9887_conf && dev->ctl_automute)
+		dev->tda9887_conf |= TDA9887_AUTOMUTE;
+	dev->automute       = 0;
+
+	INIT_LIST_HEAD(&dev->video_q.queue);
+	init_timer(&dev->video_q.timeout);
+	dev->video_q.timeout.function = saa7134_buffer_timeout;
+	dev->video_q.timeout.data     = (unsigned long)(&dev->video_q);
+	dev->video_q.dev              = dev;
+
+	if (saa7134_boards[dev->board].video_out)
+		saa7134_videoport_init(dev);
+
+	return 0;
+}
+
+int saa7134_videoport_init(struct saa7134_dev *dev)
+{
+	/* enable video output */
+	int vo = saa7134_boards[dev->board].video_out;
+	int video_reg;
+	unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+
+	/* Configure videoport */
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
+	video_reg = video_out[vo][1];
+	if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
+		video_reg &= ~VP_T_CODE_P_INVERTED;
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
+	video_reg = video_out[vo][5];
+	if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
+		video_reg &= ~VP_CLK_CTRL2_DELAYED;
+	if (vid_port_opts & SET_CLOCK_INVERTED)
+		video_reg |= VP_CLK_CTRL1_INVERTED;
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
+	video_reg = video_out[vo][6];
+	if (vid_port_opts & SET_VSYNC_OFF) {
+		video_reg &= ~VP_VS_TYPE_MASK;
+		video_reg |= VP_VS_TYPE_OFF;
+	}
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+
+	/* Start videoport */
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+
+	return 0;
+}
+
+int saa7134_video_init2(struct saa7134_dev *dev)
+{
+	/* init video hw */
+	set_tvnorm(dev,&tvnorms[0]);
+	video_mux(dev,0);
+	saa7134_tvaudio_setmute(dev);
+	saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+	return 0;
+}
+
+void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
+{
+	static const char *st[] = {
+		"(no signal)", "NTSC", "PAL", "SECAM" };
+	u32 st1,st2;
+
+	st1 = saa_readb(SAA7134_STATUS_VIDEO1);
+	st2 = saa_readb(SAA7134_STATUS_VIDEO2);
+	dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n",
+		(st1 & 0x40) ? "not locked" : "locked",
+		(st2 & 0x40) ? "no"         : "yes",
+		st[st1 & 0x03]);
+	dev->nosignal = (st1 & 0x40) || (st2 & 0x40)  || !(st2 & 0x1);
+
+	if (dev->nosignal) {
+		/* no video signal -> mute audio */
+		if (dev->ctl_automute)
+			dev->automute = 1;
+		saa7134_tvaudio_setmute(dev);
+	} else {
+		/* wake up tvaudio audio carrier scan thread */
+		saa7134_tvaudio_do_scan(dev);
+	}
+
+	if ((st2 & 0x80) && !noninterlaced && !dev->nosignal)
+		saa_clearb(SAA7134_SYNC_CTRL, 0x20);
+	else
+		saa_setb(SAA7134_SYNC_CTRL, 0x20);
+
+	if (dev->mops && dev->mops->signal_change)
+		dev->mops->signal_change(dev);
+}
+
+
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
+{
+	enum v4l2_field field;
+
+	spin_lock(&dev->slock);
+	if (dev->video_q.curr) {
+		dev->video_fieldcount++;
+		field = dev->video_q.curr->vb.field;
+		if (V4L2_FIELD_HAS_BOTH(field)) {
+			/* make sure we have seen both fields */
+			if ((status & 0x10) == 0x00) {
+				dev->video_q.curr->top_seen = 1;
+				goto done;
+			}
+			if (!dev->video_q.curr->top_seen)
+				goto done;
+		} else if (field == V4L2_FIELD_TOP) {
+			if ((status & 0x10) != 0x10)
+				goto done;
+		} else if (field == V4L2_FIELD_BOTTOM) {
+			if ((status & 0x10) != 0x00)
+				goto done;
+		}
+		dev->video_q.curr->vb.field_count = dev->video_fieldcount;
+		saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
+	}
+	saa7134_buffer_next(dev,&dev->video_q);
+
+ done:
+	spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
new file mode 100644
index 000000000000..c24b6512bd8f
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -0,0 +1,855 @@
+/*
+ *
+ * v4l2 device driver for philips saa7134 based TV cards
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.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.
+ */
+
+#define SAA7134_VERSION "0, 2, 17"
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+#include <linux/input.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/videobuf-dma-sg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+#include <media/videobuf-dvb.h>
+#endif
+
+#define UNSET (-1U)
+
+/* ----------------------------------------------------------- */
+/* enums                                                       */
+
+enum saa7134_tvaudio_mode {
+	TVAUDIO_FM_MONO       = 1,
+	TVAUDIO_FM_BG_STEREO  = 2,
+	TVAUDIO_FM_SAT_STEREO = 3,
+	TVAUDIO_FM_K_STEREO   = 4,
+	TVAUDIO_NICAM_AM      = 5,
+	TVAUDIO_NICAM_FM      = 6,
+};
+
+enum saa7134_audio_in {
+	TV    = 1,
+	LINE1 = 2,
+	LINE2 = 3,
+	LINE2_LEFT,
+};
+
+enum saa7134_video_out {
+	CCIR656 = 1,
+};
+
+/* ----------------------------------------------------------- */
+/* static data                                                 */
+
+struct saa7134_tvnorm {
+	char          *name;
+	v4l2_std_id   id;
+
+	/* video decoder */
+	unsigned int  sync_control;
+	unsigned int  luma_control;
+	unsigned int  chroma_ctrl1;
+	unsigned int  chroma_gain;
+	unsigned int  chroma_ctrl2;
+	unsigned int  vgate_misc;
+
+	/* video scaler */
+	unsigned int  h_start;
+	unsigned int  h_stop;
+	unsigned int  video_v_start;
+	unsigned int  video_v_stop;
+	unsigned int  vbi_v_start_0;
+	unsigned int  vbi_v_stop_0;
+	unsigned int  src_timing;
+	unsigned int  vbi_v_start_1;
+};
+
+struct saa7134_tvaudio {
+	char         *name;
+	v4l2_std_id  std;
+	enum         saa7134_tvaudio_mode mode;
+	int          carr1;
+	int          carr2;
+};
+
+struct saa7134_format {
+	char           *name;
+	unsigned int   fourcc;
+	unsigned int   depth;
+	unsigned int   pm;
+	unsigned int   vshift;   /* vertical downsampling (for planar yuv) */
+	unsigned int   hshift;   /* horizontal downsampling (for planar yuv) */
+	unsigned int   bswap:1;
+	unsigned int   wswap:1;
+	unsigned int   yuv:1;
+	unsigned int   planar:1;
+	unsigned int   uvswap:1;
+};
+
+struct saa7134_card_ir {
+	struct rc_dev		*dev;
+
+	char                    name[32];
+	char                    phys[32];
+	unsigned                users;
+
+	u32			polling;
+	u32			last_gpio;
+	u32			mask_keycode, mask_keydown, mask_keyup;
+
+	bool                    running;
+
+	struct timer_list       timer;
+
+	/* IR core raw decoding */
+	u32                     raw_decode;
+};
+
+/* ----------------------------------------------------------- */
+/* card configuration                                          */
+
+#define SAA7134_BOARD_NOAUTO        UNSET
+#define SAA7134_BOARD_UNKNOWN           0
+#define SAA7134_BOARD_PROTEUS_PRO       1
+#define SAA7134_BOARD_FLYVIDEO3000      2
+#define SAA7134_BOARD_FLYVIDEO2000      3
+#define SAA7134_BOARD_EMPRESS           4
+#define SAA7134_BOARD_MONSTERTV         5
+#define SAA7134_BOARD_MD9717            6
+#define SAA7134_BOARD_TVSTATION_RDS     7
+#define SAA7134_BOARD_CINERGY400	8
+#define SAA7134_BOARD_MD5044		9
+#define SAA7134_BOARD_KWORLD           10
+#define SAA7134_BOARD_CINERGY600       11
+#define SAA7134_BOARD_MD7134           12
+#define SAA7134_BOARD_TYPHOON_90031    13
+#define SAA7134_BOARD_ELSA             14
+#define SAA7134_BOARD_ELSA_500TV       15
+#define SAA7134_BOARD_ASUSTeK_TVFM7134 16
+#define SAA7134_BOARD_VA1000POWER      17
+#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18
+#define SAA7134_BOARD_VIDEOMATE_TV     19
+#define SAA7134_BOARD_CRONOS_PLUS      20
+#define SAA7134_BOARD_10MOONSTVMASTER  21
+#define SAA7134_BOARD_MD2819           22
+#define SAA7134_BOARD_BMK_MPEX_TUNER   23
+#define SAA7134_BOARD_TVSTATION_DVR    24
+#define SAA7134_BOARD_ASUSTEK_TVFM7133	25
+#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26
+#define SAA7134_BOARD_MANLI_MTV002     27
+#define SAA7134_BOARD_MANLI_MTV001     28
+#define SAA7134_BOARD_TG3000TV         29
+#define SAA7134_BOARD_ECS_TVP3XP       30
+#define SAA7134_BOARD_ECS_TVP3XP_4CB5  31
+#define SAA7134_BOARD_AVACSSMARTTV     32
+#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
+#define SAA7134_BOARD_NOVAC_PRIMETV7133 34
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35
+#define SAA7134_BOARD_UPMOST_PURPLE_TV 36
+#define SAA7134_BOARD_ITEMS_MTV005     37
+#define SAA7134_BOARD_CINERGY200       38
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39
+#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41
+#define SAA7134_BOARD_SABRENT_SBTTVFM  42
+#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43
+#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_307    45
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46
+#define SAA7134_BOARD_CINERGY400_CARDBUS 47
+#define SAA7134_BOARD_CINERGY600_MK3   48
+#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49
+#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50
+#define SAA7134_BOARD_PROVIDEO_PV952   51
+#define SAA7134_BOARD_AVERMEDIA_305    52
+#define SAA7134_BOARD_ASUSTeK_TVFM7135 53
+#define SAA7134_BOARD_FLYTVPLATINUM_FM 54
+#define SAA7134_BOARD_FLYDVBTDUO 55
+#define SAA7134_BOARD_AVERMEDIA_307    56
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57
+#define SAA7134_BOARD_ADS_INSTANT_TV 58
+#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59
+#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60
+#define SAA7134_BOARD_PHILIPS_TOUGH 61
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62
+#define SAA7134_BOARD_KWORLD_XPERT 63
+#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64
+#define SAA7134_BOARD_KWORLD_TERMINATOR 65
+#define SAA7134_BOARD_YUAN_TUN900 66
+#define SAA7134_BOARD_BEHOLD_409FM 67
+#define SAA7134_BOARD_GOTVIEW_7135 68
+#define SAA7134_BOARD_PHILIPS_EUROPA  69
+#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71
+#define SAA7134_BOARD_RTD_VFG7350 72
+#define SAA7134_BOARD_RTD_VFG7330 73
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74
+#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75
+#define SAA7134_BOARD_MONSTERTV_MOBILE 76
+#define SAA7134_BOARD_PINNACLE_PCTV_110i 77
+#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78
+#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS     79
+#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80
+#define SAA7134_BOARD_PHILIPS_TIGER  81
+#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS  82
+#define SAA7134_BOARD_CINERGY250PCI 83
+#define SAA7134_BOARD_FLYDVB_TRIO 84
+#define SAA7134_BOARD_AVERMEDIA_777 85
+#define SAA7134_BOARD_FLYDVBT_LR301 86
+#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87
+#define SAA7134_BOARD_TEVION_DVBT_220RF 88
+#define SAA7134_BOARD_ELSA_700TV       89
+#define SAA7134_BOARD_KWORLD_ATSC110   90
+#define SAA7134_BOARD_AVERMEDIA_A169_B 91
+#define SAA7134_BOARD_AVERMEDIA_A169_B1 92
+#define SAA7134_BOARD_MD7134_BRIDGE_2     93
+#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
+#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95
+#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96
+#define SAA7134_BOARD_FLYDVBS_LR300 97
+#define SAA7134_BOARD_PROTEUS_2309 98
+#define SAA7134_BOARD_AVERMEDIA_A16AR   99
+#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
+#define SAA7134_BOARD_PINNACLE_PCTV_310i  101
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200A  103
+#define SAA7134_BOARD_HAUPPAUGE_HVR1110    104
+#define SAA7134_BOARD_CINERGY_HT_PCMCIA    105
+#define SAA7134_BOARD_ENCORE_ENLTV         106
+#define SAA7134_BOARD_ENCORE_ENLTV_FM      107
+#define SAA7134_BOARD_CINERGY_HT_PCI       108
+#define SAA7134_BOARD_PHILIPS_TIGER_S      109
+#define SAA7134_BOARD_AVERMEDIA_M102	   110
+#define SAA7134_BOARD_ASUS_P7131_4871	   111
+#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
+#define SAA7134_BOARD_ECS_TVP3XP_4CB6  113
+#define SAA7134_BOARD_KWORLD_DVBT_210 114
+#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_607FM_MK3	129
+#define SAA7134_BOARD_BEHOLD_M6		130
+#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131
+#define SAA7134_BOARD_GENIUS_TVGO_A11MCE   132
+#define SAA7134_BOARD_PHILIPS_SNAKE        133
+#define SAA7134_BOARD_CREATIX_CTX953       134
+#define SAA7134_BOARD_MSI_TVANYWHERE_AD11  135
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136
+#define SAA7134_BOARD_AVERMEDIA_A16D       137
+#define SAA7134_BOARD_AVERMEDIA_M115       138
+#define SAA7134_BOARD_VIDEOMATE_T750       139
+#define SAA7134_BOARD_AVERMEDIA_A700_PRO    140
+#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
+#define SAA7134_BOARD_BEHOLD_H6      142
+#define SAA7134_BOARD_BEHOLD_M63      143
+#define SAA7134_BOARD_BEHOLD_M6_EXTRA    144
+#define SAA7134_BOARD_AVERMEDIA_M103    145
+#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
+#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1   147
+#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
+#define SAA7134_BOARD_AVERMEDIA_M135A    149
+#define SAA7134_BOARD_REAL_ANGEL_220     150
+#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI  151
+#define SAA7134_BOARD_ASUSTeK_TIGER         152
+#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
+#define SAA7134_BOARD_HAUPPAUGE_HVR1150     155
+#define SAA7134_BOARD_HAUPPAUGE_HVR1120   156
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
+#define SAA7134_BOARD_BEHOLD_505RDS_MK5     159
+#define SAA7134_BOARD_BEHOLD_507RDS_MK3     160
+#define SAA7134_BOARD_BEHOLD_507RDS_MK5     161
+#define SAA7134_BOARD_BEHOLD_607FM_MK5      162
+#define SAA7134_BOARD_BEHOLD_609FM_MK3      163
+#define SAA7134_BOARD_BEHOLD_609FM_MK5      164
+#define SAA7134_BOARD_BEHOLD_607RDS_MK3     165
+#define SAA7134_BOARD_BEHOLD_607RDS_MK5     166
+#define SAA7134_BOARD_BEHOLD_609RDS_MK3     167
+#define SAA7134_BOARD_BEHOLD_609RDS_MK5     168
+#define SAA7134_BOARD_VIDEOMATE_S350        169
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_505  170
+#define SAA7134_BOARD_BEHOLD_X7             171
+#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
+#define SAA7134_BOARD_ZOLID_HYBRID_PCI		173
+#define SAA7134_BOARD_ASUS_EUROPA_HYBRID	174
+#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
+#define SAA7134_BOARD_BEHOLD_505RDS_MK3     176
+#define SAA7134_BOARD_HAWELL_HW_404M7		177
+#define SAA7134_BOARD_BEHOLD_H7             178
+#define SAA7134_BOARD_BEHOLD_A7             179
+#define SAA7134_BOARD_AVERMEDIA_M733A       180
+#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181
+#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182
+#define SAA7134_BOARD_VIDEOMATE_M1F         183
+#define SAA7134_BOARD_ENCORE_ENLTV_FM3      184
+#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185
+#define SAA7134_BOARD_BEHOLD_501            186
+#define SAA7134_BOARD_BEHOLD_503FM          187
+#define SAA7134_BOARD_SENSORAY811_911       188
+#define SAA7134_BOARD_KWORLD_PC150U         189
+#define SAA7134_BOARD_ASUSTeK_PS3_100      190
+
+#define SAA7134_MAXBOARDS 32
+#define SAA7134_INPUT_MAX 8
+
+/* ----------------------------------------------------------- */
+/* Since we support 2 remote types, lets tell them apart       */
+
+#define SAA7134_REMOTE_GPIO  1
+#define SAA7134_REMOTE_I2C   2
+
+/* ----------------------------------------------------------- */
+/* Video Output Port Register Initialization Options           */
+
+#define SET_T_CODE_POLARITY_NON_INVERTED	(1 << 0)
+#define SET_CLOCK_NOT_DELAYED			(1 << 1)
+#define SET_CLOCK_INVERTED			(1 << 2)
+#define SET_VSYNC_OFF				(1 << 3)
+
+struct saa7134_input {
+	char                    *name;
+	unsigned int            vmux;
+	enum saa7134_audio_in   amux;
+	unsigned int            gpio;
+	unsigned int            tv:1;
+};
+
+enum saa7134_mpeg_type {
+	SAA7134_MPEG_UNUSED,
+	SAA7134_MPEG_EMPRESS,
+	SAA7134_MPEG_DVB,
+};
+
+enum saa7134_mpeg_ts_type {
+	SAA7134_MPEG_TS_PARALLEL = 0,
+	SAA7134_MPEG_TS_SERIAL,
+};
+
+struct saa7134_board {
+	char                    *name;
+	unsigned int            audio_clock;
+
+	/* input switching */
+	unsigned int            gpiomask;
+	struct saa7134_input    inputs[SAA7134_INPUT_MAX];
+	struct saa7134_input    radio;
+	struct saa7134_input    mute;
+
+	/* i2c chip info */
+	unsigned int            tuner_type;
+	unsigned int		radio_type;
+	unsigned char		tuner_addr;
+	unsigned char		radio_addr;
+	unsigned char		empress_addr;
+	unsigned char		rds_addr;
+
+	unsigned int            tda9887_conf;
+	unsigned int            tuner_config;
+
+	/* peripheral I/O */
+	enum saa7134_video_out  video_out;
+	enum saa7134_mpeg_type  mpeg;
+	enum saa7134_mpeg_ts_type ts_type;
+	unsigned int            vid_port_opts;
+	unsigned int            ts_force_val:1;
+};
+
+#define card_has_radio(dev)   (NULL != saa7134_boards[dev->board].radio.name)
+#define card_is_empress(dev)  (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
+#define card_is_dvb(dev)      (SAA7134_MPEG_DVB     == saa7134_boards[dev->board].mpeg)
+#define card_has_mpeg(dev)    (SAA7134_MPEG_UNUSED  != saa7134_boards[dev->board].mpeg)
+#define card(dev)             (saa7134_boards[dev->board])
+#define card_in(dev,n)        (saa7134_boards[dev->board].inputs[n])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status                                 */
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_VIDEO         2
+#define RESOURCE_VBI           4
+
+#define INTERLACE_AUTO         0
+#define INTERLACE_ON           1
+#define INTERLACE_OFF          2
+
+#define BUFFER_TIMEOUT     msecs_to_jiffies(500)  /* 0.5 seconds */
+#define TS_BUFFER_TIMEOUT  msecs_to_jiffies(1000)  /* 1 second */
+
+struct saa7134_dev;
+struct saa7134_dma;
+
+/* saa7134 page table */
+struct saa7134_pgtable {
+	unsigned int               size;
+	__le32                     *cpu;
+	dma_addr_t                 dma;
+};
+
+/* tvaudio thread status */
+struct saa7134_thread {
+	struct task_struct         *thread;
+	unsigned int               scan1;
+	unsigned int               scan2;
+	unsigned int               mode;
+	unsigned int		   stopped;
+};
+
+/* buffer for one video/vbi/ts frame */
+struct saa7134_buf {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer vb;
+
+	/* saa7134 specific */
+	struct saa7134_format   *fmt;
+	unsigned int            top_seen;
+	int (*activate)(struct saa7134_dev *dev,
+			struct saa7134_buf *buf,
+			struct saa7134_buf *next);
+
+	/* page tables */
+	struct saa7134_pgtable  *pt;
+};
+
+struct saa7134_dmaqueue {
+	struct saa7134_dev         *dev;
+	struct saa7134_buf         *curr;
+	struct list_head           queue;
+	struct timer_list          timeout;
+	unsigned int               need_two;
+};
+
+/* video filehandle status */
+struct saa7134_fh {
+	struct saa7134_dev         *dev;
+	unsigned int               radio;
+	enum v4l2_buf_type         type;
+	unsigned int               resources;
+	enum v4l2_priority	   prio;
+
+	/* video overlay */
+	struct v4l2_window         win;
+	struct v4l2_clip           clips[8];
+	unsigned int               nclips;
+
+	/* video capture */
+	struct saa7134_format      *fmt;
+	unsigned int               width,height;
+	struct videobuf_queue      cap;
+	struct saa7134_pgtable     pt_cap;
+
+	/* vbi capture */
+	struct videobuf_queue      vbi;
+	struct saa7134_pgtable     pt_vbi;
+};
+
+/* dmasound dsp status */
+struct saa7134_dmasound {
+	struct mutex               lock;
+	int                        minor_mixer;
+	int                        minor_dsp;
+	unsigned int               users_dsp;
+
+	/* mixer */
+	enum saa7134_audio_in      input;
+	unsigned int               count;
+	unsigned int               line1;
+	unsigned int               line2;
+
+	/* dsp */
+	unsigned int               afmt;
+	unsigned int               rate;
+	unsigned int               channels;
+	unsigned int               recording_on;
+	unsigned int               dma_running;
+	unsigned int               blocks;
+	unsigned int               blksize;
+	unsigned int               bufsize;
+	struct saa7134_pgtable     pt;
+	struct videobuf_dmabuf     dma;
+	unsigned int               dma_blk;
+	unsigned int               read_offset;
+	unsigned int               read_count;
+	void *			   priv_data;
+	struct snd_pcm_substream   *substream;
+};
+
+/* ts/mpeg status */
+struct saa7134_ts {
+	/* TS capture */
+	struct saa7134_pgtable     pt_ts;
+	int                        nr_packets;
+	int                        nr_bufs;
+};
+
+/* ts/mpeg ops */
+struct saa7134_mpeg_ops {
+	enum saa7134_mpeg_type     type;
+	struct list_head           next;
+	int                        (*init)(struct saa7134_dev *dev);
+	int                        (*fini)(struct saa7134_dev *dev);
+	void                       (*signal_change)(struct saa7134_dev *dev);
+};
+
+/* global device status */
+struct saa7134_dev {
+	struct list_head           devlist;
+	struct mutex               lock;
+	spinlock_t                 slock;
+	struct v4l2_prio_state     prio;
+	struct v4l2_device         v4l2_dev;
+	/* workstruct for loading modules */
+	struct work_struct request_module_wk;
+
+	/* insmod option/autodetected */
+	int                        autodetected;
+
+	/* various device info */
+	unsigned int               resources;
+	struct video_device        *video_dev;
+	struct video_device        *radio_dev;
+	struct video_device        *vbi_dev;
+	struct saa7134_dmasound    dmasound;
+
+	/* infrared remote */
+	int                        has_remote;
+	struct saa7134_card_ir     *remote;
+
+	/* pci i/o */
+	char                       name[32];
+	int                        nr;
+	struct pci_dev             *pci;
+	unsigned char              pci_rev,pci_lat;
+	__u32                      __iomem *lmmio;
+	__u8                       __iomem *bmmio;
+
+	/* config info */
+	unsigned int               board;
+	unsigned int               tuner_type;
+	unsigned int 		   radio_type;
+	unsigned char		   tuner_addr;
+	unsigned char		   radio_addr;
+
+	unsigned int               tda9887_conf;
+	unsigned int               gpio_value;
+
+	/* i2c i/o */
+	struct i2c_adapter         i2c_adap;
+	struct i2c_client          i2c_client;
+	unsigned char              eedata[256];
+	int 			   has_rds;
+
+	/* video overlay */
+	struct v4l2_framebuffer    ovbuf;
+	struct saa7134_format      *ovfmt;
+	unsigned int               ovenable;
+	enum v4l2_field            ovfield;
+
+	/* video+ts+vbi capture */
+	struct saa7134_dmaqueue    video_q;
+	struct saa7134_dmaqueue    vbi_q;
+	unsigned int               video_fieldcount;
+	unsigned int               vbi_fieldcount;
+
+	/* various v4l controls */
+	struct saa7134_tvnorm      *tvnorm;              /* video */
+	struct saa7134_tvaudio     *tvaudio;
+	unsigned int               ctl_input;
+	int                        ctl_bright;
+	int                        ctl_contrast;
+	int                        ctl_hue;
+	int                        ctl_saturation;
+	int                        ctl_freq;
+	int                        ctl_mute;             /* audio */
+	int                        ctl_volume;
+	int                        ctl_invert;           /* private */
+	int                        ctl_mirror;
+	int                        ctl_y_odd;
+	int                        ctl_y_even;
+	int                        ctl_automute;
+
+	/* crop */
+	struct v4l2_rect           crop_bounds;
+	struct v4l2_rect           crop_defrect;
+	struct v4l2_rect           crop_current;
+
+	/* other global state info */
+	unsigned int               automute;
+	struct saa7134_thread      thread;
+	struct saa7134_input       *input;
+	struct saa7134_input       *hw_input;
+	unsigned int               hw_mute;
+	int                        last_carrier;
+	int                        nosignal;
+	unsigned int               insuspend;
+
+	/* I2C keyboard data */
+	struct IR_i2c_init_data    init_data;
+
+	/* SAA7134_MPEG_* */
+	struct saa7134_ts          ts;
+	struct saa7134_dmaqueue    ts_q;
+	int                        ts_started;
+	struct saa7134_mpeg_ops    *mops;
+
+	/* SAA7134_MPEG_EMPRESS only */
+	struct video_device        *empress_dev;
+	struct videobuf_queue      empress_tsq;
+	atomic_t 		   empress_users;
+	struct work_struct         empress_workqueue;
+	int                        empress_started;
+
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+	/* SAA7134_MPEG_DVB only */
+	struct videobuf_dvb_frontends frontends;
+	int (*original_demod_sleep)(struct dvb_frontend *fe);
+	int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+	int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
+#endif
+	void (*gate_ctrl)(struct saa7134_dev *dev, int open);
+};
+
+/* ----------------------------------------------------------- */
+
+#define saa_readl(reg)             readl(dev->lmmio + (reg))
+#define saa_writel(reg,value)      writel((value), dev->lmmio + (reg));
+#define saa_andorl(reg,mask,value) \
+  writel((readl(dev->lmmio+(reg)) & ~(mask)) |\
+  ((value) & (mask)), dev->lmmio+(reg))
+#define saa_setl(reg,bit)          saa_andorl((reg),(bit),(bit))
+#define saa_clearl(reg,bit)        saa_andorl((reg),(bit),0)
+
+#define saa_readb(reg)             readb(dev->bmmio + (reg))
+#define saa_writeb(reg,value)      writeb((value), dev->bmmio + (reg));
+#define saa_andorb(reg,mask,value) \
+  writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\
+  ((value) & (mask)), dev->bmmio+(reg))
+#define saa_setb(reg,bit)          saa_andorb((reg),(bit),(bit))
+#define saa_clearb(reg,bit)        saa_andorb((reg),(bit),0)
+
+#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)
+
+#define GRP_EMPRESS (1)
+#define saa_call_all(dev, o, f, args...) do {				\
+	if (dev->gate_ctrl)						\
+		dev->gate_ctrl(dev, 1);					\
+	v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args);	\
+	if (dev->gate_ctrl)						\
+		dev->gate_ctrl(dev, 0);					\
+} while (0)
+
+#define saa_call_empress(dev, o, f, args...) ({				\
+	long _rc;							\
+	if (dev->gate_ctrl)						\
+		dev->gate_ctrl(dev, 1);					\
+	_rc = v4l2_device_call_until_err(&(dev)->v4l2_dev,		\
+					 GRP_EMPRESS, o, f , ##args);	\
+	if (dev->gate_ctrl)						\
+		dev->gate_ctrl(dev, 0);					\
+	_rc;								\
+})
+
+/* ----------------------------------------------------------- */
+/* saa7134-core.c                                              */
+
+extern struct list_head  saa7134_devlist;
+extern struct mutex saa7134_devlist_lock;
+extern int saa7134_no_overlay;
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
+
+#define SAA7134_PGTABLE_SIZE 4096
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt);
+int  saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+			   struct scatterlist *list, unsigned int length,
+			   unsigned int startpage);
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt);
+
+int saa7134_buffer_count(unsigned int size, unsigned int count);
+int saa7134_buffer_startpage(struct saa7134_buf *buf);
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf);
+
+int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+			 struct saa7134_buf *buf);
+void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+			   unsigned int state);
+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_set_dmabits(struct saa7134_dev *dev);
+
+extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-cards.c                                             */
+
+extern struct saa7134_board saa7134_boards[];
+extern const unsigned int saa7134_bcount;
+extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
+
+extern int saa7134_board_init1(struct saa7134_dev *dev);
+extern int saa7134_board_init2(struct saa7134_dev *dev);
+int saa7134_tuner_callback(void *priv, int component, int command, int arg);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-i2c.c                                               */
+
+int saa7134_i2c_register(struct saa7134_dev *dev);
+int saa7134_i2c_unregister(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;
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev,  struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev,  struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
+int saa7134_s_std_internal(struct saa7134_dev *dev,  struct saa7134_fh *fh, v4l2_std_id *id);
+
+int saa7134_videoport_init(struct saa7134_dev *dev);
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
+
+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);
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-ts.c                                                */
+
+#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
+
+extern struct videobuf_queue_ops saa7134_ts_qops;
+
+int saa7134_ts_init1(struct saa7134_dev *dev);
+int saa7134_ts_fini(struct saa7134_dev *dev);
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
+
+int saa7134_ts_init_hw(struct saa7134_dev *dev);
+
+int saa7134_ts_start(struct saa7134_dev *dev);
+int saa7134_ts_stop(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-vbi.c                                               */
+
+extern struct videobuf_queue_ops saa7134_vbi_qops;
+extern struct video_device saa7134_vbi_template;
+
+int saa7134_vbi_init1(struct saa7134_dev *dev);
+int saa7134_vbi_fini(struct saa7134_dev *dev);
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-tvaudio.c                                           */
+
+int saa7134_tvaudio_rx2mode(u32 rx);
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev);
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+			      struct saa7134_input *in);
+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);
+int saa7134_tvaudio_close(struct saa7134_dev *dev);
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
+
+void saa7134_enable_i2s(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-oss.c                                               */
+
+extern const struct file_operations saa7134_dsp_fops;
+extern const struct file_operations saa7134_mixer_fops;
+
+int saa7134_oss_init1(struct saa7134_dev *dev);
+int saa7134_oss_fini(struct saa7134_dev *dev);
+void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status);
+
+/* ----------------------------------------------------------- */
+/* saa7134-input.c                                             */
+
+#if defined(CONFIG_VIDEO_SAA7134_RC)
+int  saa7134_input_init1(struct saa7134_dev *dev);
+void saa7134_input_fini(struct saa7134_dev *dev);
+void saa7134_input_irq(struct saa7134_dev *dev);
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
+int saa7134_ir_start(struct saa7134_dev *dev);
+void saa7134_ir_stop(struct saa7134_dev *dev);
+#else
+#define saa7134_input_init1(dev)	((void)0)
+#define saa7134_input_fini(dev)		((void)0)
+#define saa7134_input_irq(dev)		((void)0)
+#define saa7134_probe_i2c_ir(dev)	((void)0)
+#define saa7134_ir_start(dev)		((void)0)
+#define saa7134_ir_stop(dev)		((void)0)
+#endif
diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig
new file mode 100644
index 000000000000..da88b77a916c
--- /dev/null
+++ b/drivers/media/pci/saa7146/Kconfig
@@ -0,0 +1,38 @@
+config VIDEO_HEXIUM_GEMINI
+	tristate "Hexium Gemini frame grabber"
+	depends on PCI && VIDEO_V4L2 && I2C
+	select VIDEO_SAA7146_VV
+	---help---
+	  This is a video4linux driver for the Hexium Gemini frame
+	  grabber card by Hexium. Please note that the Gemini Dual
+	  card is *not* fully supported.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hexium_gemini.
+
+config VIDEO_HEXIUM_ORION
+	tristate "Hexium HV-PCI6 and Orion frame grabber"
+	depends on PCI && VIDEO_V4L2 && I2C
+	select VIDEO_SAA7146_VV
+	---help---
+	  This is a video4linux driver for the Hexium HV-PCI6 and
+	  Orion frame grabber cards by Hexium.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hexium_orion.
+
+config VIDEO_MXB
+	tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
+	depends on PCI && VIDEO_V4L2 && I2C
+	select VIDEO_SAA7146_VV
+	select VIDEO_TUNER
+	select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for the 'Multimedia eXtension Board'
+	  TV card by Siemens-Nixdorf.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mxb.
diff --git a/drivers/media/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile
new file mode 100644
index 000000000000..f3566a95e4aa
--- /dev/null
+++ b/drivers/media/pci/saa7146/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_MXB) += mxb.o
+obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
+obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c
new file mode 100644
index 000000000000..366434f5647e
--- /dev/null
+++ b/drivers/media/pci/saa7146/hexium_gemini.c
@@ -0,0 +1,430 @@
+/*
+    hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
+
+    Visit http://www.mihu.de/linux/saa7146/ and follow the link
+    to "hexium" for further details about this card.
+
+    Copyright (C) 2003 Michael Hunold <michael@mihu.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.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_GEMINI			4
+#define HEXIUM_GEMINI_DUAL		5
+
+#define HEXIUM_INPUTS	9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+	{ 0, "CVBS 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 1, "CVBS 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 2, "CVBS 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 3, "CVBS 4",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 4, "CVBS 5",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 5, "CVBS 6",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 6, "Y/C 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 7, "Y/C 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 8, "Y/C 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS	0
+
+struct hexium_data
+{
+	s8 adr;
+	u8 byte;
+};
+
+#define HEXIUM_GEMINI_V_1_0		1
+#define HEXIUM_GEMINI_DUAL_V_1_0	2
+
+struct hexium
+{
+	int type;
+
+	struct video_device	*video_dev;
+	struct i2c_adapter	i2c_adapter;
+
+	int 		cur_input;	/* current input */
+	v4l2_std_id 	cur_std;	/* current standard */
+};
+
+/* Samsung KS0127B decoder default registers */
+static u8 hexium_ks0127b[0x100]={
+/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
+/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
+/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
+/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
+/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
+/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
+/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
+/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+
+static struct hexium_data hexium_pal[] = {
+	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc[] = {
+	{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_secam[] = {
+	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_input_select[] = {
+	{ 0x02, 0x60 },
+	{ 0x02, 0x64 },
+	{ 0x02, 0x61 },
+	{ 0x02, 0x65 },
+	{ 0x02, 0x62 },
+	{ 0x02, 0x66 },
+	{ 0x02, 0x68 },
+	{ 0x02, 0x69 },
+	{ 0x02, 0x6A },
+};
+
+/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
+   are currently *not* supported*/
+static struct saa7146_standard hexium_standards[] = {
+	{
+		.name	= "PAL", 	.id	= V4L2_STD_PAL,
+		.v_offset	= 28,	.v_field 	= 288,
+		.h_offset	= 1,	.h_pixels 	= 680,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC", 	.id	= V4L2_STD_NTSC,
+		.v_offset	= 28,	.v_field 	= 240,
+		.h_offset	= 1,	.h_pixels 	= 640,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}, {
+		.name	= "SECAM", 	.id	= V4L2_STD_SECAM,
+		.v_offset	= 28,	.v_field 	= 288,
+		.h_offset	= 1,	.h_pixels 	= 720,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+   wants to capture from this device before it has been properly initialized.
+   the capture engine would badly fail, because no valid signal arrives on the
+   saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+	union i2c_smbus_data data;
+	int i = 0;
+
+	DEB_D("hexium_init_done called\n");
+
+	/* initialize the helper ics to useful values */
+	for (i = 0; i < sizeof(hexium_ks0127b); i++) {
+		data.byte = hexium_ks0127b[i];
+		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+			pr_err("hexium_init_done() failed for address 0x%02x\n",
+			       i);
+		}
+	}
+
+	return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+	union i2c_smbus_data data;
+
+	DEB_D("\n");
+
+	data.byte = hexium_input_select[input].byte;
+	if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
+{
+	union i2c_smbus_data data;
+	int i = 0;
+
+	DEB_D("\n");
+
+	while (vdec[i].adr != -1) {
+		data.byte = vdec[i].byte;
+		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+			pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
+			       i);
+			return -1;
+		}
+		i++;
+	}
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+	if (i->index >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+	DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	*input = hexium->cur_input;
+
+	DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+	if (input >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	hexium->cur_input = input;
+	hexium_set_input(hexium, input);
+	return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct hexium *hexium;
+	int ret;
+
+	DEB_EE("\n");
+
+	hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
+	if (NULL == hexium) {
+		pr_err("not enough kernel memory in hexium_attach()\n");
+		return -ENOMEM;
+	}
+	dev->ext_priv = hexium;
+
+	/* enable i2c-port pins */
+	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+	hexium->i2c_adapter = (struct i2c_adapter) {
+		.name = "hexium gemini",
+	};
+	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+	if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+		DEB_S("cannot register i2c-device. skipping.\n");
+		kfree(hexium);
+		return -EFAULT;
+	}
+
+	/*  set HWControl GPIO number 2 */
+	saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+	saa7146_write(dev, DD1_INIT, 0x07000700);
+	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	/* the rest */
+	hexium->cur_input = 0;
+	hexium_init_done(dev);
+
+	hexium_set_standard(hexium, hexium_pal);
+	hexium->cur_std = V4L2_STD_PAL;
+
+	hexium_set_input(hexium, 0);
+	hexium->cur_input = 0;
+
+	saa7146_vv_init(dev, &vv_data);
+
+	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+	ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
+	if (ret < 0) {
+		pr_err("cannot register capture v4l2 device. skipping.\n");
+		return ret;
+	}
+
+	pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
+	hexium_num++;
+
+	return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	DEB_EE("dev:%p\n", dev);
+
+	saa7146_unregister_device(&hexium->video_dev, dev);
+	saa7146_vv_release(dev);
+
+	hexium_num--;
+
+	i2c_del_adapter(&hexium->i2c_adapter);
+	kfree(hexium);
+	return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	if (V4L2_STD_PAL == std->id) {
+		hexium_set_standard(hexium, hexium_pal);
+		hexium->cur_std = V4L2_STD_PAL;
+		return 0;
+	} else if (V4L2_STD_NTSC == std->id) {
+		hexium_set_standard(hexium, hexium_ntsc);
+		hexium->cur_std = V4L2_STD_NTSC;
+		return 0;
+	} else if (V4L2_STD_SECAM == std->id) {
+		hexium_set_standard(hexium, hexium_secam);
+		hexium->cur_std = V4L2_STD_SECAM;
+		return 0;
+	}
+
+	return -1;
+}
+
+static struct saa7146_extension hexium_extension;
+
+static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
+	.ext_priv = "Hexium Gemini (4 BNC)",
+	.ext = &hexium_extension,
+};
+
+static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
+	.ext_priv = "Hexium Gemini Dual (4 BNC)",
+	.ext = &hexium_extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+	{
+	 .vendor = PCI_VENDOR_ID_PHILIPS,
+	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+	 .subvendor = 0x17c8,
+	 .subdevice = 0x2401,
+	 .driver_data = (unsigned long) &hexium_gemini_4bnc,
+	 },
+	{
+	 .vendor = PCI_VENDOR_ID_PHILIPS,
+	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+	 .subvendor = 0x17c8,
+	 .subdevice = 0x2402,
+	 .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
+	 },
+	{
+	 .vendor = 0,
+	 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+	.inputs = HEXIUM_INPUTS,
+	.capabilities = 0,
+	.stds = &hexium_standards[0],
+	.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+	.std_callback = &std_callback,
+};
+
+static struct saa7146_extension hexium_extension = {
+	.name = "hexium gemini",
+	.flags = SAA7146_USE_I2C_IRQ,
+
+	.pci_tbl = &pci_tbl[0],
+	.module = THIS_MODULE,
+
+	.attach = hexium_attach,
+	.detach = hexium_detach,
+
+	.irq_mask = 0,
+	.irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+	if (0 != saa7146_register_extension(&hexium_extension)) {
+		DEB_S("failed to register extension\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+	saa7146_unregister_extension(&hexium_extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c
new file mode 100644
index 000000000000..a1eb26d11070
--- /dev/null
+++ b/drivers/media/pci/saa7146/hexium_orion.c
@@ -0,0 +1,502 @@
+/*
+    hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
+
+    Visit http://www.mihu.de/linux/saa7146/ and follow the link
+    to "hexium" for further details about this card.
+
+    Copyright (C) 2003 Michael Hunold <michael@mihu.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.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_HV_PCI6_ORION		1
+#define HEXIUM_ORION_1SVHS_3BNC		2
+#define HEXIUM_ORION_4BNC		3
+
+#define HEXIUM_INPUTS	9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+	{ 0, "CVBS 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 1, "CVBS 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 2, "CVBS 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 3, "CVBS 4",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 4, "CVBS 5",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 5, "CVBS 6",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 6, "Y/C 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 7, "Y/C 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ 8, "Y/C 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS	0
+
+struct hexium_data
+{
+	s8 adr;
+	u8 byte;
+};
+
+struct hexium
+{
+	int type;
+	struct video_device	*video_dev;
+	struct i2c_adapter	i2c_adapter;
+
+	int cur_input;	/* current input */
+};
+
+/* Philips SAA7110 decoder default registers */
+static u8 hexium_saa7110[53]={
+/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
+/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
+/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
+/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
+/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
+/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
+/*30*/ 0x44,0x75,0x01,0x8C,0x03
+};
+
+static struct {
+	struct hexium_data data[8];
+} hexium_input_select[] = {
+{
+	{ /* cvbs 1 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0xD9 },
+		{ 0x21, 0x17 }, // 0x16,
+		{ 0x22, 0x40 },
+		{ 0x2C, 0x03 },
+		{ 0x30, 0x44 },
+		{ 0x31, 0x75 }, // ??
+		{ 0x21, 0x16 }, // 0x03,
+	}
+}, {
+	{ /* cvbs 2 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0x78 },
+		{ 0x21, 0x07 }, // 0x03,
+		{ 0x22, 0xD2 },
+		{ 0x2C, 0x83 },
+		{ 0x30, 0x60 },
+		{ 0x31, 0xB5 }, // ?
+		{ 0x21, 0x03 },
+	}
+}, {
+	{ /* cvbs 3 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0xBA },
+		{ 0x21, 0x07 }, // 0x05,
+		{ 0x22, 0x91 },
+		{ 0x2C, 0x03 },
+		{ 0x30, 0x60 },
+		{ 0x31, 0xB5 }, // ??
+		{ 0x21, 0x05 }, // 0x03,
+	}
+}, {
+	{ /* cvbs 4 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0xD8 },
+		{ 0x21, 0x17 }, // 0x16,
+		{ 0x22, 0x40 },
+		{ 0x2C, 0x03 },
+		{ 0x30, 0x44 },
+		{ 0x31, 0x75 }, // ??
+		{ 0x21, 0x16 }, // 0x03,
+	}
+}, {
+	{ /* cvbs 5 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0xB8 },
+		{ 0x21, 0x07 }, // 0x05,
+		{ 0x22, 0x91 },
+		{ 0x2C, 0x03 },
+		{ 0x30, 0x60 },
+		{ 0x31, 0xB5 }, // ??
+		{ 0x21, 0x05 }, // 0x03,
+	}
+}, {
+	{ /* cvbs 6 */
+		{ 0x06, 0x00 },
+		{ 0x20, 0x7C },
+		{ 0x21, 0x07 }, // 0x03
+		{ 0x22, 0xD2 },
+		{ 0x2C, 0x83 },
+		{ 0x30, 0x60 },
+		{ 0x31, 0xB5 }, // ??
+		{ 0x21, 0x03 },
+	}
+}, {
+	{ /* y/c 1 */
+		{ 0x06, 0x80 },
+		{ 0x20, 0x59 },
+		{ 0x21, 0x17 },
+		{ 0x22, 0x42 },
+		{ 0x2C, 0xA3 },
+		{ 0x30, 0x44 },
+		{ 0x31, 0x75 },
+		{ 0x21, 0x12 },
+	}
+}, {
+	{ /* y/c 2 */
+		{ 0x06, 0x80 },
+		{ 0x20, 0x9A },
+		{ 0x21, 0x17 },
+		{ 0x22, 0xB1 },
+		{ 0x2C, 0x13 },
+		{ 0x30, 0x60 },
+		{ 0x31, 0xB5 },
+		{ 0x21, 0x14 },
+	}
+}, {
+	{ /* y/c 3 */
+		{ 0x06, 0x80 },
+		{ 0x20, 0x3C },
+		{ 0x21, 0x27 },
+		{ 0x22, 0xC1 },
+		{ 0x2C, 0x23 },
+		{ 0x30, 0x44 },
+		{ 0x31, 0x75 },
+		{ 0x21, 0x21 },
+	}
+}
+};
+
+static struct saa7146_standard hexium_standards[] = {
+	{
+		.name	= "PAL", 	.id	= V4L2_STD_PAL,
+		.v_offset	= 16,	.v_field 	= 288,
+		.h_offset	= 1,	.h_pixels 	= 680,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC", 	.id	= V4L2_STD_NTSC,
+		.v_offset	= 16,	.v_field 	= 240,
+		.h_offset	= 1,	.h_pixels 	= 640,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}, {
+		.name	= "SECAM", 	.id	= V4L2_STD_SECAM,
+		.v_offset	= 16,	.v_field 	= 288,
+		.h_offset	= 1,	.h_pixels 	= 720,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}
+};
+
+/* this is only called for old HV-PCI6/Orion cards
+   without eeprom */
+static int hexium_probe(struct saa7146_dev *dev)
+{
+	struct hexium *hexium = NULL;
+	union i2c_smbus_data data;
+	int err = 0;
+
+	DEB_EE("\n");
+
+	/* there are no hexium orion cards with revision 0 saa7146s */
+	if (0 == dev->revision) {
+		return -EFAULT;
+	}
+
+	hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
+	if (NULL == hexium) {
+		pr_err("hexium_probe: not enough kernel memory\n");
+		return -ENOMEM;
+	}
+
+	/* enable i2c-port pins */
+	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+	saa7146_write(dev, DD1_INIT, 0x01000100);
+	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	hexium->i2c_adapter = (struct i2c_adapter) {
+		.name = "hexium orion",
+	};
+	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+	if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+		DEB_S("cannot register i2c-device. skipping.\n");
+		kfree(hexium);
+		return -EFAULT;
+	}
+
+	/* set SAA7110 control GPIO 0 */
+	saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+	/*  set HWControl GPIO number 2 */
+	saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+	mdelay(10);
+
+	/* detect newer Hexium Orion cards by subsystem ids */
+	if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
+		pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
+		/* we store the pointer in our private data field */
+		dev->ext_priv = hexium;
+		hexium->type = HEXIUM_ORION_1SVHS_3BNC;
+		return 0;
+	}
+
+	if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
+		pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
+		/* we store the pointer in our private data field */
+		dev->ext_priv = hexium;
+		hexium->type = HEXIUM_ORION_4BNC;
+		return 0;
+	}
+
+	/* check if this is an old hexium Orion card by looking at
+	   a saa7110 at address 0x4e */
+	if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
+		pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
+		/* we store the pointer in our private data field */
+		dev->ext_priv = hexium;
+		hexium->type = HEXIUM_HV_PCI6_ORION;
+		return 0;
+	}
+
+	i2c_del_adapter(&hexium->i2c_adapter);
+	kfree(hexium);
+	return -EFAULT;
+}
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+   wants to capture from this device before it has been properly initialized.
+   the capture engine would badly fail, because no valid signal arrives on the
+   saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+	union i2c_smbus_data data;
+	int i = 0;
+
+	DEB_D("hexium_init_done called\n");
+
+	/* initialize the helper ics to useful values */
+	for (i = 0; i < sizeof(hexium_saa7110); i++) {
+		data.byte = hexium_saa7110[i];
+		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+			pr_err("failed for address 0x%02x\n", i);
+		}
+	}
+
+	return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+	union i2c_smbus_data data;
+	int i = 0;
+
+	DEB_D("\n");
+
+	for (i = 0; i < 8; i++) {
+		int adr = hexium_input_select[input].data[i].adr;
+		data.byte = hexium_input_select[input].data[i].byte;
+		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
+			return -1;
+		}
+		pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
+	}
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+	if (i->index >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+	DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	*input = hexium->cur_input;
+
+	DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	if (input >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	hexium->cur_input = input;
+	hexium_set_input(hexium, input);
+
+	return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	DEB_EE("\n");
+
+	saa7146_vv_init(dev, &vv_data);
+	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+	if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
+		pr_err("cannot register capture v4l2 device. skipping.\n");
+		return -1;
+	}
+
+	pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
+	hexium_num++;
+
+	/* the rest */
+	hexium->cur_input = 0;
+	hexium_init_done(dev);
+
+	return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	DEB_EE("dev:%p\n", dev);
+
+	saa7146_unregister_device(&hexium->video_dev, dev);
+	saa7146_vv_release(dev);
+
+	hexium_num--;
+
+	i2c_del_adapter(&hexium->i2c_adapter);
+	kfree(hexium);
+	return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+	return 0;
+}
+
+static struct saa7146_extension extension;
+
+static struct saa7146_pci_extension_data hexium_hv_pci6 = {
+	.ext_priv = "Hexium HV-PCI6 / Orion",
+	.ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
+	.ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
+	.ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_4bnc = {
+	.ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
+	.ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+	{
+	 .vendor = PCI_VENDOR_ID_PHILIPS,
+	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+	 .subvendor = 0x0000,
+	 .subdevice = 0x0000,
+	 .driver_data = (unsigned long) &hexium_hv_pci6,
+	 },
+	{
+	 .vendor = PCI_VENDOR_ID_PHILIPS,
+	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+	 .subvendor = 0x17c8,
+	 .subdevice = 0x0101,
+	 .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
+	 },
+	{
+	 .vendor = PCI_VENDOR_ID_PHILIPS,
+	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+	 .subvendor = 0x17c8,
+	 .subdevice = 0x2101,
+	 .driver_data = (unsigned long) &hexium_orion_4bnc,
+	 },
+	{
+	 .vendor = 0,
+	 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+	.inputs = HEXIUM_INPUTS,
+	.capabilities = 0,
+	.stds = &hexium_standards[0],
+	.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+	.std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+	.name = "hexium HV-PCI6 Orion",
+	.flags = 0,		// SAA7146_USE_I2C_IRQ,
+
+	.pci_tbl = &pci_tbl[0],
+	.module = THIS_MODULE,
+
+	.probe = hexium_probe,
+	.attach = hexium_attach,
+	.detach = hexium_detach,
+
+	.irq_mask = 0,
+	.irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+	if (0 != saa7146_register_extension(&extension)) {
+		DEB_S("failed to register extension\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+	saa7146_unregister_extension(&extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
new file mode 100644
index 000000000000..91369daad722
--- /dev/null
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -0,0 +1,886 @@
+/*
+    mxb - v4l2 driver for the Multimedia eXtension Board
+
+    Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de>
+
+    Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html 
+    for further details about this card.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/saa7115.h>
+#include <linux/module.h>
+
+#include "tea6415c.h"
+#include "tea6420.h"
+
+#define MXB_AUDIOS	6
+
+#define I2C_SAA7111A  0x24
+#define	I2C_TDA9840   0x42
+#define	I2C_TEA6415C  0x43
+#define	I2C_TEA6420_1 0x4c
+#define	I2C_TEA6420_2 0x4d
+#define	I2C_TUNER     0x60
+
+#define MXB_BOARD_CAN_DO_VBI(dev)   (dev->revision != 0)
+
+/* global variable */
+static int mxb_num;
+
+/* initial frequence the tuner will be tuned to.
+   in verden (lower saxony, germany) 4148 is a
+   channel called "phoenix" */
+static int freq = 4148;
+module_param(freq, int, 0644);
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
+#define MXB_INPUTS 4
+enum { TUNER, AUX1, AUX3, AUX3_YC };
+
+static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
+	{ TUNER,   "Tuner",          V4L2_INPUT_TYPE_TUNER,  0x3f, 0,
+		V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD },
+	{ AUX1,	   "AUX1",           V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+		V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ AUX3,	   "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+		V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+	{ AUX3_YC, "AUX3 S-Video",   V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+		V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+/* this array holds the information, which port of the saa7146 each
+   input actually uses. the mxb uses port 0 for every input */
+static struct {
+	int hps_source;
+	int hps_sync;
+} input_port_selection[MXB_INPUTS] = {
+	{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+	{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+	{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+	{ SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+};
+
+/* this array holds the information of the audio source (mxb_audios),
+   which has to be switched corresponding to the video source (mxb_channels) */
+static int video_audio_connect[MXB_INPUTS] =
+	{ 0, 1, 3, 3 };
+
+struct mxb_routing {
+	u32 input;
+	u32 output;
+};
+
+/* these are the available audio sources, which can switched
+   to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+	    {
+		.index	= 0,
+		.name	= "Tuner",
+		.capability = V4L2_AUDCAP_STEREO,
+	} , {
+		.index	= 1,
+		.name	= "AUX1",
+		.capability = V4L2_AUDCAP_STEREO,
+	} , {
+		.index	= 2,
+		.name	= "AUX2",
+		.capability = V4L2_AUDCAP_STEREO,
+	} , {
+		.index	= 3,
+		.name	= "AUX3",
+		.capability = V4L2_AUDCAP_STEREO,
+	} , {
+		.index	= 4,
+		.name	= "Radio (X9)",
+		.capability = V4L2_AUDCAP_STEREO,
+	} , {
+		.index	= 5,
+		.name	= "CD-ROM (X10)",
+		.capability = V4L2_AUDCAP_STEREO,
+	}
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+   (see above) to the CD-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
+	{ { 1, 1 }, { 1, 1 } },	/* Tuner */
+	{ { 5, 1 }, { 6, 1 } },	/* AUX 1 */
+	{ { 4, 1 }, { 6, 1 } },	/* AUX 2 */
+	{ { 3, 1 }, { 6, 1 } },	/* AUX 3 */
+	{ { 1, 1 }, { 3, 1 } },	/* Radio */
+	{ { 1, 1 }, { 2, 1 } },	/* CD-Rom */
+	{ { 6, 1 }, { 6, 1 } }	/* Mute */
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+   (see above) to the line-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
+	{ { 2, 3 }, { 1, 2 } },
+	{ { 5, 3 }, { 6, 2 } },
+	{ { 4, 3 }, { 6, 2 } },
+	{ { 3, 3 }, { 6, 2 } },
+	{ { 2, 3 }, { 3, 2 } },
+	{ { 2, 3 }, { 2, 2 } },
+	{ { 6, 3 }, { 6, 2 } }	/* Mute */
+};
+
+struct mxb
+{
+	struct video_device	*video_dev;
+	struct video_device	*vbi_dev;
+
+	struct i2c_adapter	i2c_adapter;
+
+	struct v4l2_subdev	*saa7111a;
+	struct v4l2_subdev	*tda9840;
+	struct v4l2_subdev	*tea6415c;
+	struct v4l2_subdev	*tuner;
+	struct v4l2_subdev	*tea6420_1;
+	struct v4l2_subdev	*tea6420_2;
+
+	int	cur_mode;	/* current audio mode (mono, stereo, ...) */
+	int	cur_input;	/* current input */
+	int	cur_audinput;	/* current audio input */
+	int	cur_mute;	/* current mute status */
+	struct v4l2_frequency	cur_freq;	/* current frequency the tuner is tuned to */
+};
+
+#define saa7111a_call(mxb, o, f, args...) \
+	v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
+#define tda9840_call(mxb, o, f, args...) \
+	v4l2_subdev_call(mxb->tda9840, o, f, ##args)
+#define tea6415c_call(mxb, o, f, args...) \
+	v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
+#define tuner_call(mxb, o, f, args...) \
+	v4l2_subdev_call(mxb->tuner, o, f, ##args)
+#define call_all(dev, o, f, args...) \
+	v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+static void mxb_update_audmode(struct mxb *mxb)
+{
+	struct v4l2_tuner t = {
+		.audmode = mxb->cur_mode,
+	};
+
+	tda9840_call(mxb, tuner, s_tuner, &t);
+}
+
+static inline void tea6420_route(struct mxb *mxb, int idx)
+{
+	v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+		TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
+	v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+		TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
+	v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+		TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
+	v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+		TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0);
+}
+
+static struct saa7146_extension extension;
+
+static int mxb_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct saa7146_dev *dev = container_of(ctrl->handler,
+				struct saa7146_dev, ctrl_handler);
+	struct mxb *mxb = dev->ext_priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		mxb->cur_mute = ctrl->val;
+		/* switch the audio-source */
+		tea6420_route(mxb, ctrl->val ? 6 :
+				video_audio_connect[mxb->cur_input]);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops mxb_ctrl_ops = {
+	.s_ctrl = mxb_s_ctrl,
+};
+
+static int mxb_probe(struct saa7146_dev *dev)
+{
+	struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+	struct mxb *mxb = NULL;
+
+	v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops,
+			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	if (hdl->error)
+		return hdl->error;
+	mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
+	if (mxb == NULL) {
+		DEB_D("not enough kernel memory\n");
+		return -ENOMEM;
+	}
+
+
+	snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
+	saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+	if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+		DEB_S("cannot register i2c-device. skipping.\n");
+		kfree(mxb);
+		return -EFAULT;
+	}
+
+	mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"saa7111", I2C_SAA7111A, NULL);
+	mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"tea6420", I2C_TEA6420_1, NULL);
+	mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"tea6420", I2C_TEA6420_2, NULL);
+	mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"tea6415c", I2C_TEA6415C, NULL);
+	mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"tda9840", I2C_TDA9840, NULL);
+	mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+			"tuner", I2C_TUNER, NULL);
+
+	/* check if all devices are present */
+	if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
+	    !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
+		pr_err("did not find all i2c devices. aborting\n");
+		i2c_del_adapter(&mxb->i2c_adapter);
+		kfree(mxb);
+		return -ENODEV;
+	}
+
+	/* all devices are present, probe was successful */
+
+	/* we store the pointer in our private data field */
+	dev->ext_priv = mxb;
+
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+/* some init data for the saa7740, the so-called 'sound arena module'.
+   there are no specs available, so we simply use some init values */
+static struct {
+	int	length;
+	char	data[9];
+} mxb_saa7740_init[] = {
+	{ 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
+	{ 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
+	{ 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
+	{ 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
+	{ 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
+	{ 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
+	{ 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
+	{ 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
+	{ 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
+	{ 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
+	{ 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
+	{ 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
+	{ 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
+	{ 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
+	{ 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
+	{ 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
+	{ 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
+	{ 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
+	{ 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
+	{ 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
+	{ 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
+	{ 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
+	{ 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
+	{ 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
+	{ 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
+	{ 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
+	{ 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
+	{ 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
+	{ 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
+	{ 3, { 0x48, 0x00, 0x01 } },
+	{ 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+	{ 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+	{ 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+	{ 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+	{ 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+	{ 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+	{ 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+	{ 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+	{ 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+	{ 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+	{ 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+	{ 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+	{ 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+	{ 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+	{ 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+	{ 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+	{ 3, { 0x80, 0xb3, 0x0a } },
+	{-1, { 0 } }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+   wants to capture from this device before it has been properly initialized.
+   the capture engine would badly fail, because no valid signal arrives on the
+   saa7146, thus leading to timeouts and stuff. */
+static int mxb_init_done(struct saa7146_dev* dev)
+{
+	struct mxb* mxb = (struct mxb*)dev->ext_priv;
+	struct i2c_msg msg;
+	struct tuner_setup tun_setup;
+	v4l2_std_id std = V4L2_STD_PAL_BG;
+
+	int i = 0, err = 0;
+
+	/* mute audio on tea6420s */
+	tea6420_route(mxb, 6);
+
+	/* select video mode in saa7111a */
+	saa7111a_call(mxb, core, s_std, std);
+
+	/* select tuner-output on saa7111a */
+	i = 0;
+	saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
+		SAA7111_FMT_CCIR, 0);
+
+	/* select a tuner type */
+	tun_setup.mode_mask = T_ANALOG_TV;
+	tun_setup.addr = ADDR_UNSET;
+	tun_setup.type = TUNER_PHILIPS_PAL;
+	tuner_call(mxb, tuner, s_type_addr, &tun_setup);
+	/* tune in some frequency on tuner */
+	mxb->cur_freq.tuner = 0;
+	mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+	mxb->cur_freq.frequency = freq;
+	tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+	/* set a default video standard */
+	/* These two gpio calls set the GPIO pins that control the tda9820 */
+	saa7146_write(dev, GPIO_CTRL, 0x00404050);
+	saa7111a_call(mxb, core, s_gpio, 1);
+	saa7111a_call(mxb, core, s_std, std);
+	tuner_call(mxb, core, s_std, std);
+
+	/* switch to tuner-channel on tea6415c */
+	tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+	/* select tuner-output on multicable on tea6415c */
+	tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+
+	/* the rest for mxb */
+	mxb->cur_input = 0;
+	mxb->cur_audinput = video_audio_connect[mxb->cur_input];
+	mxb->cur_mute = 1;
+
+	mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+	mxb_update_audmode(mxb);
+
+	/* check if the saa7740 (aka 'sound arena module') is present
+	   on the mxb. if so, we must initialize it. due to lack of
+	   informations about the saa7740, the values were reverse
+	   engineered. */
+	msg.addr = 0x1b;
+	msg.flags = 0;
+	msg.len = mxb_saa7740_init[0].length;
+	msg.buf = &mxb_saa7740_init[0].data[0];
+
+	err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+	if (err == 1) {
+		/* the sound arena module is a pos, that's probably the reason
+		   philips refuses to hand out a datasheet for the saa7740...
+		   it seems to screw up the i2c bus, so we disable fast irq
+		   based i2c transactions here and rely on the slow and safe
+		   polling method ... */
+		extension.flags &= ~SAA7146_USE_I2C_IRQ;
+		for (i = 1; ; i++) {
+			if (-1 == mxb_saa7740_init[i].length)
+				break;
+
+			msg.len = mxb_saa7740_init[i].length;
+			msg.buf = &mxb_saa7740_init[i].data[0];
+			err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+			if (err != 1) {
+				DEB_D("failed to initialize 'sound arena module'\n");
+				goto err;
+			}
+		}
+		pr_info("'sound arena module' detected\n");
+	}
+err:
+	/* the rest for saa7146: you should definitely set some basic values
+	   for the input-port handling of the saa7146. */
+
+	/* ext->saa has been filled by the core driver */
+
+	/* some stuff is done via variables */
+	saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
+			input_port_selection[mxb->cur_input].hps_sync);
+
+	/* some stuff is done via direct write to the registers */
+
+	/* this is ugly, but because of the fact that this is completely
+	   hardware dependend, it should be done directly... */
+	saa7146_write(dev, DD1_STREAM_B,	0x00000000);
+	saa7146_write(dev, DD1_INIT,		0x02000200);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	return 0;
+}
+
+/* interrupt-handler. this gets called when irq_mask is != 0.
+   it must clear the interrupt-bits in irq_mask it has handled */
+/*
+void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+{
+	struct mxb* mxb = (struct mxb*)dev->ext_priv;
+}
+*/
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+	if (i->index >= MXB_INPUTS)
+		return -EINVAL;
+	memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	*i = mxb->cur_input;
+
+	DEB_EE("VIDIOC_G_INPUT %d\n", *i);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	int err = 0;
+	int i = 0;
+
+	DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+	if (input >= MXB_INPUTS)
+		return -EINVAL;
+
+	mxb->cur_input = input;
+
+	saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+			input_port_selection[input].hps_sync);
+
+	/* prepare switching of tea6415c and saa7111a;
+	   have a look at the 'background'-file for further informations  */
+	switch (input) {
+	case TUNER:
+		i = SAA7115_COMPOSITE0;
+
+		err = tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+		/* connect tuner-output always to multicable */
+		if (!err)
+			err = tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+		break;
+	case AUX3_YC:
+		/* nothing to be done here. aux3_yc is
+		   directly connected to the saa711a */
+		i = SAA7115_SVIDEO1;
+		break;
+	case AUX3:
+		/* nothing to be done here. aux3 is
+		   directly connected to the saa711a */
+		i = SAA7115_COMPOSITE1;
+		break;
+	case AUX1:
+		i = SAA7115_COMPOSITE0;
+		err = tea6415c_call(mxb, video, s_routing, 1, 17, 0);
+		break;
+	}
+
+	if (err)
+		return err;
+
+	/* switch video in saa7111a */
+	if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
+		pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
+
+	mxb->cur_audinput = video_audio_connect[input];
+	/* switch the audio-source only if necessary */
+	if (0 == mxb->cur_mute)
+		tea6420_route(mxb, mxb->cur_audinput);
+	if (mxb->cur_audinput == 0)
+		mxb_update_audmode(mxb);
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (t->index) {
+		DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+		      t->index);
+		return -EINVAL;
+	}
+
+	DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
+
+	memset(t, 0, sizeof(*t));
+	strlcpy(t->name, "TV Tuner", sizeof(t->name));
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+	t->audmode = mxb->cur_mode;
+	return call_all(dev, tuner, g_tuner, t);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (t->index) {
+		DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+		      t->index);
+		return -EINVAL;
+	}
+
+	mxb->cur_mode = t->audmode;
+	return call_all(dev, tuner, s_tuner, t);
+}
+
+static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+	return call_all(dev, video, querystd, norm);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (f->tuner)
+		return -EINVAL;
+	*f = mxb->cur_freq;
+
+	DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	struct saa7146_vv *vv = dev->vv_data;
+
+	if (f->tuner)
+		return -EINVAL;
+
+	if (V4L2_TUNER_ANALOG_TV != f->type)
+		return -EINVAL;
+
+	DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
+
+	/* tune in desired frequency */
+	tuner_call(mxb, tuner, s_frequency, f);
+	/* let the tuner subdev clamp the frequency to the tuner range */
+	tuner_call(mxb, tuner, g_frequency, f);
+	mxb->cur_freq = *f;
+	if (mxb->cur_audinput == 0)
+		mxb_update_audmode(mxb);
+
+	if (mxb->cur_input)
+		return 0;
+
+	/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+	spin_lock(&dev->slock);
+	vv->vbi_fieldcount = 0;
+	spin_unlock(&dev->slock);
+
+	return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	if (a->index >= MXB_AUDIOS)
+		return -EINVAL;
+	*a = mxb_audios[a->index];
+	return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	DEB_EE("VIDIOC_G_AUDIO\n");
+	*a = mxb_audios[mxb->cur_audinput];
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
+	if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) {
+		if (mxb->cur_audinput != a->index) {
+			mxb->cur_audinput = a->index;
+			tea6420_route(mxb, a->index);
+			if (mxb->cur_audinput == 0)
+				mxb_update_audmode(mxb);
+		}
+		return 0;
+	}
+	return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (v4l2_chip_match_host(&reg->match)) {
+		reg->val = saa7146_read(dev, reg->reg);
+		reg->size = 4;
+		return 0;
+	}
+	call_all(dev, core, g_register, reg);
+	return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (v4l2_chip_match_host(&reg->match)) {
+		saa7146_write(dev, reg->reg, reg->val);
+		reg->size = 4;
+		return 0;
+	}
+	return call_all(dev, core, s_register, reg);
+}
+#endif
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct mxb *mxb;
+
+	DEB_EE("dev:%p\n", dev);
+
+	saa7146_vv_init(dev, &vv_data);
+	if (mxb_probe(dev)) {
+		saa7146_vv_release(dev);
+		return -1;
+	}
+	mxb = (struct mxb *)dev->ext_priv;
+
+	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+	vv_data.vid_ops.vidioc_querystd = vidioc_querystd;
+	vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+	vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+	vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+	vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+	vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+	vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio;
+	vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	vv_data.vid_ops.vidioc_g_register = vidioc_g_register;
+	vv_data.vid_ops.vidioc_s_register = vidioc_s_register;
+#endif
+	if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+		ERR("cannot register capture v4l2 device. skipping.\n");
+		saa7146_vv_release(dev);
+		return -1;
+	}
+
+	/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+	if (MXB_BOARD_CAN_DO_VBI(dev)) {
+		if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+			ERR("cannot register vbi v4l2 device. skipping.\n");
+		}
+	}
+
+	pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
+
+	mxb_num++;
+	mxb_init_done(dev);
+	return 0;
+}
+
+static int mxb_detach(struct saa7146_dev *dev)
+{
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	DEB_EE("dev:%p\n", dev);
+
+	/* mute audio on tea6420s */
+	tea6420_route(mxb, 6);
+
+	saa7146_unregister_device(&mxb->video_dev,dev);
+	if (MXB_BOARD_CAN_DO_VBI(dev))
+		saa7146_unregister_device(&mxb->vbi_dev, dev);
+	saa7146_vv_release(dev);
+
+	mxb_num--;
+
+	i2c_del_adapter(&mxb->i2c_adapter);
+	kfree(mxb);
+
+	return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
+{
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (V4L2_STD_PAL_I == standard->id) {
+		v4l2_std_id std = V4L2_STD_PAL_I;
+
+		DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
+		/* These two gpio calls set the GPIO pins that control the tda9820 */
+		saa7146_write(dev, GPIO_CTRL, 0x00404050);
+		saa7111a_call(mxb, core, s_gpio, 0);
+		saa7111a_call(mxb, core, s_std, std);
+		if (mxb->cur_input == 0)
+			tuner_call(mxb, core, s_std, std);
+	} else {
+		v4l2_std_id std = V4L2_STD_PAL_BG;
+
+		if (mxb->cur_input)
+			std = standard->id;
+		DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
+		/* These two gpio calls set the GPIO pins that control the tda9820 */
+		saa7146_write(dev, GPIO_CTRL, 0x00404050);
+		saa7111a_call(mxb, core, s_gpio, 1);
+		saa7111a_call(mxb, core, s_std, std);
+		if (mxb->cur_input == 0)
+			tuner_call(mxb, core, s_std, std);
+	}
+	return 0;
+}
+
+static struct saa7146_standard standard[] = {
+	{
+		.name	= "PAL-BG", 	.id	= V4L2_STD_PAL_BG,
+		.v_offset	= 0x17,	.v_field 	= 288,
+		.h_offset	= 0x14,	.h_pixels 	= 680,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "PAL-I", 	.id	= V4L2_STD_PAL_I,
+		.v_offset	= 0x17,	.v_field 	= 288,
+		.h_offset	= 0x14,	.h_pixels 	= 680,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC", 	.id	= V4L2_STD_NTSC,
+		.v_offset	= 0x16,	.v_field 	= 240,
+		.h_offset	= 0x06,	.h_pixels 	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}, {
+		.name	= "SECAM", 	.id	= V4L2_STD_SECAM,
+		.v_offset	= 0x14,	.v_field 	= 288,
+		.h_offset	= 0x14,	.h_pixels 	= 720,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}
+};
+
+static struct saa7146_pci_extension_data mxb = {
+	.ext_priv = "Multimedia eXtension Board",
+	.ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+	{
+		.vendor    = PCI_VENDOR_ID_PHILIPS,
+		.device	   = PCI_DEVICE_ID_PHILIPS_SAA7146,
+		.subvendor = 0x0000,
+		.subdevice = 0x0000,
+		.driver_data = (unsigned long)&mxb,
+	}, {
+		.vendor	= 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+	.inputs		= MXB_INPUTS,
+	.capabilities	= V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO,
+	.stds		= &standard[0],
+	.num_stds	= sizeof(standard)/sizeof(struct saa7146_standard),
+	.std_callback	= &std_callback,
+};
+
+static struct saa7146_extension extension = {
+	.name		= "Multimedia eXtension Board",
+	.flags		= SAA7146_USE_I2C_IRQ,
+
+	.pci_tbl	= &pci_tbl[0],
+	.module		= THIS_MODULE,
+
+	.attach		= mxb_attach,
+	.detach		= mxb_detach,
+
+	.irq_mask	= 0,
+	.irq_func	= NULL,
+};
+
+static int __init mxb_init_module(void)
+{
+	if (saa7146_register_extension(&extension)) {
+		DEB_S("failed to register extension\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit mxb_cleanup_module(void)
+{
+	saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7164/Kconfig b/drivers/media/pci/saa7164/Kconfig
new file mode 100644
index 000000000000..a53db7d1c96e
--- /dev/null
+++ b/drivers/media/pci/saa7164/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_SAA7164
+	tristate "NXP SAA7164 support"
+	depends on DVB_CORE && VIDEO_DEV && PCI && I2C
+	select I2C_ALGOBIT
+	select FW_LOADER
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEOBUF_DVB
+	select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  This is a video4linux driver for NXP SAA7164 based
+	  TV cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called saa7164
+
diff --git a/drivers/media/pci/saa7164/Makefile b/drivers/media/pci/saa7164/Makefile
new file mode 100644
index 000000000000..ba0e33a1ee24
--- /dev/null
+++ b/drivers/media/pci/saa7164/Makefile
@@ -0,0 +1,12 @@
+saa7164-objs	:= saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
+			saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \
+			saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c
new file mode 100644
index 000000000000..eff7135cf0e8
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-api.c
@@ -0,0 +1,1524 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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/wait.h>
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i)
+{
+	int ret;
+
+	if (!(saa_debug & DBGLVL_CPU))
+		return 0;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	i->deviceinst = 0;
+	i->devicespec = 0;
+	i->mode = 0;
+	i->status = 0;
+
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad);
+
+	return ret;
+}
+
+int saa7164_api_collect_debug(struct saa7164_dev *dev)
+{
+	struct tmComResDebugGetData d;
+	u8 more = 255;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	while (more--) {
+
+		memset(&d, 0, sizeof(d));
+
+		ret = saa7164_cmd_send(dev, 0, GET_CUR,
+			GET_DEBUG_DATA_CONTROL, sizeof(d), &d);
+		if (ret != SAA_OK)
+			printk(KERN_ERR "%s() error, ret = 0x%x\n",
+				__func__, ret);
+
+		if (d.dwResult != SAA_OK)
+			break;
+
+		printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr,
+			d.ucDebugData);
+	}
+
+	return 0;
+}
+
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level)
+{
+	struct tmComResDebugSetLevel lvl;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level);
+
+	/* Retrieve current state */
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel);
+
+	lvl.dwDebugLevel = level;
+
+	/* set new state */
+	ret = saa7164_cmd_send(dev, 0, SET_CUR,
+		SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_vbi_format(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResProbeCommit fmt, rsp;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__,
+		port->nr, port->hwcfg.unitid);
+
+	fmt.bmHint = 0;
+	fmt.bFormatIndex = 1;
+	fmt.bFrameIndex = 1;
+
+	/* Probe, see if it can support this format */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+		SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret);
+
+	/* See of the format change was successful */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+		GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret);
+	} else {
+		/* Compare requested vs received, should be same */
+		if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) {
+			dprintk(DBGLVL_API, "SET/PROBE Verified\n");
+
+			/* Ask the device to select the negotiated format */
+			ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+				SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt);
+			if (ret != SAA_OK)
+				printk(KERN_ERR "%s() commit error, ret = 0x%x\n",
+					__func__, ret);
+
+			ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+				GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp);
+			if (ret != SAA_OK)
+				printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n",
+					__func__, ret);
+
+			if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) {
+				printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n",
+					__func__, ret);
+			} else
+				dprintk(DBGLVL_API, "SET/COMMIT Verified\n");
+
+			dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint);
+			dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n",
+				rsp.bFormatIndex);
+			dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n",
+				rsp.bFrameIndex);
+		} else
+			printk(KERN_ERR "%s() compare failed\n", __func__);
+	}
+
+	if (ret == SAA_OK)
+		dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr);
+
+	return ret;
+}
+
+int saa7164_api_set_gop_size(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoGopStructure gs;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	gs.ucRefFrameDist = port->encoder_params.refdist;
+	gs.ucGOPSize = port->encoder_params.gop_size;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_GOP_STRUCTURE_CONTROL,
+		sizeof(gs), &gs);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoBitRate vb;
+	struct tmComResEncAudioBitRate ab;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+		port->hwcfg.sourceid);
+
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+		port->encoder_profile = EU_PROFILE_PS_DVD;
+	else
+		port->encoder_profile = EU_PROFILE_TS_HQ;
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Resolution */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Establish video bitrates */
+	if (port->encoder_params.bitrate_mode ==
+		V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+		vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT;
+	else
+		vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK;
+	vb.dwVideoBitRate = port->encoder_params.bitrate;
+	vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_BIT_RATE_CONTROL,
+		sizeof(struct tmComResEncVideoBitRate),
+		&vb);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Establish audio bitrates */
+	ab.ucAudioBitRateMode = 0;
+	ab.dwAudioBitRate = 384000;
+	ab.dwAudioBitRatePeak = ab.dwAudioBitRate;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_AUDIO_BIT_RATE_CONTROL,
+		sizeof(struct tmComResEncAudioBitRate),
+		&ab);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+			ret);
+
+	saa7164_api_set_aspect_ratio(port);
+	saa7164_api_set_gop_size(port);
+
+	return ret;
+}
+
+int saa7164_api_get_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoBitRate v;
+	struct tmComResEncAudioBitRate a;
+	struct tmComResEncVideoInputAspectRatio ar;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+		port->hwcfg.sourceid);
+
+	port->encoder_profile = 0;
+	port->video_format = 0;
+	port->video_resolution = 0;
+	port->audio_format = 0;
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8),
+		&port->video_resolution);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Aspect Ratio */
+	ar.width = 0;
+	ar.height = 0;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_INPUT_ASPECT_CONTROL,
+		sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile);
+	dprintk(DBGLVL_ENC, "video_format    = %d\n", port->video_format);
+	dprintk(DBGLVL_ENC, "audio_format    = %d\n", port->audio_format);
+	dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution);
+	dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n",
+		v.ucVideoBitRateMode);
+	dprintk(DBGLVL_ENC, "v.dwVideoBitRate     = %d\n",
+		v.dwVideoBitRate);
+	dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n",
+		v.dwVideoBitRatePeak);
+	dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n",
+		a.ucAudioBitRateMode);
+	dprintk(DBGLVL_ENC, "a.dwVideoBitRate     = %d\n",
+		a.dwAudioBitRate);
+	dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n",
+		a.dwAudioBitRatePeak);
+	dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n",
+		ar.width, ar.height);
+
+	return ret;
+}
+
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoInputAspectRatio ar;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s(%d)\n", __func__,
+		port->encoder_params.ctl_aspect);
+
+	switch (port->encoder_params.ctl_aspect) {
+	case V4L2_MPEG_VIDEO_ASPECT_1x1:
+		ar.width = 1;
+		ar.height = 1;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_4x3:
+		ar.width = 4;
+		ar.height = 3;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_16x9:
+		ar.width = 16;
+		ar.height = 9;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_221x100:
+		ar.width = 221;
+		ar.height = 100;
+		break;
+	default:
+		BUG();
+	}
+
+	dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__,
+		port->encoder_params.ctl_aspect,
+		ar.width, ar.height);
+
+	/* Aspect Ratio */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_INPUT_ASPECT_CONTROL,
+		sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+	u16 val;
+
+	if (ctl == PU_BRIGHTNESS_CONTROL)
+		val = port->ctl_brightness;
+	else
+	if (ctl == PU_CONTRAST_CONTROL)
+		val = port->ctl_contrast;
+	else
+	if (ctl == PU_HUE_CONTROL)
+		val = port->ctl_hue;
+	else
+	if (ctl == PU_SATURATION_CONTROL)
+		val = port->ctl_saturation;
+	else
+	if (ctl == PU_SHARPNESS_CONTROL)
+		val = port->ctl_sharpness;
+	else
+		return -EINVAL;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n",
+		__func__, port->encunit.vsourceid, ctl, val);
+
+	ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR,
+		ctl, sizeof(u16), &val);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+	u16 val;
+
+	ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR,
+		ctl, sizeof(u16), &val);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n",
+		__func__, ctl, val);
+
+	if (ctl == PU_BRIGHTNESS_CONTROL)
+		port->ctl_brightness = val;
+	else
+	if (ctl == PU_CONTRAST_CONTROL)
+		port->ctl_contrast = val;
+	else
+	if (ctl == PU_HUE_CONTROL)
+		port->ctl_hue = val;
+	else
+	if (ctl == PU_SATURATION_CONTROL)
+		port->ctl_saturation = val;
+	else
+	if (ctl == PU_SHARPNESS_CONTROL)
+		port->ctl_sharpness = val;
+
+	return ret;
+}
+
+int saa7164_api_set_videomux(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 };
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n",
+		__func__, port->mux_input, inputs[port->mux_input - 1]);
+
+	/* Audio Mute */
+	ret = saa7164_api_audio_mute(port, 1);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Video Mux */
+	ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Audio Mux */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8),
+		&inputs[port->mux_input - 1]);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Audio UnMute */
+	ret = saa7164_api_audio_mute(port, 0);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute)
+{
+	struct saa7164_dev *dev = port->dev;
+	u8 v = mute;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		MUTE_CONTROL, sizeof(u8), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+/* 0 = silence, 0xff = full */
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level)
+{
+	struct saa7164_dev *dev = port->dev;
+	s16 v, min, max;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, level);
+
+	/* Obtain the min/max ranges */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN,
+		VOLUME_CONTROL, sizeof(u16), &min);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX,
+		VOLUME_CONTROL, sizeof(u16), &max);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+		level, min, max, v);
+
+	v = level;
+	if (v < min)
+		v = min;
+	if (v > max)
+		v = max;
+
+	/* Left */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Right */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		(0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+		level, min, max, v);
+
+	return ret;
+}
+
+int saa7164_api_set_audio_std(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResAudioDefaults lvl;
+	struct tmComResTunerStandard tvaudio;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	/* Establish default levels */
+	lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+	lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+	lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT;
+	lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT;
+	lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT;
+	lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT;
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults),
+		&lvl);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Manually select the appropriate TV audio standard */
+	if (port->encodernorm.id & V4L2_STD_NTSC) {
+		tvaudio.std = TU_STANDARD_NTSC_M;
+		tvaudio.country = 1;
+	} else {
+		tvaudio.std = TU_STANDARD_PAL_I;
+		tvaudio.country = 44;
+	}
+
+	ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+		TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n",
+			__func__, ret);
+	return ret;
+}
+
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResTunerStandardAuto p;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect);
+
+	/* Disable TV Audio autodetect if not already set (buggy) */
+	if (autodetect)
+		p.mode = TU_STANDARD_AUTO;
+	else
+		p.mode = TU_STANDARD_MANUAL;
+	ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+		TU_STANDARD_AUTO_CONTROL, sizeof(p), &p);
+	if (ret != SAA_OK)
+		printk(KERN_ERR
+			"%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n",
+			__func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_get_videomux(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_ENC, "%s() v_mux=%d\n",
+		__func__, port->mux_input);
+
+	return ret;
+}
+
+int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	u16 len = 0;
+	u8 buf[256];
+	int ret;
+	u8 mas;
+
+	dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__,
+		port->nr, port->type, val);
+
+	if (port->nr == 0)
+		mas = 0xd0;
+	else
+		mas = 0xe0;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0x00] = 0x04;
+	buf[0x01] = 0x00;
+	buf[0x02] = 0x00;
+	buf[0x03] = 0x00;
+
+	buf[0x04] = 0x04;
+	buf[0x05] = 0x00;
+	buf[0x06] = 0x00;
+	buf[0x07] = 0x00;
+
+	buf[0x08] = reg;
+	buf[0x09] = 0x26;
+	buf[0x0a] = mas;
+	buf[0x0b] = 0xb0;
+
+	buf[0x0c] = val;
+	buf[0x0d] = 0x00;
+	buf[0x0e] = 0x00;
+	buf[0x0f] = 0x00;
+
+	ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN,
+		EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+		return -EIO;
+	}
+
+	ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR,
+		EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+#if 0
+	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf, 16,
+		       false);
+#endif
+	return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* Disable the IF block AGC controls */
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	u8 agc_disable;
+
+	dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
+
+	if (std & V4L2_STD_NTSC) {
+		dprintk(DBGLVL_API, " NTSC\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_I) {
+		dprintk(DBGLVL_API, " PAL-I\n");
+		saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_M) {
+		dprintk(DBGLVL_API, " PAL-M\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_N) {
+		dprintk(DBGLVL_API, " PAL-N\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_Nc) {
+		dprintk(DBGLVL_API, " PAL-Nc\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_B) {
+		dprintk(DBGLVL_API, " PAL-B\n");
+		saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_DK) {
+		dprintk(DBGLVL_API, " PAL-DK\n");
+		saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_SECAM_L) {
+		dprintk(DBGLVL_API, " SECAM-L\n");
+		saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */
+		agc_disable = 0;
+	} else {
+		/* Unknown standard, assume DTV */
+		dprintk(DBGLVL_API, " Unknown (assuming DTV)\n");
+		/* Undefinded Video Standard */
+		saa7164_api_set_dif(port, 0x00, 0x80);
+		agc_disable = 1;
+	}
+
+	saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */
+	saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */
+	saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */
+	saa7164_api_set_dif(port, 0x04, 0x01); /* Active */
+	msleep(100);
+	saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
+	msleep(100);
+
+	return ret;
+}
+
+/* Ensure the dif is in the correct state for the operating mode
+ * (analog / dtv). We only configure the diff through the analog encoder
+ * so when we're in digital mode we need to find the appropriate encoder
+ * and use it to configure the DIF.
+ */
+int saa7164_api_initialize_dif(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *p = NULL;
+	int ret = -EINVAL;
+	u32 std = 0;
+
+	dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__,
+		port->nr, port->type);
+
+	if (port->type == SAA7164_MPEG_ENCODER) {
+		/* Pick any analog standard to init the diff.
+		 * we'll come back during encoder_init'
+		 * and set the correct standard if requried.
+		 */
+		std = V4L2_STD_NTSC;
+	} else
+	if (port->type == SAA7164_MPEG_DVB) {
+		if (port->nr == SAA7164_PORT_TS1)
+			p = &dev->ports[SAA7164_PORT_ENC1];
+		else
+			p = &dev->ports[SAA7164_PORT_ENC2];
+	} else
+	if (port->type == SAA7164_MPEG_VBI) {
+		std = V4L2_STD_NTSC;
+		if (port->nr == SAA7164_PORT_VBI1)
+			p = &dev->ports[SAA7164_PORT_ENC1];
+		else
+			p = &dev->ports[SAA7164_PORT_ENC2];
+	} else
+		BUG();
+
+	if (p)
+		ret = saa7164_api_configure_dif(p, std);
+
+	return ret;
+}
+
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n",
+		__func__, port->nr, port->hwcfg.unitid, mode);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
+		SAA_STATE_CONTROL, sizeof(mode), &mode);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n",
+			__func__, port->nr, port->hwcfg.unitid, ret);
+
+	return ret;
+}
+
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version)
+{
+	int ret;
+
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		GET_FW_VERSION_CONTROL, sizeof(u32), version);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
+{
+	u8 reg[] = { 0x0f, 0x00 };
+
+	if (buflen < 128)
+		return -ENOMEM;
+
+	/* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */
+	/* TODO: Pull the details from the boards struct */
+	return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg),
+		&reg[0], 128, buf);
+}
+
+int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
+	struct saa7164_port *port)
+{
+	struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
+
+	dprintk(DBGLVL_API, "    bFormatIndex  = 0x%x\n", fmt->bFormatIndex);
+	dprintk(DBGLVL_API, "    VideoStandard = 0x%x\n", fmt->VideoStandard);
+	dprintk(DBGLVL_API, "    StartLine     = %d\n", fmt->StartLine);
+	dprintk(DBGLVL_API, "    EndLine       = %d\n", fmt->EndLine);
+	dprintk(DBGLVL_API, "    FieldRate     = %d\n", fmt->FieldRate);
+	dprintk(DBGLVL_API, "    bNumLines     = %d\n", fmt->bNumLines);
+
+	/* Cache the hardware configuration in the port */
+
+	port->bufcounter = port->hwcfg.BARLocation;
+	port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+	port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+	port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+	port->bufptr32l = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+	port->bufptr32h = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	port->bufptr64 = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	dprintk(DBGLVL_API, "   = port->hwcfg.BARLocation = 0x%x\n",
+		port->hwcfg.BARLocation);
+
+	dprintk(DBGLVL_API, "   = VS_FORMAT_VBI (becomes dev->en[%d])\n",
+		port->nr);
+
+	return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
+	struct saa7164_port *port,
+	struct tmComResTSFormatDescrHeader *tsfmt)
+{
+	dprintk(DBGLVL_API, "    bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
+	dprintk(DBGLVL_API, "    bDataOffset  = 0x%x\n", tsfmt->bDataOffset);
+	dprintk(DBGLVL_API, "    bPacketLength= 0x%x\n", tsfmt->bPacketLength);
+	dprintk(DBGLVL_API, "    bStrideLength= 0x%x\n", tsfmt->bStrideLength);
+	dprintk(DBGLVL_API, "    bguid        = (....)\n");
+
+	/* Cache the hardware configuration in the port */
+
+	port->bufcounter = port->hwcfg.BARLocation;
+	port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+	port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+	port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+	port->bufptr32l = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+	port->bufptr32h = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	port->bufptr64 = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	dprintk(DBGLVL_API, "   = port->hwcfg.BARLocation = 0x%x\n",
+		port->hwcfg.BARLocation);
+
+	dprintk(DBGLVL_API, "   = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n",
+		port->nr);
+
+	return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
+	struct saa7164_port *port,
+	struct tmComResPSFormatDescrHeader *fmt)
+{
+	dprintk(DBGLVL_API, "    bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+	dprintk(DBGLVL_API, "    wPacketLength= 0x%x\n", fmt->wPacketLength);
+	dprintk(DBGLVL_API, "    wPackLength=   0x%x\n", fmt->wPackLength);
+	dprintk(DBGLVL_API, "    bPackDataType= 0x%x\n", fmt->bPackDataType);
+
+	/* Cache the hardware configuration in the port */
+	/* TODO: CHECK THIS in the port config */
+	port->bufcounter = port->hwcfg.BARLocation;
+	port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+	port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+	port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+	port->bufptr32l = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+	port->bufptr32h = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	port->bufptr64 = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	dprintk(DBGLVL_API, "   = port->hwcfg.BARLocation = 0x%x\n",
+		port->hwcfg.BARLocation);
+
+	dprintk(DBGLVL_API, "   = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n",
+		port->nr);
+
+	return 0;
+}
+
+int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
+{
+	struct saa7164_port *tsport = NULL;
+	struct saa7164_port *encport = NULL;
+	struct saa7164_port *vbiport = NULL;
+	u32 idx, next_offset;
+	int i;
+	struct tmComResDescrHeader *hdr, *t;
+	struct tmComResExtDevDescrHeader *exthdr;
+	struct tmComResPathDescrHeader *pathhdr;
+	struct tmComResAntTermDescrHeader *anttermhdr;
+	struct tmComResTunerDescrHeader *tunerunithdr;
+	struct tmComResDMATermDescrHeader *vcoutputtermhdr;
+	struct tmComResTSFormatDescrHeader *tsfmt;
+	struct tmComResPSFormatDescrHeader *psfmt;
+	struct tmComResSelDescrHeader *psel;
+	struct tmComResProcDescrHeader *pdh;
+	struct tmComResAFeatureDescrHeader *afd;
+	struct tmComResEncoderDescrHeader *edh;
+	struct tmComResVBIFormatDescrHeader *vbifmt;
+	u32 currpath = 0;
+
+	dprintk(DBGLVL_API,
+		"%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n",
+		__func__, len, (u32)sizeof(struct tmComResDescrHeader));
+
+	for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) {
+
+		hdr = (struct tmComResDescrHeader *)(buf + idx);
+
+		if (hdr->type != CS_INTERFACE)
+			return SAA_ERR_NOT_SUPPORTED;
+
+		dprintk(DBGLVL_API, "@ 0x%x =\n", idx);
+		switch (hdr->subtype) {
+		case GENERAL_REQUEST:
+			dprintk(DBGLVL_API, " GENERAL_REQUEST\n");
+			break;
+		case VC_TUNER_PATH:
+			dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
+			pathhdr = (struct tmComResPathDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, "  pathid = 0x%x\n",
+				pathhdr->pathid);
+			currpath = pathhdr->pathid;
+			break;
+		case VC_INPUT_TERMINAL:
+			dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
+			anttermhdr =
+				(struct tmComResAntTermDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, "  terminalid   = 0x%x\n",
+				anttermhdr->terminalid);
+			dprintk(DBGLVL_API, "  terminaltype = 0x%x\n",
+				anttermhdr->terminaltype);
+			switch (anttermhdr->terminaltype) {
+			case ITT_ANTENNA:
+				dprintk(DBGLVL_API, "   = ITT_ANTENNA\n");
+				break;
+			case LINE_CONNECTOR:
+				dprintk(DBGLVL_API, "   = LINE_CONNECTOR\n");
+				break;
+			case SPDIF_CONNECTOR:
+				dprintk(DBGLVL_API, "   = SPDIF_CONNECTOR\n");
+				break;
+			case COMPOSITE_CONNECTOR:
+				dprintk(DBGLVL_API,
+					"   = COMPOSITE_CONNECTOR\n");
+				break;
+			case SVIDEO_CONNECTOR:
+				dprintk(DBGLVL_API, "   = SVIDEO_CONNECTOR\n");
+				break;
+			case COMPONENT_CONNECTOR:
+				dprintk(DBGLVL_API,
+					"   = COMPONENT_CONNECTOR\n");
+				break;
+			case STANDARD_DMA:
+				dprintk(DBGLVL_API, "   = STANDARD_DMA\n");
+				break;
+			default:
+				dprintk(DBGLVL_API, "   = undefined (0x%x)\n",
+					anttermhdr->terminaltype);
+			}
+			dprintk(DBGLVL_API, "  assocterminal= 0x%x\n",
+				anttermhdr->assocterminal);
+			dprintk(DBGLVL_API, "  iterminal    = 0x%x\n",
+				anttermhdr->iterminal);
+			dprintk(DBGLVL_API, "  controlsize  = 0x%x\n",
+				anttermhdr->controlsize);
+			break;
+		case VC_OUTPUT_TERMINAL:
+			dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
+			vcoutputtermhdr =
+				(struct tmComResDMATermDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				vcoutputtermhdr->unitid);
+			dprintk(DBGLVL_API, "  terminaltype = 0x%x\n",
+				vcoutputtermhdr->terminaltype);
+			switch (vcoutputtermhdr->terminaltype) {
+			case ITT_ANTENNA:
+				dprintk(DBGLVL_API, "   = ITT_ANTENNA\n");
+				break;
+			case LINE_CONNECTOR:
+				dprintk(DBGLVL_API, "   = LINE_CONNECTOR\n");
+				break;
+			case SPDIF_CONNECTOR:
+				dprintk(DBGLVL_API, "   = SPDIF_CONNECTOR\n");
+				break;
+			case COMPOSITE_CONNECTOR:
+				dprintk(DBGLVL_API,
+					"   = COMPOSITE_CONNECTOR\n");
+				break;
+			case SVIDEO_CONNECTOR:
+				dprintk(DBGLVL_API, "   = SVIDEO_CONNECTOR\n");
+				break;
+			case COMPONENT_CONNECTOR:
+				dprintk(DBGLVL_API,
+					"   = COMPONENT_CONNECTOR\n");
+				break;
+			case STANDARD_DMA:
+				dprintk(DBGLVL_API, "   = STANDARD_DMA\n");
+				break;
+			default:
+				dprintk(DBGLVL_API, "   = undefined (0x%x)\n",
+					vcoutputtermhdr->terminaltype);
+			}
+			dprintk(DBGLVL_API, "  assocterminal= 0x%x\n",
+				vcoutputtermhdr->assocterminal);
+			dprintk(DBGLVL_API, "  sourceid     = 0x%x\n",
+				vcoutputtermhdr->sourceid);
+			dprintk(DBGLVL_API, "  iterminal    = 0x%x\n",
+				vcoutputtermhdr->iterminal);
+			dprintk(DBGLVL_API, "  BARLocation  = 0x%x\n",
+				vcoutputtermhdr->BARLocation);
+			dprintk(DBGLVL_API, "  flags        = 0x%x\n",
+				vcoutputtermhdr->flags);
+			dprintk(DBGLVL_API, "  interruptid  = 0x%x\n",
+				vcoutputtermhdr->interruptid);
+			dprintk(DBGLVL_API, "  buffercount  = 0x%x\n",
+				vcoutputtermhdr->buffercount);
+			dprintk(DBGLVL_API, "  metadatasize = 0x%x\n",
+				vcoutputtermhdr->metadatasize);
+			dprintk(DBGLVL_API, "  controlsize  = 0x%x\n",
+				vcoutputtermhdr->controlsize);
+			dprintk(DBGLVL_API, "  numformats   = 0x%x\n",
+				vcoutputtermhdr->numformats);
+
+			t = (struct tmComResDescrHeader *)
+				((struct tmComResDMATermDescrHeader *)(buf + idx));
+			next_offset = idx + (vcoutputtermhdr->len);
+			for (i = 0; i < vcoutputtermhdr->numformats; i++) {
+				t = (struct tmComResDescrHeader *)
+					(buf + next_offset);
+				switch (t->subtype) {
+				case VS_FORMAT_MPEG2TS:
+					tsfmt =
+					(struct tmComResTSFormatDescrHeader *)t;
+					if (currpath == 1)
+						tsport = &dev->ports[SAA7164_PORT_TS1];
+					else
+						tsport = &dev->ports[SAA7164_PORT_TS2];
+					memcpy(&tsport->hwcfg, vcoutputtermhdr,
+						sizeof(*vcoutputtermhdr));
+					saa7164_api_configure_port_mpeg2ts(dev,
+						tsport, tsfmt);
+					break;
+				case VS_FORMAT_MPEG2PS:
+					psfmt =
+					(struct tmComResPSFormatDescrHeader *)t;
+					if (currpath == 1)
+						encport = &dev->ports[SAA7164_PORT_ENC1];
+					else
+						encport = &dev->ports[SAA7164_PORT_ENC2];
+					memcpy(&encport->hwcfg, vcoutputtermhdr,
+						sizeof(*vcoutputtermhdr));
+					saa7164_api_configure_port_mpeg2ps(dev,
+						encport, psfmt);
+					break;
+				case VS_FORMAT_VBI:
+					vbifmt =
+					(struct tmComResVBIFormatDescrHeader *)t;
+					if (currpath == 1)
+						vbiport = &dev->ports[SAA7164_PORT_VBI1];
+					else
+						vbiport = &dev->ports[SAA7164_PORT_VBI2];
+					memcpy(&vbiport->hwcfg, vcoutputtermhdr,
+						sizeof(*vcoutputtermhdr));
+					memcpy(&vbiport->vbi_fmt_ntsc, vbifmt,
+						sizeof(*vbifmt));
+					saa7164_api_configure_port_vbi(dev,
+						vbiport);
+					break;
+				case VS_FORMAT_RDS:
+					dprintk(DBGLVL_API,
+						"   = VS_FORMAT_RDS\n");
+					break;
+				case VS_FORMAT_UNCOMPRESSED:
+					dprintk(DBGLVL_API,
+					"   = VS_FORMAT_UNCOMPRESSED\n");
+					break;
+				case VS_FORMAT_TYPE:
+					dprintk(DBGLVL_API,
+						"   = VS_FORMAT_TYPE\n");
+					break;
+				default:
+					dprintk(DBGLVL_API,
+						"   = undefined (0x%x)\n",
+						t->subtype);
+				}
+				next_offset += t->len;
+			}
+
+			break;
+		case TUNER_UNIT:
+			dprintk(DBGLVL_API, " TUNER_UNIT\n");
+			tunerunithdr =
+				(struct tmComResTunerDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				tunerunithdr->unitid);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				tunerunithdr->sourceid);
+			dprintk(DBGLVL_API, "  iunit = 0x%x\n",
+				tunerunithdr->iunit);
+			dprintk(DBGLVL_API, "  tuningstandards = 0x%x\n",
+				tunerunithdr->tuningstandards);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				tunerunithdr->controlsize);
+			dprintk(DBGLVL_API, "  controls = 0x%x\n",
+				tunerunithdr->controls);
+
+			if (tunerunithdr->unitid == tunerunithdr->iunit) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->tunerunit, tunerunithdr,
+					sizeof(struct tmComResTunerDescrHeader));
+				dprintk(DBGLVL_API,
+					"  (becomes dev->enc[%d] tuner)\n",
+					encport->nr);
+			}
+			break;
+		case VC_SELECTOR_UNIT:
+			psel = (struct tmComResSelDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				psel->unitid);
+			dprintk(DBGLVL_API, "  nrinpins = 0x%x\n",
+				psel->nrinpins);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				psel->sourceid);
+			break;
+		case VC_PROCESSING_UNIT:
+			pdh = (struct tmComResProcDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				pdh->unitid);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				pdh->sourceid);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				pdh->controlsize);
+			if (pdh->controlsize == 0x04) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->vidproc, pdh,
+					sizeof(struct tmComResProcDescrHeader));
+				dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n",
+					encport->nr);
+			}
+			break;
+		case FEATURE_UNIT:
+			afd = (struct tmComResAFeatureDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, " FEATURE_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				afd->unitid);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				afd->sourceid);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				afd->controlsize);
+			if (currpath == 1)
+				encport = &dev->ports[SAA7164_PORT_ENC1];
+			else
+				encport = &dev->ports[SAA7164_PORT_ENC2];
+			memcpy(&encport->audfeat, afd,
+				sizeof(struct tmComResAFeatureDescrHeader));
+			dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n",
+				encport->nr);
+			break;
+		case ENCODER_UNIT:
+			edh = (struct tmComResEncoderDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, " ENCODER_UNIT\n");
+			dprintk(DBGLVL_API, "  subtype = 0x%x\n", edh->subtype);
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n", edh->unitid);
+			dprintk(DBGLVL_API, "  vsourceid = 0x%x\n",
+			edh->vsourceid);
+			dprintk(DBGLVL_API, "  asourceid = 0x%x\n",
+				edh->asourceid);
+			dprintk(DBGLVL_API, "  iunit = 0x%x\n", edh->iunit);
+			if (edh->iunit == edh->unitid) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->encunit, edh,
+					sizeof(struct tmComResEncoderDescrHeader));
+				dprintk(DBGLVL_API,
+					"  (becomes dev->enc[%d])\n",
+					encport->nr);
+			}
+			break;
+		case EXTENSION_UNIT:
+			dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
+			exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx);
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				exthdr->unitid);
+			dprintk(DBGLVL_API, "  deviceid = 0x%x\n",
+				exthdr->deviceid);
+			dprintk(DBGLVL_API, "  devicetype = 0x%x\n",
+				exthdr->devicetype);
+			if (exthdr->devicetype & 0x1)
+				dprintk(DBGLVL_API, "   = Decoder Device\n");
+			if (exthdr->devicetype & 0x2)
+				dprintk(DBGLVL_API, "   = GPIO Source\n");
+			if (exthdr->devicetype & 0x4)
+				dprintk(DBGLVL_API, "   = Video Decoder\n");
+			if (exthdr->devicetype & 0x8)
+				dprintk(DBGLVL_API, "   = Audio Decoder\n");
+			if (exthdr->devicetype & 0x20)
+				dprintk(DBGLVL_API, "   = Crossbar\n");
+			if (exthdr->devicetype & 0x40)
+				dprintk(DBGLVL_API, "   = Tuner\n");
+			if (exthdr->devicetype & 0x80)
+				dprintk(DBGLVL_API, "   = IF PLL\n");
+			if (exthdr->devicetype & 0x100)
+				dprintk(DBGLVL_API, "   = Demodulator\n");
+			if (exthdr->devicetype & 0x200)
+				dprintk(DBGLVL_API, "   = RDS Decoder\n");
+			if (exthdr->devicetype & 0x400)
+				dprintk(DBGLVL_API, "   = Encoder\n");
+			if (exthdr->devicetype & 0x800)
+				dprintk(DBGLVL_API, "   = IR Decoder\n");
+			if (exthdr->devicetype & 0x1000)
+				dprintk(DBGLVL_API, "   = EEPROM\n");
+			if (exthdr->devicetype & 0x2000)
+				dprintk(DBGLVL_API,
+					"   = VBI Decoder\n");
+			if (exthdr->devicetype & 0x10000)
+				dprintk(DBGLVL_API,
+					"   = Streaming Device\n");
+			if (exthdr->devicetype & 0x20000)
+				dprintk(DBGLVL_API,
+					"   = DRM Device\n");
+			if (exthdr->devicetype & 0x40000000)
+				dprintk(DBGLVL_API,
+					"   = Generic Device\n");
+			if (exthdr->devicetype & 0x80000000)
+				dprintk(DBGLVL_API,
+					"   = Config Space Device\n");
+			dprintk(DBGLVL_API, "  numgpiopins = 0x%x\n",
+				exthdr->numgpiopins);
+			dprintk(DBGLVL_API, "  numgpiogroups = 0x%x\n",
+				exthdr->numgpiogroups);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				exthdr->controlsize);
+			if (exthdr->devicetype & 0x80) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->ifunit, exthdr,
+					sizeof(struct tmComResExtDevDescrHeader));
+				dprintk(DBGLVL_API,
+					"  (becomes dev->enc[%d])\n",
+					encport->nr);
+			}
+			break;
+		case PVC_INFRARED_UNIT:
+			dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
+			break;
+		case DRM_UNIT:
+			dprintk(DBGLVL_API, " DRM_UNIT\n");
+			break;
+		default:
+			dprintk(DBGLVL_API, "default %d\n", hdr->subtype);
+		}
+
+		dprintk(DBGLVL_API, " 1.%x\n", hdr->len);
+		dprintk(DBGLVL_API, " 2.%x\n", hdr->type);
+		dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype);
+		dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid);
+
+		idx += hdr->len;
+	}
+
+	return 0;
+}
+
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
+{
+	int ret;
+	u32 buflen = 0;
+	u8 *buf;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	/* Get the total descriptor length */
+	ret = saa7164_cmd_send(dev, 0, GET_LEN,
+		GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n",
+		__func__, buflen);
+
+	/* Allocate enough storage for all of the descs */
+	buf = kzalloc(buflen, GFP_KERNEL);
+	if (!buf)
+		return SAA_ERR_NO_RESOURCES;
+
+	/* Retrieve them */
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		GET_DESCRIPTORS_CONTROL, buflen, buf);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+		goto out;
+	}
+
+	if (saa_debug & DBGLVL_API)
+		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf,
+			       buflen & ~15, false);
+
+	saa7164_api_dump_subdevs(dev, buf, buflen);
+
+out:
+	kfree(buf);
+	return ret;
+}
+
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+	u32 datalen, u8 *data)
+{
+	struct saa7164_dev *dev = bus->dev;
+	u16 len = 0;
+	int unitid;
+	u8 buf[256];
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	if (reglen > 4)
+		return -EIO;
+
+	/* Prepare the send buffer */
+	/* Bytes 00-03 source register length
+	 *       04-07 source bytes to read
+	 *       08... register address
+	 */
+	memset(buf, 0, sizeof(buf));
+	memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen);
+	*((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+	*((u32 *)(buf + 1 * sizeof(u32))) = datalen;
+
+	unitid = saa7164_i2caddr_to_unitid(bus, addr);
+	if (unitid < 0) {
+		printk(KERN_ERR
+			"%s() error, cannot translate regaddr 0x%x to unitid\n",
+			__func__, addr);
+		return -EIO;
+	}
+
+	ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+		EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+		return -EIO;
+	}
+
+	dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+	if (saa_debug & DBGLVL_I2C)
+		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf,
+			       32, false);
+
+	ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR,
+		EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+	else {
+		if (saa_debug & DBGLVL_I2C)
+			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+				       buf, sizeof(buf), false);
+		memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen);
+	}
+
+	return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* For a given 8 bit i2c address device, write the buffer */
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
+	u8 *data)
+{
+	struct saa7164_dev *dev = bus->dev;
+	u16 len = 0;
+	int unitid;
+	int reglen;
+	u8 buf[256];
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	if ((datalen == 0) || (datalen > 232))
+		return -EIO;
+
+	memset(buf, 0, sizeof(buf));
+
+	unitid = saa7164_i2caddr_to_unitid(bus, addr);
+	if (unitid < 0) {
+		printk(KERN_ERR
+			"%s() error, cannot translate regaddr 0x%x to unitid\n",
+			__func__, addr);
+		return -EIO;
+	}
+
+	reglen = saa7164_i2caddr_to_reglen(bus, addr);
+	if (reglen < 0) {
+		printk(KERN_ERR
+			"%s() error, cannot translate regaddr to reglen\n",
+			__func__);
+		return -EIO;
+	}
+
+	ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+		EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+		return -EIO;
+	}
+
+	dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+	/* Prepare the send buffer */
+	/* Bytes 00-03 dest register length
+	 *       04-07 dest bytes to write
+	 *       08... register address
+	 */
+	*((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+	*((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen;
+	memcpy((buf + 2 * sizeof(u32)), data, datalen);
+
+	if (saa_debug & DBGLVL_I2C)
+		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+			       buf, sizeof(buf), false);
+
+	ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR,
+		EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+
+	return ret == SAA_OK ? 0 : -EIO;
+}
+
+int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
+	u8 pin, u8 state)
+{
+	int ret;
+	struct tmComResGPIO t;
+
+	dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
+		__func__, unitid, pin, state);
+
+	if ((pin > 7) || (state > 2))
+		return SAA_ERR_BAD_PARAMETER;
+
+	t.pin = pin;
+	t.state = state;
+
+	ret = saa7164_cmd_send(dev, unitid, SET_CUR,
+		EXU_GPIO_CONTROL, sizeof(t), &t);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n",
+			__func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid,
+	u8 pin)
+{
+	return saa7164_api_modify_gpio(dev, unitid, pin, 1);
+}
+
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid,
+	u8 pin)
+{
+	return saa7164_api_modify_gpio(dev, unitid, pin, 0);
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
new file mode 100644
index 000000000000..66696fa8341d
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
@@ -0,0 +1,322 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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/slab.h>
+
+#include "saa7164.h"
+
+/* The PCI address space for buffer handling looks like this:
+ *
+ * +-u32 wide-------------+
+ * |                      +
+ * +-u64 wide------------------------------------+
+ * +                                             +
+ * +----------------------+
+ * | CurrentBufferPtr     + Pointer to current PCI buffer >-+
+ * +----------------------+                                 |
+ * | Unused               +                                 |
+ * +----------------------+                                 |
+ * | Pitch                + = 188 (bytes)                   |
+ * +----------------------+                                 |
+ * | PCI buffer size      + = pitch * number of lines (312) |
+ * +----------------------+                                 |
+ * |0| Buf0 Write Offset  +                                 |
+ * +----------------------+                                 v
+ * |1| Buf1 Write Offset  +                                 |
+ * +----------------------+                                 |
+ * |2| Buf2 Write Offset  +                                 |
+ * +----------------------+                                 |
+ * |3| Buf3 Write Offset  +                                 |
+ * +----------------------+                                 |
+ * ... More write offsets                                   |
+ * +---------------------------------------------+          |
+ * +0| set of ptrs to PCI pagetables             +          |
+ * +---------------------------------------------+          |
+ * +1| set of ptrs to PCI pagetables             + <--------+
+ * +---------------------------------------------+
+ * +2| set of ptrs to PCI pagetables             +
+ * +---------------------------------------------+
+ * +3| set of ptrs to PCI pagetables             + >--+
+ * +---------------------------------------------+    |
+ * ... More buffer pointers                           |  +----------------+
+ *						    +->| pt[0] TS data  |
+ *						    |  +----------------+
+ *						    |
+ *						    |  +----------------+
+ *						    +->| pt[1] TS data  |
+ *						    |  +----------------+
+ *						    | etc
+ */
+
+void saa7164_buffer_display(struct saa7164_buffer *buf)
+{
+	struct saa7164_dev *dev = buf->port->dev;
+	int i;
+
+	dprintk(DBGLVL_BUF, "%s()   buffer @ 0x%p nr=%d\n",
+		__func__, buf, buf->idx);
+	dprintk(DBGLVL_BUF, "  pci_cpu @ 0x%p    dma @ 0x%08llx len = 0x%x\n",
+		buf->cpu, (long long)buf->dma, buf->pci_size);
+	dprintk(DBGLVL_BUF, "   pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n",
+		buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size);
+
+	/* Format the Page Table Entries to point into the data buffer */
+	for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
+
+		dprintk(DBGLVL_BUF, "    pt[%02d] = 0x%p -> 0x%llx\n",
+			i, buf->pt_cpu, (u64)*(buf->pt_cpu));
+
+	}
+}
+/* Allocate a new buffer structure and associated PCI space in bytes.
+ * len must be a multiple of sizeof(u64)
+ */
+struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port,
+	u32 len)
+{
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	struct saa7164_buffer *buf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	int i;
+
+	if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) {
+		log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__);
+		goto ret;
+	}
+
+	buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL);
+	if (!buf) {
+		log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__);
+		goto ret;
+	}
+
+	buf->idx = -1;
+	buf->port = port;
+	buf->flags = SAA7164_BUFFER_FREE;
+	buf->pos = 0;
+	buf->actual_size = params->pitch * params->numberoflines;
+	buf->crc = 0;
+	/* TODO: arg len is being ignored */
+	buf->pci_size = SAA7164_PT_ENTRIES * 0x1000;
+	buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000;
+
+	/* Allocate contiguous memory */
+	buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size,
+		&buf->dma);
+	if (!buf->cpu)
+		goto fail1;
+
+	buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size,
+		&buf->pt_dma);
+	if (!buf->pt_cpu)
+		goto fail2;
+
+	/* init the buffers to a known pattern, easier during debugging */
+	memset_io(buf->cpu, 0xff, buf->pci_size);
+	buf->crc = crc32(0, buf->cpu, buf->actual_size);
+	memset_io(buf->pt_cpu, 0xff, buf->pt_size);
+
+	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p (%d pageptrs)\n",
+		__func__, buf, params->numpagetables);
+	dprintk(DBGLVL_BUF, "  pci_cpu @ 0x%p    dma @ 0x%08lx len = 0x%x\n",
+		buf->cpu, (long)buf->dma, buf->pci_size);
+	dprintk(DBGLVL_BUF, "   pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n",
+		buf->pt_cpu, (long)buf->pt_dma, buf->pt_size);
+
+	/* Format the Page Table Entries to point into the data buffer */
+	for (i = 0 ; i < params->numpagetables; i++) {
+
+		*(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */
+		dprintk(DBGLVL_BUF, "    pt[%02d] = 0x%p -> 0x%llx\n",
+			i, buf->pt_cpu, (u64)*(buf->pt_cpu));
+
+	}
+
+	goto ret;
+
+fail2:
+	pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma);
+fail1:
+	kfree(buf);
+
+	buf = NULL;
+ret:
+	return buf;
+}
+
+int saa7164_buffer_dealloc(struct saa7164_buffer *buf)
+{
+	struct saa7164_dev *dev;
+
+	if (!buf || !buf->port)
+		return SAA_ERR_BAD_PARAMETER;
+	dev = buf->port->dev;
+
+	dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n",
+		__func__, buf);
+
+	if (buf->flags != SAA7164_BUFFER_FREE)
+		log_warn(" freeing a non-free buffer\n");
+
+	pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma);
+	pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma);
+
+	kfree(buf);
+
+	return SAA_OK;
+}
+
+int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	if ((i < 0) || (i >= port->hwcfg.buffercount))
+		return -EINVAL;
+
+	dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+	saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+
+	return 0;
+}
+
+/* Write a buffer into the hardware */
+int saa7164_buffer_activate(struct saa7164_buffer *buf, int i)
+{
+	struct saa7164_port *port = buf->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if ((i < 0) || (i >= port->hwcfg.buffercount))
+		return -EINVAL;
+
+	dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+	buf->idx = i; /* Note of which buffer list index position we occupy */
+	buf->flags = SAA7164_BUFFER_BUSY;
+	buf->pos = 0;
+
+	/* TODO: Review this in light of 32v64 assignments */
+	saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+	saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma);
+	saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0);
+
+	dprintk(DBGLVL_BUF, "   buf[%d] offset 0x%llx (0x%x) "
+		"buf 0x%llx/%llx (0x%x/%x) nr=%d\n",
+		buf->idx,
+		(u64)port->bufoffset + (i * sizeof(u32)),
+		saa7164_readl(port->bufoffset + (sizeof(u32) * i)),
+		(u64)port->bufptr32h + ((sizeof(u32) * 2) * i),
+		(u64)port->bufptr32l + ((sizeof(u32) * 2) * i),
+		saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)),
+		saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)),
+		buf->idx);
+
+	return 0;
+}
+
+int saa7164_buffer_cfg_port(struct saa7164_port *port)
+{
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct list_head *c, *n;
+	int i = 0;
+
+	dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr);
+
+	saa7164_writel(port->bufcounter, 0);
+	saa7164_writel(port->pitch, params->pitch);
+	saa7164_writel(port->bufsize, params->pitch * params->numberoflines);
+
+	dprintk(DBGLVL_BUF, " configured:\n");
+	dprintk(DBGLVL_BUF, "   lmmio       0x%p\n", dev->lmmio);
+	dprintk(DBGLVL_BUF, "   bufcounter  0x%x = 0x%x\n", port->bufcounter,
+		saa7164_readl(port->bufcounter));
+
+	dprintk(DBGLVL_BUF, "   pitch       0x%x = %d\n", port->pitch,
+		saa7164_readl(port->pitch));
+
+	dprintk(DBGLVL_BUF, "   bufsize     0x%x = %d\n", port->bufsize,
+		saa7164_readl(port->bufsize));
+
+	dprintk(DBGLVL_BUF, "   buffercount = %d\n", port->hwcfg.buffercount);
+	dprintk(DBGLVL_BUF, "   bufoffset = 0x%x\n", port->bufoffset);
+	dprintk(DBGLVL_BUF, "   bufptr32h = 0x%x\n", port->bufptr32h);
+	dprintk(DBGLVL_BUF, "   bufptr32l = 0x%x\n", port->bufptr32l);
+
+	/* Poke the buffers and offsets into PCI space */
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+
+		if (buf->flags != SAA7164_BUFFER_FREE)
+			BUG();
+
+		/* Place the buffer in the h/w queue */
+		saa7164_buffer_activate(buf, i);
+
+		/* Don't exceed the device maximum # bufs */
+		if (i++ > port->hwcfg.buffercount)
+			BUG();
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev,
+	u32 len)
+{
+	struct saa7164_user_buffer *buf;
+
+	buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->data = kzalloc(len, GFP_KERNEL);
+
+	if (!buf->data) {
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->actual_size = len;
+	buf->pos = 0;
+	buf->crc = 0;
+
+	dprintk(DBGLVL_BUF, "%s()   allocated user buffer @ 0x%p\n",
+		__func__, buf);
+
+	return buf;
+}
+
+void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf)
+{
+	if (!buf)
+		return;
+
+	kfree(buf->data);
+	buf->data = NULL;
+
+	kfree(buf);
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
new file mode 100644
index 000000000000..a7f58a998752
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -0,0 +1,475 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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 "saa7164.h"
+
+/* The message bus to/from the firmware is a ring buffer in PCI address
+ * space. Establish the defaults.
+ */
+int saa7164_bus_setup(struct saa7164_dev *dev)
+{
+	struct tmComResBusInfo *b	= &dev->bus;
+
+	mutex_init(&b->lock);
+
+	b->Type			= TYPE_BUS_PCIe;
+	b->m_wMaxReqSize	= SAA_DEVICE_MAXREQUESTSIZE;
+
+	b->m_pdwSetRing		= (u8 *)(dev->bmmio +
+		((u32)dev->busdesc.CommandRing));
+
+	b->m_dwSizeSetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
+
+	b->m_pdwGetRing		= (u8 *)(dev->bmmio +
+		((u32)dev->busdesc.ResponseRing));
+
+	b->m_dwSizeGetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
+
+	b->m_dwSetWritePos	= ((u32)dev->intfdesc.BARLocation) +
+		(2 * sizeof(u64));
+	b->m_dwSetReadPos	= b->m_dwSetWritePos + (1 * sizeof(u32));
+
+	b->m_dwGetWritePos	= b->m_dwSetWritePos + (2 * sizeof(u32));
+	b->m_dwGetReadPos	= b->m_dwSetWritePos + (3 * sizeof(u32));
+
+	return 0;
+}
+
+void saa7164_bus_dump(struct saa7164_dev *dev)
+{
+	struct tmComResBusInfo *b = &dev->bus;
+
+	dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
+	dprintk(DBGLVL_BUS, " .type             = %d\n", b->Type);
+	dprintk(DBGLVL_BUS, " .dev->bmmio       = 0x%p\n", dev->bmmio);
+	dprintk(DBGLVL_BUS, " .m_wMaxReqSize    = 0x%x\n", b->m_wMaxReqSize);
+	dprintk(DBGLVL_BUS, " .m_pdwSetRing     = 0x%p\n", b->m_pdwSetRing);
+	dprintk(DBGLVL_BUS, " .m_dwSizeSetRing  = 0x%x\n", b->m_dwSizeSetRing);
+	dprintk(DBGLVL_BUS, " .m_pdwGetRing     = 0x%p\n", b->m_pdwGetRing);
+	dprintk(DBGLVL_BUS, " .m_dwSizeGetRing  = 0x%x\n", b->m_dwSizeGetRing);
+
+	dprintk(DBGLVL_BUS, " .m_dwSetReadPos   = 0x%x (0x%08x)\n",
+		b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+	dprintk(DBGLVL_BUS, " .m_dwSetWritePos  = 0x%x (0x%08x)\n",
+		b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+	dprintk(DBGLVL_BUS, " .m_dwGetReadPos   = 0x%x (0x%08x)\n",
+		b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+	dprintk(DBGLVL_BUS, " .m_dwGetWritePos  = 0x%x (0x%08x)\n",
+		b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+
+}
+
+/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
+void saa7164_bus_verify(struct saa7164_dev *dev)
+{
+	struct tmComResBusInfo *b = &dev->bus;
+	int bug = 0;
+
+	if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
+		bug++;
+
+	if (bug) {
+		saa_debug = 0xffff; /* Ensure we get the bus dump */
+		saa7164_bus_dump(dev);
+		saa_debug = 1024; /* Ensure we get the bus dump */
+		BUG();
+	}
+}
+
+void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo* m,
+	void *buf)
+{
+	dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
+	dprintk(DBGLVL_BUS, " .id               = %d\n",   m->id);
+	dprintk(DBGLVL_BUS, " .flags            = 0x%x\n", m->flags);
+	dprintk(DBGLVL_BUS, " .size             = 0x%x\n", m->size);
+	dprintk(DBGLVL_BUS, " .command          = 0x%x\n", m->command);
+	dprintk(DBGLVL_BUS, " .controlselector  = 0x%x\n", m->controlselector);
+	dprintk(DBGLVL_BUS, " .seqno            = %d\n",   m->seqno);
+	if (buf)
+		dprintk(DBGLVL_BUS, " .buffer (ignored)\n");
+}
+
+/*
+ * Places a command or a response on the bus. The implementation does not
+ * know if it is a command or a response it just places the data on the
+ * bus depending on the bus information given in the struct tmComResBusInfo
+ * structure. If the command or response does not fit into the bus ring
+ * buffer it will be refused.
+ *
+ * Return Value:
+ *  SAA_OK     The function executed successfully.
+ *  < 0        One or more members are not initialized.
+ */
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
+	void *buf)
+{
+	struct tmComResBusInfo *bus = &dev->bus;
+	u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
+	u32 new_swp, space_rem;
+	int ret = SAA_ERR_BAD_PARAMETER;
+
+	if (!msg) {
+		printk(KERN_ERR "%s() !msg\n", __func__);
+		return SAA_ERR_BAD_PARAMETER;
+	}
+
+	dprintk(DBGLVL_BUS, "%s()\n", __func__);
+
+	saa7164_bus_verify(dev);
+
+	msg->size = cpu_to_le16(msg->size);
+	msg->command = cpu_to_le32(msg->command);
+	msg->controlselector = cpu_to_le16(msg->controlselector);
+
+	if (msg->size > dev->bus.m_wMaxReqSize) {
+		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
+			__func__);
+		return SAA_ERR_BAD_PARAMETER;
+	}
+
+	if ((msg->size > 0) && (buf == NULL)) {
+		printk(KERN_ERR "%s() Missing message buffer\n", __func__);
+		return SAA_ERR_BAD_PARAMETER;
+	}
+
+	/* Lock the bus from any other access */
+	mutex_lock(&bus->lock);
+
+	bytes_to_write = sizeof(*msg) + msg->size;
+	free_write_space = 0;
+	timeout = SAA_BUS_TIMEOUT;
+	curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+	curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos));
+
+	/* Deal with ring wrapping issues */
+	if (curr_srp > curr_swp)
+		/* Deal with the wrapped ring */
+		free_write_space = curr_srp - curr_swp;
+	else
+		/* The ring has not wrapped yet */
+		free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
+
+	dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
+		bytes_to_write);
+
+	dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
+		free_write_space);
+
+	dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
+	dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
+
+	/* Process the msg and write the content onto the bus */
+	while (bytes_to_write >= free_write_space) {
+
+		if (timeout-- == 0) {
+			printk(KERN_ERR "%s() bus timeout\n", __func__);
+			ret = SAA_ERR_NO_RESOURCES;
+			goto out;
+		}
+
+		/* TODO: Review this delay, efficient? */
+		/* Wait, allowing the hardware fetch time */
+		mdelay(1);
+
+		/* Check the space usage again */
+		curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+
+		/* Deal with ring wrapping issues */
+		if (curr_srp > curr_swp)
+			/* Deal with the wrapped ring */
+			free_write_space = curr_srp - curr_swp;
+		else
+			/* Read didn't wrap around the buffer */
+			free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
+				curr_swp;
+
+	}
+
+	/* Calculate the new write position */
+	new_swp = curr_swp + bytes_to_write;
+
+	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
+	dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
+		bus->m_dwSizeSetRing);
+
+	/* Mental Note: line 462 tmmhComResBusPCIe.cpp */
+
+	/* Check if we're going to wrap again */
+	if (new_swp > bus->m_dwSizeSetRing) {
+
+		/* Ring wraps */
+		new_swp -= bus->m_dwSizeSetRing;
+
+		space_rem = bus->m_dwSizeSetRing - curr_swp;
+
+		dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__,
+			space_rem);
+
+		dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__,
+			(u32)sizeof(*msg));
+
+		if (space_rem < sizeof(*msg)) {
+			dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
+
+			/* Split the msg into pieces as the ring wraps */
+			memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem);
+			memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem,
+				sizeof(*msg) - space_rem);
+
+			memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
+				buf, msg->size);
+
+		} else if (space_rem == sizeof(*msg)) {
+			dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
+
+			/* Additional data at the beginning of the ring */
+			memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+			memcpy(bus->m_pdwSetRing, buf, msg->size);
+
+		} else {
+			/* Additional data wraps around the ring */
+			memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+			if (msg->size > 0) {
+				memcpy(bus->m_pdwSetRing + curr_swp +
+					sizeof(*msg), buf, space_rem -
+					sizeof(*msg));
+				memcpy(bus->m_pdwSetRing, (u8 *)buf +
+					space_rem - sizeof(*msg),
+					bytes_to_write - space_rem);
+			}
+
+		}
+
+	} /* (new_swp > bus->m_dwSizeSetRing) */
+	else {
+		dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
+
+		/* The ring buffer doesn't wrap, two simple copies */
+		memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+		memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
+			msg->size);
+	}
+
+	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
+
+	/* Update the bus write position */
+	saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp));
+	ret = SAA_OK;
+
+out:
+	saa7164_bus_dump(dev);
+	mutex_unlock(&bus->lock);
+	saa7164_bus_verify(dev);
+	return ret;
+}
+
+/*
+ * Receive a command or a response from the bus. The implementation does not
+ * know if it is a command or a response it simply dequeues the data,
+ * depending on the bus information given in the struct tmComResBusInfo
+ * structure.
+ *
+ * Return Value:
+ *  0          The function executed successfully.
+ *  < 0        One or more members are not initialized.
+ */
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
+	void *buf, int peekonly)
+{
+	struct tmComResBusInfo *bus = &dev->bus;
+	u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
+		new_grp, buf_size, space_rem;
+	struct tmComResInfo msg_tmp;
+	int ret = SAA_ERR_BAD_PARAMETER;
+
+	saa7164_bus_verify(dev);
+
+	if (msg == NULL)
+		return ret;
+
+	if (msg->size > dev->bus.m_wMaxReqSize) {
+		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
+			__func__);
+		return ret;
+	}
+
+	if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
+		printk(KERN_ERR
+			"%s() Missing msg buf, size should be %d bytes\n",
+			__func__, msg->size);
+		return ret;
+	}
+
+	mutex_lock(&bus->lock);
+
+	/* Peek the bus to see if a msg exists, if it's not what we're expecting
+	 * then return cleanly else read the message from the bus.
+	 */
+	curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos));
+	curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos));
+
+	if (curr_gwp == curr_grp) {
+		ret = SAA_ERR_EMPTY;
+		goto out;
+	}
+
+	bytes_to_read = sizeof(*msg);
+
+	/* Calculate write distance to current read position */
+	write_distance = 0;
+	if (curr_gwp >= curr_grp)
+		/* Write doesn't wrap around the ring */
+		write_distance = curr_gwp - curr_grp;
+	else
+		/* Write wraps around the ring */
+		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
+
+	if (bytes_to_read > write_distance) {
+		printk(KERN_ERR "%s() No message/response found\n", __func__);
+		ret = SAA_ERR_INVALID_COMMAND;
+		goto out;
+	}
+
+	/* Calculate the new read position */
+	new_grp = curr_grp + bytes_to_read;
+	if (new_grp > bus->m_dwSizeGetRing) {
+
+		/* Ring wraps */
+		new_grp -= bus->m_dwSizeGetRing;
+		space_rem = bus->m_dwSizeGetRing - curr_grp;
+
+		memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
+		memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
+			bytes_to_read - space_rem);
+
+	} else {
+		/* No wrapping */
+		memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
+	}
+
+	/* No need to update the read positions, because this was a peek */
+	/* If the caller specifically want to peek, return */
+	if (peekonly) {
+		memcpy(msg, &msg_tmp, sizeof(*msg));
+		goto peekout;
+	}
+
+	/* Check if the command/response matches what is expected */
+	if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) ||
+		(msg_tmp.controlselector != msg->controlselector) ||
+		(msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) {
+
+		printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
+		saa7164_bus_dumpmsg(dev, msg, buf);
+		saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
+		ret = SAA_ERR_INVALID_COMMAND;
+		goto out;
+	}
+
+	/* Get the actual command and response from the bus */
+	buf_size = msg->size;
+
+	bytes_to_read = sizeof(*msg) + msg->size;
+	/* Calculate write distance to current read position */
+	write_distance = 0;
+	if (curr_gwp >= curr_grp)
+		/* Write doesn't wrap around the ring */
+		write_distance = curr_gwp - curr_grp;
+	else
+		/* Write wraps around the ring */
+		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
+
+	if (bytes_to_read > write_distance) {
+		printk(KERN_ERR "%s() Invalid bus state, missing msg "
+			"or mangled ring, faulty H/W / bad code?\n", __func__);
+		ret = SAA_ERR_INVALID_COMMAND;
+		goto out;
+	}
+
+	/* Calculate the new read position */
+	new_grp = curr_grp + bytes_to_read;
+	if (new_grp > bus->m_dwSizeGetRing) {
+
+		/* Ring wraps */
+		new_grp -= bus->m_dwSizeGetRing;
+		space_rem = bus->m_dwSizeGetRing - curr_grp;
+
+		if (space_rem < sizeof(*msg)) {
+			/* msg wraps around the ring */
+			memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem);
+			memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing,
+				sizeof(*msg) - space_rem);
+			if (buf)
+				memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) -
+					space_rem, buf_size);
+
+		} else if (space_rem == sizeof(*msg)) {
+			memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+			if (buf)
+				memcpy(buf, bus->m_pdwGetRing, buf_size);
+		} else {
+			/* Additional data wraps around the ring */
+			memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+			if (buf) {
+				memcpy(buf, bus->m_pdwGetRing + curr_grp +
+					sizeof(*msg), space_rem - sizeof(*msg));
+				memcpy(buf + space_rem - sizeof(*msg),
+					bus->m_pdwGetRing, bytes_to_read -
+					space_rem);
+			}
+
+		}
+
+	} else {
+		/* No wrapping */
+		memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+		if (buf)
+			memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
+				buf_size);
+	}
+
+	/* Update the read positions, adjusting the ring */
+	saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp));
+
+peekout:
+	msg->size = le16_to_cpu(msg->size);
+	msg->command = le32_to_cpu(msg->command);
+	msg->controlselector = le16_to_cpu(msg->controlselector);
+	ret = SAA_OK;
+out:
+	mutex_unlock(&bus->lock);
+	saa7164_bus_verify(dev);
+	return ret;
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c
new file mode 100644
index 000000000000..5b72da5ce418
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-cards.c
@@ -0,0 +1,773 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "saa7164.h"
+
+/* The Bridge API needs to understand register widths (in bytes) for the
+ * attached I2C devices, so we can simplify the virtual i2c mechansms
+ * and keep the -i2c.c implementation clean.
+ */
+#define REGLEN_8bit	1
+#define REGLEN_16bit	2
+
+struct saa7164_board saa7164_boards[] = {
+	[SAA7164_BOARD_UNKNOWN] = {
+		/* Bridge will not load any firmware, without knowing
+		 * the rev this would be fatal. */
+		.name		= "Unknown",
+	},
+	[SAA7164_BOARD_UNKNOWN_REV2] = {
+		/* Bridge will load the v2 f/w and dump descriptors */
+		/* Required during new board bringup */
+		.name		= "Generic Rev2",
+		.chiprev	= SAA7164_CHIP_REV2,
+	},
+	[SAA7164_BOARD_UNKNOWN_REV3] = {
+		/* Bridge will load the v2 f/w and dump descriptors */
+		/* Required during new board bringup */
+		.name		= "Generic Rev3",
+		.chiprev	= SAA7164_CHIP_REV3,
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2200] = {
+		.name		= "Hauppauge WinTV-HVR2200",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x1d,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1b,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1e,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x10 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1f,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x12 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = {
+		.name		= "Hauppauge WinTV-HVR2200",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV2,
+		.unit		= {{
+			.id		= 0x06,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x05,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x10 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1e,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1f,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x12 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = {
+		.name		= "Hauppauge WinTV-HVR2200",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV2,
+		.unit		= {{
+			.id		= 0x1d,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x05,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1b,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1c,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1e,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x10 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1f,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x12 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = {
+		.name		= "Hauppauge WinTV-HVR2200",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x1d,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x05,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1b,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1c,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1e,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x10 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1f,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x12 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2250] = {
+		.name		= "Hauppauge WinTV-HVR2250",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x22,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x07,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x08,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x1e,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x20,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x23,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = {
+		.name		= "Hauppauge WinTV-HVR2250",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x28,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x07,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x08,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x24,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x26,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x29,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2250_3] = {
+		.name		= "Hauppauge WinTV-HVR2250",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x26,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x07,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x08,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-1 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x22,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x24,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (TOP)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x32 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x27,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "CX24228/S5H1411-2 (QAM)",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x34 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+	[SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = {
+		.name		= "Hauppauge WinTV-HVR2200",
+		.porta		= SAA7164_MPEG_DVB,
+		.portb		= SAA7164_MPEG_DVB,
+		.chiprev	= SAA7164_CHIP_REV3,
+		.unit		= {{
+			.id		= 0x23,
+			.type		= SAA7164_UNIT_EEPROM,
+			.name		= "4K EEPROM",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_0,
+			.i2c_bus_addr	= 0xa0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x04,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x05,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x21,
+			.type		= SAA7164_UNIT_TUNER,
+			.name		= "TDA18271-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0xc0 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x22,
+			.type		= SAA7164_UNIT_ANALOG_DEMODULATOR,
+			.name		= "TDA8290-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x84 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x24,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-1",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_1,
+			.i2c_bus_addr	= 0x10 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		}, {
+			.id		= 0x25,
+			.type		= SAA7164_UNIT_DIGITAL_DEMODULATOR,
+			.name		= "TDA10048-2",
+			.i2c_bus_nr	= SAA7164_I2C_BUS_2,
+			.i2c_bus_addr	= 0x12 >> 1,
+			.i2c_reg_len	= REGLEN_8bit,
+		} },
+	},
+};
+const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs                                                  */
+
+struct saa7164_subid saa7164_subids[] = {
+	{
+		.subvendor = 0x0070,
+		.subdevice = 0x8880,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2250,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8810,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2250,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8980,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2200,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8900,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2200_2,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8901,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2200_3,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x88A1,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2250_3,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8891,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8851,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8940,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2200_4,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0x8953,
+		.card      = SAA7164_BOARD_HAUPPAUGE_HVR2200_5,
+	},
+};
+const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids);
+
+void saa7164_card_list(struct saa7164_dev *dev)
+{
+	int i;
+
+	if (0 == dev->pci->subsystem_vendor &&
+	    0 == dev->pci->subsystem_device) {
+		printk(KERN_ERR
+			"%s: Board has no valid PCIe Subsystem ID and can't\n"
+			"%s: be autodetected. Pass card=<n> insmod option to\n"
+			"%s: workaround that. Send complaints to the vendor\n"
+			"%s: of the TV card. Best regards,\n"
+			"%s:         -- tux\n",
+			dev->name, dev->name, dev->name, dev->name, dev->name);
+	} else {
+		printk(KERN_ERR
+			"%s: Your board isn't known (yet) to the driver.\n"
+			"%s: Try to pick one of the existing card configs via\n"
+			"%s: card=<n> insmod option.  Updating to the latest\n"
+			"%s: version might help as well.\n",
+			dev->name, dev->name, dev->name, dev->name);
+	}
+
+	printk(KERN_ERR "%s: Here are valid choices for the card=<n> insmod "
+		"option:\n", dev->name);
+
+	for (i = 0; i < saa7164_bcount; i++)
+		printk(KERN_ERR "%s:    card=%d -> %s\n",
+		       dev->name, i, saa7164_boards[i].name);
+}
+
+/* TODO: clean this define up into the -cards.c structs */
+#define PCIEBRIDGE_UNITID 2
+
+void saa7164_gpio_setup(struct saa7164_dev *dev)
+{
+	switch (dev->board) {
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+		/*
+		GPIO 2: s5h1411 / tda10048-1 demod reset
+		GPIO 3: s5h1411 / tda10048-2 demod reset
+		GPIO 7: IRBlaster Zilog reset
+		 */
+
+		/* Reset parts by going in and out of reset */
+		saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2);
+		saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3);
+
+		msleep(20);
+
+		saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2);
+		saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3);
+		break;
+	}
+}
+
+static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data)
+{
+	struct tveeprom tv;
+
+	/* TODO: Assumption: eeprom on bus 0 */
+	tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
+		eeprom_data);
+
+	/* Make sure we support the board model */
+	switch (tv.model) {
+	case 88001:
+		/* Development board - Limit circulation */
+		/* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+		 * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */
+	case 88021:
+		/* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+		 * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */
+		break;
+	case 88041:
+		/* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+		 * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */
+		break;
+	case 88061:
+		/* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+		 * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */
+		break;
+	case 89519:
+	case 89609:
+		/* WinTV-HVR2200 (PCIe, Retail, full-height)
+		 * DVB-T (TDA18271/TDA10048) and basic analog, no IR */
+		break;
+	case 89619:
+		/* WinTV-HVR2200 (PCIe, Retail, half-height)
+		 * DVB-T (TDA18271/TDA10048) and basic analog, no IR */
+		break;
+	default:
+		printk(KERN_ERR "%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);
+}
+
+void saa7164_card_setup(struct saa7164_dev *dev)
+{
+	static u8 eeprom[256];
+
+	if (dev->i2c_bus[0].i2c_rc == 0) {
+		if (saa7164_api_read_eeprom(dev, &eeprom[0],
+			sizeof(eeprom)) < 0)
+			return;
+	}
+
+	switch (dev->board) {
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+		hauppauge_eeprom(dev, &eeprom[0]);
+		break;
+	}
+}
+
+/* With most other drivers, the kernel expects to communicate with subdrivers
+ * through i2c. This bridge does not allow that, it does not expose any direct
+ * access to I2C. Instead we have to communicate through the device f/w for
+ * register access to 'processing units'. Each unit has a unique
+ * id, regardless of how the physical implementation occurs across
+ * the three physical i2c busses. The being said if we want leverge of
+ * the existing kernel drivers for tuners and demods we have to 'speak i2c',
+ * to this bridge implements 3 virtual i2c buses. This is a helper function
+ * for those.
+ *
+ * Description: Translate the kernels notion of an i2c address and bus into
+ * the appropriate unitid.
+ */
+int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr)
+{
+	/* For a given bus and i2c device address, return the saa7164 unique
+	 * unitid. < 0 on error */
+
+	struct saa7164_dev *dev = bus->dev;
+	struct saa7164_unit *unit;
+	int i;
+
+	for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+		unit = &saa7164_boards[dev->board].unit[i];
+
+		if (unit->type == SAA7164_UNIT_UNDEFINED)
+			continue;
+		if ((bus->nr == unit->i2c_bus_nr) &&
+			(addr == unit->i2c_bus_addr))
+			return unit->id;
+	}
+
+	return -1;
+}
+
+/* The 7164 API needs to know the i2c register length in advance.
+ * this is a helper function. Based on a specific chip addr and bus return the
+ * reg length.
+ */
+int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr)
+{
+	/* For a given bus and i2c device address, return the
+	 * saa7164 registry address width. < 0 on error
+	 */
+
+	struct saa7164_dev *dev = bus->dev;
+	struct saa7164_unit *unit;
+	int i;
+
+	for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+		unit = &saa7164_boards[dev->board].unit[i];
+
+		if (unit->type == SAA7164_UNIT_UNDEFINED)
+			continue;
+
+		if ((bus->nr == unit->i2c_bus_nr) &&
+			(addr == unit->i2c_bus_addr))
+			return unit->i2c_reg_len;
+	}
+
+	return -1;
+}
+/* TODO: implement a 'findeeprom' functio like the above and fix any other
+ * eeprom related todo's in -api.c.
+ */
+
+/* Translate a unitid into a x readable device name, for display purposes.  */
+char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid)
+{
+	char *undefed = "UNDEFINED";
+	char *bridge = "BRIDGE";
+	struct saa7164_unit *unit;
+	int i;
+
+	if (unitid == 0)
+		return bridge;
+
+	for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+		unit = &saa7164_boards[dev->board].unit[i];
+
+		if (unit->type == SAA7164_UNIT_UNDEFINED)
+			continue;
+
+		if (unitid == unit->id)
+				return unit->name;
+	}
+
+	return undefed;
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
new file mode 100644
index 000000000000..62fac7f9d04e
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -0,0 +1,589 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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/wait.h>
+
+#include "saa7164.h"
+
+int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev)
+{
+	int i, ret = -1;
+
+	mutex_lock(&dev->lock);
+	for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+		if (dev->cmds[i].inuse == 0) {
+			dev->cmds[i].inuse = 1;
+			dev->cmds[i].signalled = 0;
+			dev->cmds[i].timeout = 0;
+			ret = dev->cmds[i].seqno;
+			break;
+		}
+	}
+	mutex_unlock(&dev->lock);
+
+	return ret;
+}
+
+void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno)
+{
+	mutex_lock(&dev->lock);
+	if ((dev->cmds[seqno].inuse == 1) &&
+		(dev->cmds[seqno].seqno == seqno)) {
+		dev->cmds[seqno].inuse = 0;
+		dev->cmds[seqno].signalled = 0;
+		dev->cmds[seqno].timeout = 0;
+	}
+	mutex_unlock(&dev->lock);
+}
+
+void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno)
+{
+	mutex_lock(&dev->lock);
+	if ((dev->cmds[seqno].inuse == 1) &&
+		(dev->cmds[seqno].seqno == seqno)) {
+		dev->cmds[seqno].timeout = 1;
+	}
+	mutex_unlock(&dev->lock);
+}
+
+u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno)
+{
+	int ret = 0;
+
+	mutex_lock(&dev->lock);
+	if ((dev->cmds[seqno].inuse == 1) &&
+		(dev->cmds[seqno].seqno == seqno)) {
+		ret = dev->cmds[seqno].timeout;
+	}
+	mutex_unlock(&dev->lock);
+
+	return ret;
+}
+
+/* Commands to the f/w get marshelled to/from this code then onto the PCI
+ * -bus/c running buffer. */
+int saa7164_irq_dequeue(struct saa7164_dev *dev)
+{
+	int ret = SAA_OK, i = 0;
+	u32 timeout;
+	wait_queue_head_t *q = NULL;
+	u8 tmp[512];
+	dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+	/* While any outstand message on the bus exists... */
+	do {
+
+		/* Peek the msg bus */
+		struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
+		ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
+		if (ret != SAA_OK)
+			break;
+
+		q = &dev->cmds[tRsp.seqno].wait;
+		timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno);
+		dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout);
+		if (!timeout) {
+			dprintk(DBGLVL_CMD,
+				"%s() signalled seqno(%d) (for dequeue)\n",
+				__func__, tRsp.seqno);
+			dev->cmds[tRsp.seqno].signalled = 1;
+			wake_up(q);
+		} else {
+			printk(KERN_ERR
+				"%s() found timed out command on the bus\n",
+					__func__);
+
+			/* Clean the bus */
+			ret = saa7164_bus_get(dev, &tRsp, &tmp, 0);
+			printk(KERN_ERR "%s() ret = %x\n", __func__, ret);
+			if (ret == SAA_ERR_EMPTY)
+				/* Someone else already fetched the response */
+				return SAA_OK;
+
+			if (ret != SAA_OK)
+				return ret;
+		}
+
+		/* It's unlikely to have more than 4 or 5 pending messages,
+		 * ensure we exit at some point regardless.
+		 */
+	} while (i++ < 32);
+
+	return ret;
+}
+
+/* Commands to the f/w get marshelled to/from this code then onto the PCI
+ * -bus/c running buffer. */
+int saa7164_cmd_dequeue(struct saa7164_dev *dev)
+{
+	int loop = 1;
+	int ret;
+	u32 timeout;
+	wait_queue_head_t *q = NULL;
+	u8 tmp[512];
+	dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+	while (loop) {
+
+		struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
+		ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
+		if (ret == SAA_ERR_EMPTY)
+			return SAA_OK;
+
+		if (ret != SAA_OK)
+			return ret;
+
+		q = &dev->cmds[tRsp.seqno].wait;
+		timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno);
+		dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout);
+		if (timeout) {
+			printk(KERN_ERR "found timed out command on the bus\n");
+
+			/* Clean the bus */
+			ret = saa7164_bus_get(dev, &tRsp, &tmp, 0);
+			printk(KERN_ERR "ret = %x\n", ret);
+			if (ret == SAA_ERR_EMPTY)
+				/* Someone else already fetched the response */
+				return SAA_OK;
+
+			if (ret != SAA_OK)
+				return ret;
+
+			if (tRsp.flags & PVC_CMDFLAG_CONTINUE)
+				printk(KERN_ERR "split response\n");
+			else
+				saa7164_cmd_free_seqno(dev, tRsp.seqno);
+
+			printk(KERN_ERR " timeout continue\n");
+			continue;
+		}
+
+		dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n",
+			__func__, tRsp.seqno);
+		dev->cmds[tRsp.seqno].signalled = 1;
+		wake_up(q);
+		return SAA_OK;
+	}
+
+	return SAA_OK;
+}
+
+int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg,
+	void *buf)
+{
+	struct tmComResBusInfo *bus = &dev->bus;
+	u8 cmd_sent;
+	u16 size, idx;
+	u32 cmds;
+	void *tmp;
+	int ret = -1;
+
+	if (!msg) {
+		printk(KERN_ERR "%s() !msg\n", __func__);
+		return SAA_ERR_BAD_PARAMETER;
+	}
+
+	mutex_lock(&dev->cmds[msg->id].lock);
+
+	size = msg->size;
+	idx = 0;
+	cmds = size / bus->m_wMaxReqSize;
+	if (size % bus->m_wMaxReqSize == 0)
+		cmds -= 1;
+
+	cmd_sent = 0;
+
+	/* Split the request into smaller chunks */
+	for (idx = 0; idx < cmds; idx++) {
+
+		msg->flags |= SAA_CMDFLAG_CONTINUE;
+		msg->size = bus->m_wMaxReqSize;
+		tmp = buf + idx * bus->m_wMaxReqSize;
+
+		ret = saa7164_bus_set(dev, msg, tmp);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() set failed %d\n", __func__, ret);
+
+			if (cmd_sent) {
+				ret = SAA_ERR_BUSY;
+				goto out;
+			}
+			ret = SAA_ERR_OVERFLOW;
+			goto out;
+		}
+		cmd_sent = 1;
+	}
+
+	/* If not the last command... */
+	if (idx != 0)
+		msg->flags &= ~SAA_CMDFLAG_CONTINUE;
+
+	msg->size = size - idx * bus->m_wMaxReqSize;
+
+	ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() set last failed %d\n", __func__, ret);
+
+		if (cmd_sent) {
+			ret = SAA_ERR_BUSY;
+			goto out;
+		}
+		ret = SAA_ERR_OVERFLOW;
+		goto out;
+	}
+	ret = SAA_OK;
+
+out:
+	mutex_unlock(&dev->cmds[msg->id].lock);
+	return ret;
+}
+
+/* Wait for a signal event, without holding a mutex. Either return TIMEOUT if
+ * the event never occurred, or SAA_OK if it was signaled during the wait.
+ */
+int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
+{
+	wait_queue_head_t *q = NULL;
+	int ret = SAA_BUS_TIMEOUT;
+	unsigned long stamp;
+	int r;
+
+	if (saa_debug >= 4)
+		saa7164_bus_dump(dev);
+
+	dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno);
+
+	mutex_lock(&dev->lock);
+	if ((dev->cmds[seqno].inuse == 1) &&
+		(dev->cmds[seqno].seqno == seqno)) {
+		q = &dev->cmds[seqno].wait;
+	}
+	mutex_unlock(&dev->lock);
+
+	if (q) {
+		/* If we haven't been signalled we need to wait */
+		if (dev->cmds[seqno].signalled == 0) {
+			stamp = jiffies;
+			dprintk(DBGLVL_CMD,
+				"%s(seqno=%d) Waiting (signalled=%d)\n",
+				__func__, seqno, dev->cmds[seqno].signalled);
+
+			/* Wait for signalled to be flagged or timeout */
+			/* In a highly stressed system this can easily extend
+			 * into multiple seconds before the deferred worker
+			 * is scheduled, and we're woken up via signal.
+			 * We typically are signalled in < 50ms but it can
+			 * take MUCH longer.
+			 */
+			wait_event_timeout(*q, dev->cmds[seqno].signalled,
+				(HZ * waitsecs));
+			r = time_before(jiffies, stamp + (HZ * waitsecs));
+			if (r)
+				ret = SAA_OK;
+			else
+				saa7164_cmd_timeout_seqno(dev, seqno);
+
+			dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d "
+				"(signalled=%d)\n", __func__, seqno, r,
+				dev->cmds[seqno].signalled);
+		} else
+			ret = SAA_OK;
+	} else
+		printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n",
+			__func__, seqno);
+
+	return ret;
+}
+
+void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno)
+{
+	int i;
+	dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+	mutex_lock(&dev->lock);
+	for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+		if (dev->cmds[i].inuse == 1) {
+			dprintk(DBGLVL_CMD,
+				"seqno %d inuse, sig = %d, t/out = %d\n",
+				dev->cmds[i].seqno,
+				dev->cmds[i].signalled,
+				dev->cmds[i].timeout);
+		}
+	}
+
+	for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+		if ((dev->cmds[i].inuse == 1) && ((i == 0) ||
+			(dev->cmds[i].signalled) || (dev->cmds[i].timeout))) {
+			dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n",
+				__func__, i);
+			dev->cmds[i].signalled = 1;
+			wake_up(&dev->cmds[i].wait);
+		}
+	}
+	mutex_unlock(&dev->lock);
+}
+
+int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
+	u16 controlselector, u16 size, void *buf)
+{
+	struct tmComResInfo command_t, *pcommand_t;
+	struct tmComResInfo response_t, *presponse_t;
+	u8 errdata[256];
+	u16 resp_dsize;
+	u16 data_recd;
+	u32 loop;
+	int ret;
+	int safety = 0;
+
+	dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, "
+		"sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id,
+		command, controlselector);
+
+	if ((size == 0) || (buf == NULL)) {
+		printk(KERN_ERR "%s() Invalid param\n", __func__);
+		return SAA_ERR_BAD_PARAMETER;
+	}
+
+	/* Prepare some basic command/response structures */
+	memset(&command_t, 0, sizeof(command_t));
+	memset(&response_t, 0, sizeof(response_t));
+	pcommand_t = &command_t;
+	presponse_t = &response_t;
+	command_t.id = id;
+	command_t.command = command;
+	command_t.controlselector = controlselector;
+	command_t.size = size;
+
+	/* Allocate a unique sequence number */
+	ret = saa7164_cmd_alloc_seqno(dev);
+	if (ret < 0) {
+		printk(KERN_ERR "%s() No free sequences\n", __func__);
+		ret = SAA_ERR_NO_RESOURCES;
+		goto out;
+	}
+
+	command_t.seqno = (u8)ret;
+
+	/* Send Command */
+	resp_dsize = size;
+	pcommand_t->size = size;
+
+	dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n",
+		__func__, pcommand_t->seqno);
+
+	dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n",
+		__func__, pcommand_t->size);
+
+	ret = saa7164_cmd_set(dev, pcommand_t, buf);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() set command failed %d\n", __func__, ret);
+
+		if (ret != SAA_ERR_BUSY)
+			saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+		else
+			/* Flag a timeout, because at least one
+			 * command was sent */
+			saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno);
+
+		goto out;
+	}
+
+	/* With split responses we have to collect the msgs piece by piece */
+	data_recd = 0;
+	loop = 1;
+	while (loop) {
+		dprintk(DBGLVL_CMD, "%s() loop\n", __func__);
+
+		ret = saa7164_cmd_wait(dev, pcommand_t->seqno);
+		dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret);
+
+		/* if power is down and this is not a power command ... */
+
+		if (ret == SAA_BUS_TIMEOUT) {
+			printk(KERN_ERR "Event timed out\n");
+			saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno);
+			return ret;
+		}
+
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "spurious error\n");
+			return ret;
+		}
+
+		/* Peek response */
+		ret = saa7164_bus_get(dev, presponse_t, NULL, 1);
+		if (ret == SAA_ERR_EMPTY) {
+			dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__);
+			continue;
+		}
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "peek failed\n");
+			return ret;
+		}
+
+		dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n",
+			__func__, presponse_t->seqno);
+
+		dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n",
+			__func__, presponse_t->flags);
+
+		dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n",
+			__func__, presponse_t->size);
+
+		/* Check if the response was for our command */
+		if (presponse_t->seqno != pcommand_t->seqno) {
+
+			dprintk(DBGLVL_CMD,
+				"wrong event: seqno = %d, "
+				"expected seqno = %d, "
+				"will dequeue regardless\n",
+				presponse_t->seqno, pcommand_t->seqno);
+
+			ret = saa7164_cmd_dequeue(dev);
+			if (ret != SAA_OK) {
+				printk(KERN_ERR "dequeue failed, ret = %d\n",
+					ret);
+				if (safety++ > 16) {
+					printk(KERN_ERR
+					"dequeue exceeded, safety exit\n");
+					return SAA_ERR_BUSY;
+				}
+			}
+
+			continue;
+		}
+
+		if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) {
+
+			memset(&errdata[0], 0, sizeof(errdata));
+
+			ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0);
+			if (ret != SAA_OK) {
+				printk(KERN_ERR "get error(2)\n");
+				return ret;
+			}
+
+			saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+
+			dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n",
+				__func__, errdata[0], errdata[1], errdata[2],
+				errdata[3]);
+
+			/* Map error codes */
+			dprintk(DBGLVL_CMD, "%s() cmd, error code  = 0x%x\n",
+				__func__, errdata[0]);
+
+			switch (errdata[0]) {
+			case PVC_ERRORCODE_INVALID_COMMAND:
+				dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n",
+					__func__);
+				ret = SAA_ERR_INVALID_COMMAND;
+				break;
+			case PVC_ERRORCODE_INVALID_DATA:
+				dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n",
+					__func__);
+				ret = SAA_ERR_BAD_PARAMETER;
+				break;
+			case PVC_ERRORCODE_TIMEOUT:
+				dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__);
+				ret = SAA_ERR_TIMEOUT;
+				break;
+			case PVC_ERRORCODE_NAK:
+				dprintk(DBGLVL_CMD, "%s() NAK\n", __func__);
+				ret = SAA_ERR_NULL_PACKET;
+				break;
+			case PVC_ERRORCODE_UNKNOWN:
+			case PVC_ERRORCODE_INVALID_CONTROL:
+				dprintk(DBGLVL_CMD,
+					"%s() UNKNOWN OR INVALID CONTROL\n",
+					__func__);
+			default:
+				dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__);
+				ret = SAA_ERR_NOT_SUPPORTED;
+			}
+
+			/* See of other commands are on the bus */
+			if (saa7164_cmd_dequeue(dev) != SAA_OK)
+				printk(KERN_ERR "dequeue(2) failed\n");
+
+			return ret;
+		}
+
+		/* If response is invalid */
+		if ((presponse_t->id != pcommand_t->id) ||
+			(presponse_t->command != pcommand_t->command) ||
+			(presponse_t->controlselector !=
+				pcommand_t->controlselector) ||
+			(((resp_dsize - data_recd) != presponse_t->size) &&
+				!(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) ||
+			((resp_dsize - data_recd) < presponse_t->size)) {
+
+			/* Invalid */
+			dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__);
+			ret = saa7164_bus_get(dev, presponse_t, NULL, 0);
+			if (ret != SAA_OK) {
+				printk(KERN_ERR "get failed\n");
+				return ret;
+			}
+
+			/* See of other commands are on the bus */
+			if (saa7164_cmd_dequeue(dev) != SAA_OK)
+				printk(KERN_ERR "dequeue(3) failed\n");
+			continue;
+		}
+
+		/* OK, now we're actually getting out correct response */
+		ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "get failed\n");
+			return ret;
+		}
+
+		data_recd = presponse_t->size + data_recd;
+		if (resp_dsize == data_recd) {
+			dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__);
+			break;
+		}
+
+		/* See of other commands are on the bus */
+		if (saa7164_cmd_dequeue(dev) != SAA_OK)
+			printk(KERN_ERR "dequeue(3) failed\n");
+
+		continue;
+
+	} /* (loop) */
+
+	/* Release the sequence number allocation */
+	saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+
+	/* if powerdown signal all pending commands */
+
+	dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__);
+
+	/* See of other commands are on the bus */
+	if (saa7164_cmd_dequeue(dev) != SAA_OK)
+		printk(KERN_ERR "dequeue(4) failed\n");
+
+	ret = SAA_OK;
+out:
+	return ret;
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
new file mode 100644
index 000000000000..2c9ad878bef3
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -0,0 +1,1488 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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 <asm/div64.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+#include "saa7164.h"
+
+MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ *  1 Basic
+ *  2
+ *  4 i2c
+ *  8 api
+ * 16 cmd
+ * 32 bus
+ */
+
+unsigned int saa_debug;
+module_param_named(debug, saa_debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+unsigned int fw_debug;
+module_param(fw_debug, int, 0644);
+MODULE_PARM_DESC(fw_debug, "Firware debug level def:2");
+
+unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS;
+module_param(encoder_buffers, int, 0644);
+MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS;
+module_param(vbi_buffers, int, 0644);
+MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int waitsecs = 10;
+module_param(waitsecs, int, 0644);
+MODULE_PARM_DESC(waitsecs, "timeout on firmware messages");
+
+static unsigned int card[]  = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
+module_param_array(card,  int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+unsigned int print_histogram = 64;
+module_param(print_histogram, int, 0644);
+MODULE_PARM_DESC(print_histogram, "print histogram values once");
+
+unsigned int crc_checking = 1;
+module_param(crc_checking, int, 0644);
+MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
+
+unsigned int guard_checking = 1;
+module_param(guard_checking, int, 0644);
+MODULE_PARM_DESC(guard_checking,
+	"enable dma sanity checking for buffer overruns");
+
+static unsigned int saa7164_devcount;
+
+static DEFINE_MUTEX(devlist);
+LIST_HEAD(saa7164_devlist);
+
+#define INT_SIZE 16
+
+static void saa7164_pack_verifier(struct saa7164_buffer *buf)
+{
+	u8 *p = (u8 *)buf->cpu;
+	int i;
+
+	for (i = 0; i < buf->actual_size; i += 2048) {
+
+		if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
+			(*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) {
+			printk(KERN_ERR "No pack at 0x%x\n", i);
+#if 0
+			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+				       p + 1, 32, false);
+#endif
+		}
+	}
+}
+
+#define FIXED_VIDEO_PID 0xf1
+#define FIXED_AUDIO_PID 0xf2
+
+static void saa7164_ts_verifier(struct saa7164_buffer *buf)
+{
+	struct saa7164_port *port = buf->port;
+	u32 i;
+	u8 cc, a;
+	u16 pid;
+	u8 __iomem *bufcpu = (u8 *)buf->cpu;
+
+	port->sync_errors = 0;
+	port->v_cc_errors = 0;
+	port->a_cc_errors = 0;
+
+	for (i = 0; i < buf->actual_size; i += 188) {
+		if (*(bufcpu + i) != 0x47)
+			port->sync_errors++;
+
+		/* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
+		pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
+		cc = *(bufcpu + i + 3) & 0x0f;
+
+		if (pid == FIXED_VIDEO_PID) {
+			a = ((port->last_v_cc + 1) & 0x0f);
+			if (a != cc) {
+				printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
+					port->last_v_cc, cc, i);
+				port->v_cc_errors++;
+			}
+
+			port->last_v_cc = cc;
+		} else
+		if (pid == FIXED_AUDIO_PID) {
+			a = ((port->last_a_cc + 1) & 0x0f);
+			if (a != cc) {
+				printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
+					port->last_a_cc, cc, i);
+				port->a_cc_errors++;
+			}
+
+			port->last_a_cc = cc;
+		}
+
+	}
+
+	/* Only report errors if we've been through this function atleast
+	 * once already and the cached cc values are primed. First time through
+	 * always generates errors.
+	 */
+	if (port->v_cc_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors);
+
+	if (port->a_cc_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors);
+
+	if (port->sync_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "sync_errors = %d\n", port->sync_errors);
+
+	if (port->done_first_interrupt == 1)
+		port->done_first_interrupt++;
+}
+
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
+{
+	int i;
+
+	memset(hg, 0, sizeof(struct saa7164_histogram));
+	strcpy(hg->name, name);
+
+	/* First 30ms x 1ms */
+	for (i = 0; i < 30; i++)
+		hg->counter1[0 + i].val = i;
+
+	/* 30 - 200ms x 10ms  */
+	for (i = 0; i < 18; i++)
+		hg->counter1[30 + i].val = 30 + (i * 10);
+
+	/* 200 - 2000ms x 100ms  */
+	for (i = 0; i < 15; i++)
+		hg->counter1[48 + i].val = 200 + (i * 200);
+
+	/* Catch all massive value (2secs) */
+	hg->counter1[55].val = 2000;
+
+	/* Catch all massive value (4secs) */
+	hg->counter1[56].val = 4000;
+
+	/* Catch all massive value (8secs) */
+	hg->counter1[57].val = 8000;
+
+	/* Catch all massive value (15secs) */
+	hg->counter1[58].val = 15000;
+
+	/* Catch all massive value (30secs) */
+	hg->counter1[59].val = 30000;
+
+	/* Catch all massive value (60secs) */
+	hg->counter1[60].val = 60000;
+
+	/* Catch all massive value (5mins) */
+	hg->counter1[61].val = 300000;
+
+	/* Catch all massive value (15mins) */
+	hg->counter1[62].val = 900000;
+
+	/* Catch all massive values (1hr) */
+	hg->counter1[63].val = 3600000;
+}
+
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
+{
+	int i;
+	for (i = 0; i < 64; i++) {
+		if (val <= hg->counter1[i].val) {
+			hg->counter1[i].count++;
+			hg->counter1[i].update_time = jiffies;
+			break;
+		}
+	}
+}
+
+static void saa7164_histogram_print(struct saa7164_port *port,
+	struct saa7164_histogram *hg)
+{
+	u32 entries = 0;
+	int i;
+
+	printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name);
+	for (i = 0; i < 64; i++) {
+		if (hg->counter1[i].count == 0)
+			continue;
+
+		printk(KERN_ERR " %4d %12d %Ld\n",
+			hg->counter1[i].val,
+			hg->counter1[i].count,
+			hg->counter1[i].update_time);
+
+		entries++;
+	}
+	printk(KERN_ERR "Total: %d\n", entries);
+}
+
+static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf = NULL;
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct list_head *c, *n;
+	int i = 0;
+	u8 __iomem *p;
+
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+
+		buf = list_entry(c, struct saa7164_buffer, list);
+		if (i++ > port->hwcfg.buffercount) {
+			printk(KERN_ERR "%s() illegal i count %d\n",
+				__func__, i);
+			break;
+		}
+
+		if (buf->idx == bufnr) {
+
+			/* Found the buffer, deal with it */
+			dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
+
+			if (crc_checking) {
+				/* Throw a new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
+
+			if (guard_checking) {
+				p = (u8 *)buf->cpu;
+				if ((*(p + buf->actual_size + 0) != 0xff) ||
+					(*(p + buf->actual_size + 1) != 0xff) ||
+					(*(p + buf->actual_size + 2) != 0xff) ||
+					(*(p + buf->actual_size + 3) != 0xff) ||
+					(*(p + buf->actual_size + 0x10) != 0xff) ||
+					(*(p + buf->actual_size + 0x11) != 0xff) ||
+					(*(p + buf->actual_size + 0x12) != 0xff) ||
+					(*(p + buf->actual_size + 0x13) != 0xff)) {
+						printk(KERN_ERR "%s() buf %p guard buffer breach\n",
+							__func__, buf);
+#if 0
+			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+				       p + buf->actual_size - 32, 64, false);
+#endif
+				}
+			}
+
+			if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) {
+				/* Validate the incoming buffer content */
+				if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+					saa7164_ts_verifier(buf);
+				else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+					saa7164_pack_verifier(buf);
+			}
+
+			/* find a free user buffer and clone to it */
+			if (!list_empty(&port->list_buf_free.list)) {
+
+				/* Pull the first buffer from the used list */
+				ubuf = list_first_entry(&port->list_buf_free.list,
+					struct saa7164_user_buffer, list);
+
+				if (buf->actual_size <= ubuf->actual_size) {
+
+					memcpy_fromio(ubuf->data, buf->cpu,
+						ubuf->actual_size);
+
+					if (crc_checking) {
+						/* Throw a new checksum on the read buffer */
+						ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+					}
+
+					/* Requeue the buffer on the free list */
+					ubuf->pos = 0;
+
+					list_move_tail(&ubuf->list,
+						&port->list_buf_used.list);
+
+					/* Flag any userland waiters */
+					wake_up_interruptible(&port->wait_read);
+
+				} else {
+					printk(KERN_ERR "buf %p bufsize fails match\n", buf);
+				}
+
+			} else
+				printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n");
+
+			/* Ensure offset into buffer remains 0, fill buffer
+			 * with known bad data. We check for this data at a later point
+			 * in time. */
+			saa7164_buffer_zero_offsets(port, bufnr);
+			memset_io(buf->cpu, 0xff, buf->pci_size);
+			if (crc_checking) {
+				/* Throw yet aanother new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
+
+			break;
+		}
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+}
+
+static void saa7164_work_enchandler(struct work_struct *w)
+{
+	struct saa7164_port *port =
+		container_of(w, struct saa7164_port, workenc);
+	struct saa7164_dev *dev = port->dev;
+
+	u32 wp, mcb, rp, cnt = 0;
+
+	port->last_svc_msecs_diff = port->last_svc_msecs;
+	port->last_svc_msecs = jiffies_to_msecs(jiffies);
+
+	port->last_svc_msecs_diff = port->last_svc_msecs -
+		port->last_svc_msecs_diff;
+
+	saa7164_histogram_update(&port->svc_interval,
+		port->last_svc_msecs_diff);
+
+	port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+		port->last_irq_msecs;
+
+	saa7164_histogram_update(&port->irq_svc_interval,
+		port->last_irq_svc_msecs_diff);
+
+	dprintk(DBGLVL_IRQ,
+		"%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+		__func__,
+		port->last_svc_msecs_diff,
+		port->last_irq_svc_msecs_diff,
+		port->last_svc_wp,
+		port->last_svc_rp
+		);
+
+	/* Current write position */
+	wp = saa7164_readl(port->bufcounter);
+	if (wp > (port->hwcfg.buffercount - 1)) {
+		printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+		return;
+	}
+
+	/* Most current complete buffer */
+	if (wp == 0)
+		mcb = (port->hwcfg.buffercount - 1);
+	else
+		mcb = wp - 1;
+
+	while (1) {
+		if (port->done_first_interrupt == 0) {
+			port->done_first_interrupt++;
+			rp = mcb;
+		} else
+			rp = (port->last_svc_rp + 1) % 8;
+
+		if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+			printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+			break;
+		}
+
+		saa7164_work_enchandler_helper(port, rp);
+		port->last_svc_rp = rp;
+		cnt++;
+
+		if (rp == mcb)
+			break;
+	}
+
+	/* TODO: Convert this into a /proc/saa7164 style readable file */
+	if (print_histogram == port->nr) {
+		saa7164_histogram_print(port, &port->irq_interval);
+		saa7164_histogram_print(port, &port->svc_interval);
+		saa7164_histogram_print(port, &port->irq_svc_interval);
+		saa7164_histogram_print(port, &port->read_interval);
+		saa7164_histogram_print(port, &port->poll_interval);
+		/* TODO: fix this to preserve any previous state */
+		print_histogram = 64 + port->nr;
+	}
+}
+
+static void saa7164_work_vbihandler(struct work_struct *w)
+{
+	struct saa7164_port *port =
+		container_of(w, struct saa7164_port, workenc);
+	struct saa7164_dev *dev = port->dev;
+
+	u32 wp, mcb, rp, cnt = 0;
+
+	port->last_svc_msecs_diff = port->last_svc_msecs;
+	port->last_svc_msecs = jiffies_to_msecs(jiffies);
+	port->last_svc_msecs_diff = port->last_svc_msecs -
+		port->last_svc_msecs_diff;
+
+	saa7164_histogram_update(&port->svc_interval,
+		port->last_svc_msecs_diff);
+
+	port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+		port->last_irq_msecs;
+
+	saa7164_histogram_update(&port->irq_svc_interval,
+		port->last_irq_svc_msecs_diff);
+
+	dprintk(DBGLVL_IRQ,
+		"%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+		__func__,
+		port->last_svc_msecs_diff,
+		port->last_irq_svc_msecs_diff,
+		port->last_svc_wp,
+		port->last_svc_rp
+		);
+
+	/* Current write position */
+	wp = saa7164_readl(port->bufcounter);
+	if (wp > (port->hwcfg.buffercount - 1)) {
+		printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+		return;
+	}
+
+	/* Most current complete buffer */
+	if (wp == 0)
+		mcb = (port->hwcfg.buffercount - 1);
+	else
+		mcb = wp - 1;
+
+	while (1) {
+		if (port->done_first_interrupt == 0) {
+			port->done_first_interrupt++;
+			rp = mcb;
+		} else
+			rp = (port->last_svc_rp + 1) % 8;
+
+		if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+			printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+			break;
+		}
+
+		saa7164_work_enchandler_helper(port, rp);
+		port->last_svc_rp = rp;
+		cnt++;
+
+		if (rp == mcb)
+			break;
+	}
+
+	/* TODO: Convert this into a /proc/saa7164 style readable file */
+	if (print_histogram == port->nr) {
+		saa7164_histogram_print(port, &port->irq_interval);
+		saa7164_histogram_print(port, &port->svc_interval);
+		saa7164_histogram_print(port, &port->irq_svc_interval);
+		saa7164_histogram_print(port, &port->read_interval);
+		saa7164_histogram_print(port, &port->poll_interval);
+		/* TODO: fix this to preserve any previous state */
+		print_histogram = 64 + port->nr;
+	}
+}
+
+static void saa7164_work_cmdhandler(struct work_struct *w)
+{
+	struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
+
+	/* Wake up any complete commands */
+	saa7164_irq_dequeue(dev);
+}
+
+static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
+{
+	struct saa7164_port *port = buf->port;
+
+	/* Feed the transport payload into the kernel demux */
+	dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
+		SAA7164_TS_NUMBER_OF_LINES);
+
+}
+
+static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	/* Store old time */
+	port->last_irq_msecs_diff = port->last_irq_msecs;
+
+	/* Collect new stats */
+	port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+	/* Calculate stats */
+	port->last_irq_msecs_diff = port->last_irq_msecs -
+		port->last_irq_msecs_diff;
+
+	saa7164_histogram_update(&port->irq_interval,
+		port->last_irq_msecs_diff);
+
+	dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+		port->last_irq_msecs_diff);
+
+	/* Tis calls the vbi irq handler */
+	schedule_work(&port->workenc);
+	return 0;
+}
+
+static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	/* Store old time */
+	port->last_irq_msecs_diff = port->last_irq_msecs;
+
+	/* Collect new stats */
+	port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+	/* Calculate stats */
+	port->last_irq_msecs_diff = port->last_irq_msecs -
+		port->last_irq_msecs_diff;
+
+	saa7164_histogram_update(&port->irq_interval,
+		port->last_irq_msecs_diff);
+
+	dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+		port->last_irq_msecs_diff);
+
+	schedule_work(&port->workenc);
+	return 0;
+}
+
+static irqreturn_t saa7164_irq_ts(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct list_head *c, *n;
+	int wp, i = 0, rp;
+
+	/* Find the current write point from the hardware */
+	wp = saa7164_readl(port->bufcounter);
+	if (wp > (port->hwcfg.buffercount - 1))
+		BUG();
+
+	/* Find the previous buffer to the current write point */
+	if (wp == 0)
+		rp = (port->hwcfg.buffercount - 1);
+	else
+		rp = wp - 1;
+
+	/* Lookup the WP in the buffer list */
+	/* TODO: turn this into a worker thread */
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		if (i++ > port->hwcfg.buffercount)
+			BUG();
+
+		if (buf->idx == rp) {
+			/* Found the buffer, deal with it */
+			dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
+				__func__, wp, rp);
+			saa7164_buffer_deliver(buf);
+			break;
+		}
+
+	}
+	return 0;
+}
+
+/* Primary IRQ handler and dispatch mechanism */
+static irqreturn_t saa7164_irq(int irq, void *dev_id)
+{
+	struct saa7164_dev *dev = dev_id;
+	struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1];
+	struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2];
+	struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1];
+	struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2];
+	struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1];
+	struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2];
+
+	u32 intid, intstat[INT_SIZE/4];
+	int i, handled = 0, bit;
+
+	if (dev == NULL) {
+		printk(KERN_ERR "%s() No device specified\n", __func__);
+		handled = 0;
+		goto out;
+	}
+
+	/* Check that the hardware is accessible. If the status bytes are
+	 * 0xFF then the device is not accessible, the the IRQ belongs
+	 * to another driver.
+	 * 4 x u32 interrupt registers.
+	 */
+	for (i = 0; i < INT_SIZE/4; i++) {
+
+		/* TODO: Convert into saa7164_readl() */
+		/* Read the 4 hardware interrupt registers */
+		intstat[i] = saa7164_readl(dev->int_status + (i * 4));
+
+		if (intstat[i])
+			handled = 1;
+	}
+	if (handled == 0)
+		goto out;
+
+	/* For each of the HW interrupt registers */
+	for (i = 0; i < INT_SIZE/4; i++) {
+
+		if (intstat[i]) {
+			/* Each function of the board has it's own interruptid.
+			 * Find the function that triggered then call
+			 * it's handler.
+			 */
+			for (bit = 0; bit < 32; bit++) {
+
+				if (((intstat[i] >> bit) & 0x00000001) == 0)
+					continue;
+
+				/* Calculate the interrupt id (0x00 to 0x7f) */
+
+				intid = (i * 32) + bit;
+				if (intid == dev->intfdesc.bInterruptId) {
+					/* A response to an cmd/api call */
+					schedule_work(&dev->workcmd);
+				} else if (intid == porta->hwcfg.interruptid) {
+
+					/* Transport path 1 */
+					saa7164_irq_ts(porta);
+
+				} else if (intid == portb->hwcfg.interruptid) {
+
+					/* Transport path 2 */
+					saa7164_irq_ts(portb);
+
+				} else if (intid == portc->hwcfg.interruptid) {
+
+					/* Encoder path 1 */
+					saa7164_irq_encoder(portc);
+
+				} else if (intid == portd->hwcfg.interruptid) {
+
+					/* Encoder path 2 */
+					saa7164_irq_encoder(portd);
+
+				} else if (intid == porte->hwcfg.interruptid) {
+
+					/* VBI path 1 */
+					saa7164_irq_vbi(porte);
+
+				} else if (intid == portf->hwcfg.interruptid) {
+
+					/* VBI path 2 */
+					saa7164_irq_vbi(portf);
+
+				} else {
+					/* Find the function */
+					dprintk(DBGLVL_IRQ,
+						"%s() unhandled interrupt "
+						"reg 0x%x bit 0x%x "
+						"intid = 0x%x\n",
+						__func__, i, bit, intid);
+				}
+			}
+
+			/* Ack it */
+			saa7164_writel(dev->int_ack + (i * 4), intstat[i]);
+
+		}
+	}
+out:
+	return IRQ_RETVAL(handled);
+}
+
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev)
+{
+	struct saa7164_fw_status *s = &dev->fw_status;
+
+	dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS);
+	dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE);
+	dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC);
+	dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST);
+	dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD);
+	dev->fw_status.remainheap =
+		saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP);
+
+	dprintk(1, "Firmware status:\n");
+	dprintk(1, " .status     = 0x%08x\n", s->status);
+	dprintk(1, " .mode       = 0x%08x\n", s->mode);
+	dprintk(1, " .spec       = 0x%08x\n", s->spec);
+	dprintk(1, " .inst       = 0x%08x\n", s->inst);
+	dprintk(1, " .cpuload    = 0x%08x\n", s->cpuload);
+	dprintk(1, " .remainheap = 0x%08x\n", s->remainheap);
+}
+
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev)
+{
+	u32 reg;
+
+	reg = saa7164_readl(SAA_DEVICE_VERSION);
+	dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n",
+		(reg & 0x0000fc00) >> 10,
+		(reg & 0x000003e0) >> 5,
+		(reg & 0x0000001f),
+		(reg & 0xffff0000) >> 16,
+		reg);
+
+	return reg;
+}
+
+/* TODO: Debugging func, remove */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr)
+{
+	int i;
+
+	dprintk(1, "--------------------> "
+		"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+	for (i = 0; i < 0x100; i += 16)
+		dprintk(1, "region0[0x%08x] = "
+			"%02x %02x %02x %02x %02x %02x %02x %02x"
+			" %02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+			(u8)saa7164_readb(addr + i + 0),
+			(u8)saa7164_readb(addr + i + 1),
+			(u8)saa7164_readb(addr + i + 2),
+			(u8)saa7164_readb(addr + i + 3),
+			(u8)saa7164_readb(addr + i + 4),
+			(u8)saa7164_readb(addr + i + 5),
+			(u8)saa7164_readb(addr + i + 6),
+			(u8)saa7164_readb(addr + i + 7),
+			(u8)saa7164_readb(addr + i + 8),
+			(u8)saa7164_readb(addr + i + 9),
+			(u8)saa7164_readb(addr + i + 10),
+			(u8)saa7164_readb(addr + i + 11),
+			(u8)saa7164_readb(addr + i + 12),
+			(u8)saa7164_readb(addr + i + 13),
+			(u8)saa7164_readb(addr + i + 14),
+			(u8)saa7164_readb(addr + i + 15)
+			);
+}
+
+static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
+{
+	dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
+		&dev->hwdesc, (u32)sizeof(struct tmComResHWDescr));
+
+	dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength);
+	dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType);
+	dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+		dev->hwdesc.bDescriptorSubtype);
+
+	dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion);
+	dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency);
+	dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes);
+	dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities);
+	dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n",
+		dev->hwdesc.dwDeviceRegistersLocation);
+
+	dprintk(1, " .dwHostMemoryRegion = 0x%x\n",
+		dev->hwdesc.dwHostMemoryRegion);
+
+	dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n",
+		dev->hwdesc.dwHostMemoryRegionSize);
+
+	dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n",
+		dev->hwdesc.dwHostHibernatMemRegion);
+
+	dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n",
+		dev->hwdesc.dwHostHibernatMemRegionSize);
+}
+
+static void saa7164_dump_intfdesc(struct saa7164_dev *dev)
+{
+	dprintk(1, "@0x%p intfdesc "
+		"sizeof(struct tmComResInterfaceDescr) = %d bytes\n",
+		&dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr));
+
+	dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength);
+	dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType);
+	dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+		dev->intfdesc.bDescriptorSubtype);
+
+	dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags);
+	dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType);
+	dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId);
+	dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface);
+	dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId);
+	dprintk(1, " .bDebugInterruptId = 0x%x\n",
+		dev->intfdesc.bDebugInterruptId);
+
+	dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation);
+}
+
+static void saa7164_dump_busdesc(struct saa7164_dev *dev)
+{
+	dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n",
+		&dev->busdesc, (u32)sizeof(struct tmComResBusDescr));
+
+	dprintk(1, " .CommandRing   = 0x%016Lx\n", dev->busdesc.CommandRing);
+	dprintk(1, " .ResponseRing  = 0x%016Lx\n", dev->busdesc.ResponseRing);
+	dprintk(1, " .CommandWrite  = 0x%x\n", dev->busdesc.CommandWrite);
+	dprintk(1, " .CommandRead   = 0x%x\n", dev->busdesc.CommandRead);
+	dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite);
+	dprintk(1, " .ResponseRead  = 0x%x\n", dev->busdesc.ResponseRead);
+}
+
+/* Much of the hardware configuration and PCI registers are configured
+ * dynamically depending on firmware. We have to cache some initial
+ * structures then use these to locate other important structures
+ * from PCI space.
+ */
+static void saa7164_get_descriptors(struct saa7164_dev *dev)
+{
+	memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr));
+	memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr),
+		sizeof(struct tmComResInterfaceDescr));
+	memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
+		sizeof(struct tmComResBusDescr));
+
+	if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) {
+		printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n");
+		printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength,
+			(u32)sizeof(struct tmComResHWDescr));
+	} else
+		saa7164_dump_hwdesc(dev);
+
+	if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
+		printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
+		printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
+			(u32)sizeof(struct tmComResInterfaceDescr));
+	} else
+		saa7164_dump_intfdesc(dev);
+
+	saa7164_dump_busdesc(dev);
+}
+
+static int saa7164_pci_quirks(struct saa7164_dev *dev)
+{
+	return 0;
+}
+
+static int get_resources(struct saa7164_dev *dev)
+{
+	if (request_mem_region(pci_resource_start(dev->pci, 0),
+		pci_resource_len(dev->pci, 0), dev->name)) {
+
+		if (request_mem_region(pci_resource_start(dev->pci, 2),
+			pci_resource_len(dev->pci, 2), dev->name))
+			return 0;
+	}
+
+	printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
+		dev->name,
+		(u64)pci_resource_start(dev->pci, 0),
+		(u64)pci_resource_start(dev->pci, 2));
+
+	return -EBUSY;
+}
+
+static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
+{
+	struct saa7164_port *port = NULL;
+
+	if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
+		BUG();
+
+	port = &dev->ports[portnr];
+
+	port->dev = dev;
+	port->nr = portnr;
+
+	if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2))
+		port->type = SAA7164_MPEG_DVB;
+	else
+	if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) {
+		port->type = SAA7164_MPEG_ENCODER;
+
+		/* We need a deferred interrupt handler for cmd handling */
+		INIT_WORK(&port->workenc, saa7164_work_enchandler);
+	} else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) {
+		port->type = SAA7164_MPEG_VBI;
+
+		/* We need a deferred interrupt handler for cmd handling */
+		INIT_WORK(&port->workenc, saa7164_work_vbihandler);
+	} else
+		BUG();
+
+	/* Init all the critical resources */
+	mutex_init(&port->dvb.lock);
+	INIT_LIST_HEAD(&port->dmaqueue.list);
+	mutex_init(&port->dmaqueue_lock);
+
+	INIT_LIST_HEAD(&port->list_buf_used.list);
+	INIT_LIST_HEAD(&port->list_buf_free.list);
+	init_waitqueue_head(&port->wait_read);
+
+
+	saa7164_histogram_reset(&port->irq_interval, "irq intervals");
+	saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
+	saa7164_histogram_reset(&port->irq_svc_interval,
+		"irq to deferred intervals");
+	saa7164_histogram_reset(&port->read_interval,
+		"encoder/vbi read() intervals");
+	saa7164_histogram_reset(&port->poll_interval,
+		"encoder/vbi poll() intervals");
+
+	return 0;
+}
+
+static int saa7164_dev_setup(struct saa7164_dev *dev)
+{
+	int i;
+
+	mutex_init(&dev->lock);
+	atomic_inc(&dev->refcount);
+	dev->nr = saa7164_devcount++;
+
+	snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
+
+	mutex_lock(&devlist);
+	list_add_tail(&dev->devlist, &saa7164_devlist);
+	mutex_unlock(&devlist);
+
+	/* board config */
+	dev->board = UNSET;
+	if (card[dev->nr] < saa7164_bcount)
+		dev->board = card[dev->nr];
+
+	for (i = 0; UNSET == dev->board  &&  i < saa7164_idcount; i++)
+		if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor &&
+			dev->pci->subsystem_device ==
+				saa7164_subids[i].subdevice)
+				dev->board = saa7164_subids[i].card;
+
+	if (UNSET == dev->board) {
+		dev->board = SAA7164_BOARD_UNKNOWN;
+		saa7164_card_list(dev);
+	}
+
+	dev->pci_bus  = dev->pci->bus->number;
+	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+
+	/* I2C Defaults / setup */
+	dev->i2c_bus[0].dev = dev;
+	dev->i2c_bus[0].nr = 0;
+	dev->i2c_bus[1].dev = dev;
+	dev->i2c_bus[1].nr = 1;
+	dev->i2c_bus[2].dev = dev;
+	dev->i2c_bus[2].nr = 2;
+
+	/* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */
+	saa7164_port_init(dev, SAA7164_PORT_TS1);
+	saa7164_port_init(dev, SAA7164_PORT_TS2);
+	saa7164_port_init(dev, SAA7164_PORT_ENC1);
+	saa7164_port_init(dev, SAA7164_PORT_ENC2);
+	saa7164_port_init(dev, SAA7164_PORT_VBI1);
+	saa7164_port_init(dev, SAA7164_PORT_VBI2);
+
+	if (get_resources(dev) < 0) {
+		printk(KERN_ERR "CORE %s No more PCIe resources for "
+		       "subsystem: %04x:%04x\n",
+		       dev->name, dev->pci->subsystem_vendor,
+		       dev->pci->subsystem_device);
+
+		saa7164_devcount--;
+		return -ENODEV;
+	}
+
+	/* PCI/e allocations */
+	dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
+			     pci_resource_len(dev->pci, 0));
+
+	dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
+			     pci_resource_len(dev->pci, 2));
+
+	dev->bmmio = (u8 __iomem *)dev->lmmio;
+	dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
+
+	/* Inerrupt and ack register locations offset of bmmio */
+	dev->int_status = 0x183000 + 0xf80;
+	dev->int_ack = 0x183000 + 0xf90;
+
+	printk(KERN_INFO
+		"CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+	       dev->name, dev->pci->subsystem_vendor,
+	       dev->pci->subsystem_device, saa7164_boards[dev->board].name,
+	       dev->board, card[dev->nr] == dev->board ?
+	       "insmod option" : "autodetected");
+
+	saa7164_pci_quirks(dev);
+
+	return 0;
+}
+
+static void saa7164_dev_unregister(struct saa7164_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	release_mem_region(pci_resource_start(dev->pci, 0),
+		pci_resource_len(dev->pci, 0));
+
+	release_mem_region(pci_resource_start(dev->pci, 2),
+		pci_resource_len(dev->pci, 2));
+
+	if (!atomic_dec_and_test(&dev->refcount))
+		return;
+
+	iounmap(dev->lmmio);
+	iounmap(dev->lmmio2);
+
+	return;
+}
+
+#ifdef CONFIG_PROC_FS
+static int saa7164_proc_show(struct seq_file *m, void *v)
+{
+	struct saa7164_dev *dev;
+	struct tmComResBusInfo *b;
+	struct list_head *list;
+	int i, c;
+
+	if (saa7164_devcount == 0)
+		return 0;
+
+	list_for_each(list, &saa7164_devlist) {
+		dev = list_entry(list, struct saa7164_dev, devlist);
+		seq_printf(m, "%s = %p\n", dev->name, dev);
+
+		/* Lock the bus from any other access */
+		b = &dev->bus;
+		mutex_lock(&b->lock);
+
+		seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n",
+			b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+		seq_printf(m, " .m_pdwSetReadPos  = 0x%x (0x%08x)\n",
+			b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+		seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n",
+			b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+		seq_printf(m, " .m_pdwGetReadPos  = 0x%x (0x%08x)\n",
+			b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+		c = 0;
+		seq_printf(m, "\n  Set Ring:\n");
+		seq_printf(m, "\n addr  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+		for (i = 0; i < b->m_dwSizeSetRing; i++) {
+			if (c == 0)
+				seq_printf(m, " %04x:", i);
+
+			seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
+
+			if (++c == 16) {
+				seq_printf(m, "\n");
+				c = 0;
+			}
+		}
+
+		c = 0;
+		seq_printf(m, "\n  Get Ring:\n");
+		seq_printf(m, "\n addr  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+		for (i = 0; i < b->m_dwSizeGetRing; i++) {
+			if (c == 0)
+				seq_printf(m, " %04x:", i);
+
+			seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
+
+			if (++c == 16) {
+				seq_printf(m, "\n");
+				c = 0;
+			}
+		}
+
+		mutex_unlock(&b->lock);
+
+	}
+
+	return 0;
+}
+
+static int saa7164_proc_open(struct inode *inode, struct file *filp)
+{
+	return single_open(filp, saa7164_proc_show, NULL);
+}
+
+static const struct file_operations saa7164_proc_fops = {
+	.open		= saa7164_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int saa7164_proc_create(void)
+{
+	struct proc_dir_entry *pe;
+
+	pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops);
+	if (!pe)
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
+
+static int saa7164_thread_function(void *data)
+{
+	struct saa7164_dev *dev = data;
+	struct tmFwInfoStruct fwinfo;
+	u64 last_poll_time = 0;
+
+	dprintk(DBGLVL_THR, "thread started\n");
+
+	set_freezable();
+
+	while (1) {
+		msleep_interruptible(100);
+		if (kthread_should_stop())
+			break;
+		try_to_freeze();
+
+		dprintk(DBGLVL_THR, "thread running\n");
+
+		/* Dump the firmware debug message to console */
+		/* Polling this costs us 1-2% of the arm CPU */
+		/* convert this into a respnde to interrupt 0x7a */
+		saa7164_api_collect_debug(dev);
+
+		/* Monitor CPU load every 1 second */
+		if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) {
+			saa7164_api_get_load_info(dev, &fwinfo);
+			last_poll_time = jiffies_to_msecs(jiffies);
+		}
+
+	}
+
+	dprintk(DBGLVL_THR, "thread exiting\n");
+	return 0;
+}
+
+static int __devinit saa7164_initdev(struct pci_dev *pci_dev,
+				     const struct pci_device_id *pci_id)
+{
+	struct saa7164_dev *dev;
+	int err, i;
+	u32 version;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (NULL == dev)
+		return -ENOMEM;
+
+	/* pci init */
+	dev->pci = pci_dev;
+	if (pci_enable_device(pci_dev)) {
+		err = -EIO;
+		goto fail_free;
+	}
+
+	if (saa7164_dev_setup(dev) < 0) {
+		err = -EINVAL;
+		goto fail_free;
+	}
+
+	/* print pci info */
+	dev->pci_rev = pci_dev->revision;
+	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+	printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+	       "latency: %d, mmio: 0x%llx\n", dev->name,
+	       pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+	       dev->pci_lat,
+		(unsigned long long)pci_resource_start(pci_dev, 0));
+
+	pci_set_master(pci_dev);
+	/* TODO */
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+		err = -EIO;
+		goto fail_irq;
+	}
+
+	err = request_irq(pci_dev->irq, saa7164_irq,
+		IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+	if (err < 0) {
+		printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name,
+			pci_dev->irq);
+		err = -EIO;
+		goto fail_irq;
+	}
+
+	pci_set_drvdata(pci_dev, dev);
+
+	/* Init the internal command list */
+	for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+		dev->cmds[i].seqno = i;
+		dev->cmds[i].inuse = 0;
+		mutex_init(&dev->cmds[i].lock);
+		init_waitqueue_head(&dev->cmds[i].wait);
+	}
+
+	/* We need a deferred interrupt handler for cmd handling */
+	INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler);
+
+	/* Only load the firmware if we know the board */
+	if (dev->board != SAA7164_BOARD_UNKNOWN) {
+
+		err = saa7164_downloadfirmware(dev);
+		if (err < 0) {
+			printk(KERN_ERR
+				"Failed to boot firmware, no features "
+				"registered\n");
+			goto fail_fw;
+		}
+
+		saa7164_get_descriptors(dev);
+		saa7164_dumpregs(dev, 0);
+		saa7164_getcurrentfirmwareversion(dev);
+		saa7164_getfirmwarestatus(dev);
+		err = saa7164_bus_setup(dev);
+		if (err < 0)
+			printk(KERN_ERR
+				"Failed to setup the bus, will continue\n");
+		saa7164_bus_dump(dev);
+
+		/* Ping the running firmware via the command bus and get the
+		 * firmware version, this checks the bus is running OK.
+		 */
+		version = 0;
+		if (saa7164_api_get_fw_version(dev, &version) == SAA_OK)
+			dprintk(1, "Bus is operating correctly using "
+				"version %d.%d.%d.%d (0x%x)\n",
+				(version & 0x0000fc00) >> 10,
+				(version & 0x000003e0) >> 5,
+				(version & 0x0000001f),
+				(version & 0xffff0000) >> 16,
+				version);
+		else
+			printk(KERN_ERR
+				"Failed to communicate with the firmware\n");
+
+		/* Bring up the I2C buses */
+		saa7164_i2c_register(&dev->i2c_bus[0]);
+		saa7164_i2c_register(&dev->i2c_bus[1]);
+		saa7164_i2c_register(&dev->i2c_bus[2]);
+		saa7164_gpio_setup(dev);
+		saa7164_card_setup(dev);
+
+		/* Parse the dynamic device configuration, find various
+		 * media endpoints (MPEG, WMV, PS, TS) and cache their
+		 * configuration details into the driver, so we can
+		 * reference them later during simething_register() func,
+		 * interrupt handlers, deferred work handlers etc.
+		 */
+		saa7164_api_enum_subdevs(dev);
+
+		/* Begin to create the video sub-systems and register funcs */
+		if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) {
+			if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) {
+				printk(KERN_ERR "%s() Failed to register "
+					"dvb adapters on porta\n",
+					__func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) {
+			if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"dvb adapters on portb\n",
+					__func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) {
+			if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"mpeg encoder\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) {
+			if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"mpeg encoder\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) {
+			if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"vbi device\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) {
+			if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"vbi device\n", __func__);
+			}
+		}
+		saa7164_api_set_debug(dev, fw_debug);
+
+		if (fw_debug) {
+			dev->kthread = kthread_run(saa7164_thread_function, dev,
+				"saa7164 debug");
+			if (!dev->kthread)
+				printk(KERN_ERR "%s() Failed to create "
+					"debug kernel thread\n", __func__);
+		}
+
+	} /* != BOARD_UNKNOWN */
+	else
+		printk(KERN_ERR "%s() Unsupported board detected, "
+			"registering without firmware\n", __func__);
+
+	dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug);
+	dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs);
+
+fail_fw:
+	return 0;
+
+fail_irq:
+	saa7164_dev_unregister(dev);
+fail_free:
+	kfree(dev);
+	return err;
+}
+
+static void saa7164_shutdown(struct saa7164_dev *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+}
+
+static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
+{
+	struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
+
+	if (dev->board != SAA7164_BOARD_UNKNOWN) {
+		if (fw_debug && dev->kthread) {
+			kthread_stop(dev->kthread);
+			dev->kthread = NULL;
+		}
+		if (dev->firmwareloaded)
+			saa7164_api_set_debug(dev, 0x00);
+	}
+
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].irq_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].svc_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].irq_svc_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].read_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].poll_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1],
+		&dev->ports[SAA7164_PORT_VBI1].read_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2],
+		&dev->ports[SAA7164_PORT_VBI2].poll_interval);
+
+	saa7164_shutdown(dev);
+
+	if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
+		saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]);
+
+	if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB)
+		saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]);
+
+	if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER)
+		saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]);
+
+	if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER)
+		saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]);
+
+	if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI)
+		saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]);
+
+	if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI)
+		saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]);
+
+	saa7164_i2c_unregister(&dev->i2c_bus[0]);
+	saa7164_i2c_unregister(&dev->i2c_bus[1]);
+	saa7164_i2c_unregister(&dev->i2c_bus[2]);
+
+	pci_disable_device(pci_dev);
+
+	/* unregister stuff */
+	free_irq(pci_dev->irq, dev);
+	pci_set_drvdata(pci_dev, NULL);
+
+	mutex_lock(&devlist);
+	list_del(&dev->devlist);
+	mutex_unlock(&devlist);
+
+	saa7164_dev_unregister(dev);
+	kfree(dev);
+}
+
+static struct pci_device_id saa7164_pci_tbl[] = {
+	{
+		/* SAA7164 */
+		.vendor       = 0x1131,
+		.device       = 0x7164,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	}, {
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl);
+
+static struct pci_driver saa7164_pci_driver = {
+	.name     = "saa7164",
+	.id_table = saa7164_pci_tbl,
+	.probe    = saa7164_initdev,
+	.remove   = __devexit_p(saa7164_finidev),
+	/* TODO */
+	.suspend  = NULL,
+	.resume   = NULL,
+};
+
+static int __init saa7164_init(void)
+{
+	printk(KERN_INFO "saa7164 driver loaded\n");
+
+#ifdef CONFIG_PROC_FS
+	saa7164_proc_create();
+#endif
+	return pci_register_driver(&saa7164_pci_driver);
+}
+
+static void __exit saa7164_fini(void)
+{
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("saa7164", NULL);
+#endif
+	pci_unregister_driver(&saa7164_pci_driver);
+}
+
+module_init(saa7164_init);
+module_exit(saa7164_fini);
+
diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
new file mode 100644
index 000000000000..5c5cc3ebf9bd
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-dvb.c
@@ -0,0 +1,556 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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 "saa7164.h"
+
+#include "tda10048.h"
+#include "tda18271.h"
+#include "s5h1411.h"
+
+#define DRIVER_NAME "saa7164"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* addr is in the card struct, get it from there */
+static struct tda10048_config hauppauge_hvr2200_1_config = {
+	.demod_address    = 0x10 >> 1,
+	.output_mode      = TDA10048_SERIAL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3500,
+	.dtv8_if_freq_khz = TDA10048_IF_4000,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+};
+static struct tda10048_config hauppauge_hvr2200_2_config = {
+	.demod_address    = 0x12 >> 1,
+	.output_mode      = TDA10048_SERIAL_OUTPUT,
+	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
+	.inversion        = TDA10048_INVERSION_ON,
+	.dtv6_if_freq_khz = TDA10048_IF_3300,
+	.dtv7_if_freq_khz = TDA10048_IF_3500,
+	.dtv8_if_freq_khz = TDA10048_IF_4000,
+	.clk_freq_khz     = TDA10048_CLK_16000,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+		      .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config hauppauge_hvr22x0_tuner_config = {
+	.std_map	= &hauppauge_tda18271_std_map,
+	.gate		= TDA18271_GATE_ANALOG,
+	.role		= TDA18271_MASTER,
+};
+
+static struct tda18271_config hauppauge_hvr22x0s_tuner_config = {
+	.std_map	= &hauppauge_tda18271_std_map,
+	.gate		= TDA18271_GATE_ANALOG,
+	.role		= TDA18271_SLAVE,
+	.output_opt     = TDA18271_OUTPUT_LT_OFF,
+	.rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config hauppauge_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_ON,
+	.qam_if        = S5H1411_IF_4000,
+	.vsb_if        = S5H1411_IF_3250,
+	.inversion     = S5H1411_INVERSION_ON,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static int saa7164_dvb_stop_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_DVB, "%s()    Stopped\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_dvb_acquire_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_dvb_pause_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_DVB, "%s()   Paused\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ */
+static int saa7164_dvb_stop_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct list_head *p, *q;
+	int ret;
+
+	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+	ret = saa7164_dvb_pause_port(port);
+	ret = saa7164_dvb_acquire_port(port);
+	ret = saa7164_dvb_stop_port(port);
+
+	/* Mark the hardware buffers as free */
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(p, q, &port->dmaqueue.list) {
+		buf = list_entry(p, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	return ret;
+}
+
+static int saa7164_dvb_start_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0, result;
+
+	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+	saa7164_buffer_cfg_port(port);
+
+	/* Acquire the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+			__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() acquire/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_DVB, "%s()   Acquired\n", __func__);
+
+	/* Pause the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() pause/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_DVB, "%s()   Paused\n", __func__);
+
+	/* Start the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() run/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+	} else
+		dprintk(DBGLVL_DVB, "%s()   Running\n", __func__);
+
+out:
+	return ret;
+}
+
+static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct saa7164_port *port = (struct saa7164_port *) demux->priv;
+	struct saa7164_dvb *dvb = &port->dvb;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	if (dvb) {
+		mutex_lock(&dvb->lock);
+		if (dvb->feeding++ == 0) {
+			/* Start transport */
+			ret = saa7164_dvb_start_port(port);
+		}
+		mutex_unlock(&dvb->lock);
+		dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
+			__func__, port->nr, dvb->feeding);
+	}
+
+	return ret;
+}
+
+static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct saa7164_port *port = (struct saa7164_port *) demux->priv;
+	struct saa7164_dvb *dvb = &port->dvb;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+	if (dvb) {
+		mutex_lock(&dvb->lock);
+		if (--dvb->feeding == 0) {
+			/* Stop transport */
+			ret = saa7164_dvb_stop_streaming(port);
+		}
+		mutex_unlock(&dvb->lock);
+		dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
+			__func__, port->nr, dvb->feeding);
+	}
+
+	return ret;
+}
+
+static int dvb_register(struct saa7164_port *port)
+{
+	struct saa7164_dvb *dvb = &port->dvb;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	int result, i;
+
+	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+	if (port->type != SAA7164_MPEG_DVB)
+		BUG();
+
+	/* Sanity check that the PCI configuration space is active */
+	if (port->hwcfg.BARLocation == 0) {
+		result = -ENOMEM;
+		printk(KERN_ERR "%s: dvb_register_adapter failed "
+		       "(errno = %d), NO PCI configuration\n",
+			DRIVER_NAME, result);
+		goto fail_adapter;
+	}
+
+	/* Init and establish defaults */
+	port->hw_streamingparams.bitspersample = 8;
+	port->hw_streamingparams.samplesperline = 188;
+	port->hw_streamingparams.numberoflines =
+		(SAA7164_TS_NUMBER_OF_LINES * 188) / 188;
+
+	port->hw_streamingparams.pitch = 188;
+	port->hw_streamingparams.linethreshold = 0;
+	port->hw_streamingparams.pagetablelistvirt = NULL;
+	port->hw_streamingparams.pagetablelistphys = NULL;
+	port->hw_streamingparams.numpagetables = 2 +
+		((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+
+	port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			port->hw_streamingparams.numberoflines *
+			port->hw_streamingparams.pitch);
+
+		if (!buf) {
+			result = -ENOMEM;
+			printk(KERN_ERR "%s: dvb_register_adapter failed "
+			       "(errno = %d), unable to allocate buffers\n",
+				DRIVER_NAME, result);
+			goto fail_adapter;
+		}
+
+		mutex_lock(&port->dmaqueue_lock);
+		list_add_tail(&buf->list, &port->dmaqueue.list);
+		mutex_unlock(&port->dmaqueue_lock);
+	}
+
+	/* register adapter */
+	result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
+			&dev->pci->dev, adapter_nr);
+	if (result < 0) {
+		printk(KERN_ERR "%s: dvb_register_adapter failed "
+		       "(errno = %d)\n", DRIVER_NAME, result);
+		goto fail_adapter;
+	}
+	dvb->adapter.priv = port;
+
+	/* register frontend */
+	result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+	if (result < 0) {
+		printk(KERN_ERR "%s: dvb_register_frontend failed "
+		       "(errno = %d)\n", DRIVER_NAME, result);
+		goto fail_frontend;
+	}
+
+	/* register demux stuff */
+	dvb->demux.dmx.capabilities =
+		DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+		DMX_MEMORY_BASED_FILTERING;
+	dvb->demux.priv       = port;
+	dvb->demux.filternum  = 256;
+	dvb->demux.feednum    = 256;
+	dvb->demux.start_feed = saa7164_dvb_start_feed;
+	dvb->demux.stop_feed  = saa7164_dvb_stop_feed;
+	result = dvb_dmx_init(&dvb->demux);
+	if (result < 0) {
+		printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
+		       DRIVER_NAME, result);
+		goto fail_dmx;
+	}
+
+	dvb->dmxdev.filternum    = 256;
+	dvb->dmxdev.demux        = &dvb->demux.dmx;
+	dvb->dmxdev.capabilities = 0;
+	result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+	if (result < 0) {
+		printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
+		       DRIVER_NAME, result);
+		goto fail_dmxdev;
+	}
+
+	dvb->fe_hw.source = DMX_FRONTEND_0;
+	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	if (result < 0) {
+		printk(KERN_ERR "%s: add_frontend failed "
+		       "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
+		goto fail_fe_hw;
+	}
+
+	dvb->fe_mem.source = DMX_MEMORY_FE;
+	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+	if (result < 0) {
+		printk(KERN_ERR "%s: add_frontend failed "
+		       "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
+		goto fail_fe_mem;
+	}
+
+	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	if (result < 0) {
+		printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
+		       DRIVER_NAME, result);
+		goto fail_fe_conn;
+	}
+
+	/* register network adapter */
+	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+	return 0;
+
+fail_fe_conn:
+	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+	dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+	dvb_dmx_release(&dvb->demux);
+fail_dmx:
+	dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+	dvb_frontend_detach(dvb->frontend);
+	dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+	return result;
+}
+
+int saa7164_dvb_unregister(struct saa7164_port *port)
+{
+	struct saa7164_dvb *dvb = &port->dvb;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *b;
+	struct list_head *c, *n;
+
+	dprintk(DBGLVL_DVB, "%s()\n", __func__);
+
+	if (port->type != SAA7164_MPEG_DVB)
+		BUG();
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		b = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(b);
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	if (dvb->frontend == NULL)
+		return 0;
+
+	dvb_net_release(&dvb->net);
+	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	dvb_dmxdev_release(&dvb->dmxdev);
+	dvb_dmx_release(&dvb->demux);
+	dvb_unregister_frontend(dvb->frontend);
+	dvb_frontend_detach(dvb->frontend);
+	dvb_unregister_adapter(&dvb->adapter);
+	return 0;
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card.
+ */
+int saa7164_dvb_register(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_dvb *dvb = &port->dvb;
+	struct saa7164_i2c *i2c_bus = NULL;
+	int ret;
+
+	dprintk(DBGLVL_DVB, "%s()\n", __func__);
+
+	/* init frontend */
+	switch (dev->board) {
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+		i2c_bus = &dev->i2c_bus[port->nr + 1];
+		switch (port->nr) {
+		case 0:
+			port->dvb.frontend = dvb_attach(tda10048_attach,
+				&hauppauge_hvr2200_1_config,
+				&i2c_bus->i2c_adap);
+
+			if (port->dvb.frontend != NULL) {
+				/* TODO: addr is in the card struct */
+				dvb_attach(tda18271_attach, port->dvb.frontend,
+					0xc0 >> 1, &i2c_bus->i2c_adap,
+					&hauppauge_hvr22x0_tuner_config);
+			}
+
+			break;
+		case 1:
+			port->dvb.frontend = dvb_attach(tda10048_attach,
+				&hauppauge_hvr2200_2_config,
+				&i2c_bus->i2c_adap);
+
+			if (port->dvb.frontend != NULL) {
+				/* TODO: addr is in the card struct */
+				dvb_attach(tda18271_attach, port->dvb.frontend,
+					0xc0 >> 1, &i2c_bus->i2c_adap,
+					&hauppauge_hvr22x0s_tuner_config);
+			}
+
+			break;
+		}
+		break;
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+	case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+		i2c_bus = &dev->i2c_bus[port->nr + 1];
+
+		port->dvb.frontend = dvb_attach(s5h1411_attach,
+			&hauppauge_s5h1411_config,
+			&i2c_bus->i2c_adap);
+
+		if (port->dvb.frontend != NULL) {
+			if (port->nr == 0) {
+				/* Master TDA18271 */
+				/* TODO: addr is in the card struct */
+				dvb_attach(tda18271_attach, port->dvb.frontend,
+					0xc0 >> 1, &i2c_bus->i2c_adap,
+					&hauppauge_hvr22x0_tuner_config);
+			} else {
+				/* Slave TDA18271 */
+				dvb_attach(tda18271_attach, port->dvb.frontend,
+					0xc0 >> 1, &i2c_bus->i2c_adap,
+					&hauppauge_hvr22x0s_tuner_config);
+			}
+		}
+
+		break;
+	default:
+		printk(KERN_ERR "%s: The frontend isn't supported\n",
+		       dev->name);
+		break;
+	}
+	if (NULL == dvb->frontend) {
+		printk(KERN_ERR "%s() Frontend initialization failed\n",
+		       __func__);
+		return -1;
+	}
+
+	/* register everything */
+	ret = dvb_register(port);
+	if (ret < 0) {
+		if (dvb->frontend->ops.release)
+			dvb->frontend->ops.release(dvb->frontend);
+		return ret;
+	}
+
+	return 0;
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c
new file mode 100644
index 000000000000..a9ed686ad08a
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-encoder.c
@@ -0,0 +1,1500 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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 "saa7164.h"
+
+#define ENCODER_MAX_BITRATE 6500000
+#define ENCODER_MIN_BITRATE 1000000
+#define ENCODER_DEF_BITRATE 5000000
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+	V4L2_CID_BRIGHTNESS,
+	V4L2_CID_CONTRAST,
+	V4L2_CID_SATURATION,
+	V4L2_CID_HUE,
+	V4L2_CID_AUDIO_VOLUME,
+	V4L2_CID_SHARPNESS,
+	V4L2_CID_MPEG_STREAM_TYPE,
+	V4L2_CID_MPEG_VIDEO_ASPECT,
+	V4L2_CID_MPEG_VIDEO_B_FRAMES,
+	V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+	V4L2_CID_MPEG_AUDIO_MUTE,
+	V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+	V4L2_CID_MPEG_VIDEO_BITRATE,
+	V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+	0
+};
+
+/* Take the encoder configuration form the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_encoder_configure(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	port->encoder_params.width = port->width;
+	port->encoder_params.height = port->height;
+	port->encoder_params.is_50hz =
+		(port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+	/* Set up the DIF (enable it) for analog mode by default */
+	saa7164_api_initialize_dif(port);
+
+	/* Configure the correct video standard */
+	saa7164_api_configure_dif(port, port->encodernorm.id);
+
+	/* Ensure the audio decoder is correct configured */
+	saa7164_api_set_audio_std(port);
+}
+
+static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
+{
+	struct list_head *c, *n, *p, *q, *l, *v;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(buf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
+	list_for_each_safe(p, q, &port->list_buf_used.list) {
+		ubuf = list_entry(p, struct saa7164_user_buffer, list);
+		list_del(p);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
+	list_for_each_safe(l, v, &port->list_buf_free.list) {
+		ubuf = list_entry(l, struct saa7164_user_buffer, list);
+		list_del(l);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+
+	return 0;
+}
+
+/* Dynamic buffer switch at encoder start time */
+static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	int result = -ENODEV, i;
+	int len = 0;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (port->encoder_params.stream_type ==
+		V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
+		dprintk(DBGLVL_ENC,
+			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n",
+			__func__);
+		params->samplesperline = 128;
+		params->numberoflines = 256;
+		params->pitch = 128;
+		params->numpagetables = 2 +
+			((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
+	} else
+	if (port->encoder_params.stream_type ==
+		V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
+		dprintk(DBGLVL_ENC,
+			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n",
+			__func__);
+		params->samplesperline = 188;
+		params->numberoflines = 312;
+		params->pitch = 188;
+		params->numpagetables = 2 +
+			((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+	} else
+		BUG();
+
+	/* Init and establish defaults */
+	params->bitspersample = 8;
+	params->linethreshold = 0;
+	params->pagetablelistvirt = NULL;
+	params->pagetablelistphys = NULL;
+	params->numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources, buffers (hard) */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			params->numberoflines *
+			params->pitch);
+
+		if (!buf) {
+			printk(KERN_ERR "%s() failed "
+			       "(errno = %d), unable to allocate buffer\n",
+				__func__, result);
+			result = -ENOMEM;
+			goto failed;
+		} else {
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&buf->list, &port->dmaqueue.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+		}
+	}
+
+	/* Allocate some kernel buffers for copying
+	 * to userpsace.
+	 */
+	len = params->numberoflines * params->pitch;
+
+	if (encoder_buffers < 16)
+		encoder_buffers = 16;
+	if (encoder_buffers > 512)
+		encoder_buffers = 512;
+
+	for (i = 0; i < encoder_buffers; i++) {
+
+		ubuf = saa7164_buffer_alloc_user(dev, len);
+		if (ubuf) {
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+		}
+
+	}
+
+	result = 0;
+
+failed:
+	return result;
+}
+
+static int saa7164_encoder_initialize(struct saa7164_port *port)
+{
+	saa7164_encoder_configure(port);
+	return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	unsigned int i;
+
+	dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+		if (*id & saa7164_tvnorms[i].id)
+			break;
+	}
+	if (i == ARRAY_SIZE(saa7164_tvnorms))
+		return -EINVAL;
+
+	port->encodernorm = saa7164_tvnorms[i];
+
+	/* Update the audio decoder while is not running in
+	 * auto detect mode.
+	 */
+	saa7164_api_set_audio_std(port);
+
+	dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+	struct v4l2_input *i)
+{
+	int n;
+
+	char *inputs[] = { "tuner", "composite", "svideo", "aux",
+		"composite 2", "svideo 2", "aux 2" };
+
+	if (i->index >= 7)
+		return -EINVAL;
+
+	strcpy(i->name, inputs[i->index]);
+
+	if (i->index == 0)
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	else
+		i->type  = V4L2_INPUT_TYPE_CAMERA;
+
+	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+		i->std |= saa7164_tvnorms[n].id;
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (saa7164_api_get_videomux(port) != SAA_OK)
+		return -EIO;
+
+	*i = (port->mux_input - 1);
+
+	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
+
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
+
+	if (i >= 7)
+		return -EINVAL;
+
+	port->mux_input = i + 1;
+
+	if (saa7164_api_set_videomux(port) != SAA_OK)
+		return -EIO;
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "tuner");
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	/* Update the A/V core */
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency = port->freq;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *tsport;
+	struct dvb_frontend *fe;
+
+	/* TODO: Pull this for the std */
+	struct analog_parameters params = {
+		.mode      = V4L2_TUNER_ANALOG_TV,
+		.audmode   = V4L2_TUNER_MODE_STEREO,
+		.std       = port->encodernorm.id,
+		.frequency = f->frequency
+	};
+
+	/* Stop the encoder */
+	dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
+		f->frequency, f->tuner);
+
+	if (f->tuner != 0)
+		return -EINVAL;
+
+	if (f->type != V4L2_TUNER_ANALOG_TV)
+		return -EINVAL;
+
+	port->freq = f->frequency;
+
+	/* Update the hardware */
+	if (port->nr == SAA7164_PORT_ENC1)
+		tsport = &dev->ports[SAA7164_PORT_TS1];
+	else
+	if (port->nr == SAA7164_PORT_ENC2)
+		tsport = &dev->ports[SAA7164_PORT_TS2];
+	else
+		BUG();
+
+	fe = tsport->dvb.frontend;
+
+	if (fe && fe->ops.tuner_ops.set_analog_params)
+		fe->ops.tuner_ops.set_analog_params(fe, &params);
+	else
+		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+	saa7164_encoder_initialize(port);
+
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctl->value = port->ctl_brightness;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctl->value = port->ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		ctl->value = port->ctl_saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctl->value = port->ctl_hue;
+		break;
+	case V4L2_CID_SHARPNESS:
+		ctl->value = port->ctl_sharpness;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctl->value = port->ctl_volume;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_brightness = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_BRIGHTNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_contrast = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SATURATION:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_saturation = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_SATURATION_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_HUE:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_hue = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SHARPNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_sharpness = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		if ((ctl->value >= -83) && (ctl->value <= 24)) {
+			port->ctl_volume = ctl->value;
+			saa7164_api_set_audio_volume(port, port->ctl_volume);
+		} else
+			ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_encoder_params *params = &port->encoder_params;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		ctrl->value = params->bitrate;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		ctrl->value = params->stream_type;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		ctrl->value = params->ctl_mute;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctrl->value = params->ctl_aspect;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		ctrl->value = params->bitrate_mode;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		ctrl->value = params->refdist;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		ctrl->value = params->bitrate_peak;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		ctrl->value = params->gop_size;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_get_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+	int ret = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+			(ctrl->value <= ENCODER_MAX_BITRATE))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+			(ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 1))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+			(ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 255))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ||
+			(ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		if ((ctrl->value >= 1) &&
+			(ctrl->value <= 3))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+			(ctrl->value <= ENCODER_MAX_BITRATE))
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_encoder_params *params = &port->encoder_params;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		params->bitrate = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		params->stream_type = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		params->ctl_mute = ctrl->value;
+		ret = saa7164_api_audio_mute(port, params->ctl_mute);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		params->ctl_aspect = ctrl->value;
+		ret = saa7164_api_set_aspect_ratio(port);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		params->bitrate_mode = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		params->refdist = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		params->bitrate_peak = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		params->gop_size = ctrl->value;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* TODO: Update the hardware */
+
+	return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+			err = saa7164_set_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+	struct v4l2_capability *cap)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	strcpy(cap->driver, dev->name);
+	strlcpy(cap->card, saa7164_boards[dev->board].name,
+		sizeof(cap->card));
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+	cap->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE     |
+		0;
+
+	cap->capabilities |= V4L2_CAP_TUNER;
+	cap->version = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = port->width;
+	f->fmt.pix.height       = port->height;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+	return 0;
+}
+
+static int fill_queryctrl(struct saa7164_encoder_params *params,
+	struct v4l2_queryctrl *c)
+{
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+	case V4L2_CID_CONTRAST:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+	case V4L2_CID_SATURATION:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+	case V4L2_CID_HUE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+	case V4L2_CID_SHARPNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+	case V4L2_CID_AUDIO_VOLUME:
+		return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		return v4l2_ctrl_query_fill(c,
+			ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+			100000, ENCODER_DEF_BITRATE);
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+			1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_ASPECT_1x1,
+			V4L2_MPEG_VIDEO_ASPECT_221x100,
+			1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+			V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+			1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		return v4l2_ctrl_query_fill(c,
+			1, 3, 1, 1);
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		return v4l2_ctrl_query_fill(c,
+			ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+			100000, ENCODER_DEF_BITRATE);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+	struct v4l2_queryctrl *c)
+{
+	struct saa7164_encoder_fh *fh = priv;
+	struct saa7164_port *port = fh->port;
+	int i, next;
+	u32 id = c->id;
+
+	memset(c, 0, sizeof(*c));
+
+	next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+	c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+		if (next) {
+			if (c->id < saa7164_v4l2_ctrls[i])
+				c->id = saa7164_v4l2_ctrls[i];
+			else
+				continue;
+		}
+
+		if (c->id == saa7164_v4l2_ctrls[i])
+			return fill_queryctrl(&port->encoder_params, c);
+
+		if (c->id < saa7164_v4l2_ctrls[i])
+			break;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_encoder_stop_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s()    Stopped\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_encoder_acquire_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_encoder_pause_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct list_head *c, *n;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	ret = saa7164_encoder_pause_port(port);
+	ret = saa7164_encoder_acquire_port(port);
+	ret = saa7164_encoder_stop_port(port);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
+		port->nr);
+
+	/* Reset the state of any allocated buffer resources */
+	mutex_lock(&port->dmaqueue_lock);
+
+	/* Reset the hard and soft buffer state */
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+		buf->pos = 0;
+	}
+
+	list_for_each_safe(c, n, &port->list_buf_used.list) {
+		ubuf = list_entry(c, struct saa7164_user_buffer, list);
+		ubuf->pos = 0;
+		list_move_tail(&ubuf->list, &port->list_buf_free.list);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+
+	/* Free any allocated resources */
+	saa7164_encoder_buffers_dealloc(port);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
+
+	return ret;
+}
+
+static int saa7164_encoder_start_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result, ret = 0;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	port->done_first_interrupt = 0;
+
+	/* allocate all of the PCIe DMA buffer resources on the fly,
+	 * allowing switching between TS and PS payloads without
+	 * requiring a complete driver reload.
+	 */
+	saa7164_encoder_buffers_alloc(port);
+
+	/* Configure the encoder with any cache values */
+	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
+
+	/* Place the empty buffers on the hardware */
+	saa7164_buffer_cfg_port(port);
+
+	/* Acquire the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+			__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() acquire/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Acquired\n", __func__);
+
+	/* Pause the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() pause/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
+
+	/* Start the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() run/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Running\n", __func__);
+
+out:
+	return ret;
+}
+
+static int fops_open(struct file *file)
+{
+	struct saa7164_dev *dev;
+	struct saa7164_port *port;
+	struct saa7164_encoder_fh *fh;
+
+	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+	if (!port)
+		return -ENODEV;
+
+	dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	file->private_data = fh;
+	fh->port = port;
+
+	return 0;
+}
+
+static int fops_release(struct file *file)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+			/* stop mpeg capture then cancel buffers */
+			saa7164_encoder_stop_streaming(port);
+		}
+	}
+
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
+{
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	u32 crc;
+
+	mutex_lock(&port->dmaqueue_lock);
+	if (!list_empty(&port->list_buf_used.list)) {
+		ubuf = list_first_entry(&port->list_buf_used.list,
+			struct saa7164_user_buffer, list);
+
+		if (crc_checking) {
+			crc = crc32(0, ubuf->data, ubuf->actual_size);
+			if (crc != ubuf->crc) {
+				printk(KERN_ERR
+		"%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
+					__func__,
+					ubuf, ubuf->crc, crc);
+			}
+		}
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
+
+	return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+	size_t count, loff_t *pos)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	int rem, cnt;
+	u8 *p;
+
+	port->last_read_msecs_diff = port->last_read_msecs;
+	port->last_read_msecs = jiffies_to_msecs(jiffies);
+	port->last_read_msecs_diff = port->last_read_msecs -
+		port->last_read_msecs_diff;
+
+	saa7164_histogram_update(&port->read_interval,
+		port->last_read_msecs_diff);
+
+	if (*pos) {
+		printk(KERN_ERR "%s() ESPIPE\n", __func__);
+		return -ESPIPE;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+			if (saa7164_encoder_initialize(port) < 0) {
+				printk(KERN_ERR "%s() EINVAL\n", __func__);
+				return -EINVAL;
+			}
+
+			saa7164_encoder_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_enc_next_buf(port))) {
+				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = saa7164_enc_next_buf(port);
+
+	while ((count > 0) && ubuf) {
+
+		/* set remaining bytes to copy */
+		rem = ubuf->actual_size - ubuf->pos;
+		cnt = rem > count ? count : rem;
+
+		p = ubuf->data + ubuf->pos;
+
+		dprintk(DBGLVL_ENC,
+			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+		if (copy_to_user(buffer, p, cnt)) {
+			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+			if (!ret) {
+				printk(KERN_ERR "%s() EFAULT\n", __func__);
+				ret = -EFAULT;
+			}
+			goto err;
+		}
+
+		ubuf->pos += cnt;
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if (ubuf->pos > ubuf->actual_size)
+			printk(KERN_ERR "read() pos > actual, huh?\n");
+
+		if (ubuf->pos == ubuf->actual_size) {
+
+			/* finished with current buffer, take next buffer */
+
+			/* Requeue the buffer on the free list */
+			ubuf->pos = 0;
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_move_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+			/* Dequeue next */
+			if ((file->f_flags & O_NONBLOCK) == 0) {
+				if (wait_event_interruptible(port->wait_read,
+					saa7164_enc_next_buf(port))) {
+						break;
+				}
+			}
+			ubuf = saa7164_enc_next_buf(port);
+		}
+	}
+err:
+	if (!ret && !ubuf)
+		ret = -EAGAIN;
+
+	return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+	struct saa7164_encoder_fh *fh =
+		(struct saa7164_encoder_fh *)file->private_data;
+	struct saa7164_port *port = fh->port;
+	unsigned int mask = 0;
+
+	port->last_poll_msecs_diff = port->last_poll_msecs;
+	port->last_poll_msecs = jiffies_to_msecs(jiffies);
+	port->last_poll_msecs_diff = port->last_poll_msecs -
+		port->last_poll_msecs_diff;
+
+	saa7164_histogram_update(&port->poll_interval,
+		port->last_poll_msecs_diff);
+
+	if (!video_is_registered(port->v4l_device))
+		return -EIO;
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+			if (saa7164_encoder_initialize(port) < 0)
+				return -EINVAL;
+			saa7164_encoder_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_enc_next_buf(port))) {
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	if (!list_empty(&port->list_buf_used.list))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static const struct v4l2_file_operations mpeg_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fops_open,
+	.release	= fops_release,
+	.read		= fops_read,
+	.poll		= fops_poll,
+	.unlocked_ioctl	= video_ioctl2,
+};
+
+int saa7164_g_chip_ident(struct file *file, void *fh,
+	struct v4l2_dbg_chip_ident *chip)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	return 0;
+}
+
+int saa7164_g_register(struct file *file, void *fh,
+	struct v4l2_dbg_register *reg)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
+int saa7164_s_register(struct file *file, void *fh,
+	struct v4l2_dbg_register *reg)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+	.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_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_g_ctrl		 = vidioc_g_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+	.vidioc_g_chip_ident	 = saa7164_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register	 = saa7164_g_register,
+	.vidioc_s_register	 = saa7164_s_register,
+#endif
+};
+
+static struct video_device saa7164_mpeg_template = {
+	.name          = "saa7164",
+	.fops          = &mpeg_fops,
+	.ioctl_ops     = &mpeg_ioctl_ops,
+	.minor         = -1,
+	.tvnorms       = SAA7164_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_encoder_alloc(
+	struct saa7164_port *port,
+	struct pci_dev *pci,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+
+	*vfd = *template;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+		type, saa7164_boards[dev->board].name);
+
+	vfd->parent  = &pci->dev;
+	vfd->release = video_device_release;
+	return vfd;
+}
+
+int saa7164_encoder_register(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result = -ENODEV;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (port->type != SAA7164_MPEG_ENCODER)
+		BUG();
+
+	/* Sanity check that the PCI configuration space is active */
+	if (port->hwcfg.BARLocation == 0) {
+		printk(KERN_ERR "%s() failed "
+		       "(errno = %d), NO PCI configuration\n",
+			__func__, result);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	/* Establish encoder defaults here */
+	/* Set default TV standard */
+	port->encodernorm = saa7164_tvnorms[0];
+	port->width = 720;
+	port->mux_input = 1; /* Composite */
+	port->video_format = EU_VIDEO_FORMAT_MPEG_2;
+	port->audio_format = 0;
+	port->video_resolution = 0;
+	port->ctl_brightness = 127;
+	port->ctl_contrast = 66;
+	port->ctl_hue = 128;
+	port->ctl_saturation = 62;
+	port->ctl_sharpness = 8;
+	port->encoder_params.bitrate = ENCODER_DEF_BITRATE;
+	port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE;
+	port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+	port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
+	port->encoder_params.ctl_mute = 0;
+	port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3;
+	port->encoder_params.refdist = 1;
+	port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE;
+
+	if (port->encodernorm.id & V4L2_STD_525_60)
+		port->height = 480;
+	else
+		port->height = 576;
+
+	/* Allocate and register the video device node */
+	port->v4l_device = saa7164_encoder_alloc(port,
+		dev->pci, &saa7164_mpeg_template, "mpeg");
+
+	if (!port->v4l_device) {
+		printk(KERN_INFO "%s: can't allocate mpeg device\n",
+			dev->name);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	video_set_drvdata(port->v4l_device, port);
+	result = video_register_device(port->v4l_device,
+		VFL_TYPE_GRABBER, -1);
+	if (result < 0) {
+		printk(KERN_INFO "%s: can't register mpeg device\n",
+			dev->name);
+		/* TODO: We're going to leak here if we don't dealloc
+		 The buffers above. The unreg function can't deal wit it.
+		*/
+		goto failed;
+	}
+
+	printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
+		dev->name, port->v4l_device->num);
+
+	/* Configure the hardware defaults */
+	saa7164_api_set_videomux(port);
+	saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+	saa7164_api_audio_mute(port, 0);
+	saa7164_api_set_audio_volume(port, 20);
+	saa7164_api_set_aspect_ratio(port);
+
+	/* Disable audio standard detection, it's buggy */
+	saa7164_api_set_audio_detection(port, 0);
+
+	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
+
+	result = 0;
+failed:
+	return result;
+}
+
+void saa7164_encoder_unregister(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	if (port->type != SAA7164_MPEG_ENCODER)
+		BUG();
+
+	if (port->v4l_device) {
+		if (port->v4l_device->minor != -1)
+			video_unregister_device(port->v4l_device);
+		else
+			video_device_release(port->v4l_device);
+
+		port->v4l_device = NULL;
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+}
+
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
new file mode 100644
index 000000000000..a266bf0169e6
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -0,0 +1,613 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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/firmware.h>
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+#define SAA7164_REV2_FIRMWARE		"NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV2_FIRMWARE_SIZE	4019072
+
+#define SAA7164_REV3_FIRMWARE		"NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV3_FIRMWARE_SIZE	4019072
+
+struct fw_header {
+	u32	firmwaresize;
+	u32	bslsize;
+	u32	reserved;
+	u32	version;
+};
+
+int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
+{
+	u32 timeout = SAA_DEVICE_TIMEOUT;
+	while ((saa7164_readl(reg) & 0x01) == 0) {
+		timeout -= 10;
+		if (timeout == 0) {
+			printk(KERN_ERR "%s() timeout (no d/l ack)\n",
+				__func__);
+			return -EBUSY;
+		}
+		msleep(100);
+	}
+
+	return 0;
+}
+
+int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
+{
+	u32 timeout = SAA_DEVICE_TIMEOUT;
+	while (saa7164_readl(reg) & 0x01) {
+		timeout -= 10;
+		if (timeout == 0) {
+			printk(KERN_ERR "%s() timeout (no d/l clr)\n",
+				__func__);
+			return -EBUSY;
+		}
+		msleep(100);
+	}
+
+	return 0;
+}
+
+/* TODO: move dlflags into dev-> and change to write/readl/b */
+/* TODO: Excessive levels of debug */
+int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
+	u32 dlflags, u8 *dst, u32 dstsize)
+{
+	u32 reg, timeout, offset;
+	u8 *srcbuf = NULL;
+	int ret;
+
+	u32 dlflag = dlflags;
+	u32 dlflag_ack = dlflag + 4;
+	u32 drflag = dlflag_ack + 4;
+	u32 drflag_ack = drflag + 4;
+	u32 bleflag = drflag_ack + 4;
+
+	dprintk(DBGLVL_FW,
+		"%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n",
+		__func__, src, srcsize, dlflags, dst, dstsize);
+
+	if ((src == NULL) || (dst == NULL)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	srcbuf = kzalloc(4 * 1048576, GFP_KERNEL);
+	if (NULL == srcbuf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (srcsize > (4*1048576)) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(srcbuf, src, srcsize);
+
+	dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag);
+	dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack);
+	dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag);
+	dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack);
+	dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag);
+
+	reg = saa7164_readl(dlflag);
+	dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg);
+	if (reg == 1)
+		dprintk(DBGLVL_FW,
+			"%s() Download flag already set, please reboot\n",
+			__func__);
+
+	/* Indicate download start */
+	saa7164_writel(dlflag, 1);
+	ret = saa7164_dl_wait_ack(dev, dlflag_ack);
+	if (ret < 0)
+		goto out;
+
+	/* Ack download start, then wait for wait */
+	saa7164_writel(dlflag, 0);
+	ret = saa7164_dl_wait_clr(dev, dlflag_ack);
+	if (ret < 0)
+		goto out;
+
+	/* Deal with the raw firmware, in the appropriate chunk size */
+	for (offset = 0; srcsize > dstsize;
+		srcsize -= dstsize, offset += dstsize) {
+
+		dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize);
+		memcpy(dst, srcbuf + offset, dstsize);
+
+		/* Flag the data as ready */
+		saa7164_writel(drflag, 1);
+		ret = saa7164_dl_wait_ack(dev, drflag_ack);
+		if (ret < 0)
+			goto out;
+
+		/* Wait for indication data was received */
+		saa7164_writel(drflag, 0);
+		ret = saa7164_dl_wait_clr(dev, drflag_ack);
+		if (ret < 0)
+			goto out;
+
+	}
+
+	dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize);
+	/* Write last block to the device */
+	memcpy(dst, srcbuf+offset, srcsize);
+
+	/* Flag the data as ready */
+	saa7164_writel(drflag, 1);
+	ret = saa7164_dl_wait_ack(dev, drflag_ack);
+	if (ret < 0)
+		goto out;
+
+	saa7164_writel(drflag, 0);
+	timeout = 0;
+	while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) {
+		if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) {
+			printk(KERN_ERR "%s() image corrupt\n", __func__);
+			ret = -EBUSY;
+			goto out;
+		}
+
+		if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) {
+			printk(KERN_ERR "%s() device memory corrupt\n",
+				__func__);
+			ret = -EBUSY;
+			goto out;
+		}
+
+		msleep(10); /* Checkpatch throws a < 20ms warning */
+		if (timeout++ > 60)
+			break;
+	}
+
+	printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__);
+
+	ret = saa7164_dl_wait_clr(dev, drflag_ack);
+	if (ret < 0)
+		goto out;
+
+	printk(KERN_INFO "%s() Image booted successfully.\n", __func__);
+	ret = 0;
+
+out:
+	kfree(srcbuf);
+	return ret;
+}
+
+/* TODO: Excessive debug */
+/* Load the firmware. Optionally it can be in ROM or newer versions
+ * can be on disk, saving the expense of the ROM hardware. */
+int saa7164_downloadfirmware(struct saa7164_dev *dev)
+{
+	/* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */
+	u32 tmp, filesize, version, err_flags, first_timeout, fwlength;
+	u32 second_timeout, updatebootloader = 1, bootloadersize = 0;
+	const struct firmware *fw = NULL;
+	struct fw_header *hdr, *boothdr = NULL, *fwhdr;
+	u32 bootloaderversion = 0, fwloadersize;
+	u8 *bootloaderoffset = NULL, *fwloaderoffset;
+	char *fwname;
+	int ret;
+
+	dprintk(DBGLVL_FW, "%s()\n", __func__);
+
+	if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) {
+		fwname = SAA7164_REV2_FIRMWARE;
+		fwlength = SAA7164_REV2_FIRMWARE_SIZE;
+	} else {
+		fwname = SAA7164_REV3_FIRMWARE;
+		fwlength = SAA7164_REV3_FIRMWARE_SIZE;
+	}
+
+	version = saa7164_getcurrentfirmwareversion(dev);
+
+	if (version == 0x00) {
+
+		second_timeout = 100;
+		first_timeout = 100;
+		err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
+		dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
+			__func__, err_flags);
+
+		while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
+			dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
+				__func__, err_flags);
+			msleep(10); /* Checkpatch throws a < 20ms warning */
+
+			if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
+				printk(KERN_ERR "%s() firmware corrupt\n",
+					__func__);
+				break;
+			}
+			if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
+				printk(KERN_ERR "%s() device memory corrupt\n",
+					__func__);
+				break;
+			}
+			if (err_flags & SAA_DEVICE_NO_IMAGE) {
+				printk(KERN_ERR "%s() no first image\n",
+				__func__);
+				break;
+			}
+			if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
+				first_timeout -= 10;
+				if (first_timeout == 0) {
+					printk(KERN_ERR
+						"%s() no first image\n",
+						__func__);
+					break;
+				}
+			} else if (err_flags & SAA_DEVICE_IMAGE_LOADING) {
+				second_timeout -= 10;
+				if (second_timeout == 0) {
+					printk(KERN_ERR
+					"%s() FW load time exceeded\n",
+						__func__);
+					break;
+				}
+			} else {
+				second_timeout -= 10;
+				if (second_timeout == 0) {
+					printk(KERN_ERR
+					"%s() Unknown bootloader flags 0x%x\n",
+						__func__, err_flags);
+					break;
+				}
+			}
+
+			err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
+		} /* While != Booting */
+
+		if (err_flags == SAA_DEVICE_IMAGE_BOOTING) {
+			dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n",
+				__func__);
+			first_timeout = SAA_DEVICE_TIMEOUT;
+			second_timeout = 60 * SAA_DEVICE_TIMEOUT;
+			second_timeout = 100;
+
+			err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
+			dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
+				__func__, err_flags);
+			while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
+				dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
+					__func__, err_flags);
+				msleep(10); /* Checkpatch throws a < 20ms warning */
+
+				if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
+					printk(KERN_ERR
+						"%s() firmware corrupt\n",
+						__func__);
+					break;
+				}
+				if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
+					printk(KERN_ERR
+						"%s() device memory corrupt\n",
+						__func__);
+					break;
+				}
+				if (err_flags & SAA_DEVICE_NO_IMAGE) {
+					printk(KERN_ERR "%s() no first image\n",
+						__func__);
+					break;
+				}
+				if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
+					first_timeout -= 10;
+					if (first_timeout == 0) {
+						printk(KERN_ERR
+						"%s() no second image\n",
+							__func__);
+						break;
+					}
+				} else if (err_flags &
+					SAA_DEVICE_IMAGE_LOADING) {
+					second_timeout -= 10;
+					if (second_timeout == 0) {
+						printk(KERN_ERR
+						"%s() FW load time exceeded\n",
+							__func__);
+						break;
+					}
+				} else {
+					second_timeout -= 10;
+					if (second_timeout == 0) {
+						printk(KERN_ERR
+					"%s() Unknown bootloader flags 0x%x\n",
+							__func__, err_flags);
+						break;
+					}
+				}
+
+				err_flags =
+				saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
+			} /* err_flags != SAA_DEVICE_IMAGE_BOOTING */
+
+			dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n",
+				__func__,
+				saa7164_readl(SAA_BOOTLOADERERROR_FLAGS),
+				saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS));
+
+		} /* err_flags == SAA_DEVICE_IMAGE_BOOTING */
+
+		/* It's possible for both firmwares to have booted,
+		 * but that doesn't mean they've finished booting yet.
+		 */
+		if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+			SAA_DEVICE_IMAGE_BOOTING) &&
+			(saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
+			SAA_DEVICE_IMAGE_BOOTING)) {
+
+
+			dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n",
+				__func__);
+
+			first_timeout = SAA_DEVICE_TIMEOUT;
+			while (first_timeout) {
+				msleep(10); /* Checkpatch throws a < 20ms warning */
+
+				version =
+					saa7164_getcurrentfirmwareversion(dev);
+				if (version) {
+					dprintk(DBGLVL_FW,
+					"%s() All f/w loaded successfully\n",
+						__func__);
+					break;
+				} else {
+					first_timeout -= 10;
+					if (first_timeout == 0) {
+						printk(KERN_ERR
+						"%s() FW did not boot\n",
+							__func__);
+						break;
+					}
+				}
+			}
+		}
+		version = saa7164_getcurrentfirmwareversion(dev);
+	} /* version == 0 */
+
+	/* Has the firmware really booted? */
+	if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+		SAA_DEVICE_IMAGE_BOOTING) &&
+		(saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
+		SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) {
+
+		printk(KERN_ERR
+			"%s() The firmware hung, probably bad firmware\n",
+			__func__);
+
+		/* Tell the second stage loader we have a deadlock */
+		saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET,
+			SAA_DEVICE_DEADLOCK_DETECTED);
+
+		saa7164_getfirmwarestatus(dev);
+
+		return -ENOMEM;
+	}
+
+	dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n",
+		(version & 0x0000fc00) >> 10,
+		(version & 0x000003e0) >> 5,
+		(version & 0x0000001f),
+		(version & 0xffff0000) >> 16);
+
+	/* Load the firmwware from the disk if required */
+	if (version == 0) {
+
+		printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n",
+			__func__, fwname);
+
+		ret = request_firmware(&fw, fwname, &dev->pci->dev);
+		if (ret) {
+			printk(KERN_ERR "%s() Upload failed. "
+				"(file not found?)\n", __func__);
+			return -ENOMEM;
+		}
+
+		printk(KERN_INFO "%s() firmware read %Zu bytes.\n",
+			__func__, fw->size);
+
+		if (fw->size != fwlength) {
+			printk(KERN_ERR "xc5000: firmware incorrect size\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		printk(KERN_INFO "%s() firmware loaded.\n", __func__);
+
+		hdr = (struct fw_header *)fw->data;
+		printk(KERN_INFO "Firmware file header part 1:\n");
+		printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize);
+		printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize);
+		printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved);
+		printk(KERN_INFO " .Version = 0x%x\n", hdr->version);
+
+		/* Retrieve bootloader if reqd */
+		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0))
+			/* Second bootloader in the firmware file */
+			filesize = hdr->reserved * 16;
+		else
+			filesize = (hdr->firmwaresize + hdr->bslsize) *
+				16 + sizeof(struct fw_header);
+
+		printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n",
+			__func__, filesize);
+
+		/* Get bootloader (if reqd) and firmware header */
+		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
+			/* Second boot loader is required */
+
+			/* Get the loader header */
+			boothdr = (struct fw_header *)(fw->data +
+				sizeof(struct fw_header));
+
+			bootloaderversion =
+				saa7164_readl(SAA_DEVICE_2ND_VERSION);
+			dprintk(DBGLVL_FW, "Onboard BootLoader:\n");
+			dprintk(DBGLVL_FW, "->Flag 0x%x\n",
+				saa7164_readl(SAA_BOOTLOADERERROR_FLAGS));
+			dprintk(DBGLVL_FW, "->Ack 0x%x\n",
+				saa7164_readl(SAA_DATAREADY_FLAG_ACK));
+			dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version);
+			dprintk(DBGLVL_FW, "->Loader Version 0x%x\n",
+				bootloaderversion);
+
+			if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+				0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK)
+				== 0x00) && (version == 0x00)) {
+
+				dprintk(DBGLVL_FW, "BootLoader version in  "
+					"rom %d.%d.%d.%d\n",
+					(bootloaderversion & 0x0000fc00) >> 10,
+					(bootloaderversion & 0x000003e0) >> 5,
+					(bootloaderversion & 0x0000001f),
+					(bootloaderversion & 0xffff0000) >> 16
+					);
+				dprintk(DBGLVL_FW, "BootLoader version "
+					"in file %d.%d.%d.%d\n",
+					(boothdr->version & 0x0000fc00) >> 10,
+					(boothdr->version & 0x000003e0) >> 5,
+					(boothdr->version & 0x0000001f),
+					(boothdr->version & 0xffff0000) >> 16
+					);
+
+				if (bootloaderversion == boothdr->version)
+					updatebootloader = 0;
+			}
+
+			/* Calculate offset to firmware header */
+			tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 +
+				(sizeof(struct fw_header) +
+				sizeof(struct fw_header));
+
+			fwhdr = (struct fw_header *)(fw->data+tmp);
+		} else {
+			/* No second boot loader */
+			fwhdr = hdr;
+		}
+
+		dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n",
+			(fwhdr->version & 0x0000fc00) >> 10,
+			(fwhdr->version & 0x000003e0) >> 5,
+			(fwhdr->version & 0x0000001f),
+			(fwhdr->version & 0xffff0000) >> 16
+			);
+
+		if (version == fwhdr->version) {
+			/* No download, firmware already on board */
+			ret = 0;
+			goto out;
+		}
+
+		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
+			if (updatebootloader) {
+				/* Get ready to upload the bootloader */
+				bootloadersize = (boothdr->firmwaresize +
+					boothdr->bslsize) * 16 +
+					sizeof(struct fw_header);
+
+				bootloaderoffset = (u8 *)(fw->data +
+					sizeof(struct fw_header));
+
+				dprintk(DBGLVL_FW, "bootloader d/l starts.\n");
+				printk(KERN_INFO "%s() FirmwareSize = 0x%x\n",
+					__func__, boothdr->firmwaresize);
+				printk(KERN_INFO "%s() BSLSize = 0x%x\n",
+					__func__, boothdr->bslsize);
+				printk(KERN_INFO "%s() Reserved = 0x%x\n",
+					__func__, boothdr->reserved);
+				printk(KERN_INFO "%s() Version = 0x%x\n",
+					__func__, boothdr->version);
+				ret = saa7164_downloadimage(
+					dev,
+					bootloaderoffset,
+					bootloadersize,
+					SAA_DOWNLOAD_FLAGS,
+					dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
+					SAA_DEVICE_BUFFERBLOCKSIZE);
+				if (ret < 0) {
+					printk(KERN_ERR
+						"bootloader d/l has failed\n");
+					goto out;
+				}
+				dprintk(DBGLVL_FW,
+					"bootloader download complete.\n");
+
+			}
+
+			printk(KERN_ERR "starting firmware download(2)\n");
+			bootloadersize = (boothdr->firmwaresize +
+				boothdr->bslsize) * 16 +
+				sizeof(struct fw_header);
+
+			bootloaderoffset =
+				(u8 *)(fw->data + sizeof(struct fw_header));
+
+			fwloaderoffset = bootloaderoffset + bootloadersize;
+
+			/* TODO: fix this bounds overrun here with old f/ws */
+			fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) *
+				16 + sizeof(struct fw_header);
+
+			ret = saa7164_downloadimage(
+				dev,
+				fwloaderoffset,
+				fwloadersize,
+				SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET,
+				dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET,
+				SAA_DEVICE_2ND_BUFFERBLOCKSIZE);
+			if (ret < 0) {
+				printk(KERN_ERR "firmware download failed\n");
+				goto out;
+			}
+			printk(KERN_ERR "firmware download complete.\n");
+
+		} else {
+
+			/* No bootloader update reqd, download firmware only */
+			printk(KERN_ERR "starting firmware download(3)\n");
+
+			ret = saa7164_downloadimage(
+				dev,
+				(u8 *)fw->data,
+				fw->size,
+				SAA_DOWNLOAD_FLAGS,
+				dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
+				SAA_DEVICE_BUFFERBLOCKSIZE);
+			if (ret < 0) {
+				printk(KERN_ERR "firmware download failed\n");
+				goto out;
+			}
+			printk(KERN_ERR "firmware download complete.\n");
+		}
+	}
+
+	dev->firmwareloaded = 1;
+	ret = 0;
+
+out:
+	release_firmware(fw);
+	return ret;
+}
diff --git a/drivers/media/pci/saa7164/saa7164-i2c.c b/drivers/media/pci/saa7164/saa7164-i2c.c
new file mode 100644
index 000000000000..4f7e3b42263f
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-i2c.c
@@ -0,0 +1,125 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "saa7164.h"
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct saa7164_i2c *bus = i2c_adap->algo_data;
+	struct saa7164_dev *dev = bus->dev;
+	int i, retval = 0;
+
+	dprintk(DBGLVL_I2C, "%s(num = %d)\n", __func__, num);
+
+	for (i = 0 ; i < num; i++) {
+		dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x  len = 0x%x\n",
+			__func__, num, msgs[i].addr, msgs[i].len);
+		if (msgs[i].flags & I2C_M_RD) {
+			/* Unsupported - Yet*/
+			printk(KERN_ERR "%s() Unsupported - Yet\n", __func__);
+			continue;
+		} 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 = saa7164_api_i2c_read(bus, msgs[i].addr,
+				msgs[i].len, msgs[i].buf,
+				msgs[i+1].len, msgs[i+1].buf
+				);
+
+			i++;
+
+			if (retval < 0)
+				goto err;
+		} else {
+			/* write */
+			retval = saa7164_api_i2c_write(bus, msgs[i].addr,
+				msgs[i].len, msgs[i].buf);
+		}
+		if (retval < 0)
+			goto err;
+	}
+	return num;
+
+err:
+	return retval;
+}
+
+static u32 saa7164_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm saa7164_i2c_algo_template = {
+	.master_xfer	= i2c_xfer,
+	.functionality	= saa7164_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter saa7164_i2c_adap_template = {
+	.name              = "saa7164",
+	.owner             = THIS_MODULE,
+	.algo              = &saa7164_i2c_algo_template,
+};
+
+static struct i2c_client saa7164_i2c_client_template = {
+	.name	= "saa7164 internal",
+};
+
+int saa7164_i2c_register(struct saa7164_i2c *bus)
+{
+	struct saa7164_dev *dev = bus->dev;
+
+	dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr);
+
+	bus->i2c_adap = saa7164_i2c_adap_template;
+	bus->i2c_client = saa7164_i2c_client_template;
+
+	bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+	strlcpy(bus->i2c_adap.name, bus->dev->name,
+		sizeof(bus->i2c_adap.name));
+
+	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;
+
+	if (0 != bus->i2c_rc)
+		printk(KERN_ERR "%s: i2c bus %d register FAILED\n",
+			dev->name, bus->nr);
+
+	return bus->i2c_rc;
+}
+
+int saa7164_i2c_unregister(struct saa7164_i2c *bus)
+{
+	i2c_del_adapter(&bus->i2c_adap);
+	return 0;
+}
diff --git a/drivers/media/pci/saa7164/saa7164-reg.h b/drivers/media/pci/saa7164/saa7164-reg.h
new file mode 100644
index 000000000000..2bbf81583d33
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-reg.h
@@ -0,0 +1,219 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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.
+ */
+
+/* TODO: Retest the driver with errors expressed as negatives */
+
+/* Result codes */
+#define SAA_OK				0
+#define SAA_ERR_BAD_PARAMETER		0x09
+#define SAA_ERR_NO_RESOURCES		0x0c
+#define SAA_ERR_NOT_SUPPORTED		0x13
+#define SAA_ERR_BUSY			0x15
+#define SAA_ERR_READ			0x17
+#define SAA_ERR_TIMEOUT			0x1f
+#define SAA_ERR_OVERFLOW		0x20
+#define SAA_ERR_EMPTY			0x22
+#define SAA_ERR_NOT_STARTED		0x23
+#define SAA_ERR_ALREADY_STARTED		0x24
+#define SAA_ERR_NOT_STOPPED		0x25
+#define SAA_ERR_ALREADY_STOPPED		0x26
+#define SAA_ERR_INVALID_COMMAND		0x3e
+#define SAA_ERR_NULL_PACKET		0x59
+
+/* Errors and flags from the silicon */
+#define PVC_ERRORCODE_UNKNOWN		0x00
+#define PVC_ERRORCODE_INVALID_COMMAND	0x01
+#define PVC_ERRORCODE_INVALID_CONTROL	0x02
+#define PVC_ERRORCODE_INVALID_DATA	0x03
+#define PVC_ERRORCODE_TIMEOUT		0x04
+#define PVC_ERRORCODE_NAK		0x05
+#define PVC_RESPONSEFLAG_ERROR		0x01
+#define PVC_RESPONSEFLAG_OVERFLOW	0x02
+#define PVC_RESPONSEFLAG_RESET		0x04
+#define PVC_RESPONSEFLAG_INTERFACE	0x08
+#define PVC_RESPONSEFLAG_CONTINUED	0x10
+#define PVC_CMDFLAG_INTERRUPT		0x02
+#define PVC_CMDFLAG_INTERFACE		0x04
+#define PVC_CMDFLAG_SERIALIZE		0x08
+#define PVC_CMDFLAG_CONTINUE		0x10
+
+/* Silicon Commands */
+#define GET_DESCRIPTORS_CONTROL		0x01
+#define GET_STRING_CONTROL		0x03
+#define GET_LANGUAGE_CONTROL		0x05
+#define SET_POWER_CONTROL		0x07
+#define GET_FW_STATUS_CONTROL		0x08
+#define GET_FW_VERSION_CONTROL		0x09
+#define SET_DEBUG_LEVEL_CONTROL		0x0B
+#define GET_DEBUG_DATA_CONTROL		0x0C
+#define GET_PRODUCTION_INFO_CONTROL	0x0D
+
+/* cmd defines */
+#define SAA_CMDFLAG_CONTINUE		0x10
+#define SAA_CMD_MAX_MSG_UNITS		256
+
+/* Some defines */
+#define SAA_BUS_TIMEOUT			50
+#define SAA_DEVICE_TIMEOUT		5000
+#define SAA_DEVICE_MAXREQUESTSIZE	256
+
+/* Register addresses */
+#define SAA_DEVICE_VERSION		0x30
+#define SAA_DOWNLOAD_FLAGS		0x34
+#define SAA_DOWNLOAD_FLAG		0x34
+#define SAA_DOWNLOAD_FLAG_ACK		0x38
+#define SAA_DATAREADY_FLAG		0x3C
+#define SAA_DATAREADY_FLAG_ACK		0x40
+
+/* Boot loader register and bit definitions */
+#define SAA_BOOTLOADERERROR_FLAGS	0x44
+#define SAA_DEVICE_IMAGE_SEARCHING	0x01
+#define SAA_DEVICE_IMAGE_LOADING	0x02
+#define SAA_DEVICE_IMAGE_BOOTING	0x03
+#define SAA_DEVICE_IMAGE_CORRUPT	0x04
+#define SAA_DEVICE_MEMORY_CORRUPT	0x08
+#define SAA_DEVICE_NO_IMAGE		0x10
+
+/* Register addresses */
+#define SAA_DEVICE_2ND_VERSION			0x50
+#define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET	0x54
+
+/* Register addresses */
+#define SAA_SECONDSTAGEERROR_FLAGS		0x64
+
+/* Bootloader regs and flags */
+#define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET	0x6C
+#define SAA_DEVICE_DEADLOCK_DETECTED		0xDEADDEAD
+
+/* Basic firmware status registers */
+#define SAA_DEVICE_SYSINIT_STATUS_OFFSET	0x70
+#define SAA_DEVICE_SYSINIT_STATUS		0x70
+#define SAA_DEVICE_SYSINIT_MODE			0x74
+#define SAA_DEVICE_SYSINIT_SPEC			0x78
+#define SAA_DEVICE_SYSINIT_INST			0x7C
+#define SAA_DEVICE_SYSINIT_CPULOAD		0x80
+#define SAA_DEVICE_SYSINIT_REMAINHEAP		0x84
+
+#define SAA_DEVICE_DOWNLOAD_OFFSET		0x1000
+#define SAA_DEVICE_BUFFERBLOCKSIZE		0x1000
+
+#define SAA_DEVICE_2ND_BUFFERBLOCKSIZE		0x100000
+#define SAA_DEVICE_2ND_DOWNLOAD_OFFSET		0x200000
+
+/* Descriptors */
+#define CS_INTERFACE	0x24
+
+/* Descriptor subtypes */
+#define VC_INPUT_TERMINAL		0x02
+#define VC_OUTPUT_TERMINAL		0x03
+#define VC_SELECTOR_UNIT		0x04
+#define VC_PROCESSING_UNIT		0x05
+#define FEATURE_UNIT			0x06
+#define TUNER_UNIT			0x09
+#define ENCODER_UNIT			0x0A
+#define EXTENSION_UNIT			0x0B
+#define VC_TUNER_PATH			0xF0
+#define PVC_HARDWARE_DESCRIPTOR		0xF1
+#define PVC_INTERFACE_DESCRIPTOR	0xF2
+#define PVC_INFRARED_UNIT		0xF3
+#define DRM_UNIT			0xF4
+#define GENERAL_REQUEST			0xF5
+
+/* Format Types */
+#define VS_FORMAT_TYPE         0x02
+#define VS_FORMAT_TYPE_I       0x01
+#define VS_FORMAT_UNCOMPRESSED 0x04
+#define VS_FRAME_UNCOMPRESSED  0x05
+#define VS_FORMAT_MPEG2PS      0x09
+#define VS_FORMAT_MPEG2TS      0x0A
+#define VS_FORMAT_MPEG4SL      0x0B
+#define VS_FORMAT_WM9          0x0C
+#define VS_FORMAT_DIVX         0x0D
+#define VS_FORMAT_VBI          0x0E
+#define VS_FORMAT_RDS          0x0F
+
+/* Device extension commands */
+#define EXU_REGISTER_ACCESS_CONTROL	0x00
+#define EXU_GPIO_CONTROL		0x01
+#define EXU_GPIO_GROUP_CONTROL		0x02
+#define EXU_INTERRUPT_CONTROL		0x03
+
+/* State Transition and args */
+#define SAA_PROBE_CONTROL	0x01
+#define SAA_COMMIT_CONTROL	0x02
+#define SAA_STATE_CONTROL	0x03
+#define SAA_DMASTATE_STOP	0x00
+#define SAA_DMASTATE_ACQUIRE	0x01
+#define SAA_DMASTATE_PAUSE	0x02
+#define SAA_DMASTATE_RUN	0x03
+
+/* A/V Mux Input Selector */
+#define SU_INPUT_SELECT_CONTROL 0x01
+
+/* Encoder Profiles */
+#define EU_PROFILE_PS_DVD	0x06
+#define EU_PROFILE_TS_HQ	0x09
+#define EU_VIDEO_FORMAT_MPEG_2	0x02
+
+/* Tuner */
+#define TU_AUDIO_MODE_CONTROL  0x17
+
+/* Video Formats */
+#define TU_STANDARD_CONTROL		0x00
+#define TU_STANDARD_AUTO_CONTROL	0x01
+#define TU_STANDARD_NONE		0x00
+#define TU_STANDARD_NTSC_M		0x01
+#define TU_STANDARD_PAL_I		0x08
+#define TU_STANDARD_MANUAL		0x00
+#define TU_STANDARD_AUTO		0x01
+
+/* Video Controls */
+#define PU_BRIGHTNESS_CONTROL	0x02
+#define PU_CONTRAST_CONTROL	0x03
+#define PU_HUE_CONTROL		0x06
+#define PU_SATURATION_CONTROL	0x07
+#define PU_SHARPNESS_CONTROL	0x08
+
+/* Audio Controls */
+#define MUTE_CONTROL		0x01
+#define VOLUME_CONTROL		0x02
+#define AUDIO_DEFAULT_CONTROL	0x0D
+
+/* Default Volume Levels */
+#define TMHW_LEV_ADJ_DECLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_MONOLEV_DEFAULT    0x00
+#define TMHW_LEV_ADJ_NICLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_SAPLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_ADCLEV_DEFAULT     0x00
+
+/* Encoder Related Commands */
+#define EU_PROFILE_CONTROL		0x00
+#define EU_VIDEO_FORMAT_CONTROL		0x01
+#define EU_VIDEO_BIT_RATE_CONTROL	0x02
+#define EU_VIDEO_RESOLUTION_CONTROL	0x03
+#define EU_VIDEO_GOP_STRUCTURE_CONTROL	0x04
+#define EU_VIDEO_INPUT_ASPECT_CONTROL	0x0A
+#define EU_AUDIO_FORMAT_CONTROL		0x0C
+#define EU_AUDIO_BIT_RATE_CONTROL	0x0D
+
+/* Firmware Debugging */
+#define SET_DEBUG_LEVEL_CONTROL	0x0B
+#define GET_DEBUG_DATA_CONTROL	0x0C
diff --git a/drivers/media/pci/saa7164/saa7164-types.h b/drivers/media/pci/saa7164/saa7164-types.h
new file mode 100644
index 000000000000..1d2140a3eb38
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-types.h
@@ -0,0 +1,442 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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.
+ */
+
+/* TODO: Cleanup and shorten the namespace */
+
+/* Some structues are passed directly to/from the firmware and
+ * have strict alignment requirements. This is one of them.
+ */
+struct tmComResHWDescr {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubtype;
+	u16	bcdSpecVersion;
+	u32	dwClockFrequency;
+	u32	dwClockUpdateRes;
+	u8	bCapabilities;
+	u32	dwDeviceRegistersLocation;
+	u32	dwHostMemoryRegion;
+	u32	dwHostMemoryRegionSize;
+	u32	dwHostHibernatMemRegion;
+	u32	dwHostHibernatMemRegionSize;
+} __attribute__((packed));
+
+/* This is DWORD aligned on windows but I can't find the right
+ * gcc syntax to match the binary data from the device.
+ * I've manually padded with Reserved[3] bytes to match the hardware,
+ * but this could break if GCC decies to pack in a different way.
+ */
+struct tmComResInterfaceDescr {
+	u8	bLength;
+	u8	bDescriptorType;
+	u8	bDescriptorSubtype;
+	u8	bFlags;
+	u8	bInterfaceType;
+	u8	bInterfaceId;
+	u8	bBaseInterface;
+	u8	bInterruptId;
+	u8	bDebugInterruptId;
+	u8	BARLocation;
+	u8	Reserved[3];
+};
+
+struct tmComResBusDescr {
+	u64	CommandRing;
+	u64	ResponseRing;
+	u32	CommandWrite;
+	u32	CommandRead;
+	u32	ResponseWrite;
+	u32	ResponseRead;
+};
+
+enum tmBusType {
+	NONE		= 0,
+	TYPE_BUS_PCI	= 1,
+	TYPE_BUS_PCIe	= 2,
+	TYPE_BUS_USB	= 3,
+	TYPE_BUS_I2C	= 4
+};
+
+struct tmComResBusInfo {
+	enum tmBusType Type;
+	u16	m_wMaxReqSize;
+	u8	*m_pdwSetRing;
+	u32	m_dwSizeSetRing;
+	u8	*m_pdwGetRing;
+	u32	m_dwSizeGetRing;
+	u32	m_dwSetWritePos;
+	u32	m_dwSetReadPos;
+	u32	m_dwGetWritePos;
+	u32	m_dwGetReadPos;
+
+	/* All access is protected */
+	struct mutex lock;
+
+};
+
+struct tmComResInfo {
+	u8	id;
+	u8	flags;
+	u16	size;
+	u32	command;
+	u16	controlselector;
+	u8	seqno;
+} __attribute__((packed));
+
+enum tmComResCmd {
+	SET_CUR  = 0x01,
+	GET_CUR  = 0x81,
+	GET_MIN  = 0x82,
+	GET_MAX  = 0x83,
+	GET_RES  = 0x84,
+	GET_LEN  = 0x85,
+	GET_INFO = 0x86,
+	GET_DEF  = 0x87
+};
+
+struct cmd {
+	u8 seqno;
+	u32 inuse;
+	u32 timeout;
+	u32 signalled;
+	struct mutex lock;
+	wait_queue_head_t wait;
+};
+
+struct tmDescriptor {
+	u32	pathid;
+	u32	size;
+	void	*descriptor;
+};
+
+struct tmComResDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+} __attribute__((packed));
+
+struct tmComResExtDevDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u32	devicetype;
+	u16	deviceid;
+	u32	numgpiopins;
+	u8	numgpiogroups;
+	u8	controlsize;
+} __attribute__((packed));
+
+struct tmComResGPIO {
+	u32	pin;
+	u8	state;
+} __attribute__((packed));
+
+struct tmComResPathDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	pathid;
+} __attribute__((packed));
+
+/* terminaltype */
+enum tmComResTermType {
+	ITT_ANTENNA              = 0x0203,
+	LINE_CONNECTOR           = 0x0603,
+	SPDIF_CONNECTOR          = 0x0605,
+	COMPOSITE_CONNECTOR      = 0x0401,
+	SVIDEO_CONNECTOR         = 0x0402,
+	COMPONENT_CONNECTOR      = 0x0403,
+	STANDARD_DMA             = 0xF101
+};
+
+struct tmComResAntTermDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	terminalid;
+	u16	terminaltype;
+	u8	assocterminal;
+	u8	iterminal;
+	u8	controlsize;
+} __attribute__((packed));
+
+struct tmComResTunerDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	sourceid;
+	u8	iunit;
+	u32	tuningstandards;
+	u8	controlsize;
+	u32	controls;
+} __attribute__((packed));
+
+enum tmBufferFlag {
+	/* the buffer does not contain any valid data */
+	TM_BUFFER_FLAG_EMPTY,
+
+	/* the buffer is filled with valid data */
+	TM_BUFFER_FLAG_DONE,
+
+	/* the buffer is the dummy buffer - TODO??? */
+	TM_BUFFER_FLAG_DUMMY_BUFFER
+};
+
+struct tmBuffer {
+	u64		*pagetablevirt;
+	u64		pagetablephys;
+	u16		offset;
+	u8		*context;
+	u64		timestamp;
+	enum tmBufferFlag BufferFlag;
+	u32		lostbuffers;
+	u32		validbuffers;
+	u64		*dummypagevirt;
+	u64		dummypagephys;
+	u64		*addressvirt;
+};
+
+struct tmHWStreamParameters {
+	u32	bitspersample;
+	u32	samplesperline;
+	u32	numberoflines;
+	u32	pitch;
+	u32	linethreshold;
+	u64	**pagetablelistvirt;
+	u64	*pagetablelistphys;
+	u32	numpagetables;
+	u32	numpagetableentries;
+};
+
+struct tmStreamParameters {
+	struct tmHWStreamParameters	HWStreamParameters;
+	u64				qwDummyPageTablePhys;
+	u64				*pDummyPageTableVirt;
+};
+
+struct tmComResDMATermDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtyle;
+	u8	unitid;
+	u16	terminaltype;
+	u8	assocterminal;
+	u8	sourceid;
+	u8	iterminal;
+	u32	BARLocation;
+	u8	flags;
+	u8	interruptid;
+	u8	buffercount;
+	u8	metadatasize;
+	u8	numformats;
+	u8	controlsize;
+} __attribute__((packed));
+
+/*
+ *
+ * Description:
+ *  This is the transport stream format header.
+ *
+ * Settings:
+ *  bLength                 - The size of this descriptor in bytes.
+ *  bDescriptorType         - CS_INTERFACE.
+ *  bDescriptorSubtype      - VS_FORMAT_MPEG2TS descriptor subtype.
+ *  bFormatIndex            - A non-zero constant that uniquely identifies the
+ *                            format.
+ *  bDataOffset             - Offset to TSP packet within MPEG-2 TS transport
+ *                            stride, in bytes.
+ *  bPacketLength           - Length of TSP packet, in bytes (typically 188).
+ *  bStrideLength           - Length of MPEG-2 TS transport stride.
+ *  guidStrideFormat        - A Globally Unique Identifier indicating the
+ *                            format of the stride data (if any). Set to zeros
+ *                            if there is no Stride Data, or if the Stride
+ *                            Data is to be ignored by the application.
+ *
+ */
+struct tmComResTSFormatDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	bFormatIndex;
+	u8	bDataOffset;
+	u8	bPacketLength;
+	u8	bStrideLength;
+	u8	guidStrideFormat[16];
+} __attribute__((packed));
+
+/* Encoder related structures */
+
+/* A/V Mux Selector */
+struct tmComResSelDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	nrinpins;
+	u8	sourceid;
+} __attribute__((packed));
+
+/* A/V Audio processor definitions */
+struct tmComResProcDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	sourceid;
+	u16	wreserved;
+	u8	controlsize;
+} __attribute__((packed));
+
+/* Video bitrate control message */
+#define EU_VIDEO_BIT_RATE_MODE_CONSTANT		(0)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_AVERAGE (1)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK	(2)
+struct tmComResEncVideoBitRate {
+	u8	ucVideoBitRateMode;
+	u32	dwVideoBitRate;
+	u32	dwVideoBitRatePeak;
+} __attribute__((packed));
+
+/* Video Encoder Aspect Ratio message */
+struct tmComResEncVideoInputAspectRatio {
+	u8	width;
+	u8	height;
+} __attribute__((packed));
+
+/* Video Encoder GOP IBP message */
+/* 1. IPPPPPPPPPPPPPP */
+/* 2. IBPBPBPBPBPBPBP */
+/* 3. IBBPBBPBBPBBP   */
+#define SAA7164_ENCODER_DEFAULT_GOP_DIST (1)
+#define SAA7164_ENCODER_DEFAULT_GOP_SIZE (15)
+struct tmComResEncVideoGopStructure {
+	u8	ucGOPSize;	/* GOP Size 12, 15 */
+	u8	ucRefFrameDist; /* Reference Frame Distance */
+} __attribute__((packed));
+
+/* Encoder processor definition */
+struct tmComResEncoderDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	vsourceid;
+	u8	asourceid;
+	u8	iunit;
+	u32	dwmControlCap;
+	u32	dwmProfileCap;
+	u32	dwmVidFormatCap;
+	u8	bmVidBitrateCap;
+	u16	wmVidResolutionsCap;
+	u16	wmVidFrmRateCap;
+	u32	dwmAudFormatCap;
+	u8	bmAudBitrateCap;
+} __attribute__((packed));
+
+/* Audio processor definition */
+struct tmComResAFeatureDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	sourceid;
+	u8	controlsize;
+} __attribute__((packed));
+
+/* Audio control messages */
+struct tmComResAudioDefaults {
+	u8	ucDecoderLevel;
+	u8	ucDecoderFM_Level;
+	u8	ucMonoLevel;
+	u8	ucNICAM_Level;
+	u8	ucSAP_Level;
+	u8	ucADC_Level;
+} __attribute__((packed));
+
+/* Audio bitrate control message */
+struct tmComResEncAudioBitRate {
+	u8	ucAudioBitRateMode;
+	u32	dwAudioBitRate;
+	u32	dwAudioBitRatePeak;
+} __attribute__((packed));
+
+/* Tuner / AV Decoder messages */
+struct tmComResTunerStandard {
+	u8	std;
+	u32	country;
+} __attribute__((packed));
+
+struct tmComResTunerStandardAuto {
+	u8	mode;
+} __attribute__((packed));
+
+/* EEPROM definition for PS stream types */
+struct tmComResPSFormatDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	bFormatIndex;
+	u16	wPacketLength;
+	u16	wPackLength;
+	u8	bPackDataType;
+} __attribute__((packed));
+
+/* VBI control structure */
+struct tmComResVBIFormatDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype; /* VS_FORMAT_VBI */
+	u8	bFormatIndex;
+	u32	VideoStandard; /* See KS_AnalogVideoStandard, NTSC = 1 */
+	u8	StartLine; /* NTSC Start = 10 */
+	u8	EndLine; /* NTSC = 21 */
+	u8	FieldRate; /* 60 for NTSC */
+	u8	bNumLines; /* Unused - scheduled for removal */
+} __attribute__((packed));
+
+struct tmComResProbeCommit {
+	u16	bmHint;
+	u8	bFormatIndex;
+	u8	bFrameIndex;
+} __attribute__((packed));
+
+struct tmComResDebugSetLevel {
+	u32	dwDebugLevel;
+} __attribute__((packed));
+
+struct tmComResDebugGetData {
+	u32	dwResult;
+	u8	ucDebugData[256];
+} __attribute__((packed));
+
+struct tmFwInfoStruct {
+	u32	status;
+	u32	mode;
+	u32	devicespec;
+	u32	deviceinst;
+	u32	CPULoad;
+	u32	RemainHeap;
+	u32	CPUClock;
+	u32	RAMSpeed;
+} __attribute__((packed));
diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c
new file mode 100644
index 000000000000..d8e6c8f14079
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-vbi.c
@@ -0,0 +1,1374 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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 "saa7164.h"
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+	0
+};
+
+/* Take the encoder configuration from the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_vbi_configure(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	port->vbi_params.width = port->width;
+	port->vbi_params.height = port->height;
+	port->vbi_params.is_50hz =
+		(port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+	/* Set up the DIF (enable it) for analog mode by default */
+	saa7164_api_initialize_dif(port);
+
+	/* Configure the correct video standard */
+#if 0
+	saa7164_api_configure_dif(port, port->encodernorm.id);
+#endif
+
+#if 0
+	/* Ensure the audio decoder is correct configured */
+	saa7164_api_set_audio_std(port);
+#endif
+	dprintk(DBGLVL_VBI, "%s() ends\n", __func__);
+}
+
+static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port)
+{
+	struct list_head *c, *n, *p, *q, *l, *v;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(buf);
+	}
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr);
+	list_for_each_safe(p, q, &port->list_buf_used.list) {
+		ubuf = list_entry(p, struct saa7164_user_buffer, list);
+		list_del(p);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr);
+	list_for_each_safe(l, v, &port->list_buf_free.list) {
+		ubuf = list_entry(l, struct saa7164_user_buffer, list);
+		list_del(l);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+	dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr);
+
+	return 0;
+}
+
+/* Dynamic buffer switch at vbi start time */
+static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	int result = -ENODEV, i;
+	int len = 0;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* TODO: NTSC SPECIFIC */
+	/* Init and establish defaults */
+	params->samplesperline = 1440;
+	params->numberoflines = 12;
+	params->numberoflines = 18;
+	params->pitch = 1600;
+	params->pitch = 1440;
+	params->numpagetables = 2 +
+		((params->numberoflines * params->pitch) / PAGE_SIZE);
+	params->bitspersample = 8;
+	params->linethreshold = 0;
+	params->pagetablelistvirt = NULL;
+	params->pagetablelistphys = NULL;
+	params->numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources, buffers (hard) */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			params->numberoflines *
+			params->pitch);
+
+		if (!buf) {
+			printk(KERN_ERR "%s() failed "
+			       "(errno = %d), unable to allocate buffer\n",
+				__func__, result);
+			result = -ENOMEM;
+			goto failed;
+		} else {
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&buf->list, &port->dmaqueue.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+		}
+	}
+
+	/* Allocate some kernel buffers for copying
+	 * to userpsace.
+	 */
+	len = params->numberoflines * params->pitch;
+
+	if (vbi_buffers < 16)
+		vbi_buffers = 16;
+	if (vbi_buffers > 512)
+		vbi_buffers = 512;
+
+	for (i = 0; i < vbi_buffers; i++) {
+
+		ubuf = saa7164_buffer_alloc_user(dev, len);
+		if (ubuf) {
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+		}
+
+	}
+
+	result = 0;
+
+failed:
+	return result;
+}
+
+
+static int saa7164_vbi_initialize(struct saa7164_port *port)
+{
+	saa7164_vbi_configure(port);
+	return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	unsigned int i;
+
+	dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+		if (*id & saa7164_tvnorms[i].id)
+			break;
+	}
+	if (i == ARRAY_SIZE(saa7164_tvnorms))
+		return -EINVAL;
+
+	port->encodernorm = saa7164_tvnorms[i];
+
+	/* Update the audio decoder while is not running in
+	 * auto detect mode.
+	 */
+	saa7164_api_set_audio_std(port);
+
+	dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+	struct v4l2_input *i)
+{
+	int n;
+
+	char *inputs[] = { "tuner", "composite", "svideo", "aux",
+		"composite 2", "svideo 2", "aux 2" };
+
+	if (i->index >= 7)
+		return -EINVAL;
+
+	strcpy(i->name, inputs[i->index]);
+
+	if (i->index == 0)
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	else
+		i->type  = V4L2_INPUT_TYPE_CAMERA;
+
+	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+		i->std |= saa7164_tvnorms[n].id;
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (saa7164_api_get_videomux(port) != SAA_OK)
+		return -EIO;
+
+	*i = (port->mux_input - 1);
+
+	dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i);
+
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i);
+
+	if (i >= 7)
+		return -EINVAL;
+
+	port->mux_input = i + 1;
+
+	if (saa7164_api_set_videomux(port) != SAA_OK)
+		return -EIO;
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "tuner");
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	/* Update the A/V core */
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency = port->freq;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *tsport;
+	struct dvb_frontend *fe;
+
+	/* TODO: Pull this for the std */
+	struct analog_parameters params = {
+		.mode      = V4L2_TUNER_ANALOG_TV,
+		.audmode   = V4L2_TUNER_MODE_STEREO,
+		.std       = port->encodernorm.id,
+		.frequency = f->frequency
+	};
+
+	/* Stop the encoder */
+	dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__,
+		f->frequency, f->tuner);
+
+	if (f->tuner != 0)
+		return -EINVAL;
+
+	if (f->type != V4L2_TUNER_ANALOG_TV)
+		return -EINVAL;
+
+	port->freq = f->frequency;
+
+	/* Update the hardware */
+	if (port->nr == SAA7164_PORT_VBI1)
+		tsport = &dev->ports[SAA7164_PORT_TS1];
+	else
+	if (port->nr == SAA7164_PORT_VBI2)
+		tsport = &dev->ports[SAA7164_PORT_TS2];
+	else
+		BUG();
+
+	fe = tsport->dvb.frontend;
+
+	if (fe && fe->ops.tuner_ops.set_analog_params)
+		fe->ops.tuner_ops.set_analog_params(fe, &params);
+	else
+		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+	saa7164_vbi_initialize(port);
+
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctl->value = port->ctl_brightness;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctl->value = port->ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		ctl->value = port->ctl_saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctl->value = port->ctl_hue;
+		break;
+	case V4L2_CID_SHARPNESS:
+		ctl->value = port->ctl_sharpness;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctl->value = port->ctl_volume;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_brightness = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_BRIGHTNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_contrast = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SATURATION:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_saturation = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_SATURATION_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_HUE:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_hue = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SHARPNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_sharpness = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		if ((ctl->value >= -83) && (ctl->value <= 24)) {
+			port->ctl_volume = ctl->value;
+			saa7164_api_set_audio_volume(port, port->ctl_volume);
+		} else
+			ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_vbi_params *params = &port->vbi_params;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		ctrl->value = params->stream_type;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		ctrl->value = params->ctl_mute;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctrl->value = params->ctl_aspect;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		ctrl->value = params->refdist;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		ctrl->value = params->gop_size;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_get_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+	int ret = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+			(ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 1))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+			(ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 255))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		if ((ctrl->value >= 1) &&
+			(ctrl->value <= 3))
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_vbi_params *params = &port->vbi_params;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		params->stream_type = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		params->ctl_mute = ctrl->value;
+		ret = saa7164_api_audio_mute(port, params->ctl_mute);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		params->ctl_aspect = ctrl->value;
+		ret = saa7164_api_set_aspect_ratio(port);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		params->refdist = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		params->gop_size = ctrl->value;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* TODO: Update the hardware */
+
+	return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+			err = saa7164_set_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+	struct v4l2_capability *cap)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	strcpy(cap->driver, dev->name);
+	strlcpy(cap->card, saa7164_boards[dev->board].name,
+		sizeof(cap->card));
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+	cap->capabilities =
+		V4L2_CAP_VBI_CAPTURE |
+		V4L2_CAP_READWRITE     |
+		0;
+
+	cap->capabilities |= V4L2_CAP_TUNER;
+	cap->version = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "VBI", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = port->width;
+	f->fmt.pix.height       = port->height;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+	return 0;
+}
+
+static int fill_queryctrl(struct saa7164_vbi_params *params,
+	struct v4l2_queryctrl *c)
+{
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+	case V4L2_CID_CONTRAST:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+	case V4L2_CID_SATURATION:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+	case V4L2_CID_HUE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+	case V4L2_CID_SHARPNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+	case V4L2_CID_AUDIO_VOLUME:
+		return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+			1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_ASPECT_1x1,
+			V4L2_MPEG_VIDEO_ASPECT_221x100,
+			1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		return v4l2_ctrl_query_fill(c,
+			1, 3, 1, 1);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+	struct v4l2_queryctrl *c)
+{
+	struct saa7164_vbi_fh *fh = priv;
+	struct saa7164_port *port = fh->port;
+	int i, next;
+	u32 id = c->id;
+
+	memset(c, 0, sizeof(*c));
+
+	next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+	c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+		if (next) {
+			if (c->id < saa7164_v4l2_ctrls[i])
+				c->id = saa7164_v4l2_ctrls[i];
+			else
+				continue;
+		}
+
+		if (c->id == saa7164_v4l2_ctrls[i])
+			return fill_queryctrl(&port->vbi_params, c);
+
+		if (c->id < saa7164_v4l2_ctrls[i])
+			break;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_vbi_stop_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s()    Stopped\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_vbi_acquire_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_vbi_pause_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s()   Paused\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_vbi_stop_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct list_head *c, *n;
+	int ret;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	ret = saa7164_vbi_pause_port(port);
+	ret = saa7164_vbi_acquire_port(port);
+	ret = saa7164_vbi_stop_port(port);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__,
+		port->nr);
+
+	/* Reset the state of any allocated buffer resources */
+	mutex_lock(&port->dmaqueue_lock);
+
+	/* Reset the hard and soft buffer state */
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+		buf->pos = 0;
+	}
+
+	list_for_each_safe(c, n, &port->list_buf_used.list) {
+		ubuf = list_entry(c, struct saa7164_user_buffer, list);
+		ubuf->pos = 0;
+		list_move_tail(&ubuf->list, &port->list_buf_free.list);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+
+	/* Free any allocated resources */
+	saa7164_vbi_buffers_dealloc(port);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr);
+
+	return ret;
+}
+
+static int saa7164_vbi_start_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result, ret = 0;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	port->done_first_interrupt = 0;
+
+	/* allocate all of the PCIe DMA buffer resources on the fly,
+	 * allowing switching between TS and PS payloads without
+	 * requiring a complete driver reload.
+	 */
+	saa7164_vbi_buffers_alloc(port);
+
+	/* Configure the encoder with any cache values */
+#if 0
+	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
+#endif
+
+	/* Place the empty buffers on the hardware */
+	saa7164_buffer_cfg_port(port);
+
+	/* Negotiate format */
+	if (saa7164_api_set_vbi_format(port) != SAA_OK) {
+		printk(KERN_ERR "%s() No supported VBI format\n", __func__);
+		ret = -EIO;
+		goto out;
+	}
+
+	/* Acquire the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+			__func__, result);
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Acquired\n", __func__);
+
+	/* Pause the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_vbi_stop_port(port);
+		if (result != SAA_OK) {
+			printk(KERN_ERR "%s() pause/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Paused\n", __func__);
+
+	/* Start the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_vbi_acquire_port(port);
+		result = saa7164_vbi_stop_port(port);
+		if (result != SAA_OK) {
+			printk(KERN_ERR "%s() run/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Running\n", __func__);
+
+out:
+	return ret;
+}
+
+int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	/* ntsc */
+	f->fmt.vbi.samples_per_line = 1600;
+	f->fmt.vbi.samples_per_line = 1440;
+	f->fmt.vbi.sampling_rate = 27000000;
+	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset = 0;
+	f->fmt.vbi.flags = 0;
+	f->fmt.vbi.start[0] = 10;
+	f->fmt.vbi.count[0] = 18;
+	f->fmt.vbi.start[1] = 263 + 10 + 1;
+	f->fmt.vbi.count[1] = 18;
+	return 0;
+}
+
+static int fops_open(struct file *file)
+{
+	struct saa7164_dev *dev;
+	struct saa7164_port *port;
+	struct saa7164_vbi_fh *fh;
+
+	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+	if (!port)
+		return -ENODEV;
+
+	dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	file->private_data = fh;
+	fh->port = port;
+
+	return 0;
+}
+
+static int fops_release(struct file *file)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+			/* stop vbi capture then cancel buffers */
+			saa7164_vbi_stop_streaming(port);
+		}
+	}
+
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
+{
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	u32 crc;
+
+	mutex_lock(&port->dmaqueue_lock);
+	if (!list_empty(&port->list_buf_used.list)) {
+		ubuf = list_first_entry(&port->list_buf_used.list,
+			struct saa7164_user_buffer, list);
+
+		if (crc_checking) {
+			crc = crc32(0, ubuf->data, ubuf->actual_size);
+			if (crc != ubuf->crc) {
+				printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
+					__func__,
+					ubuf, ubuf->crc, crc);
+			}
+		}
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf);
+
+	return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+	size_t count, loff_t *pos)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	int rem, cnt;
+	u8 *p;
+
+	port->last_read_msecs_diff = port->last_read_msecs;
+	port->last_read_msecs = jiffies_to_msecs(jiffies);
+	port->last_read_msecs_diff = port->last_read_msecs -
+		port->last_read_msecs_diff;
+
+	saa7164_histogram_update(&port->read_interval,
+		port->last_read_msecs_diff);
+
+	if (*pos) {
+		printk(KERN_ERR "%s() ESPIPE\n", __func__);
+		return -ESPIPE;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+			if (saa7164_vbi_initialize(port) < 0) {
+				printk(KERN_ERR "%s() EINVAL\n", __func__);
+				return -EINVAL;
+			}
+
+			saa7164_vbi_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_vbi_next_buf(port))) {
+				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = saa7164_vbi_next_buf(port);
+
+	while ((count > 0) && ubuf) {
+
+		/* set remaining bytes to copy */
+		rem = ubuf->actual_size - ubuf->pos;
+		cnt = rem > count ? count : rem;
+
+		p = ubuf->data + ubuf->pos;
+
+		dprintk(DBGLVL_VBI,
+			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+		if (copy_to_user(buffer, p, cnt)) {
+			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+			if (!ret) {
+				printk(KERN_ERR "%s() EFAULT\n", __func__);
+				ret = -EFAULT;
+			}
+			goto err;
+		}
+
+		ubuf->pos += cnt;
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if (ubuf->pos > ubuf->actual_size)
+			printk(KERN_ERR "read() pos > actual, huh?\n");
+
+		if (ubuf->pos == ubuf->actual_size) {
+
+			/* finished with current buffer, take next buffer */
+
+			/* Requeue the buffer on the free list */
+			ubuf->pos = 0;
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_move_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+			/* Dequeue next */
+			if ((file->f_flags & O_NONBLOCK) == 0) {
+				if (wait_event_interruptible(port->wait_read,
+					saa7164_vbi_next_buf(port))) {
+						break;
+				}
+			}
+			ubuf = saa7164_vbi_next_buf(port);
+		}
+	}
+err:
+	if (!ret && !ubuf) {
+		printk(KERN_ERR "%s() EAGAIN\n", __func__);
+		ret = -EAGAIN;
+	}
+
+	return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+	struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
+	struct saa7164_port *port = fh->port;
+	unsigned int mask = 0;
+
+	port->last_poll_msecs_diff = port->last_poll_msecs;
+	port->last_poll_msecs = jiffies_to_msecs(jiffies);
+	port->last_poll_msecs_diff = port->last_poll_msecs -
+		port->last_poll_msecs_diff;
+
+	saa7164_histogram_update(&port->poll_interval,
+		port->last_poll_msecs_diff);
+
+	if (!video_is_registered(port->v4l_device))
+		return -EIO;
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+			if (saa7164_vbi_initialize(port) < 0)
+				return -EINVAL;
+			saa7164_vbi_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_vbi_next_buf(port))) {
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	if (!list_empty(&port->list_buf_used.list))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+static const struct v4l2_file_operations vbi_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fops_open,
+	.release	= fops_release,
+	.read		= fops_read,
+	.poll		= fops_poll,
+	.unlocked_ioctl	= video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+	.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_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_g_ctrl		 = vidioc_g_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+#if 0
+	.vidioc_g_chip_ident	 = saa7164_g_chip_ident,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+#if 0
+	.vidioc_g_register	 = saa7164_g_register,
+	.vidioc_s_register	 = saa7164_s_register,
+#endif
+#endif
+	.vidioc_g_fmt_vbi_cap	 = saa7164_vbi_fmt,
+	.vidioc_try_fmt_vbi_cap	 = saa7164_vbi_fmt,
+	.vidioc_s_fmt_vbi_cap	 = saa7164_vbi_fmt,
+};
+
+static struct video_device saa7164_vbi_template = {
+	.name          = "saa7164",
+	.fops          = &vbi_fops,
+	.ioctl_ops     = &vbi_ioctl_ops,
+	.minor         = -1,
+	.tvnorms       = SAA7164_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_vbi_alloc(
+	struct saa7164_port *port,
+	struct pci_dev *pci,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+
+	*vfd = *template;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+		type, saa7164_boards[dev->board].name);
+
+	vfd->parent  = &pci->dev;
+	vfd->release = video_device_release;
+	return vfd;
+}
+
+int saa7164_vbi_register(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result = -ENODEV;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	if (port->type != SAA7164_MPEG_VBI)
+		BUG();
+
+	/* Sanity check that the PCI configuration space is active */
+	if (port->hwcfg.BARLocation == 0) {
+		printk(KERN_ERR "%s() failed "
+		       "(errno = %d), NO PCI configuration\n",
+			__func__, result);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	/* Establish VBI defaults here */
+
+	/* Allocate and register the video device node */
+	port->v4l_device = saa7164_vbi_alloc(port,
+		dev->pci, &saa7164_vbi_template, "vbi");
+
+	if (!port->v4l_device) {
+		printk(KERN_INFO "%s: can't allocate vbi device\n",
+			dev->name);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	video_set_drvdata(port->v4l_device, port);
+	result = video_register_device(port->v4l_device,
+		VFL_TYPE_VBI, -1);
+	if (result < 0) {
+		printk(KERN_INFO "%s: can't register vbi device\n",
+			dev->name);
+		/* TODO: We're going to leak here if we don't dealloc
+		 The buffers above. The unreg function can't deal wit it.
+		*/
+		goto failed;
+	}
+
+	printk(KERN_INFO "%s: registered device vbi%d [vbi]\n",
+		dev->name, port->v4l_device->num);
+
+	/* Configure the hardware defaults */
+
+	result = 0;
+failed:
+	return result;
+}
+
+void saa7164_vbi_unregister(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	if (port->type != SAA7164_MPEG_VBI)
+		BUG();
+
+	if (port->v4l_device) {
+		if (port->v4l_device->minor != -1)
+			video_unregister_device(port->v4l_device);
+		else
+			video_device_release(port->v4l_device);
+
+		port->v4l_device = NULL;
+	}
+
+}
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
new file mode 100644
index 000000000000..437284e747c9
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -0,0 +1,616 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.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.
+ */
+
+/*
+	Driver architecture
+	*******************
+
+	saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c
+		|	: Standard Linux driver framework for creating
+		|	: exposing and managing interfaces to the rest
+		|	: of the kernel or userland. Also uses _fw.c to load
+		|	: firmware direct into the PCIe bus, bypassing layers.
+		V
+	saa7164_api..()	: Translate kernel specific functions/features
+		|	: into command buffers.
+		V
+	saa7164_cmd..()	: Manages the flow of command packets on/off,
+		|	: the bus. Deal with bus errors, timeouts etc.
+		V
+	saa7164_bus..() : Manage a read/write memory ring buffer in the
+		|	: PCIe Address space.
+		|
+		|		saa7164_fw...()	: Load any frimware
+		|			|	: direct into the device
+		V			V
+	<- ----------------- PCIe address space -------------------- ->
+*/
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/kdev_t.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <dvb_demux.h>
+#include <dvb_frontend.h>
+#include <dvb_net.h>
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "saa7164-reg.h"
+#include "saa7164-types.h"
+
+#define SAA7164_MAXBOARDS 8
+
+#define UNSET (-1U)
+#define SAA7164_BOARD_NOAUTO			UNSET
+#define SAA7164_BOARD_UNKNOWN			0
+#define SAA7164_BOARD_UNKNOWN_REV2		1
+#define SAA7164_BOARD_UNKNOWN_REV3		2
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250		3
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200		4
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2	5
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3	6
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2	7
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3	8
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4	9
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5	10
+
+#define SAA7164_MAX_UNITS		8
+#define SAA7164_TS_NUMBER_OF_LINES	312
+#define SAA7164_PS_NUMBER_OF_LINES	256
+#define SAA7164_PT_ENTRIES		16 /* (312 * 188) / 4096 */
+#define SAA7164_MAX_ENCODER_BUFFERS	64 /* max 5secs of latency at 6Mbps */
+#define SAA7164_MAX_VBI_BUFFERS		64
+
+/* Port related defines */
+#define SAA7164_PORT_TS1	(0)
+#define SAA7164_PORT_TS2	(SAA7164_PORT_TS1 + 1)
+#define SAA7164_PORT_ENC1	(SAA7164_PORT_TS2 + 1)
+#define SAA7164_PORT_ENC2	(SAA7164_PORT_ENC1 + 1)
+#define SAA7164_PORT_VBI1	(SAA7164_PORT_ENC2 + 1)
+#define SAA7164_PORT_VBI2	(SAA7164_PORT_VBI1 + 1)
+#define SAA7164_MAX_PORTS	(SAA7164_PORT_VBI2 + 1)
+
+#define DBGLVL_FW    4
+#define DBGLVL_DVB   8
+#define DBGLVL_I2C  16
+#define DBGLVL_API  32
+#define DBGLVL_CMD  64
+#define DBGLVL_BUS 128
+#define DBGLVL_IRQ 256
+#define DBGLVL_BUF 512
+#define DBGLVL_ENC 1024
+#define DBGLVL_VBI 2048
+#define DBGLVL_THR 4096
+#define DBGLVL_CPU 8192
+
+#define SAA7164_NORMS \
+	(V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP |  V4L2_STD_NTSC_443)
+
+enum port_t {
+	SAA7164_MPEG_UNDEFINED = 0,
+	SAA7164_MPEG_DVB,
+	SAA7164_MPEG_ENCODER,
+	SAA7164_MPEG_VBI,
+};
+
+enum saa7164_i2c_bus_nr {
+	SAA7164_I2C_BUS_0 = 0,
+	SAA7164_I2C_BUS_1,
+	SAA7164_I2C_BUS_2,
+};
+
+enum saa7164_buffer_flags {
+	SAA7164_BUFFER_UNDEFINED = 0,
+	SAA7164_BUFFER_FREE,
+	SAA7164_BUFFER_BUSY,
+	SAA7164_BUFFER_FULL
+};
+
+enum saa7164_unit_type {
+	SAA7164_UNIT_UNDEFINED = 0,
+	SAA7164_UNIT_DIGITAL_DEMODULATOR,
+	SAA7164_UNIT_ANALOG_DEMODULATOR,
+	SAA7164_UNIT_TUNER,
+	SAA7164_UNIT_EEPROM,
+	SAA7164_UNIT_ZILOG_IRBLASTER,
+	SAA7164_UNIT_ENCODER,
+};
+
+/* The PCIe bridge doesn't grant direct access to i2c.
+ * Instead, you address i2c devices using a uniqely
+ * allocated 'unitid' value via a messaging API. This
+ * is a problem. The kernel and existing demod/tuner
+ * drivers expect to talk 'i2c', so we have to maintain
+ * a translation layer, and a series of functions to
+ * convert i2c bus + device address into a unit id.
+ */
+struct saa7164_unit {
+	enum saa7164_unit_type type;
+	u8	id;
+	char	*name;
+	enum saa7164_i2c_bus_nr i2c_bus_nr;
+	u8	i2c_bus_addr;
+	u8	i2c_reg_len;
+};
+
+struct saa7164_board {
+	char	*name;
+	enum port_t porta, portb, portc,
+		portd, porte, portf;
+	enum {
+		SAA7164_CHIP_UNDEFINED = 0,
+		SAA7164_CHIP_REV2,
+		SAA7164_CHIP_REV3,
+	} chiprev;
+	struct	saa7164_unit unit[SAA7164_MAX_UNITS];
+};
+
+struct saa7164_subid {
+	u16     subvendor;
+	u16     subdevice;
+	u32     card;
+};
+
+struct saa7164_encoder_fh {
+	struct saa7164_port *port;
+	atomic_t v4l_reading;
+};
+
+struct saa7164_vbi_fh {
+	struct saa7164_port *port;
+	atomic_t v4l_reading;
+};
+
+struct saa7164_histogram_bucket {
+	u32 val;
+	u32 count;
+	u64 update_time;
+};
+
+struct saa7164_histogram {
+	char name[32];
+	struct saa7164_histogram_bucket counter1[64];
+};
+
+struct saa7164_user_buffer {
+	struct list_head list;
+
+	/* Attributes */
+	u8  *data;
+	u32 pos;
+	u32 actual_size;
+
+	u32 crc;
+};
+
+struct saa7164_fw_status {
+
+	/* RISC Core details */
+	u32	status;
+	u32	mode;
+	u32	spec;
+	u32	inst;
+	u32	cpuload;
+	u32	remainheap;
+
+	/* Firmware version */
+	u32	version;
+	u32	major;
+	u32	sub;
+	u32	rel;
+	u32	buildnr;
+};
+
+struct saa7164_dvb {
+	struct mutex lock;
+	struct dvb_adapter adapter;
+	struct dvb_frontend *frontend;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend fe_hw;
+	struct dmx_frontend fe_mem;
+	struct dvb_net net;
+	int feeding;
+};
+
+struct saa7164_i2c {
+	struct saa7164_dev		*dev;
+
+	enum saa7164_i2c_bus_nr		nr;
+
+	/* I2C I/O */
+	struct i2c_adapter		i2c_adap;
+	struct i2c_client		i2c_client;
+	u32				i2c_rc;
+};
+
+struct saa7164_ctrl {
+	struct v4l2_queryctrl v;
+};
+
+struct saa7164_tvnorm {
+	char		*name;
+	v4l2_std_id	id;
+};
+
+struct saa7164_encoder_params {
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 is_50hz;
+	u32 bitrate; /* bps */
+	u32 bitrate_peak; /* bps */
+	u32 bitrate_mode;
+	u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+	u32 audio_sampling_freq;
+	u32 ctl_mute;
+	u32 ctl_aspect;
+	u32 refdist;
+	u32 gop_size;
+};
+
+struct saa7164_vbi_params {
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 is_50hz;
+	u32 bitrate; /* bps */
+	u32 bitrate_peak; /* bps */
+	u32 bitrate_mode;
+	u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+	u32 audio_sampling_freq;
+	u32 ctl_mute;
+	u32 ctl_aspect;
+	u32 refdist;
+	u32 gop_size;
+};
+
+struct saa7164_port;
+
+struct saa7164_buffer {
+	struct list_head list;
+
+	/* Note of which h/w buffer list index position we occupy */
+	int idx;
+
+	struct saa7164_port *port;
+
+	/* Hardware Specific */
+	/* PCI Memory allocations */
+	enum saa7164_buffer_flags flags; /* Free, Busy, Full */
+
+	/* A block of page align PCI memory */
+	u32 pci_size;	/* PCI allocation size in bytes */
+	u64 __iomem *cpu;	/* Virtual address */
+	dma_addr_t dma;	/* Physical address */
+	u32 crc;	/* Checksum for the entire buffer data */
+
+	/* A page table that splits the block into a number of entries */
+	u32 pt_size;		/* PCI allocation size in bytes */
+	u64 __iomem *pt_cpu;		/* Virtual address */
+	dma_addr_t pt_dma;	/* Physical address */
+
+	/* Encoder fops */
+	u32 pos;
+	u32 actual_size;
+};
+
+struct saa7164_port {
+
+	struct saa7164_dev *dev;
+	enum port_t type;
+	int nr;
+
+	/* --- Generic port attributes --- */
+
+	/* HW stream parameters */
+	struct tmHWStreamParameters hw_streamingparams;
+
+	/* DMA configuration values, is seeded during initialization */
+	struct tmComResDMATermDescrHeader hwcfg;
+
+	/* hardware specific registers */
+	u32 bufcounter;
+	u32 pitch;
+	u32 bufsize;
+	u32 bufoffset;
+	u32 bufptr32l;
+	u32 bufptr32h;
+	u64 bufptr64;
+
+	u32 numpte;	/* Number of entries in array, only valid in head */
+
+	struct mutex dmaqueue_lock;
+	struct saa7164_buffer dmaqueue;
+
+	u64 last_irq_msecs, last_svc_msecs;
+	u64 last_irq_msecs_diff, last_svc_msecs_diff;
+	u32 last_svc_wp;
+	u32 last_svc_rp;
+	u64 last_irq_svc_msecs_diff;
+	u64 last_read_msecs, last_read_msecs_diff;
+	u64 last_poll_msecs, last_poll_msecs_diff;
+
+	struct saa7164_histogram irq_interval;
+	struct saa7164_histogram svc_interval;
+	struct saa7164_histogram irq_svc_interval;
+	struct saa7164_histogram read_interval;
+	struct saa7164_histogram poll_interval;
+
+	/* --- DVB Transport Specific --- */
+	struct saa7164_dvb dvb;
+
+	/* --- Encoder/V4L related attributes --- */
+	/* Encoder */
+	/* Defaults established in saa7164-encoder.c */
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 freq;
+	u32 ts_packet_size;
+	u32 ts_packet_count;
+	u8 mux_input;
+	u8 encoder_profile;
+	u8 video_format;
+	u8 audio_format;
+	u8 video_resolution;
+	u16 ctl_brightness;
+	u16 ctl_contrast;
+	u16 ctl_hue;
+	u16 ctl_saturation;
+	u16 ctl_sharpness;
+	s8 ctl_volume;
+
+	struct tmComResAFeatureDescrHeader audfeat;
+	struct tmComResEncoderDescrHeader encunit;
+	struct tmComResProcDescrHeader vidproc;
+	struct tmComResExtDevDescrHeader ifunit;
+	struct tmComResTunerDescrHeader tunerunit;
+
+	struct work_struct workenc;
+
+	/* V4L Encoder Video */
+	struct saa7164_encoder_params encoder_params;
+	struct video_device *v4l_device;
+	atomic_t v4l_reader_count;
+
+	struct saa7164_buffer list_buf_used;
+	struct saa7164_buffer list_buf_free;
+	wait_queue_head_t wait_read;
+
+	/* V4L VBI */
+	struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc;
+	struct saa7164_vbi_params vbi_params;
+
+	/* Debug */
+	u32 sync_errors;
+	u32 v_cc_errors;
+	u32 a_cc_errors;
+	u8 last_v_cc;
+	u8 last_a_cc;
+	u32 done_first_interrupt;
+};
+
+struct saa7164_dev {
+	struct list_head	devlist;
+	atomic_t		refcount;
+
+	/* pci stuff */
+	struct pci_dev	*pci;
+	unsigned char	pci_rev, pci_lat;
+	int		pci_bus, pci_slot;
+	u32		__iomem *lmmio;
+	u8		__iomem *bmmio;
+	u32		__iomem *lmmio2;
+	u8		__iomem *bmmio2;
+	int		pci_irqmask;
+
+	/* board details */
+	int	nr;
+	int	hwrevision;
+	u32	board;
+	char	name[16];
+
+	/* firmware status */
+	struct saa7164_fw_status	fw_status;
+	u32				firmwareloaded;
+
+	struct tmComResHWDescr		hwdesc;
+	struct tmComResInterfaceDescr	intfdesc;
+	struct tmComResBusDescr		busdesc;
+
+	struct tmComResBusInfo		bus;
+
+	/* Interrupt status and ack registers */
+	u32 int_status;
+	u32 int_ack;
+
+	struct cmd			cmds[SAA_CMD_MAX_MSG_UNITS];
+	struct mutex			lock;
+
+	/* I2c related */
+	struct saa7164_i2c i2c_bus[3];
+
+	/* Transport related */
+	struct saa7164_port ports[SAA7164_MAX_PORTS];
+
+	/* Deferred command/api interrupts handling */
+	struct work_struct workcmd;
+
+	/* A kernel thread to monitor the firmware log, used
+	 * only in debug mode.
+	 */
+	struct task_struct *kthread;
+
+};
+
+extern struct list_head saa7164_devlist;
+extern unsigned int waitsecs;
+extern unsigned int encoder_buffers;
+extern unsigned int vbi_buffers;
+
+/* ----------------------------------------------------------- */
+/* saa7164-core.c                                              */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr);
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
+
+/* ----------------------------------------------------------- */
+/* saa7164-fw.c                                                */
+int saa7164_downloadfirmware(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-i2c.c                                               */
+extern int saa7164_i2c_register(struct saa7164_i2c *bus);
+extern int saa7164_i2c_unregister(struct saa7164_i2c *bus);
+extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus,
+	unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------- */
+/* saa7164-bus.c                                               */
+int saa7164_bus_setup(struct saa7164_dev *dev);
+void saa7164_bus_dump(struct saa7164_dev *dev);
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
+	void *buf);
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
+	void *buf, int peekonly);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cmd.c                                               */
+int saa7164_cmd_send(struct saa7164_dev *dev,
+	u8 id, enum tmComResCmd command, u16 controlselector,
+	u16 size, void *buf);
+void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
+int saa7164_irq_dequeue(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-api.c                                               */
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version);
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev);
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+	u32 datalen, u8 *data);
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr,
+	u32 datalen, u8 *data);
+int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr,
+	u32 datalen, u8 *data);
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen);
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode);
+int saa7164_api_initialize_dif(struct saa7164_port *port);
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std);
+int saa7164_api_set_encoder(struct saa7164_port *port);
+int saa7164_api_get_encoder(struct saa7164_port *port);
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port);
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_set_videomux(struct saa7164_port *port);
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute);
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level);
+int saa7164_api_set_audio_std(struct saa7164_port *port);
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect);
+int saa7164_api_get_videomux(struct saa7164_port *port);
+int saa7164_api_set_vbi_format(struct saa7164_port *port);
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level);
+int saa7164_api_collect_debug(struct saa7164_dev *dev);
+int saa7164_api_get_load_info(struct saa7164_dev *dev,
+	struct tmFwInfoStruct *i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cards.c                                             */
+extern struct saa7164_board saa7164_boards[];
+extern const unsigned int saa7164_bcount;
+
+extern struct saa7164_subid saa7164_subids[];
+extern const unsigned int saa7164_idcount;
+
+extern void saa7164_card_list(struct saa7164_dev *dev);
+extern void saa7164_gpio_setup(struct saa7164_dev *dev);
+extern void saa7164_card_setup(struct saa7164_dev *dev);
+
+extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr);
+extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr);
+extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid);
+
+/* ----------------------------------------------------------- */
+/* saa7164-dvb.c                                               */
+extern int saa7164_dvb_register(struct saa7164_port *port);
+extern int saa7164_dvb_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-buffer.c                                            */
+extern struct saa7164_buffer *saa7164_buffer_alloc(
+	struct saa7164_port *port, u32 len);
+extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
+extern void saa7164_buffer_display(struct saa7164_buffer *buf);
+extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
+extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
+extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
+	struct saa7164_dev *dev, u32 len);
+extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
+extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-encoder.c                                            */
+int saa7164_encoder_register(struct saa7164_port *port);
+void saa7164_encoder_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-vbi.c                                            */
+int saa7164_vbi_register(struct saa7164_port *port);
+void saa7164_vbi_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+
+extern unsigned int crc_checking;
+
+extern unsigned int saa_debug;
+#define dprintk(level, fmt, arg...)\
+	do { if (saa_debug & level)\
+		printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+	} while (0)
+
+#define log_warn(fmt, arg...)\
+	do { \
+		printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\
+	} while (0)
+
+#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
+#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
+
+#define saa7164_readb(reg)             readl(dev->bmmio + (reg))
+#define saa7164_writeb(reg, value)     writel((value), dev->bmmio + (reg))
+
diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig
new file mode 100644
index 000000000000..6749f67cab8a
--- /dev/null
+++ b/drivers/media/pci/sta2x11/Kconfig
@@ -0,0 +1,12 @@
+config STA2X11_VIP
+	tristate "STA2X11 VIP Video For Linux"
+	depends on STA2X11
+	select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEOBUF_DMA_CONTIG
+	depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
+	help
+	  Say Y for support for STA2X11 VIP (Video Input Port) capture
+	  device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sta2x11_vip.
diff --git a/drivers/media/pci/sta2x11/Makefile b/drivers/media/pci/sta2x11/Makefile
new file mode 100644
index 000000000000..d6c471d1d1b4
--- /dev/null
+++ b/drivers/media/pci/sta2x11/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
new file mode 100644
index 000000000000..4c10205264d4
--- /dev/null
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -0,0 +1,1550 @@
+/*
+ * This is the driver for the STA2x11 Video Input Port.
+ *
+ * Copyright (C) 2010       WindRiver Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: Andreas Kies <andreas.kies@windriver.com>
+ *		Vlad Lungu <vlad.lungu@windriver.com>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#include <linux/videodev2.h>
+
+#include <linux/kmod.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "sta2x11_vip.h"
+
+#define DRV_NAME "sta2x11_vip"
+#define DRV_VERSION "1.3"
+
+#ifndef PCI_DEVICE_ID_STMICRO_VIP
+#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D
+#endif
+
+#define MAX_FRAMES 4
+
+/*Register offsets*/
+#define DVP_CTL		0x00
+#define DVP_TFO		0x04
+#define DVP_TFS		0x08
+#define DVP_BFO		0x0C
+#define DVP_BFS		0x10
+#define DVP_VTP         0x14
+#define DVP_VBP         0x18
+#define DVP_VMP		0x1C
+#define DVP_ITM		0x98
+#define DVP_ITS		0x9C
+#define DVP_STA		0xA0
+#define DVP_HLFLN	0xA8
+#define DVP_RGB		0xC0
+#define DVP_PKZ		0xF0
+
+/*Register fields*/
+#define DVP_CTL_ENA	0x00000001
+#define DVP_CTL_RST	0x80000000
+#define DVP_CTL_DIS	(~0x00040001)
+
+#define DVP_IT_VSB	0x00000008
+#define DVP_IT_VST	0x00000010
+#define DVP_IT_FIFO	0x00000020
+
+#define DVP_HLFLN_SD	0x00000001
+
+#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg))
+#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg))
+
+#define SAVE_COUNT 8
+#define AUX_COUNT 3
+#define IRQ_COUNT 1
+
+/**
+ * struct sta2x11_vip - All internal data for one instance of device
+ * @v4l2_dev: device registered in v4l layer
+ * @video_dev: properties of our device
+ * @pdev: PCI device
+ * @adapter: contains I2C adapter information
+ * @register_save_area: All relevant register are saved here during suspend
+ * @decoder: contains information about video DAC
+ * @format: pixel format, fixed UYVY
+ * @std: video standard (e.g. PAL/NTSC)
+ * @input: input line for video signal ( 0 or 1 )
+ * @users: Number of open of device ( max. 1 )
+ * @disabled: Device is in power down state
+ * @mutex: ensures exclusive opening of device
+ * @slock: for excluse acces of registers
+ * @vb_vidq: queue maintained by videobuf layer
+ * @capture: linked list of capture buffer
+ * @active: struct videobuf_buffer currently beingg filled
+ * @started: device is ready to capture frame
+ * @closing: device will be shut down
+ * @tcount: Number of top frames
+ * @bcount: Number of bottom frames
+ * @overflow: Number of FIFO overflows
+ * @mem_spare: small buffer of unused frame
+ * @dma_spare: dma addres of mem_spare
+ * @iomem: hardware base address
+ * @config: I2C and gpio config from platform
+ *
+ * All non-local data is accessed via this structure.
+ */
+
+struct sta2x11_vip {
+	struct v4l2_device v4l2_dev;
+	struct video_device *video_dev;
+	struct pci_dev *pdev;
+	struct i2c_adapter *adapter;
+	unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT];
+	struct v4l2_subdev *decoder;
+	struct v4l2_pix_format format;
+	v4l2_std_id std;
+	unsigned int input;
+	int users;
+	int disabled;
+	struct mutex mutex;	/* exclusive access during open */
+	spinlock_t slock;	/* spin lock for hardware and queue access */
+	struct videobuf_queue vb_vidq;
+	struct list_head capture;
+	struct videobuf_buffer *active;
+	int started, closing, tcount, bcount;
+	int overflow;
+	void *mem_spare;
+	dma_addr_t dma_spare;
+	void *iomem;
+	struct vip_config *config;
+};
+
+static const unsigned int registers_to_save[AUX_COUNT] = {
+	DVP_HLFLN, DVP_RGB, DVP_PKZ
+};
+
+static struct v4l2_pix_format formats_50[] = {
+	{			/*PAL interlaced */
+	 .width = 720,
+	 .height = 576,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_INTERLACED,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 576,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+	{			/*PAL top */
+	 .width = 720,
+	 .height = 288,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_TOP,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 288,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+	{			/*PAL bottom */
+	 .width = 720,
+	 .height = 288,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_BOTTOM,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 288,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+
+};
+
+static struct v4l2_pix_format formats_60[] = {
+	{			/*NTSC interlaced */
+	 .width = 720,
+	 .height = 480,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_INTERLACED,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 480,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+	{			/*NTSC top */
+	 .width = 720,
+	 .height = 240,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_TOP,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 240,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+	{			/*NTSC bottom */
+	 .width = 720,
+	 .height = 240,
+	 .pixelformat = V4L2_PIX_FMT_UYVY,
+	 .field = V4L2_FIELD_BOTTOM,
+	 .bytesperline = 720 * 2,
+	 .sizeimage = 720 * 2 * 240,
+	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
+};
+
+/**
+ * buf_setup - Get size and number of video buffer
+ * @vq: queue in videobuf
+ * @count: Number of buffers (1..MAX_FRAMES).
+ *		0 use default value.
+ * @size:  size of buffer in bytes
+ *
+ * returns size and number of buffers
+ * a preset value of 0 returns the default number.
+ * return value: 0, always succesfull.
+ */
+static int buf_setup(struct videobuf_queue *vq, unsigned int *count,
+		     unsigned int *size)
+{
+	struct sta2x11_vip *vip = vq->priv_data;
+
+	*size = vip->format.width * vip->format.height * 2;
+	if (0 == *count || MAX_FRAMES < *count)
+		*count = MAX_FRAMES;
+	return 0;
+};
+
+/**
+ * buf_prepare - prepare buffer for usage
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be prepared
+ * @field: type of video data (interlaced/non-interlaced)
+ *
+ * Allocate or realloc buffer
+ * return value: 0, successful.
+ *
+ * -EINVAL, supplied buffer is too small.
+ *
+ *  other, buffer could not be locked.
+ */
+static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+		       enum v4l2_field field)
+{
+	struct sta2x11_vip *vip = vq->priv_data;
+	int ret;
+
+	vb->size = vip->format.width * vip->format.height * 2;
+	if ((0 != vb->baddr) && (vb->bsize < vb->size))
+		return -EINVAL;
+	vb->width = vip->format.width;
+	vb->height = vip->format.height;
+	vb->field = field;
+
+	if (VIDEOBUF_NEEDS_INIT == vb->state) {
+		ret = videobuf_iolock(vq, vb, NULL);
+		if (ret)
+			goto fail;
+	}
+	vb->state = VIDEOBUF_PREPARED;
+	return 0;
+fail:
+	videobuf_dma_contig_free(vq, vb);
+	vb->state = VIDEOBUF_NEEDS_INIT;
+	return ret;
+}
+
+/**
+ * buf_queu - queue buffer for filling
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be queued
+ *
+ * if capturing is already running, the buffer will be queued. Otherwise
+ * capture is started and the buffer is used directly.
+ */
+static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct sta2x11_vip *vip = vq->priv_data;
+	u32 dma;
+
+	vb->state = VIDEOBUF_QUEUED;
+
+	if (vip->active) {
+		list_add_tail(&vb->queue, &vip->capture);
+		return;
+	}
+
+	vip->started = 1;
+	vip->tcount = 0;
+	vip->bcount = 0;
+	vip->active = vb;
+	vb->state = VIDEOBUF_ACTIVE;
+
+	dma = videobuf_to_dma_contig(vb);
+
+	REG_WRITE(vip, DVP_TFO, (0 << 16) | (0));
+	/* despite of interlace mode, upper and lower frames start at zero */
+	REG_WRITE(vip, DVP_BFO, (0 << 16) | (0));
+
+	switch (vip->format.field) {
+	case V4L2_FIELD_INTERLACED:
+		REG_WRITE(vip, DVP_TFS,
+			  ((vip->format.height / 2 - 1) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_VTP, dma);
+		REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2);
+		REG_WRITE(vip, DVP_VMP, 4 * vip->format.width);
+		break;
+	case V4L2_FIELD_TOP:
+		REG_WRITE(vip, DVP_TFS,
+			  ((vip->format.height - 1) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_BFS, ((0) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_VTP, dma);
+		REG_WRITE(vip, DVP_VBP, dma);
+		REG_WRITE(vip, DVP_VMP, 2 * vip->format.width);
+		break;
+	case V4L2_FIELD_BOTTOM:
+		REG_WRITE(vip, DVP_TFS, ((0) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_BFS,
+			  ((vip->format.height) << 16) |
+			  (2 * vip->format.width - 1));
+		REG_WRITE(vip, DVP_VTP, dma);
+		REG_WRITE(vip, DVP_VBP, dma);
+		REG_WRITE(vip, DVP_VMP, 2 * vip->format.width);
+		break;
+
+	default:
+		pr_warning("VIP: unknown field format\n");
+		return;
+	}
+
+	REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA);
+}
+
+/**
+ * buff_release - release buffer
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be released
+ *
+ * release buffer in videobuf layer
+ */
+static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+
+	videobuf_dma_contig_free(vq, vb);
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops vip_qops = {
+	.buf_setup = buf_setup,
+	.buf_prepare = buf_prepare,
+	.buf_queue = buf_queue,
+	.buf_release = buf_release,
+};
+
+/**
+ * vip_open - open video device
+ * @file: descriptor of device
+ *
+ * open device, make sure it is only opened once.
+ * return value: 0, no error.
+ *
+ * -EBUSY, device is already opened
+ *
+ * -ENOMEM, no memory for auxiliary DMA buffer
+ */
+static int vip_open(struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	mutex_lock(&vip->mutex);
+	vip->users++;
+
+	if (vip->users > 1) {
+		vip->users--;
+		mutex_unlock(&vip->mutex);
+		return -EBUSY;
+	}
+
+	file->private_data = dev;
+	vip->overflow = 0;
+	vip->started = 0;
+	vip->closing = 0;
+	vip->active = NULL;
+
+	INIT_LIST_HEAD(&vip->capture);
+	vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64,
+					    &vip->dma_spare, GFP_KERNEL);
+	if (!vip->mem_spare) {
+		vip->users--;
+		mutex_unlock(&vip->mutex);
+		return -ENOMEM;
+	}
+
+	mutex_unlock(&vip->mutex);
+	videobuf_queue_dma_contig_init_cached(&vip->vb_vidq,
+					      &vip_qops,
+					      &vip->pdev->dev,
+					      &vip->slock,
+					      V4L2_BUF_TYPE_VIDEO_CAPTURE,
+					      V4L2_FIELD_INTERLACED,
+					      sizeof(struct videobuf_buffer),
+					      vip, NULL);
+	REG_READ(vip, DVP_ITS);
+	REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD);
+	REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST);
+	REG_WRITE(vip, DVP_CTL, DVP_CTL_RST);
+	REG_WRITE(vip, DVP_CTL, 0);
+	REG_READ(vip, DVP_ITS);
+	return 0;
+}
+
+/**
+ * vip_close - close video device
+ * @file: descriptor of device
+ *
+ * close video device, wait until all pending operations are finished
+ * ( maximum FRAME_MAX buffers pending )
+ * Turn off interrupts.
+ *
+ * return value: 0, always succesful.
+ */
+static int vip_close(struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	vip->closing = 1;
+	if (vip->active)
+		videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0);
+	spin_lock_irq(&vip->slock);
+
+	REG_WRITE(vip, DVP_ITM, 0);
+	REG_WRITE(vip, DVP_CTL, DVP_CTL_RST);
+	REG_WRITE(vip, DVP_CTL, 0);
+	REG_READ(vip, DVP_ITS);
+
+	vip->started = 0;
+	vip->active = NULL;
+
+	spin_unlock_irq(&vip->slock);
+
+	videobuf_stop(&vip->vb_vidq);
+	videobuf_mmap_free(&vip->vb_vidq);
+
+	dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare);
+	file->private_data = NULL;
+	mutex_lock(&vip->mutex);
+	vip->users--;
+	mutex_unlock(&vip->mutex);
+	return 0;
+}
+
+/**
+ * vip_read - read from video input
+ * @file: descriptor of device
+ * @data: user buffer
+ * @count: number of bytes to be read
+ * @ppos: position within stream
+ *
+ * read video data from video device.
+ * handling is done in generic videobuf layer
+ * return value: provided by videobuf layer
+ */
+static ssize_t vip_read(struct file *file, char __user *data,
+			size_t count, loff_t *ppos)
+{
+	struct video_device *dev = file->private_data;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0,
+				    file->f_flags & O_NONBLOCK);
+}
+
+/**
+ * vip_mmap - map user buffer
+ * @file: descriptor of device
+ * @vma: user buffer
+ *
+ * map user space buffer into kernel mode, including DMA address.
+ * handling is done in generic videobuf layer.
+ * return value: provided by videobuf layer
+ */
+static int vip_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct video_device *dev = file->private_data;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_mmap_mapper(&vip->vb_vidq, vma);
+}
+
+/**
+ * vip_poll - poll for event
+ * @file: descriptor of device
+ * @wait: contains events to be waited for
+ *
+ * wait for event related to video device.
+ * handling is done in generic videobuf layer.
+ * return value: provided by videobuf layer
+ */
+static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct video_device *dev = file->private_data;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_poll_stream(file, &vip->vb_vidq, wait);
+}
+
+/**
+ * vidioc_querycap - return capabilities of device
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @cap: contains return values
+ *
+ * the capabilities of the device are returned
+ *
+ * return value: 0, no error.
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	memset(cap, 0, sizeof(struct v4l2_capability));
+	strcpy(cap->driver, DRV_NAME);
+	strcpy(cap->card, DRV_NAME);
+	cap->version = 0;
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+		 pci_name(vip->pdev));
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+	    V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+/**
+ * vidioc_s_std - set video standard
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains standard to be set
+ *
+ * the video standard is set
+ *
+ * return value: 0, no error.
+ *
+ * -EIO, no input signal detected
+ *
+ * other, returned from video DAC.
+ */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+	v4l2_std_id oldstd = vip->std, newstd;
+	int status;
+
+	if (V4L2_STD_ALL == *std) {
+		v4l2_subdev_call(vip->decoder, core, s_std, *std);
+		ssleep(2);
+		v4l2_subdev_call(vip->decoder, video, querystd, &newstd);
+		v4l2_subdev_call(vip->decoder, video, g_input_status, &status);
+		if (status & V4L2_IN_ST_NO_SIGNAL)
+			return -EIO;
+		*std = vip->std = newstd;
+		if (oldstd != *std) {
+			if (V4L2_STD_525_60 & (*std))
+				vip->format = formats_60[0];
+			else
+				vip->format = formats_50[0];
+		}
+		return 0;
+	}
+
+	if (oldstd != *std) {
+		if (V4L2_STD_525_60 & (*std))
+			vip->format = formats_60[0];
+		else
+			vip->format = formats_50[0];
+	}
+
+	return v4l2_subdev_call(vip->decoder, core, s_std, *std);
+}
+
+/**
+ * vidioc_g_std - get video standard
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains return values
+ *
+ * the current video standard is returned
+ *
+ * return value: 0, no error.
+ */
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	*std = vip->std;
+	return 0;
+}
+
+/**
+ * vidioc_querystd - get possible video standards
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains return values
+ *
+ * all possible video standards are returned
+ *
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return v4l2_subdev_call(vip->decoder, video, querystd, std);
+
+}
+
+/**
+ * vidioc_queryctl - get possible control settings
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains return values
+ *
+ * return possible values for a control
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_queryctrl(struct file *file, void *priv,
+			    struct v4l2_queryctrl *ctrl)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl);
+}
+
+/**
+ * vidioc_g_ctl - get control value
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains return values
+ *
+ * return setting for a control value
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl);
+}
+
+/**
+ * vidioc_s_ctl - set control value
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains value to be set
+ *
+ * set value for a specific control
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl);
+}
+
+/**
+ * vidioc_enum_input - return name of input line
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @inp: contains return values
+ *
+ * the user friendly name of the input line is returned
+ *
+ * return value: 0, no error.
+ *
+ * -EINVAL, input line number out of range
+ */
+static int vidioc_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *inp)
+{
+	if (inp->index > 1)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std = V4L2_STD_ALL;
+	sprintf(inp->name, "Camera %u", inp->index);
+
+	return 0;
+}
+
+/**
+ * vidioc_s_input - set input line
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @i: new input line number
+ *
+ * the current active input line is set
+ *
+ * return value: 0, no error.
+ *
+ * -EINVAL, line number out of range
+ */
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+	int ret;
+
+	if (i > 1)
+		return -EINVAL;
+	ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0);
+
+	if (!ret)
+		vip->input = i;
+
+	return 0;
+}
+
+/**
+ * vidioc_g_input - return input line
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @i: returned input line number
+ *
+ * the current active input line is returned
+ *
+ * return value: always 0.
+ */
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	*i = vip->input;
+	return 0;
+}
+
+/**
+ * vidioc_enum_fmt_vid_cap - return video capture format
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: returned format information
+ *
+ * returns name and format of video capture
+ * Only UYVY is supported by hardware.
+ *
+ * return value: always 0.
+ */
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+
+	if (f->index != 0)
+		return -EINVAL;
+
+	strcpy(f->description, "4:2:2, packed, UYVY");
+	f->pixelformat = V4L2_PIX_FMT_UYVY;
+	f->flags = 0;
+	return 0;
+}
+
+/**
+ * vidioc_try_fmt_vid_cap - set video capture format
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: new format
+ *
+ * new video format is set which includes width and
+ * field type. width is fixed to 720, no scaling.
+ * Only UYVY is supported by this hardware.
+ * the minimum height is 200, the maximum is 576 (PAL)
+ *
+ * return value: 0, no error
+ *
+ * -EINVAL, pixel or field format not supported
+ *
+ */
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+	int interlace_lim;
+
+	if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat)
+		return -EINVAL;
+
+	if (V4L2_STD_525_60 & vip->std)
+		interlace_lim = 240;
+	else
+		interlace_lim = 288;
+
+	switch (f->fmt.pix.field) {
+	case V4L2_FIELD_ANY:
+		if (interlace_lim < f->fmt.pix.height)
+			f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+		else
+			f->fmt.pix.field = V4L2_FIELD_BOTTOM;
+		break;
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+		if (interlace_lim < f->fmt.pix.height)
+			f->fmt.pix.height = interlace_lim;
+		break;
+	case V4L2_FIELD_INTERLACED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	f->fmt.pix.height &= ~1;
+	if (2 * interlace_lim < f->fmt.pix.height)
+		f->fmt.pix.height = 2 * interlace_lim;
+	if (200 > f->fmt.pix.height)
+		f->fmt.pix.height = 200;
+	f->fmt.pix.width = 720;
+	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+	f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height;
+	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+	f->fmt.pix.priv = 0;
+	return 0;
+}
+
+/**
+ * vidioc_s_fmt_vid_cap - set current video format parameters
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: returned format information
+ *
+ * set new capture format
+ * return value: 0, no error
+ *
+ * other, delivered by video DAC routine.
+ */
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+	int ret;
+
+	ret = vidioc_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format));
+	return 0;
+}
+
+/**
+ * vidioc_g_fmt_vid_cap - get current video format parameters
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: contains format information
+ *
+ * returns current video format parameters
+ *
+ * return value: 0, always successful
+ */
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format));
+	return 0;
+}
+
+/**
+ * vidioc_reqfs - request buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *p)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_reqbufs(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_querybuf - query buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * query buffer state.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_querybuf(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_qbuf - queue a buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_qbuf(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_dqbuf - dequeue a buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK);
+}
+
+/**
+ * vidioc_streamon - turn on streaming
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @type: type of capture
+ *
+ * turn on streaming.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_streamon(&vip->vb_vidq);
+}
+
+/**
+ * vidioc_streamoff - turn off streaming
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @type: type of capture
+ *
+ * turn off streaming.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct video_device *dev = priv;
+	struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+	return videobuf_streamoff(&vip->vb_vidq);
+}
+
+static const struct v4l2_file_operations vip_fops = {
+	.owner = THIS_MODULE,
+	.open = vip_open,
+	.release = vip_close,
+	.ioctl = video_ioctl2,
+	.read = vip_read,
+	.mmap = vip_mmap,
+	.poll = vip_poll
+};
+
+static const struct v4l2_ioctl_ops vip_ioctl_ops = {
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_s_std = vidioc_s_std,
+	.vidioc_g_std = vidioc_g_std,
+	.vidioc_querystd = vidioc_querystd,
+	.vidioc_queryctrl = vidioc_queryctrl,
+	.vidioc_g_ctrl = vidioc_g_ctrl,
+	.vidioc_s_ctrl = vidioc_s_ctrl,
+	.vidioc_enum_input = vidioc_enum_input,
+	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+	.vidioc_s_input = vidioc_s_input,
+	.vidioc_g_input = vidioc_g_input,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+};
+
+static struct video_device video_dev_template = {
+	.name = DRV_NAME,
+	.release = video_device_release,
+	.fops = &vip_fops,
+	.ioctl_ops = &vip_ioctl_ops,
+	.tvnorms = V4L2_STD_ALL,
+};
+
+/**
+ * vip_irq - interrupt routine
+ * @irq: Number of interrupt ( not used, correct number is assumed )
+ * @vip: local data structure containing all information
+ *
+ * check for both frame interrupts set ( top and bottom ).
+ * check FIFO overflow, but limit number of log messages after open.
+ * signal a complete buffer if done.
+ * dequeue a new buffer if available.
+ * disable VIP if no buffer available.
+ *
+ * return value: IRQ_NONE, interrupt was not generated by VIP
+ *
+ * IRQ_HANDLED, interrupt done.
+ */
+static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip)
+{
+	u32 status, dma;
+	unsigned long flags;
+	struct videobuf_buffer *vb;
+
+	status = REG_READ(vip, DVP_ITS);
+
+	if (!status) {
+		pr_debug("VIP: irq ignored\n");
+		return IRQ_NONE;
+	}
+
+	if (!vip->started)
+		return IRQ_HANDLED;
+
+	if (status & DVP_IT_VSB)
+		vip->bcount++;
+
+	if (status & DVP_IT_VST)
+		vip->tcount++;
+
+	if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) {
+		/* this is bad, we are too slow, hope the condition is gone
+		 * on the next frame */
+		pr_info("VIP: both irqs\n");
+		return IRQ_HANDLED;
+	}
+
+	if (status & DVP_IT_FIFO) {
+		if (5 > vip->overflow++)
+			pr_info("VIP: fifo overflow\n");
+	}
+
+	if (2 > vip->tcount)
+		return IRQ_HANDLED;
+
+	if (status & DVP_IT_VSB)
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&vip->slock, flags);
+
+	REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA);
+	if (vip->active) {
+		do_gettimeofday(&vip->active->ts);
+		vip->active->field_count++;
+		vip->active->state = VIDEOBUF_DONE;
+		wake_up(&vip->active->done);
+		vip->active = NULL;
+	}
+	if (!vip->closing) {
+		if (list_empty(&vip->capture))
+			goto done;
+
+		vb = list_first_entry(&vip->capture, struct videobuf_buffer,
+				      queue);
+		if (NULL == vb) {
+			pr_info("VIP: no buffer\n");
+			goto done;
+		}
+		vb->state = VIDEOBUF_ACTIVE;
+		list_del(&vb->queue);
+		vip->active = vb;
+		dma = videobuf_to_dma_contig(vb);
+		switch (vip->format.field) {
+		case V4L2_FIELD_INTERLACED:
+			REG_WRITE(vip, DVP_VTP, dma);
+			REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2);
+			break;
+		case V4L2_FIELD_TOP:
+		case V4L2_FIELD_BOTTOM:
+			REG_WRITE(vip, DVP_VTP, dma);
+			REG_WRITE(vip, DVP_VBP, dma);
+			break;
+		default:
+			pr_warning("VIP: unknown field format\n");
+			goto done;
+			break;
+		}
+		REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA);
+	}
+done:
+	spin_unlock_irqrestore(&vip->slock, flags);
+	return IRQ_HANDLED;
+}
+
+/**
+ * vip_gpio_reserve - reserve gpio pin
+ * @dev: device
+ * @pin: GPIO pin number
+ * @dir: direction, input or output
+ * @name: GPIO pin name
+ *
+ */
+static int vip_gpio_reserve(struct device *dev, int pin, int dir,
+			    const char *name)
+{
+	int ret;
+
+	if (pin == -1)
+		return 0;
+
+	ret = gpio_request(pin, name);
+	if (ret) {
+		dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name);
+		return ret;
+	}
+
+	ret = gpio_direction_output(pin, dir);
+	if (ret) {
+		dev_err(dev, "Failed to set direction for pin %d (%s)\n",
+			pin, name);
+		gpio_free(pin);
+		return ret;
+	}
+
+	ret = gpio_export(pin, false);
+	if (ret) {
+		dev_err(dev, "Failed to export pin %d (%s)\n", pin, name);
+		gpio_free(pin);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * vip_gpio_release - release gpio pin
+ * @dev: device
+ * @pin: GPIO pin number
+ * @name: GPIO pin name
+ *
+ */
+static void vip_gpio_release(struct device *dev, int pin, const char *name)
+{
+	if (pin != -1) {
+		dev_dbg(dev, "releasing pin %d (%s)\n",	pin, name);
+		gpio_unexport(pin);
+		gpio_free(pin);
+	}
+}
+
+/**
+ * sta2x11_vip_init_one - init one instance of video device
+ * @pdev: PCI device
+ * @ent: (not used)
+ *
+ * allocate reset pins for DAC.
+ * Reset video DAC, this is done via reset line.
+ * allocate memory for managing device
+ * request interrupt
+ * map IO region
+ * register device
+ * find and initialize video DAC
+ *
+ * return value: 0, no error
+ *
+ * -ENOMEM, no memory
+ *
+ * -ENODEV, device could not be detected or registered
+ */
+static int __devinit sta2x11_vip_init_one(struct pci_dev *pdev,
+					  const struct pci_device_id *ent)
+{
+	int ret;
+	struct sta2x11_vip *vip;
+	struct vip_config *config;
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	config = dev_get_platdata(&pdev->dev);
+	if (!config) {
+		dev_info(&pdev->dev, "VIP slot disabled\n");
+		ret = -EINVAL;
+		goto disable;
+	}
+
+	ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0,
+			       config->pwr_name);
+	if (ret)
+		goto disable;
+
+	if (config->reset_pin >= 0) {
+		ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
+				       config->reset_name);
+		if (ret) {
+			vip_gpio_release(&pdev->dev, config->pwr_pin,
+					 config->pwr_name);
+			goto disable;
+		}
+	}
+
+	if (config->pwr_pin != -1) {
+		/* Datasheet says 5ms between PWR and RST */
+		usleep_range(5000, 25000);
+		ret = gpio_direction_output(config->pwr_pin, 1);
+	}
+
+	if (config->reset_pin != -1) {
+		/* Datasheet says 5ms between PWR and RST */
+		usleep_range(5000, 25000);
+		ret = gpio_direction_output(config->reset_pin, 1);
+	}
+	usleep_range(5000, 25000);
+
+	vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL);
+	if (!vip) {
+		ret = -ENOMEM;
+		goto release_gpios;
+	}
+
+	vip->pdev = pdev;
+	vip->std = V4L2_STD_PAL;
+	vip->format = formats_50[0];
+	vip->config = config;
+
+	if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev))
+		goto free_mem;
+
+	dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n",
+		(unsigned long)pci_resource_start(pdev, 0),
+		(unsigned long)pci_resource_len(pdev, 0), pdev->irq);
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRV_NAME);
+	if (ret)
+		goto unreg;
+
+	vip->iomem = pci_iomap(pdev, 0, 0x100);
+	if (!vip->iomem) {
+		ret = -ENOMEM; /* FIXME */
+		goto release;
+	}
+
+	pci_enable_msi(pdev);
+
+	INIT_LIST_HEAD(&vip->capture);
+	spin_lock_init(&vip->slock);
+	mutex_init(&vip->mutex);
+	vip->started = 0;
+	vip->disabled = 0;
+
+	ret = request_irq(pdev->irq,
+			  (irq_handler_t) vip_irq,
+			  IRQF_SHARED, DRV_NAME, vip);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	vip->video_dev = video_device_alloc();
+	if (!vip->video_dev) {
+		ret = -ENOMEM;
+		goto release_irq;
+	}
+
+	*(vip->video_dev) = video_dev_template;
+	video_set_drvdata(vip->video_dev, vip);
+
+	ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1);
+	if (ret)
+		goto vrelease;
+
+	vip->adapter = i2c_get_adapter(vip->config->i2c_id);
+	if (!vip->adapter) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "no I2C adapter found\n");
+		goto vunreg;
+	}
+
+	vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter,
+					   "adv7180", vip->config->i2c_addr,
+					   NULL);
+	if (!vip->decoder) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "no decoder found\n");
+		goto vunreg;
+	}
+
+	i2c_put_adapter(vip->adapter);
+
+	v4l2_subdev_call(vip->decoder, core, init, 0);
+
+	pr_info("STA2X11 Video Input Port (VIP) loaded\n");
+	return 0;
+
+vunreg:
+	video_set_drvdata(vip->video_dev, NULL);
+vrelease:
+	if (video_is_registered(vip->video_dev))
+		video_unregister_device(vip->video_dev);
+	else
+		video_device_release(vip->video_dev);
+release_irq:
+	free_irq(pdev->irq, vip);
+	pci_disable_msi(pdev);
+unmap:
+	pci_iounmap(pdev, vip->iomem);
+	mutex_destroy(&vip->mutex);
+release:
+	pci_release_regions(pdev);
+unreg:
+	v4l2_device_unregister(&vip->v4l2_dev);
+free_mem:
+	kfree(vip);
+release_gpios:
+	vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name);
+	vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name);
+disable:
+	/*
+	 * do not call pci_disable_device on sta2x11 because it break all
+	 * other Bus masters on this EP
+	 */
+	return ret;
+}
+
+/**
+ * sta2x11_vip_remove_one - release device
+ * @pdev: PCI device
+ *
+ * Undo everything done in .._init_one
+ *
+ * unregister video device
+ * free interrupt
+ * unmap ioadresses
+ * free memory
+ * free GPIO pins
+ */
+static void __devexit sta2x11_vip_remove_one(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+	struct sta2x11_vip *vip =
+	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+
+	video_set_drvdata(vip->video_dev, NULL);
+	video_unregister_device(vip->video_dev);
+	/*do not call video_device_release() here, is already done */
+	free_irq(pdev->irq, vip);
+	pci_disable_msi(pdev);
+	pci_iounmap(pdev, vip->iomem);
+	pci_release_regions(pdev);
+
+	v4l2_device_unregister(&vip->v4l2_dev);
+	mutex_destroy(&vip->mutex);
+
+	vip_gpio_release(&pdev->dev, vip->config->pwr_pin,
+			 vip->config->pwr_name);
+	vip_gpio_release(&pdev->dev, vip->config->reset_pin,
+			 vip->config->reset_name);
+
+	kfree(vip);
+	/*
+	 * do not call pci_disable_device on sta2x11 because it break all
+	 * other Bus masters on this EP
+	 */
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * sta2x11_vip_suspend - set device into power save mode
+ * @pdev: PCI device
+ * @state: new state of device
+ *
+ * all relevant registers are saved and an attempt to set a new state is made.
+ *
+ * return value: 0 always indicate success,
+ * even if device could not be disabled. (workaround for hardware problem)
+ *
+ * reurn value : 0, always succesful, even if hardware does not not support
+ * power down mode.
+ */
+static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+	struct sta2x11_vip *vip =
+	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&vip->slock, flags);
+	vip->register_save_area[0] = REG_READ(vip, DVP_CTL);
+	REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS);
+	vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM);
+	REG_WRITE(vip, DVP_ITM, 0);
+	for (i = 1; i < SAVE_COUNT; i++)
+		vip->register_save_area[i] = REG_READ(vip, 4 * i);
+	for (i = 0; i < AUX_COUNT; i++)
+		vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] =
+		    REG_READ(vip, registers_to_save[i]);
+	spin_unlock_irqrestore(&vip->slock, flags);
+	/* save pci state */
+	pci_save_state(pdev);
+	if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) {
+		/*
+		 * do not call pci_disable_device on sta2x11 because it
+		 * break all other Bus masters on this EP
+		 */
+		vip->disabled = 1;
+	}
+
+	pr_info("VIP: suspend\n");
+	return 0;
+}
+
+/**
+ * sta2x11_vip_resume - resume device operation
+ * @pdev : PCI device
+ *
+ * re-enable device, set PCI state to powered and restore registers.
+ * resume normal device operation afterwards.
+ *
+ * return value: 0, no error.
+ *
+ * other, could not set device to power on state.
+ */
+static int sta2x11_vip_resume(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+	struct sta2x11_vip *vip =
+	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+	unsigned long flags;
+	int ret, i;
+
+	pr_info("VIP: resume\n");
+	/* restore pci state */
+	if (vip->disabled) {
+		ret = pci_enable_device(pdev);
+		if (ret) {
+			pr_warning("VIP: Can't enable device.\n");
+			return ret;
+		}
+		vip->disabled = 0;
+	}
+	ret = pci_set_power_state(pdev, PCI_D0);
+	if (ret) {
+		/*
+		 * do not call pci_disable_device on sta2x11 because it
+		 * break all other Bus masters on this EP
+		 */
+		pr_warning("VIP: Can't enable device.\n");
+		vip->disabled = 1;
+		return ret;
+	}
+
+	pci_restore_state(pdev);
+
+	spin_lock_irqsave(&vip->slock, flags);
+	for (i = 1; i < SAVE_COUNT; i++)
+		REG_WRITE(vip, 4 * i, vip->register_save_area[i]);
+	for (i = 0; i < AUX_COUNT; i++)
+		REG_WRITE(vip, registers_to_save[i],
+			  vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]);
+	REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]);
+	REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]);
+	spin_unlock_irqrestore(&vip->slock, flags);
+	return 0;
+}
+
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = {
+	{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)},
+	{0,}
+};
+
+static struct pci_driver sta2x11_vip_driver = {
+	.name = DRV_NAME,
+	.probe = sta2x11_vip_init_one,
+	.remove = __devexit_p(sta2x11_vip_remove_one),
+	.id_table = sta2x11_vip_pci_tbl,
+#ifdef CONFIG_PM
+	.suspend = sta2x11_vip_suspend,
+	.resume = sta2x11_vip_resume,
+#endif
+};
+
+static int __init sta2x11_vip_init_module(void)
+{
+	return pci_register_driver(&sta2x11_vip_driver);
+}
+
+static void __exit sta2x11_vip_exit_module(void)
+{
+	pci_unregister_driver(&sta2x11_vip_driver);
+}
+
+#ifdef MODULE
+module_init(sta2x11_vip_init_module);
+module_exit(sta2x11_vip_exit_module);
+#else
+late_initcall_sync(sta2x11_vip_init_module);
+#endif
+
+MODULE_DESCRIPTION("STA2X11 Video Input Port driver");
+MODULE_AUTHOR("Wind River");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("sta2x11 video input");
+MODULE_VERSION(DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl);
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.h b/drivers/media/pci/sta2x11/sta2x11_vip.h
new file mode 100644
index 000000000000..4f81a13666eb
--- /dev/null
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011 Wind River Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * Author:  Anders Wallin <anders.wallin@windriver.com>
+ *
+ */
+
+#ifndef __STA2X11_VIP_H
+#define __STA2X11_VIP_H
+
+/**
+ * struct vip_config - video input configuration data
+ * @pwr_name: ADV powerdown name
+ * @pwr_pin: ADV powerdown pin
+ * @reset_name: ADV reset name
+ * @reset_pin: ADV reset pin
+ */
+struct vip_config {
+	const char *pwr_name;
+	int pwr_pin;
+	const char *reset_name;
+	int reset_pin;
+	int i2c_id;
+	int i2c_addr;
+};
+
+#endif /* __STA2X11_VIP_H */
diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig
new file mode 100644
index 000000000000..314e417addae
--- /dev/null
+++ b/drivers/media/pci/ttpci/Kconfig
@@ -0,0 +1,159 @@
+config TTPCI_EEPROM
+	tristate
+	depends on I2C
+	default n
+
+config DVB_AV7110
+	tristate "AV7110 cards"
+	depends on DVB_CORE && PCI && I2C
+	select TTPCI_EEPROM
+	select VIDEO_SAA7146_VV
+	depends on VIDEO_DEV	# dependencies of VIDEO_SAA7146_VV
+	select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for SAA7146 and AV7110 based DVB cards as produced
+	  by Fujitsu-Siemens, Technotrend, Hauppauge and others.
+
+	  This driver only supports the fullfeatured cards with
+	  onboard MPEG2 decoder.
+
+	  This driver needs an external firmware. Please use the script
+	  "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
+	  download/extract it, and then copy it to /usr/lib/hotplug/firmware
+	  or /lib/firmware (depending on configuration of firmware hotplug).
+
+	  Alternatively, you can download the file and use the kernel's
+	  EXTRA_FIRMWARE configuration option to build it into your
+	  kernel image by adding the filename to the EXTRA_FIRMWARE
+	  configuration option string.
+
+	  Say Y if you own such a card and want to use it.
+
+config DVB_AV7110_OSD
+	bool "AV7110 OSD support"
+	depends on DVB_AV7110
+	default y if DVB_AV7110=y || DVB_AV7110=m
+	help
+	  The AV7110 firmware provides some code to generate an OnScreenDisplay
+	  on the video output. This is kind of nonstandard and not guaranteed to
+	  be maintained.
+
+	  Anyway, some popular DVB software like VDR uses this OSD to render
+	  its menus, so say Y if you want to use this software.
+
+	  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_BUDGET_CORE && I2C
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  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.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget.
+
+config DVB_BUDGET_CI
+	tristate "Budget cards with onboard CI connector"
+	depends on DVB_BUDGET_CORE && I2C
+	select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+	depends on RC_CORE
+	help
+	  Support for simple SAA7146 based DVB cards
+	  (so called Budget- or Nova-PCI cards) without onboard
+	  MPEG2 decoder, but with onboard Common Interface connector.
+
+	  Note: The Common Interface is not yet supported by this driver
+	  due to lack of information from the vendor.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-ci.
+
+config DVB_BUDGET_AV
+	tristate "Budget cards with analog video inputs"
+	depends on DVB_BUDGET_CORE && I2C
+	select VIDEO_SAA7146_VV
+	depends on VIDEO_DEV	# dependencies of VIDEO_SAA7146_VV
+	select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for simple SAA7146 based DVB cards
+	  (so called Budget- or Nova-PCI cards) without onboard
+	  MPEG2 decoder, but with one or more analog video inputs.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-av.
+
+config DVB_BUDGET_PATCH
+	tristate "AV7110 cards with Budget Patch"
+	depends on DVB_BUDGET_CORE && I2C
+	depends on DVB_AV7110
+	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for Budget Patch (full TS) modification on
+	  SAA7146+AV7110 based cards (DVB-S cards). This
+	  driver doesn't use onboard MPEG2 decoder. The
+	  card is driven in Budget-only mode. Card is
+	  required to have loaded firmware to tune properly.
+	  Firmware can be loaded by insertion and removal of
+	  standard AV7110 driver prior to loading this
+	  driver.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-patch.
diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile
new file mode 100644
index 000000000000..98905963ff08
--- /dev/null
+++ b/drivers/media/pci/ttpci/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the kernel SAA7146 FULL TS DVB device driver
+# and the AV7110 DVB device driver
+#
+
+dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
+
+ifdef CONFIG_INPUT_EVDEV
+dvb-ttpci-objs += av7110_ir.o
+endif
+
+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
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
new file mode 100644
index 000000000000..4bd8bd56befc
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -0,0 +1,2939 @@
+/*
+ * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB)
+ * av7110.c: initialization and demux stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+
+#include "ttpci-eeprom.h"
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+#include "av7110_ca.h"
+#include "av7110_ipack.h"
+
+#include "bsbe1.h"
+#include "lnbp21.h"
+#include "bsru6.h"
+
+#define TS_WIDTH  376
+#define TS_HEIGHT 512
+#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
+#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
+
+
+int av7110_debug;
+
+static int vidmode = CVBS_RGB_OUT;
+static int pids_off;
+static int adac = DVB_ADAC_TI;
+static int hw_sections;
+static int rgb_on;
+static int volume = 255;
+static int budgetpatch;
+static int wss_cfg_4_3 = 0x4008;
+static int wss_cfg_16_9 = 0x0007;
+static int tv_standard;
+static int full_ts;
+
+module_param_named(debug, av7110_debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");
+module_param(vidmode, int, 0444);
+MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC");
+module_param(pids_off, int, 0444);
+MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed");
+module_param(adac, int, 0444);
+MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)");
+module_param(hw_sections, int, 0444);
+MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware");
+module_param(rgb_on, int, 0444);
+MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control"
+		" signal on SCART pin 16 to switch SCART video mode from CVBS to RGB");
+module_param(volume, int, 0444);
+MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)");
+module_param(budgetpatch, int, 0444);
+MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)");
+module_param(full_ts, int, 0444);
+MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable");
+module_param(wss_cfg_4_3, int, 0444);
+MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data");
+module_param(wss_cfg_16_9, int, 0444);
+MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data");
+module_param(tv_standard, int, 0444);
+MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static void restart_feeds(struct av7110 *av7110);
+static int budget_start_feed(struct dvb_demux_feed *feed);
+static int budget_stop_feed(struct dvb_demux_feed *feed);
+
+static int av7110_num;
+
+#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \
+{\
+	if (fe_func != NULL) { \
+		av7110_copy = fe_func; \
+		fe_func = av7110_func; \
+	} \
+}
+
+
+static void init_av7110_av(struct av7110 *av7110)
+{
+	int ret;
+	struct saa7146_dev *dev = av7110->dev;
+
+	/* set internal volume control to maximum */
+	av7110->adac_type = DVB_ADAC_TI;
+	ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right);
+	if (ret < 0)
+		printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret);
+
+	ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType,
+			    1, (u16) av7110->display_ar);
+	if (ret < 0)
+		printk("dvb-ttpci: unable to set aspect ratio\n");
+	ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType,
+			    1, av7110->display_panscan);
+	if (ret < 0)
+		printk("dvb-ttpci: unable to set pan scan\n");
+
+	ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3);
+	if (ret < 0)
+		printk("dvb-ttpci: unable to configure 4:3 wss\n");
+	ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9);
+	if (ret < 0)
+		printk("dvb-ttpci: unable to configure 16:9 wss\n");
+
+	ret = av7710_set_video_mode(av7110, vidmode);
+	if (ret < 0)
+		printk("dvb-ttpci:cannot set video mode:%d\n",ret);
+
+	/* handle different card types */
+	/* remaining inits according to card and frontend type */
+	av7110->analog_tuner_flags = 0;
+	av7110->current_input = 0;
+	if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a)
+		av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on
+	if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) {
+		printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n",
+			av7110->dvb_adapter.num);
+		av7110->adac_type = DVB_ADAC_CRYSTAL;
+		i2c_writereg(av7110, 0x20, 0x01, 0xd2);
+		i2c_writereg(av7110, 0x20, 0x02, 0x49);
+		i2c_writereg(av7110, 0x20, 0x03, 0x00);
+		i2c_writereg(av7110, 0x20, 0x04, 0x00);
+
+		/**
+		 * some special handling for the Siemens DVB-C cards...
+		 */
+	} else if (0 == av7110_init_analog_module(av7110)) {
+		/* done. */
+	}
+	else if (dev->pci->subsystem_vendor == 0x110a) {
+		printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n",
+			av7110->dvb_adapter.num);
+		av7110->adac_type = DVB_ADAC_NONE;
+	}
+	else {
+		av7110->adac_type = adac;
+		printk("dvb-ttpci: adac type set to %d @ card %d\n",
+			av7110->adac_type, av7110->dvb_adapter.num);
+	}
+
+	if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) {
+		// switch DVB SCART on
+		ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0);
+		if (ret < 0)
+			printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret);
+		ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1);
+		if (ret < 0)
+			printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret);
+		if (rgb_on &&
+		    ((av7110->dev->pci->subsystem_vendor == 0x110a) ||
+		     (av7110->dev->pci->subsystem_vendor == 0x13c2)) &&
+		     (av7110->dev->pci->subsystem_device == 0x0000)) {
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16
+			//saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8
+		}
+	}
+
+	if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e)
+		av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on
+
+	ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right);
+	if (ret < 0)
+		printk("dvb-ttpci:cannot set volume :%d\n",ret);
+}
+
+static void recover_arm(struct av7110 *av7110)
+{
+	dprintk(4, "%p\n",av7110);
+
+	av7110_bootarm(av7110);
+	msleep(100);
+
+	init_av7110_av(av7110);
+
+	/* card-specific recovery */
+	if (av7110->recover)
+		av7110->recover(av7110);
+
+	restart_feeds(av7110);
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_check_ir_config(av7110, true);
+#endif
+}
+
+static void av7110_arm_sync(struct av7110 *av7110)
+{
+	if (av7110->arm_thread)
+		kthread_stop(av7110->arm_thread);
+
+	av7110->arm_thread = NULL;
+}
+
+static int arm_thread(void *data)
+{
+	struct av7110 *av7110 = data;
+	u16 newloops = 0;
+	int timeout;
+
+	dprintk(4, "%p\n",av7110);
+
+	for (;;) {
+		timeout = wait_event_interruptible_timeout(av7110->arm_wait,
+			kthread_should_stop(), 5 * HZ);
+
+		if (-ERESTARTSYS == timeout || kthread_should_stop()) {
+			/* got signal or told to quit*/
+			break;
+		}
+
+		if (!av7110->arm_ready)
+			continue;
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+		av7110_check_ir_config(av7110, false);
+#endif
+
+		if (mutex_lock_interruptible(&av7110->dcomlock))
+			break;
+		newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2);
+		mutex_unlock(&av7110->dcomlock);
+
+		if (newloops == av7110->arm_loops || av7110->arm_errors > 3) {
+			printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n",
+			       av7110->dvb_adapter.num);
+
+			recover_arm(av7110);
+
+			if (mutex_lock_interruptible(&av7110->dcomlock))
+				break;
+			newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1;
+			mutex_unlock(&av7110->dcomlock);
+		}
+		av7110->arm_loops = newloops;
+		av7110->arm_errors = 0;
+	}
+
+	return 0;
+}
+
+
+/****************************************************************************
+ * IRQ handling
+ ****************************************************************************/
+
+static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len,
+				u8 *buffer2, size_t buffer2_len,
+				struct dvb_demux_filter *dvbdmxfilter,
+				enum dmx_success success,
+				struct av7110 *av7110)
+{
+	if (!dvbdmxfilter->feed->demux->dmx.frontend)
+		return 0;
+	if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE)
+		return 0;
+
+	switch (dvbdmxfilter->type) {
+	case DMX_TYPE_SEC:
+		if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len)
+			return 0;
+		if (dvbdmxfilter->doneq) {
+			struct dmx_section_filter *filter = &dvbdmxfilter->filter;
+			int i;
+			u8 xor, neq = 0;
+
+			for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+				xor = filter->filter_value[i] ^ buffer1[i];
+				neq |= dvbdmxfilter->maskandnotmode[i] & xor;
+			}
+			if (!neq)
+				return 0;
+		}
+		return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len,
+						  buffer2, buffer2_len,
+						  &dvbdmxfilter->filter,
+						  DMX_OK);
+	case DMX_TYPE_TS:
+		if (!(dvbdmxfilter->feed->ts_type & TS_PACKET))
+			return 0;
+		if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY)
+			return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len,
+							 buffer2, buffer2_len,
+							 &dvbdmxfilter->feed->feed.ts,
+							 DMX_OK);
+		else
+			av7110_p2t_write(buffer1, buffer1_len,
+					 dvbdmxfilter->feed->pid,
+					 &av7110->p2t_filter[dvbdmxfilter->index]);
+	default:
+		return 0;
+	}
+}
+
+
+//#define DEBUG_TIMING
+static inline void print_time(char *s)
+{
+#ifdef DEBUG_TIMING
+	struct timeval tv;
+	do_gettimeofday(&tv);
+	printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec);
+#endif
+}
+
+#define DEBI_READ 0
+#define DEBI_WRITE 1
+static inline void start_debi_dma(struct av7110 *av7110, int dir,
+				  unsigned long addr, unsigned int len)
+{
+	dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len);
+	if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
+		printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__);
+		return;
+	}
+
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */
+	SAA7146_IER_ENABLE(av7110->dev, MASK_19);
+	if (len < 5)
+		len = 5; /* we want a real DEBI DMA */
+	if (dir == DEBI_WRITE)
+		iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3);
+	else
+		irdebi(av7110, DEBISWAB, addr, 0, len);
+}
+
+static void debiirq(unsigned long cookie)
+{
+	struct av7110 *av7110 = (struct av7110 *)cookie;
+	int type = av7110->debitype;
+	int handle = (type >> 8) & 0x1f;
+	unsigned int xfer = 0;
+
+	print_time("debi");
+	dprintk(4, "type 0x%04x\n", type);
+
+	if (type == -1) {
+		printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n",
+		       jiffies, saa7146_read(av7110->dev, PSR),
+		       saa7146_read(av7110->dev, SSR));
+		goto debi_done;
+	}
+	av7110->debitype = -1;
+
+	switch (type & 0xff) {
+
+	case DATA_TS_RECORD:
+		dvb_dmx_swfilter_packets(&av7110->demux,
+					 (const u8 *) av7110->debi_virt,
+					 av7110->debilen / 188);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_PES_RECORD:
+		if (av7110->demux.recording)
+			av7110_record_cb(&av7110->p2t[handle],
+					 (u8 *) av7110->debi_virt,
+					 av7110->debilen);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_IPMPE:
+	case DATA_FSECTION:
+	case DATA_PIPING:
+		if (av7110->handle2filter[handle])
+			DvbDmxFilterCallback((u8 *)av7110->debi_virt,
+					     av7110->debilen, NULL, 0,
+					     av7110->handle2filter[handle],
+					     DMX_OK, av7110);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_CI_GET:
+	{
+		u8 *data = av7110->debi_virt;
+
+		if ((data[0] < 2) && data[2] == 0xff) {
+			int flags = 0;
+			if (data[5] > 0)
+				flags |= CA_CI_MODULE_PRESENT;
+			if (data[5] > 5)
+				flags |= CA_CI_MODULE_READY;
+			av7110->ci_slot[data[0]].flags = flags;
+		} else
+			ci_get_data(&av7110->ci_rbuffer,
+				    av7110->debi_virt,
+				    av7110->debilen);
+		xfer = RX_BUFF;
+		break;
+	}
+
+	case DATA_COMMON_INTERFACE:
+		CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen);
+#if 0
+	{
+		int i;
+
+		printk("av7110%d: ", av7110->num);
+		printk("%02x ", *(u8 *)av7110->debi_virt);
+		printk("%02x ", *(1+(u8 *)av7110->debi_virt));
+		for (i = 2; i < av7110->debilen; i++)
+			printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt)));
+		for (i = 2; i < av7110->debilen; i++)
+			printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt)));
+
+		printk("\n");
+	}
+#endif
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_DEBUG_MESSAGE:
+		((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0;
+		printk("%s\n", (s8 *) av7110->debi_virt);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_CI_PUT:
+		dprintk(4, "debi DATA_CI_PUT\n");
+	case DATA_MPEG_PLAY:
+		dprintk(4, "debi DATA_MPEG_PLAY\n");
+	case DATA_BMP_LOAD:
+		dprintk(4, "debi DATA_BMP_LOAD\n");
+		xfer = TX_BUFF;
+		break;
+	default:
+		break;
+	}
+debi_done:
+	spin_lock(&av7110->debilock);
+	if (xfer)
+		iwdebi(av7110, DEBINOSWAP, xfer, 0, 2);
+	ARM_ClearMailBox(av7110);
+	spin_unlock(&av7110->debilock);
+}
+
+/* irq from av7110 firmware writing the mailbox register in the DPRAM */
+static void gpioirq(unsigned long cookie)
+{
+	struct av7110 *av7110 = (struct av7110 *)cookie;
+	u32 rxbuf, txbuf;
+	int len;
+
+	if (av7110->debitype != -1)
+		/* we shouldn't get any irq while a debi xfer is running */
+		printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n",
+		       jiffies, saa7146_read(av7110->dev, PSR),
+		       saa7146_read(av7110->dev, SSR));
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
+		printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__);
+		BUG(); /* maybe we should try resetting the debi? */
+	}
+
+	spin_lock(&av7110->debilock);
+	ARM_ClearIrq(av7110);
+
+	/* see what the av7110 wants */
+	av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2);
+	av7110->debilen  = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+	rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+	txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+	len = (av7110->debilen + 3) & ~3;
+
+	print_time("gpio");
+	dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen);
+
+	switch (av7110->debitype & 0xff) {
+
+	case DATA_TS_PLAY:
+	case DATA_PES_PLAY:
+		break;
+
+	case DATA_MPEG_VIDEO_EVENT:
+	{
+		u32 h_ar;
+		struct video_event event;
+
+		av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2);
+		h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2);
+
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+
+		av7110->video_size.h = h_ar & 0xfff;
+
+		event.type = VIDEO_EVENT_SIZE_CHANGED;
+		event.u.size.w = av7110->video_size.w;
+		event.u.size.h = av7110->video_size.h;
+		switch ((h_ar >> 12) & 0xf)
+		{
+		case 3:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_16_9;
+			av7110->videostate.video_format = VIDEO_FORMAT_16_9;
+			break;
+		case 4:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_221_1;
+			av7110->videostate.video_format = VIDEO_FORMAT_221_1;
+			break;
+		default:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_4_3;
+			av7110->videostate.video_format = VIDEO_FORMAT_4_3;
+		}
+
+		dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n",
+			av7110->video_size.w, av7110->video_size.h,
+			av7110->video_size.aspect_ratio);
+
+		dvb_video_add_event(av7110, &event);
+		break;
+	}
+
+	case DATA_CI_PUT:
+	{
+		int avail;
+		struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer;
+
+		avail = dvb_ringbuffer_avail(cibuf);
+		if (avail <= 2) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;
+		len |= DVB_RINGBUFFER_PEEK(cibuf, 1);
+		if (avail < len + 2) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		DVB_RINGBUFFER_SKIP(cibuf, 2);
+
+		dvb_ringbuffer_read(cibuf, av7110->debi_virt, len);
+
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		dprintk(8, "DMA: CI\n");
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len);
+		spin_unlock(&av7110->debilock);
+		wake_up(&cibuf->queue);
+		return;
+	}
+
+	case DATA_MPEG_PLAY:
+		if (!av7110->playing) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		len = 0;
+		if (av7110->debitype & 0x100) {
+			spin_lock(&av7110->aout.lock);
+			len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048);
+			spin_unlock(&av7110->aout.lock);
+		}
+		if (len <= 0 && (av7110->debitype & 0x200)
+		    &&av7110->videostate.play_state != VIDEO_FREEZED) {
+			spin_lock(&av7110->avout.lock);
+			len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048);
+			spin_unlock(&av7110->avout.lock);
+		}
+		if (len <= 0) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len);
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		dprintk(8, "DMA: MPEG_PLAY\n");
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_BMP_LOAD:
+		len = av7110->debilen;
+		dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len);
+		if (!len) {
+			av7110->bmp_state = BMP_LOADED;
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			wake_up(&av7110->bmpq);
+			dprintk(8, "gpio DATA_BMP_LOAD done\n");
+			break;
+		}
+		if (len > av7110->bmplen)
+			len = av7110->bmplen;
+		if (len > 2 * 1024)
+			len = 2 * 1024;
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len);
+		av7110->bmpp += len;
+		av7110->bmplen -= len;
+		dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len);
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_CI_GET:
+	case DATA_COMMON_INTERFACE:
+	case DATA_FSECTION:
+	case DATA_IPMPE:
+	case DATA_PIPING:
+		if (!len || len > 4 * 1024) {
+			iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+			break;
+		}
+		/* fall through */
+
+	case DATA_TS_RECORD:
+	case DATA_PES_RECORD:
+		dprintk(8, "DMA: TS_REC etc.\n");
+		start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_DEBUG_MESSAGE:
+		if (!len || len > 0xff) {
+			iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+			break;
+		}
+		start_debi_dma(av7110, DEBI_READ, Reserved, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_IRCOMMAND:
+		if (av7110->ir.ir_handler)
+			av7110->ir.ir_handler(av7110,
+				swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4)));
+		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+		break;
+
+	default:
+		printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n",
+		       av7110->debitype, av7110->debilen);
+		break;
+	}
+	av7110->debitype = -1;
+	ARM_ClearMailBox(av7110);
+	spin_unlock(&av7110->debilock);
+}
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+static int dvb_osd_ioctl(struct file *file,
+			 unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (cmd == OSD_SEND_CMD)
+		return av7110_osd_cmd(av7110, (osd_cmd_t *) parg);
+	if (cmd == OSD_GET_CAPABILITY)
+		return av7110_osd_capability(av7110, (osd_cap_t *) parg);
+
+	return -EINVAL;
+}
+
+
+static const struct file_operations dvb_osd_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= dvb_generic_ioctl,
+	.open		= dvb_generic_open,
+	.release	= dvb_generic_release,
+	.llseek		= noop_llseek,
+};
+
+static struct dvb_device dvbdev_osd = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_osd_fops,
+	.kernel_ioctl	= dvb_osd_ioctl,
+};
+#endif /* CONFIG_DVB_AV7110_OSD */
+
+
+static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+			  u16 subpid, u16 pcrpid)
+{
+	u16 aflags = 0;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (vpid == 0x1fff || apid == 0x1fff ||
+	    ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) {
+		vpid = apid = ttpid = subpid = pcrpid = 0;
+		av7110->pids[DMX_PES_VIDEO] = 0;
+		av7110->pids[DMX_PES_AUDIO] = 0;
+		av7110->pids[DMX_PES_TELETEXT] = 0;
+		av7110->pids[DMX_PES_PCR] = 0;
+	}
+
+	if (av7110->audiostate.bypass_mode)
+		aflags |= 0x8000;
+
+	return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6,
+			     pcrpid, vpid, apid, ttpid, subpid, aflags);
+}
+
+int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+		u16 subpid, u16 pcrpid)
+{
+	int ret = 0;
+	dprintk(4, "%p\n", av7110);
+
+	if (mutex_lock_interruptible(&av7110->pid_mutex))
+		return -ERESTARTSYS;
+
+	if (!(vpid & 0x8000))
+		av7110->pids[DMX_PES_VIDEO] = vpid;
+	if (!(apid & 0x8000))
+		av7110->pids[DMX_PES_AUDIO] = apid;
+	if (!(ttpid & 0x8000))
+		av7110->pids[DMX_PES_TELETEXT] = ttpid;
+	if (!(pcrpid & 0x8000))
+		av7110->pids[DMX_PES_PCR] = pcrpid;
+
+	av7110->pids[DMX_PES_SUBTITLE] = 0;
+
+	if (av7110->fe_synced) {
+		pcrpid = av7110->pids[DMX_PES_PCR];
+		ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid);
+	}
+
+	mutex_unlock(&av7110->pid_mutex);
+	return ret;
+}
+
+
+/******************************************************************************
+ * hardware filter functions
+ ******************************************************************************/
+
+static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter)
+{
+	struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed;
+	struct av7110 *av7110 = dvbdmxfeed->demux->priv;
+	u16 buf[20];
+	int ret, i;
+	u16 handle;
+//	u16 mode = 0x0320;
+	u16 mode = 0xb96a;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->full_ts)
+		return 0;
+
+	if (dvbdmxfilter->type == DMX_TYPE_SEC) {
+		if (hw_sections) {
+			buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) |
+				dvbdmxfilter->maskandmode[0];
+			for (i = 3; i < 18; i++)
+				buf[i + 4 - 2] =
+					(dvbdmxfilter->filter.filter_value[i] << 8) |
+					dvbdmxfilter->maskandmode[i];
+			mode = 4;
+		}
+	} else if ((dvbdmxfeed->ts_type & TS_PACKET) &&
+		   !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) {
+		av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed);
+	}
+
+	buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter;
+	buf[1] = 16;
+	buf[2] = dvbdmxfeed->pid;
+	buf[3] = mode;
+
+	ret = av7110_fw_request(av7110, buf, 20, &handle, 1);
+	if (ret != 0 || handle >= 32) {
+		printk("dvb-ttpci: %s error  buf %04x %04x %04x %04x  "
+				"ret %d  handle %04x\n",
+				__func__, buf[0], buf[1], buf[2], buf[3],
+				ret, handle);
+		dvbdmxfilter->hw_handle = 0xffff;
+		if (!ret)
+			ret = -1;
+		return ret;
+	}
+
+	av7110->handle2filter[handle] = dvbdmxfilter;
+	dvbdmxfilter->hw_handle = handle;
+
+	return ret;
+}
+
+static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter)
+{
+	struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv;
+	u16 buf[3];
+	u16 answ[2];
+	int ret;
+	u16 handle;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->full_ts)
+		return 0;
+
+	handle = dvbdmxfilter->hw_handle;
+	if (handle >= 32) {
+		printk("%s tried to stop invalid filter %04x, filter type = %x\n",
+				__func__, handle, dvbdmxfilter->type);
+		return -EINVAL;
+	}
+
+	av7110->handle2filter[handle] = NULL;
+
+	buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter;
+	buf[1] = 1;
+	buf[2] = handle;
+	ret = av7110_fw_request(av7110, buf, 3, answ, 2);
+	if (ret != 0 || answ[1] != handle) {
+		printk("dvb-ttpci: %s error  cmd %04x %04x %04x  ret %x  "
+				"resp %04x %04x  pid %d\n",
+				__func__, buf[0], buf[1], buf[2], ret,
+				answ[0], answ[1], dvbdmxfilter->feed->pid);
+		if (!ret)
+			ret = -1;
+	}
+	return ret;
+}
+
+
+static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct av7110 *av7110 = dvbdmx->priv;
+	u16 *pid = dvbdmx->pids, npids[5];
+	int i;
+	int ret = 0;
+
+	dprintk(4, "%p\n", av7110);
+
+	npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff;
+	i = dvbdmxfeed->pes_type;
+	npids[i] = (pid[i]&0x8000) ? 0 : pid[i];
+	if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) {
+		npids[i] = 0;
+		ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+		if (!ret)
+			ret = StartHWFilter(dvbdmxfeed->filter);
+		return ret;
+	}
+	if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) {
+		ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+		if (ret)
+			return ret;
+	}
+
+	if (dvbdmxfeed->pes_type < 2 && npids[0])
+		if (av7110->fe_synced)
+		{
+			ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+			if (ret)
+				return ret;
+		}
+
+	if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) {
+		if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000))
+			ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed);
+		if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000))
+			ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed);
+	}
+	return ret;
+}
+
+static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct av7110 *av7110 = dvbdmx->priv;
+	u16 *pid = dvbdmx->pids, npids[5];
+	int i;
+
+	int ret = 0;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (dvbdmxfeed->pes_type <= 1) {
+		ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ?  RP_VIDEO : RP_AUDIO);
+		if (ret)
+			return ret;
+		if (!av7110->rec_mode)
+			dvbdmx->recording = 0;
+		if (!av7110->playing)
+			dvbdmx->playing = 0;
+	}
+	npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff;
+	i = dvbdmxfeed->pes_type;
+	switch (i) {
+	case 2: //teletext
+		if (dvbdmxfeed->ts_type & TS_PACKET)
+			ret = StopHWFilter(dvbdmxfeed->filter);
+		npids[2] = 0;
+		break;
+	case 0:
+	case 1:
+	case 4:
+		if (!pids_off)
+			return 0;
+		npids[i] = (pid[i]&0x8000) ? 0 : pid[i];
+		break;
+	}
+	if (!ret)
+		ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+	return ret;
+}
+
+static int av7110_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = demux->priv;
+	int ret = 0;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	if (!av7110->full_ts && feed->pid > 0x1fff)
+		return -EINVAL;
+
+	if (feed->type == DMX_TYPE_TS) {
+		if ((feed->ts_type & TS_DECODER) &&
+		    (feed->pes_type <= DMX_TS_PES_PCR)) {
+			switch (demux->dmx.frontend->source) {
+			case DMX_MEMORY_FE:
+				if (feed->ts_type & TS_DECODER)
+				       if (feed->pes_type < 2 &&
+					   !(demux->pids[0] & 0x8000) &&
+					   !(demux->pids[1] & 0x8000)) {
+					       dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+					       dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+					       ret = av7110_av_start_play(av7110,RP_AV);
+					       if (!ret)
+						       demux->playing = 1;
+					}
+				break;
+			default:
+				ret = dvb_feed_start_pid(feed);
+				break;
+			}
+		} else if ((feed->ts_type & TS_PACKET) &&
+			   (demux->dmx.frontend->source != DMX_MEMORY_FE)) {
+			ret = StartHWFilter(feed->filter);
+		}
+	}
+
+	if (av7110->full_ts) {
+		budget_start_feed(feed);
+		return ret;
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
+		int i;
+
+		for (i = 0; i < demux->filternum; i++) {
+			if (demux->filter[i].state != DMX_STATE_READY)
+				continue;
+			if (demux->filter[i].type != DMX_TYPE_SEC)
+				continue;
+			if (demux->filter[i].filter.parent != &feed->feed.sec)
+				continue;
+			demux->filter[i].state = DMX_STATE_GO;
+			if (demux->dmx.frontend->source != DMX_MEMORY_FE) {
+				ret = StartHWFilter(&demux->filter[i]);
+				if (ret)
+					break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+
+static int av7110_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = demux->priv;
+	int i, rc, ret = 0;
+	dprintk(4, "%p\n", av7110);
+
+	if (feed->type == DMX_TYPE_TS) {
+		if (feed->ts_type & TS_DECODER) {
+			if (feed->pes_type >= DMX_TS_PES_OTHER ||
+			    !demux->pesfilter[feed->pes_type])
+				return -EINVAL;
+			demux->pids[feed->pes_type] |= 0x8000;
+			demux->pesfilter[feed->pes_type] = NULL;
+		}
+		if (feed->ts_type & TS_DECODER &&
+		    feed->pes_type < DMX_TS_PES_OTHER) {
+			ret = dvb_feed_stop_pid(feed);
+		} else
+			if ((feed->ts_type & TS_PACKET) &&
+			    (demux->dmx.frontend->source != DMX_MEMORY_FE))
+				ret = StopHWFilter(feed->filter);
+	}
+
+	if (av7110->full_ts) {
+		budget_stop_feed(feed);
+		return ret;
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
+		for (i = 0; i<demux->filternum; i++) {
+			if (demux->filter[i].state == DMX_STATE_GO &&
+			    demux->filter[i].filter.parent == &feed->feed.sec) {
+				demux->filter[i].state = DMX_STATE_READY;
+				if (demux->dmx.frontend->source != DMX_MEMORY_FE) {
+					rc = StopHWFilter(&demux->filter[i]);
+					if (!ret)
+						ret = rc;
+					/* keep going, stop as many filters as possible */
+				}
+			}
+		}
+	}
+
+	return ret;
+}
+
+
+static void restart_feeds(struct av7110 *av7110)
+{
+	struct dvb_demux *dvbdmx = &av7110->demux;
+	struct dvb_demux_feed *feed;
+	int mode;
+	int feeding;
+	int i, j;
+
+	dprintk(4, "%p\n", av7110);
+
+	mode = av7110->playing;
+	av7110->playing = 0;
+	av7110->rec_mode = 0;
+
+	feeding = av7110->feeding1; /* full_ts mod */
+
+	for (i = 0; i < dvbdmx->feednum; i++) {
+		feed = &dvbdmx->feed[i];
+		if (feed->state == DMX_STATE_GO) {
+			if (feed->type == DMX_TYPE_SEC) {
+				for (j = 0; j < dvbdmx->filternum; j++) {
+					if (dvbdmx->filter[j].type != DMX_TYPE_SEC)
+						continue;
+					if (dvbdmx->filter[j].filter.parent != &feed->feed.sec)
+						continue;
+					if (dvbdmx->filter[j].state == DMX_STATE_GO)
+						dvbdmx->filter[j].state = DMX_STATE_READY;
+				}
+			}
+			av7110_start_feed(feed);
+		}
+	}
+
+	av7110->feeding1 = feeding; /* full_ts mod */
+
+	if (mode)
+		av7110_av_start_play(av7110, mode);
+}
+
+static int dvb_get_stc(struct dmx_demux *demux, unsigned int num,
+		       uint64_t *stc, unsigned int *base)
+{
+	int ret;
+	u16 fwstc[4];
+	u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC);
+	struct dvb_demux *dvbdemux;
+	struct av7110 *av7110;
+
+	/* pointer casting paranoia... */
+	BUG_ON(!demux);
+	dvbdemux = demux->priv;
+	BUG_ON(!dvbdemux);
+	av7110 = dvbdemux->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (num != 0)
+		return -EINVAL;
+
+	ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4);
+	if (ret) {
+		printk(KERN_ERR "%s: av7110_fw_request error\n", __func__);
+		return ret;
+	}
+	dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n",
+		fwstc[0], fwstc[1], fwstc[2], fwstc[3]);
+
+	*stc =	(((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) |
+		(((uint64_t)  fwstc[1]) << 16) | ((uint64_t) fwstc[0]);
+	*base = 1;
+
+	dprintk(4, "stc = %lu\n", (unsigned long)*stc);
+
+	return 0;
+}
+
+
+/******************************************************************************
+ * SEC device file operations
+ ******************************************************************************/
+
+
+static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		return Set22K(av7110, 1);
+
+	case SEC_TONE_OFF:
+		return Set22K(av7110, 0);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe,
+					 struct dvb_diseqc_master_cmd* cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1);
+}
+
+static int av7110_diseqc_send_burst(struct dvb_frontend* fe,
+				    fe_sec_mini_cmd_t minicmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	return av7110_diseqc_send(av7110, 0, NULL, minicmd);
+}
+
+/* simplified code from budget-core.c */
+static int stop_ts_capture(struct av7110 *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	if (--budget->feeding1)
+		return budget->feeding1;
+	saa7146_write(budget->dev, MC1, MASK_20);	/* DMA3 off */
+	SAA7146_IER_DISABLE(budget->dev, MASK_10);
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);
+	return 0;
+}
+
+static int start_ts_capture(struct av7110 *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	if (budget->feeding1)
+		return ++budget->feeding1;
+	memset(budget->grabbing, 0x00, TS_BUFLEN);
+	budget->ttbp = 0;
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);  /* VPE */
+	SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
+	saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
+	return ++budget->feeding1;
+}
+
+static int budget_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *budget = demux->priv;
+	int status;
+
+	dprintk(2, "av7110: %p\n", budget);
+
+	spin_lock(&budget->feedlock1);
+	feed->pusi_seen = 0; /* have a clean section start */
+	status = start_ts_capture(budget);
+	spin_unlock(&budget->feedlock1);
+	return status;
+}
+
+static int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *budget = demux->priv;
+	int status;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	spin_lock(&budget->feedlock1);
+	status = stop_ts_capture(budget);
+	spin_unlock(&budget->feedlock1);
+	return status;
+}
+
+static void vpeirq(unsigned long cookie)
+{
+	struct av7110 *budget = (struct av7110 *)cookie;
+	u8 *mem = (u8 *) (budget->grabbing);
+	u32 olddma = budget->ttbp;
+	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+	struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1;
+
+	/* nearest lower position divisible by 188 */
+	newdma -= newdma % 188;
+
+	if (newdma >= TS_BUFLEN)
+		return;
+
+	budget->ttbp = newdma;
+
+	if (!budget->feeding1 || (newdma == olddma))
+		return;
+
+	/* Ensure streamed PCI data is synced to CPU */
+	pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE);
+
+#if 0
+	/* track rps1 activity */
+	printk("vpeirq: %02x Event Counter 1 0x%04x\n",
+	       mem[olddma],
+	       saa7146_read(budget->dev, EC1R) & 0x3fff);
+#endif
+
+	if (newdma > olddma)
+		/* no wraparound, dump olddma..newdma */
+		dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188);
+	else {
+		/* wraparound, dump olddma..buflen and 0..newdma */
+		dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+		dvb_dmx_swfilter_packets(demux, mem, newdma / 188);
+	}
+}
+
+static int av7110_register(struct av7110 *av7110)
+{
+	int ret, i;
+	struct dvb_demux *dvbdemux = &av7110->demux;
+	struct dvb_demux *dvbdemux1 = &av7110->demux1;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->registered)
+		return -1;
+
+	av7110->registered = 1;
+
+	dvbdemux->priv = (void *) av7110;
+
+	for (i = 0; i < 32; i++)
+		av7110->handle2filter[i] = NULL;
+
+	dvbdemux->filternum = (av7110->full_ts) ? 256 : 32;
+	dvbdemux->feednum = (av7110->full_ts) ? 256 : 32;
+	dvbdemux->start_feed = av7110_start_feed;
+	dvbdemux->stop_feed = av7110_stop_feed;
+	dvbdemux->write_to_decoder = av7110_write_to_decoder;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+
+	dvb_dmx_init(&av7110->demux);
+	av7110->demux.dmx.get_stc = dvb_get_stc;
+
+	av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32;
+	av7110->dmxdev.demux = &dvbdemux->dmx;
+	av7110->dmxdev.capabilities = 0;
+
+	dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter);
+
+	av7110->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	av7110->mem_frontend.source = DMX_MEMORY_FE;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx,
+					     &av7110->hw_frontend);
+	if (ret < 0)
+		return ret;
+
+	av7110_av_register(av7110);
+	av7110_ca_register(av7110);
+
+#ifdef CONFIG_DVB_AV7110_OSD
+	dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev,
+			    &dvbdev_osd, av7110, DVB_DEVICE_OSD);
+#endif
+
+	dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx);
+
+	if (budgetpatch) {
+		/* initialize software demux1 without its own frontend
+		 * demux1 hardware is connected to frontend0 of demux0
+		 */
+		dvbdemux1->priv = (void *) av7110;
+
+		dvbdemux1->filternum = 256;
+		dvbdemux1->feednum = 256;
+		dvbdemux1->start_feed = budget_start_feed;
+		dvbdemux1->stop_feed = budget_stop_feed;
+		dvbdemux1->write_to_decoder = NULL;
+
+		dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+					       DMX_MEMORY_BASED_FILTERING);
+
+		dvb_dmx_init(&av7110->demux1);
+
+		av7110->dmxdev1.filternum = 256;
+		av7110->dmxdev1.demux = &dvbdemux1->dmx;
+		av7110->dmxdev1.capabilities = 0;
+
+		dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter);
+
+		dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx);
+		printk("dvb-ttpci: additional demux1 for budget-patch registered\n");
+	}
+	return 0;
+}
+
+
+static void dvb_unregister(struct av7110 *av7110)
+{
+	struct dvb_demux *dvbdemux = &av7110->demux;
+	struct dvb_demux *dvbdemux1 = &av7110->demux1;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->registered)
+		return;
+
+	if (budgetpatch) {
+		dvb_net_release(&av7110->dvb_net1);
+		dvbdemux->dmx.close(&dvbdemux1->dmx);
+		dvb_dmxdev_release(&av7110->dmxdev1);
+		dvb_dmx_release(&av7110->demux1);
+	}
+
+	dvb_net_release(&av7110->dvb_net);
+
+	dvbdemux->dmx.close(&dvbdemux->dmx);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend);
+
+	dvb_dmxdev_release(&av7110->dmxdev);
+	dvb_dmx_release(&av7110->demux);
+
+	if (av7110->fe != NULL) {
+		dvb_unregister_frontend(av7110->fe);
+		dvb_frontend_detach(av7110->fe);
+	}
+	dvb_unregister_device(av7110->osd_dev);
+	av7110_av_unregister(av7110);
+	av7110_ca_unregister(av7110);
+}
+
+
+/****************************************************************************
+ * I2C client commands
+ ****************************************************************************/
+
+int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val)
+{
+	u8 msg[2] = { reg, val };
+	struct i2c_msg msgs;
+
+	msgs.flags = 0;
+	msgs.addr = id / 2;
+	msgs.len = 2;
+	msgs.buf = msg;
+	return i2c_transfer(&av7110->i2c_adap, &msgs, 1);
+}
+
+u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg)
+{
+	u8 mm1[] = {0x00};
+	u8 mm2[] = {0x00};
+	struct i2c_msg msgs[2];
+
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+	msgs[0].addr = msgs[1].addr = id / 2;
+	mm1[0] = reg;
+	msgs[0].len = 1; msgs[1].len = 1;
+	msgs[0].buf = mm1; msgs[1].buf = mm2;
+	i2c_transfer(&av7110->i2c_adap, msgs, 2);
+
+	return mm2[0];
+}
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+
+static int check_firmware(struct av7110* av7110)
+{
+	u32 crc = 0, len = 0;
+	unsigned char *ptr;
+
+	/* check for firmware magic */
+	ptr = av7110->bin_fw;
+	if (ptr[0] != 'A' || ptr[1] != 'V' ||
+	    ptr[2] != 'F' || ptr[3] != 'W') {
+		printk("dvb-ttpci: this is not an av7110 firmware\n");
+		return -EINVAL;
+	}
+	ptr += 4;
+
+	/* check dpram file */
+	crc = get_unaligned_be32(ptr);
+	ptr += 4;
+	len = get_unaligned_be32(ptr);
+	ptr += 4;
+	if (len >= 512) {
+		printk("dvb-ttpci: dpram file is way too big.\n");
+		return -EINVAL;
+	}
+	if (crc != crc32_le(0, ptr, len)) {
+		printk("dvb-ttpci: crc32 of dpram file does not match.\n");
+		return -EINVAL;
+	}
+	av7110->bin_dpram = ptr;
+	av7110->size_dpram = len;
+	ptr += len;
+
+	/* check root file */
+	crc = get_unaligned_be32(ptr);
+	ptr += 4;
+	len = get_unaligned_be32(ptr);
+	ptr += 4;
+
+	if (len <= 200000 || len >= 300000 ||
+	    len > ((av7110->bin_fw + av7110->size_fw) - ptr)) {
+		printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len);
+		return -EINVAL;
+	}
+	if( crc != crc32_le(0, ptr, len)) {
+		printk("dvb-ttpci: crc32 of root file does not match.\n");
+		return -EINVAL;
+	}
+	av7110->bin_root = ptr;
+	av7110->size_root = len;
+	return 0;
+}
+
+static void put_firmware(struct av7110* av7110)
+{
+	vfree(av7110->bin_fw);
+}
+
+static int get_firmware(struct av7110* av7110)
+{
+	int ret;
+	const struct firmware *fw;
+
+	/* request the av7110 firmware, this will block until someone uploads it */
+	ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev);
+	if (ret) {
+		if (ret == -ENOENT) {
+			printk(KERN_ERR "dvb-ttpci: could not load firmware,"
+			       " file not found: dvb-ttpci-01.fw\n");
+			printk(KERN_ERR "dvb-ttpci: usually this should be in "
+			       "/usr/lib/hotplug/firmware or /lib/firmware\n");
+			printk(KERN_ERR "dvb-ttpci: and can be downloaded from"
+			       " http://www.linuxtv.org/download/dvb/firmware/\n");
+		} else
+			printk(KERN_ERR "dvb-ttpci: cannot request firmware"
+			       " (error %i)\n", ret);
+		return -EINVAL;
+	}
+
+	if (fw->size <= 200000) {
+		printk("dvb-ttpci: this firmware is way too small.\n");
+		release_firmware(fw);
+		return -EINVAL;
+	}
+
+	/* check if the firmware is available */
+	av7110->bin_fw = vmalloc(fw->size);
+	if (NULL == av7110->bin_fw) {
+		dprintk(1, "out of memory\n");
+		release_firmware(fw);
+		return -ENOMEM;
+	}
+
+	memcpy(av7110->bin_fw, fw->data, fw->size);
+	av7110->size_fw = fw->size;
+	if ((ret = check_firmware(av7110)))
+		vfree(av7110->bin_fw);
+
+	release_firmware(fw);
+	return ret;
+}
+
+static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (p->frequency + 479500) / 125;
+
+	if (p->frequency > 2000000)
+		pwr = 3;
+	else if (p->frequency > 1800000)
+		pwr = 2;
+	else if (p->frequency > 1600000)
+		pwr = 1;
+	else if (p->frequency > 1200000)
+		pwr = 0;
+	else if (p->frequency >= 1100000)
+		pwr = 1;
+	else
+		pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+	// NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config = {
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+};
+
+static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (p->frequency + 35937500 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85 | ((div >> 10) & 0x60);
+	data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+};
+
+
+
+
+static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = p->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+};
+
+
+
+static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u32 f = p->frequency;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (f + 36125000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1820_config philips_cd1516_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+};
+
+
+
+static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div, pwr;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (p->frequency + 36200000) / 166666;
+
+	if (p->frequency <= 782000000)
+		pwr = 1;
+	else
+		pwr = 2;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85;
+	data[3] = pwr << 6;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE)
+	struct av7110* av7110 = fe->dvb->priv;
+
+	return request_firmware(fw, name, &av7110->dev->pci->dev);
+#else
+	return -EINVAL;
+#endif
+}
+
+static struct sp8870_config alps_tdlb7_config = {
+
+	.demod_address = 0x71,
+	.request_firmware = alps_tdlb7_request_firmware,
+};
+
+
+static u8 nexusca_stv0297_inittab[] = {
+	0x80, 0x01,
+	0x80, 0x00,
+	0x81, 0x01,
+	0x81, 0x00,
+	0x00, 0x09,
+	0x01, 0x69,
+	0x03, 0x00,
+	0x04, 0x00,
+	0x07, 0x00,
+	0x08, 0x00,
+	0x20, 0x00,
+	0x21, 0x40,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x24, 0x40,
+	0x25, 0x88,
+	0x30, 0xff,
+	0x31, 0x00,
+	0x32, 0xff,
+	0x33, 0x00,
+	0x34, 0x50,
+	0x35, 0x7f,
+	0x36, 0x00,
+	0x37, 0x20,
+	0x38, 0x00,
+	0x40, 0x1c,
+	0x41, 0xff,
+	0x42, 0x29,
+	0x43, 0x00,
+	0x44, 0xff,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x49, 0x04,
+	0x4a, 0x00,
+	0x4b, 0x7b,
+	0x52, 0x30,
+	0x55, 0xae,
+	0x56, 0x47,
+	0x57, 0xe1,
+	0x58, 0x3a,
+	0x5a, 0x1e,
+	0x5b, 0x34,
+	0x60, 0x00,
+	0x63, 0x00,
+	0x64, 0x00,
+	0x65, 0x00,
+	0x66, 0x00,
+	0x67, 0x00,
+	0x68, 0x00,
+	0x69, 0x00,
+	0x6a, 0x02,
+	0x6b, 0x00,
+	0x70, 0xff,
+	0x71, 0x00,
+	0x72, 0x00,
+	0x73, 0x00,
+	0x74, 0x0c,
+	0x80, 0x00,
+	0x81, 0x00,
+	0x82, 0x00,
+	0x83, 0x00,
+	0x84, 0x04,
+	0x85, 0x80,
+	0x86, 0x24,
+	0x87, 0x78,
+	0x88, 0x10,
+	0x89, 0x00,
+	0x90, 0x01,
+	0x91, 0x01,
+	0xa0, 0x04,
+	0xa1, 0x00,
+	0xa2, 0x00,
+	0xb0, 0x91,
+	0xb1, 0x0b,
+	0xc0, 0x53,
+	0xc1, 0x70,
+	0xc2, 0x12,
+	0xd0, 0x00,
+	0xd1, 0x00,
+	0xd2, 0x00,
+	0xd3, 0x00,
+	0xd4, 0x00,
+	0xd5, 0x00,
+	0xde, 0x00,
+	0xdf, 0x00,
+	0x61, 0x49,
+	0x62, 0x0b,
+	0x53, 0x08,
+	0x59, 0x08,
+	0xff, 0xff,
+};
+
+static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) };
+	struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 };
+	int i;
+
+	div = (p->frequency + 36150000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0xce;
+
+	if (p->frequency < 45000000)
+		return -EINVAL;
+	else if (p->frequency < 137000000)
+		data[3] = 0x01;
+	else if (p->frequency < 403000000)
+		data[3] = 0x02;
+	else if (p->frequency < 860000000)
+		data[3] = 0x04;
+	else
+		return -EINVAL;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) {
+		printk("nexusca: pll transfer failed!\n");
+		return -EIO;
+	}
+
+	// wait for PLL lock
+	for(i = 0; i < 20; i++) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1)
+			if (data[0] & 0x40) break;
+		msleep(10);
+	}
+
+	return 0;
+}
+
+static struct stv0297_config nexusca_stv0297_config = {
+
+	.demod_address = 0x1C,
+	.inittab = nexusca_stv0297_inittab,
+	.invert = 1,
+	.stop_during_read = 1,
+};
+
+
+
+static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (36125000 + p->frequency) / 166666;
+
+	cfg = 0x88;
+
+	if (p->frequency < 175000000)
+		cpump = 2;
+	else if (p->frequency < 390000000)
+		cpump = 1;
+	else if (p->frequency < 470000000)
+		cpump = 2;
+	else if (p->frequency < 750000000)
+		cpump = 1;
+	else
+		cpump = 3;
+
+	if (p->frequency < 175000000)
+		band_select = 0x0e;
+	else if (p->frequency < 470000000)
+		band_select = 0x05;
+	else
+		band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = (cpump << 6) | band_select;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+	.demod_address = 0x55,
+};
+
+
+
+static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status)
+{
+	int ret = 0;
+	int synced = (status & FE_HAS_LOCK) ? 1 : 0;
+
+	av7110->fe_status = status;
+
+	if (av7110->fe_synced == synced)
+		return 0;
+
+	if (av7110->playing) {
+		av7110->fe_synced = synced;
+		return 0;
+	}
+
+	if (mutex_lock_interruptible(&av7110->pid_mutex))
+		return -ERESTARTSYS;
+
+	if (synced) {
+		ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO],
+			av7110->pids[DMX_PES_AUDIO],
+			av7110->pids[DMX_PES_TELETEXT], 0,
+			av7110->pids[DMX_PES_PCR]);
+		if (!ret)
+			ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+	} else {
+		ret = SetPIDs(av7110, 0, 0, 0, 0, 0);
+		if (!ret) {
+			ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0);
+			if (!ret)
+				ret = av7110_wait_msgstate(av7110, GPMQBusy);
+		}
+	}
+
+	if (!ret)
+		av7110->fe_synced = synced;
+
+	mutex_unlock(&av7110->pid_mutex);
+	return ret;
+}
+
+static int av7110_fe_set_frontend(struct dvb_frontend *fe)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret)
+		ret = av7110->fe_set_frontend(fe);
+
+	return ret;
+}
+
+static int av7110_fe_init(struct dvb_frontend* fe)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret)
+		ret = av7110->fe_init(fe);
+	return ret;
+}
+
+static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	/* call the real implementation */
+	int ret = av7110->fe_read_status(fe, status);
+	if (!ret)
+		if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK))
+			ret = av7110_fe_lock_fix(av7110, *status);
+	return ret;
+}
+
+static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret)
+		ret = av7110->fe_diseqc_reset_overload(fe);
+	return ret;
+}
+
+static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe,
+					    struct dvb_diseqc_master_cmd* cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret) {
+		av7110->saved_master_cmd = *cmd;
+		ret = av7110->fe_diseqc_send_master_cmd(fe, cmd);
+	}
+	return ret;
+}
+
+static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret) {
+		av7110->saved_minicmd = minicmd;
+		ret = av7110->fe_diseqc_send_burst(fe, minicmd);
+	}
+	return ret;
+}
+
+static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret) {
+		av7110->saved_tone = tone;
+		ret = av7110->fe_set_tone(fe, tone);
+	}
+	return ret;
+}
+
+static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret) {
+		av7110->saved_voltage = voltage;
+		ret = av7110->fe_set_voltage(fe, voltage);
+	}
+	return ret;
+}
+
+static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	int ret = av7110_fe_lock_fix(av7110, 0);
+	if (!ret)
+		ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd);
+	return ret;
+}
+
+static void dvb_s_recover(struct av7110* av7110)
+{
+	av7110_fe_init(av7110->fe);
+
+	av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage);
+	if (av7110->saved_master_cmd.msg_len) {
+		msleep(20);
+		av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd);
+	}
+	msleep(20);
+	av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd);
+	msleep(20);
+	av7110_fe_set_tone(av7110->fe, av7110->saved_tone);
+
+	av7110_fe_set_frontend(av7110->fe);
+}
+
+static u8 read_pwm(struct av7110* av7110)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+				 { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+	if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static int frontend_init(struct av7110 *av7110)
+{
+	int ret;
+
+	if (av7110->dev->pci->subsystem_vendor == 0x110a) {
+		switch(av7110->dev->pci->subsystem_device) {
+		case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??))
+			av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config,
+						    &av7110->i2c_adap, read_pwm(av7110));
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params;
+			}
+			break;
+		}
+
+	} else if (av7110->dev->pci->subsystem_vendor == 0x13c2) {
+		switch(av7110->dev->pci->subsystem_device) {
+		case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X
+		case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X
+		case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE
+
+			// try the ALPS BSRV2 first of all
+			av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+				av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops.set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
+				break;
+			}
+
+			// try the ALPS BSRU6 now
+			av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+				av7110->fe->tuner_priv = &av7110->i2c_adap;
+
+				av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops.set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
+				break;
+			}
+
+			// Try the grundig 29504-451
+			av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+				av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops.set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
+				break;
+			}
+
+			/* Try DVB-C cards */
+			switch(av7110->dev->pci->subsystem_device) {
+			case 0x0000:
+				/* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */
+				av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap,
+							read_pwm(av7110));
+				if (av7110->fe) {
+					av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params;
+				}
+				break;
+			case 0x0003:
+				/* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */
+				av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap,
+							read_pwm(av7110));
+				if (av7110->fe) {
+					av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
+				}
+				break;
+			}
+			break;
+
+		case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X
+			// try ALPS TDLB7 first, then Grundig 29504-401
+			av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params;
+				break;
+			}
+			/* fall-thru */
+
+		case 0x0008: // Hauppauge/TT DVB-T
+			// Grundig 29504-401
+			av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap);
+			if (av7110->fe)
+				av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+			break;
+
+		case 0x0002: // Hauppauge/TT DVB-C premium rev2.X
+
+			av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110));
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
+			}
+			break;
+
+		case 0x0004: // Galaxis DVB-S rev1.3
+			/* ALPS BSRV2 */
+			av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+				av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops.set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
+			}
+			break;
+
+		case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */
+			/* Grundig 29504-451 */
+			av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+				av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops.set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
+			}
+			break;
+
+		case 0x000A: // Hauppauge/TT Nexus-CA rev1.X
+
+			av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params;
+
+				/* set TDA9819 into DVB mode */
+				saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
+				saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
+
+				/* tuner on this needs a slower i2c bus speed */
+				av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+				break;
+			}
+			break;
+
+		case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */
+			/* ALPS BSBE1 */
+			av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+				av7110->fe->tuner_priv = &av7110->i2c_adap;
+
+				if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) {
+					printk("dvb-ttpci: LNBP21 not found!\n");
+					if (av7110->fe->ops.release)
+						av7110->fe->ops.release(av7110->fe);
+					av7110->fe = NULL;
+				} else {
+					av7110->fe->ops.dishnetwork_send_legacy_command = NULL;
+					av7110->recover = dvb_s_recover;
+				}
+			}
+			break;
+		}
+	}
+
+	if (!av7110->fe) {
+		/* FIXME: propagate the failure code from the lower layers */
+		ret = -ENOMEM;
+		printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       av7110->dev->pci->vendor,
+		       av7110->dev->pci->device,
+		       av7110->dev->pci->subsystem_vendor,
+		       av7110->dev->pci->subsystem_device);
+	} else {
+		FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command);
+		FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend);
+
+		ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe);
+		if (ret < 0) {
+			printk("av7110: Frontend registration failed!\n");
+			dvb_frontend_detach(av7110->fe);
+			av7110->fe = NULL;
+		}
+	}
+	return ret;
+}
+
+/* Budgetpatch note:
+ * Original hardware design by Roberto Deza:
+ * There is a DVB_Wiki at
+ * http://www.linuxtv.org/
+ *
+ * New software triggering design by Emard that works on
+ * original Roberto Deza's hardware:
+ *
+ * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin.
+ * GPIO3 is in budget-patch hardware connectd to port B VSYNC
+ * HS is an internal event of 7146, accessible with RPS
+ * and temporarily raised high every n lines
+ * (n in defined in the RPS_THRESH1 counter threshold)
+ * I think HS is raised high on the beginning of the n-th line
+ * and remains high until this n-th line that triggered
+ * it is completely received. When the receiption of n-th line
+ * ends, HS is lowered.
+ *
+ * To transmit data over DMA, 7146 needs changing state at
+ * port B VSYNC pin. Any changing of port B VSYNC will
+ * cause some DMA data transfer, with more or less packets loss.
+ * It depends on the phase and frequency of VSYNC and
+ * the way of 7146 is instructed to trigger on port B (defined
+ * in DD1_INIT register, 3rd nibble from the right valid
+ * numbers are 0-7, see datasheet)
+ *
+ * The correct triggering can minimize packet loss,
+ * dvbtraffic should give this stable bandwidths:
+ *   22k transponder = 33814 kbit/s
+ * 27.5k transponder = 38045 kbit/s
+ * by experiment it is found that the best results
+ * (stable bandwidths and almost no packet loss)
+ * are obtained using DD1_INIT triggering number 2
+ * (Va at rising edge of VS Fa = HS x VS-failing forced toggle)
+ * and a VSYNC phase that occurs in the middle of DMA transfer
+ * (about byte 188*512=96256 in the DMA window).
+ *
+ * Phase of HS is still not clear to me how to control,
+ * It just happens to be so. It can be seen if one enables
+ * RPS_IRQ and print Event Counter 1 in vpeirq(). Every
+ * time RPS_INTERRUPT is called, the Event Counter 1 will
+ * increment. That's how the 7146 is programmed to do event
+ * counting in this budget-patch.c
+ * I *think* HPS setting has something to do with the phase
+ * of HS but I can't be 100% sure in that.
+ *
+ * hardware debug note: a working budget card (including budget patch)
+ * with vpeirq() interrupt setup in mode "0x90" (every 64K) will
+ * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
+ * and that means 3*25=75 Hz of interrupt freqency, as seen by
+ * watch cat /proc/interrupts
+ *
+ * If this frequency is 3x lower (and data received in the DMA
+ * buffer don't start with 0x47, but in the middle of packets,
+ * whose lengths appear to be like 188 292 188 104 etc.
+ * this means VSYNC line is not connected in the hardware.
+ * (check soldering pcb and pins)
+ * The same behaviour of missing VSYNC can be duplicated on budget
+ * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble.
+ */
+static int __devinit av7110_attach(struct saa7146_dev* dev,
+				   struct saa7146_pci_extension_data *pci_ext)
+{
+	const int length = TS_WIDTH * TS_HEIGHT;
+	struct pci_dev *pdev = dev->pci;
+	struct av7110 *av7110;
+	struct task_struct *thread;
+	int ret, count = 0;
+
+	dprintk(4, "dev: %p\n", dev);
+
+	/* Set RPS_IRQ to 1 to track rps1 activity.
+	 * Enabling this won't send any interrupt to PC CPU.
+	 */
+#define RPS_IRQ 0
+
+	if (budgetpatch == 1) {
+		budgetpatch = 0;
+		/* autodetect the presence of budget patch
+		 * this only works if saa7146 has been recently
+		 * reset with with MASK_31 to MC1
+		 *
+		 * will wait for VBI_B event (vertical blank at port B)
+		 * and will reset GPIO3 after VBI_B is detected.
+		 * (GPIO3 should be raised high by CPU to
+		 * test if GPIO3 will generate vertical blank signal
+		 * in budget patch GPIO3 is connected to VSYNC_B
+		 */
+
+		/* RESET SAA7146 */
+		saa7146_write(dev, MC1, MASK_31);
+		/* autodetection success seems to be time-dependend after reset */
+
+		/* Fix VSYNC level */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		/* set vsync_b triggering */
+		saa7146_write(dev, DD1_STREAM_B, 0);
+		/* port B VSYNC at rising edge */
+		saa7146_write(dev, DD1_INIT, 0x00000200);
+		saa7146_write(dev, BRS_CTRL, 0x00000000);  // VBI
+		saa7146_write(dev, MC2,
+			      1 * (MASK_08 | MASK_24)  |   // BRS control
+			      0 * (MASK_09 | MASK_25)  |   // a
+			      1 * (MASK_10 | MASK_26)  |   // b
+			      0 * (MASK_06 | MASK_22)  |   // HPS_CTRL1
+			      0 * (MASK_05 | MASK_21)  |   // HPS_CTRL2
+			      0 * (MASK_01 | MASK_15)      // DEBI
+		);
+
+		/* start writing RPS1 code from beginning */
+		count = 0;
+		/* Disable RPS1 */
+		saa7146_write(dev, MC1, MASK_29);
+		/* RPS1 timeout disable */
+		saa7146_write(dev, RPS_TOV1, 0);
+		WRITE_RPS1(CMD_PAUSE | EVT_VBI_B);
+		WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+		WRITE_RPS1(GPIO3_MSK);
+		WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
+#if RPS_IRQ
+		/* issue RPS1 interrupt to increment counter */
+		WRITE_RPS1(CMD_INTERRUPT);
+#endif
+		WRITE_RPS1(CMD_STOP);
+		/* Jump to begin of RPS program as safety measure               (p37) */
+		WRITE_RPS1(CMD_JUMP);
+		WRITE_RPS1(dev->d_rps1.dma_handle);
+
+#if RPS_IRQ
+		/* set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+		 * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+		 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+		 */
+		saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+		/* set event counter 1 threshold to maximum allowed value        (rEC p55) */
+		saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+		/* Set RPS1 Address register to point to RPS code               (r108 p42) */
+		saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+		/* Enable RPS1,                                                 (rFC p33) */
+		saa7146_write(dev, MC1, (MASK_13 | MASK_29 ));
+
+		mdelay(10);
+		/* now send VSYNC_B to rps1 by rising GPIO3 */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+		mdelay(10);
+		/* if rps1 responded by lowering the GPIO3,
+		 * then we have budgetpatch hardware
+		 */
+		if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) {
+			budgetpatch = 1;
+			printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n");
+		}
+		/* Disable RPS1 */
+		saa7146_write(dev, MC1, ( MASK_29 ));
+#if RPS_IRQ
+		printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff );
+#endif
+	}
+
+	/* prepare the av7110 device struct */
+	av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL);
+	if (!av7110) {
+		dprintk(1, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	av7110->card_name = (char*) pci_ext->ext_priv;
+	av7110->dev = dev;
+	dev->ext_priv = av7110;
+
+	ret = get_firmware(av7110);
+	if (ret < 0)
+		goto err_kfree_0;
+
+	ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name,
+				   THIS_MODULE, &dev->pci->dev, adapter_nr);
+	if (ret < 0)
+		goto err_put_firmware_1;
+
+	/* the Siemens DVB needs this if you want to have the i2c chips
+	   get recognized before the main driver is fully loaded */
+	saa7146_write(dev, GPIO_CTRL, 0x500000);
+
+	strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name));
+
+	saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
+
+	ret = i2c_add_adapter(&av7110->i2c_adap);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter_2;
+
+	ttpci_eeprom_parse_mac(&av7110->i2c_adap,
+			       av7110->dvb_adapter.proposed_mac);
+	ret = -ENOMEM;
+
+	/* full-ts mod? */
+	if (full_ts)
+		av7110->full_ts = true;
+
+	/* check for full-ts flag in eeprom */
+	if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) {
+		u8 flags = i2c_readreg(av7110, 0xaa, 2);
+		if (flags != 0xff && (flags & 0x01))
+			av7110->full_ts = true;
+	}
+
+	if (av7110->full_ts) {
+		printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n");
+		spin_lock_init(&av7110->feedlock1);
+		av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
+								 &av7110->pt);
+		if (!av7110->grabbing)
+			goto err_i2c_del_3;
+
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+
+		saa7146_write(dev, DD1_INIT, 0x00000600);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		saa7146_write(dev, MC2, MASK_08 | MASK_24);
+
+		/* dma3 */
+		saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+		saa7146_write(dev, BASE_ODD3, 0);
+		saa7146_write(dev, BASE_EVEN3, 0);
+		saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+		saa7146_write(dev, PITCH3, TS_WIDTH);
+		saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90);
+		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+		saa7146_write(dev, MC2, MASK_04 | MASK_20);
+
+		tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110);
+
+	} else if (budgetpatch) {
+		spin_lock_init(&av7110->feedlock1);
+		av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
+								 &av7110->pt);
+		if (!av7110->grabbing)
+			goto err_i2c_del_3;
+
+		saa7146_write(dev, PCI_BT_V1, 0x1c1f101f);
+		saa7146_write(dev, BCS_CTRL, 0x80400040);
+		/* set dd1 stream a & b */
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, DD1_INIT, 0x03000200);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		saa7146_write(dev, BASE_ODD3, 0);
+		saa7146_write(dev, BASE_EVEN3, 0);
+		saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+		saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90);
+
+		saa7146_write(dev, PITCH3, TS_WIDTH);
+		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+
+		/* upload all */
+		saa7146_write(dev, MC2, 0x077c077c);
+		saa7146_write(dev, GPIO_CTRL, 0x000000);
+#if RPS_IRQ
+		/* set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+		 * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+		 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+		 */
+		saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+		/* set event counter 1 threshold to maximum allowed value        (rEC p55) */
+		saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+		/* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */
+		count = 0;
+
+		/* Wait Source Line Counter Threshold                           (p36) */
+		WRITE_RPS1(CMD_PAUSE | EVT_HS);
+		/* Set GPIO3=1                                                  (p42) */
+		WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+		WRITE_RPS1(GPIO3_MSK);
+		WRITE_RPS1(SAA7146_GPIO_OUTHI<<24);
+#if RPS_IRQ
+		/* issue RPS1 interrupt */
+		WRITE_RPS1(CMD_INTERRUPT);
+#endif
+		/* Wait reset Source Line Counter Threshold                     (p36) */
+		WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS);
+		/* Set GPIO3=0                                                  (p42) */
+		WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+		WRITE_RPS1(GPIO3_MSK);
+		WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
+#if RPS_IRQ
+		/* issue RPS1 interrupt */
+		WRITE_RPS1(CMD_INTERRUPT);
+#endif
+		/* Jump to begin of RPS program                                 (p37) */
+		WRITE_RPS1(CMD_JUMP);
+		WRITE_RPS1(dev->d_rps1.dma_handle);
+
+		/* Fix VSYNC level */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		/* Set RPS1 Address register to point to RPS code               (r108 p42) */
+		saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+		/* Set Source Line Counter Threshold, using BRS                 (rCC p43)
+		 * It generates HS event every TS_HEIGHT lines
+		 * this is related to TS_WIDTH set in register
+		 * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits
+		 * are set to TS_WIDTH bytes (TS_WIDTH=2*188),
+		 * then RPS_THRESH1 should be set to trigger
+		 * every TS_HEIGHT (512) lines.
+		 */
+		saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 );
+
+		/* Enable RPS1                                                  (rFC p33) */
+		saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+		/* end of budgetpatch register initialization */
+		tasklet_init (&av7110->vpe_tasklet,  vpeirq,  (unsigned long) av7110);
+	} else {
+		saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+		saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+		/* set dd1 stream a & b */
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, DD1_INIT, 0x03000000);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+		/* upload all */
+		saa7146_write(dev, MC2, 0x077c077c);
+		saa7146_write(dev, GPIO_CTRL, 0x000000);
+	}
+
+	tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110);
+	tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110);
+
+	mutex_init(&av7110->pid_mutex);
+
+	/* locks for data transfers from/to AV7110 */
+	spin_lock_init(&av7110->debilock);
+	mutex_init(&av7110->dcomlock);
+	av7110->debitype = -1;
+
+	/* default OSD window */
+	av7110->osdwin = 1;
+	mutex_init(&av7110->osd_mutex);
+
+	/* TV standard */
+	av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC
+					   : AV7110_VIDEO_MODE_PAL;
+
+	/* ARM "watchdog" */
+	init_waitqueue_head(&av7110->arm_wait);
+	av7110->arm_thread = NULL;
+
+	/* allocate and init buffers */
+	av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus);
+	if (!av7110->debi_virt)
+		goto err_saa71466_vfree_4;
+
+
+	av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS);
+	if (!av7110->iobuf)
+		goto err_pci_free_5;
+
+	ret = av7110_av_init(av7110);
+	if (ret < 0)
+		goto err_iobuf_vfree_6;
+
+	/* init BMP buffer */
+	av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN;
+	init_waitqueue_head(&av7110->bmpq);
+
+	ret = av7110_ca_init(av7110);
+	if (ret < 0)
+		goto err_av7110_av_exit_7;
+
+	/* load firmware into AV7110 cards */
+	ret = av7110_bootarm(av7110);
+	if (ret < 0)
+		goto err_av7110_ca_exit_8;
+
+	ret = av7110_firmversion(av7110);
+	if (ret < 0)
+		goto err_stop_arm_9;
+
+	if (FW_VERSION(av7110->arm_app)<0x2501)
+		printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. "
+			"System might be unstable!\n", FW_VERSION(av7110->arm_app));
+
+	thread = kthread_run(arm_thread, (void *) av7110, "arm_mon");
+	if (IS_ERR(thread)) {
+		ret = PTR_ERR(thread);
+		goto err_stop_arm_9;
+	}
+	av7110->arm_thread = thread;
+
+	/* set initial volume in mixer struct */
+	av7110->mixer.volume_left  = volume;
+	av7110->mixer.volume_right = volume;
+
+	ret = av7110_register(av7110);
+	if (ret < 0)
+		goto err_arm_thread_stop_10;
+
+	init_av7110_av(av7110);
+
+	/* special case DVB-C: these cards have an analog tuner
+	   plus need some special handling, so we have separate
+	   saa7146_ext_vv data for these... */
+	ret = av7110_init_v4l(av7110);
+	if (ret < 0)
+		goto err_av7110_unregister_11;
+
+	av7110->dvb_adapter.priv = av7110;
+	ret = frontend_init(av7110);
+	if (ret < 0)
+		goto err_av7110_exit_v4l_12;
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_ir_init(av7110);
+#endif
+	printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num);
+	av7110_num++;
+out:
+	return ret;
+
+err_av7110_exit_v4l_12:
+	av7110_exit_v4l(av7110);
+err_av7110_unregister_11:
+	dvb_unregister(av7110);
+err_arm_thread_stop_10:
+	av7110_arm_sync(av7110);
+err_stop_arm_9:
+	/* Nothing to do. Rejoice. */
+err_av7110_ca_exit_8:
+	av7110_ca_exit(av7110);
+err_av7110_av_exit_7:
+	av7110_av_exit(av7110);
+err_iobuf_vfree_6:
+	vfree(av7110->iobuf);
+err_pci_free_5:
+	pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus);
+err_saa71466_vfree_4:
+	if (av7110->grabbing)
+		saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt);
+err_i2c_del_3:
+	i2c_del_adapter(&av7110->i2c_adap);
+err_dvb_unregister_adapter_2:
+	dvb_unregister_adapter(&av7110->dvb_adapter);
+err_put_firmware_1:
+	put_firmware(av7110);
+err_kfree_0:
+	kfree(av7110);
+	goto out;
+}
+
+static int __devexit av7110_detach(struct saa7146_dev* saa)
+{
+	struct av7110 *av7110 = saa->ext_priv;
+	dprintk(4, "%p\n", av7110);
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_ir_exit(av7110);
+#endif
+	if (budgetpatch || av7110->full_ts) {
+		if (budgetpatch) {
+			/* Disable RPS1 */
+			saa7146_write(saa, MC1, MASK_29);
+			/* VSYNC LOW (inactive) */
+			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+		}
+		saa7146_write(saa, MC1, MASK_20);	/* DMA3 off */
+		SAA7146_IER_DISABLE(saa, MASK_10);
+		SAA7146_ISR_CLEAR(saa, MASK_10);
+		msleep(50);
+		tasklet_kill(&av7110->vpe_tasklet);
+		saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt);
+	}
+	av7110_exit_v4l(av7110);
+
+	av7110_arm_sync(av7110);
+
+	tasklet_kill(&av7110->debi_tasklet);
+	tasklet_kill(&av7110->gpio_tasklet);
+
+	dvb_unregister(av7110);
+
+	SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03);
+	SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03);
+
+	av7110_ca_exit(av7110);
+	av7110_av_exit(av7110);
+
+	vfree(av7110->iobuf);
+	pci_free_consistent(saa->pci, 8192, av7110->debi_virt,
+			    av7110->debi_bus);
+
+	i2c_del_adapter(&av7110->i2c_adap);
+
+	dvb_unregister_adapter (&av7110->dvb_adapter);
+
+	av7110_num--;
+
+	put_firmware(av7110);
+
+	kfree(av7110);
+
+	saa->ext_priv = NULL;
+
+	return 0;
+}
+
+
+static void av7110_irq(struct saa7146_dev* dev, u32 *isr)
+{
+	struct av7110 *av7110 = dev->ext_priv;
+
+	//print_time("av7110_irq");
+
+	/* Note: Don't try to handle the DEBI error irq (MASK_18), in
+	 * intel mode the timeout is asserted all the time...
+	 */
+
+	if (*isr & MASK_19) {
+		//printk("av7110_irq: DEBI\n");
+		/* Note 1: The DEBI irq is level triggered: We must enable it
+		 * only after we started a DMA xfer, and disable it here
+		 * immediately, or it will be signalled all the time while
+		 * DEBI is idle.
+		 * Note 2: You would think that an irq which is masked is
+		 * not signalled by the hardware. Not so for the SAA7146:
+		 * An irq is signalled as long as the corresponding bit
+		 * in the ISR is set, and disabling irqs just prevents the
+		 * hardware from setting the ISR bit. This means a) that we
+		 * must clear the ISR *after* disabling the irq (which is why
+		 * we must do it here even though saa7146_core did it already),
+		 * and b) that if we were to disable an edge triggered irq
+		 * (like the gpio irqs sadly are) temporarily we would likely
+		 * loose some. This sucks :-(
+		 */
+		SAA7146_IER_DISABLE(av7110->dev, MASK_19);
+		SAA7146_ISR_CLEAR(av7110->dev, MASK_19);
+		tasklet_schedule(&av7110->debi_tasklet);
+	}
+
+	if (*isr & MASK_03) {
+		//printk("av7110_irq: GPIO\n");
+		tasklet_schedule(&av7110->gpio_tasklet);
+	}
+
+	if (*isr & MASK_10)
+		tasklet_schedule(&av7110->vpe_tasklet);
+}
+
+
+static struct saa7146_extension av7110_extension_driver;
+
+#define MAKE_AV7110_INFO(x_var,x_name) \
+static struct saa7146_pci_extension_data x_var = { \
+	.ext_priv = x_name, \
+	.ext = &av7110_extension_driver }
+
+MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C");
+MAKE_AV7110_INFO(ttt_1_X,    "Technotrend/Hauppauge WinTV DVB-T rev1.X");
+MAKE_AV7110_INFO(ttc_1_X,    "Technotrend/Hauppauge WinTV Nexus-CA rev1.X");
+MAKE_AV7110_INFO(ttc_2_X,    "Technotrend/Hauppauge WinTV DVB-C rev2.X");
+MAKE_AV7110_INFO(tts_2_X,    "Technotrend/Hauppauge WinTV Nexus-S rev2.X");
+MAKE_AV7110_INFO(tts_2_3,    "Technotrend/Hauppauge WinTV Nexus-S rev2.3");
+MAKE_AV7110_INFO(tts_1_3se,  "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE");
+MAKE_AV7110_INFO(ttt,        "Technotrend/Hauppauge DVB-T");
+MAKE_AV7110_INFO(fsc,        "Fujitsu Siemens DVB-C");
+MAKE_AV7110_INFO(fss,        "Fujitsu Siemens DVB-S rev1.6");
+MAKE_AV7110_INFO(gxs_1_3,    "Galaxis DVB-S rev1.3");
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(fsc,         0x110a, 0x0000),
+	MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000),
+	MAKE_EXTENSION_PCI(ttt_1_X,     0x13c2, 0x0001),
+	MAKE_EXTENSION_PCI(ttc_2_X,     0x13c2, 0x0002),
+	MAKE_EXTENSION_PCI(tts_2_X,     0x13c2, 0x0003),
+	MAKE_EXTENSION_PCI(gxs_1_3,     0x13c2, 0x0004),
+	MAKE_EXTENSION_PCI(fss,         0x13c2, 0x0006),
+	MAKE_EXTENSION_PCI(ttt,         0x13c2, 0x0008),
+	MAKE_EXTENSION_PCI(ttc_1_X,     0x13c2, 0x000a),
+	MAKE_EXTENSION_PCI(tts_2_3,     0x13c2, 0x000e),
+	MAKE_EXTENSION_PCI(tts_1_3se,   0x13c2, 0x1002),
+
+/*	MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1
+/*	MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v????
+
+	{
+		.vendor    = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+
+static struct saa7146_extension av7110_extension_driver = {
+	.name		= "av7110",
+	.flags		= SAA7146_USE_I2C_IRQ,
+
+	.module		= THIS_MODULE,
+	.pci_tbl	= &pci_tbl[0],
+	.attach		= av7110_attach,
+	.detach		= __devexit_p(av7110_detach),
+
+	.irq_mask	= MASK_19 | MASK_03 | MASK_10,
+	.irq_func	= av7110_irq,
+};
+
+
+static int __init av7110_init(void)
+{
+	int retval;
+	retval = saa7146_register_extension(&av7110_extension_driver);
+	return retval;
+}
+
+
+static void __exit av7110_exit(void)
+{
+	saa7146_unregister_extension(&av7110_extension_driver);
+}
+
+module_init(av7110_init);
+module_exit(av7110_exit);
+
+MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by "
+		   "Siemens, Technotrend, Hauppauge");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
new file mode 100644
index 000000000000..88b3b2d6cc0e
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110.h
@@ -0,0 +1,314 @@
+#ifndef _AV7110_H_
+#define _AV7110_H_
+
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/osd.h>
+#include <linux/dvb/net.h>
+#include <linux/mutex.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_frontend.h"
+#include "ves1820.h"
+#include "ves1x93.h"
+#include "stv0299.h"
+#include "tda8083.h"
+#include "sp8870.h"
+#include "stv0297.h"
+#include "l64781.h"
+
+#include <media/saa7146_vv.h>
+
+
+#define ANALOG_TUNER_VES1820 1
+#define ANALOG_TUNER_STV0297 2
+
+extern int av7110_debug;
+
+#define dprintk(level,args...) \
+	    do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0)
+
+#define MAXFILT 32
+
+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;
+	long int	  pos;
+	int		  frags;
+	struct dvb_demux_feed *feed;
+};
+
+/* video MPEG decoder events: */
+/* (code copied from dvb_frontend.c, should maybe be factored out...) */
+#define MAX_VIDEO_EVENT 8
+struct dvb_video_events {
+	struct video_event	  events[MAX_VIDEO_EVENT];
+	int			  eventw;
+	int			  eventr;
+	int			  overflow;
+	wait_queue_head_t	  wait_queue;
+	spinlock_t		  lock;
+};
+
+
+struct av7110;
+
+/* infrared remote control */
+struct infrared {
+	u16	key_map[256];
+	struct input_dev	*input_dev;
+	char			input_phys[32];
+	struct timer_list	keyup_timer;
+	struct tasklet_struct	ir_tasklet;
+	void			(*ir_handler)(struct av7110 *av7110, u32 ircom);
+	u32			ir_command;
+	u32			ir_config;
+	u32			device_mask;
+	u8			protocol;
+	u8			inversion;
+	u16			last_key;
+	u16			last_toggle;
+	u8			delay_timer_finished;
+};
+
+
+/* place to store all the necessary device information */
+struct av7110 {
+
+	/* devices */
+
+	struct dvb_device	dvb_dev;
+	struct dvb_net		dvb_net;
+
+	struct video_device	*v4l_dev;
+	struct video_device	*vbi_dev;
+
+	struct saa7146_dev	*dev;
+
+	struct i2c_adapter	i2c_adap;
+
+	char			*card_name;
+
+	/* support for analog module of dvb-c */
+	int			analog_tuner_flags;
+	int			current_input;
+	u32			current_freq;
+
+	struct tasklet_struct	debi_tasklet;
+	struct tasklet_struct	gpio_tasklet;
+
+	int adac_type;	       /* audio DAC type */
+#define DVB_ADAC_TI	  0
+#define DVB_ADAC_CRYSTAL  1
+#define DVB_ADAC_MSP34x0  2
+#define DVB_ADAC_MSP34x5  3
+#define DVB_ADAC_NONE	 -1
+
+
+	/* buffers */
+
+	void		       *iobuf;	 /* memory for all buffers */
+	struct dvb_ringbuffer	avout;   /* buffer for video or A/V mux */
+#define AVOUTLEN (128*1024)
+	struct dvb_ringbuffer	aout;    /* buffer for audio */
+#define AOUTLEN (64*1024)
+	void		       *bmpbuf;
+#define BMPLEN (8*32768+1024)
+
+	/* bitmap buffers and states */
+
+	int			bmpp;
+	int			bmplen;
+	volatile int		bmp_state;
+#define BMP_NONE     0
+#define BMP_LOADING  1
+#define BMP_LOADED   2
+	wait_queue_head_t	bmpq;
+
+
+	/* DEBI and polled command interface */
+
+	spinlock_t		debilock;
+	struct mutex		dcomlock;
+	volatile int		debitype;
+	volatile int		debilen;
+
+
+	/* Recording and playback flags */
+
+	int			rec_mode;
+	int			playing;
+#define RP_NONE  0
+#define RP_VIDEO 1
+#define RP_AUDIO 2
+#define RP_AV	 3
+
+
+	/* OSD */
+
+	int			osdwin;      /* currently active window */
+	u16			osdbpp[8];
+	struct mutex		osd_mutex;
+
+	/* CA */
+
+	ca_slot_info_t		ci_slot[2];
+
+	enum av7110_video_mode	vidmode;
+	struct dmxdev		dmxdev;
+	struct dvb_demux	demux;
+
+	struct dmx_frontend	hw_frontend;
+	struct dmx_frontend	mem_frontend;
+
+	/* for budget mode demux1 */
+	struct dmxdev		dmxdev1;
+	struct dvb_demux	demux1;
+	struct dvb_net		dvb_net1;
+	spinlock_t		feedlock1;
+	int			feeding1;
+	u32			ttbp;
+	unsigned char           *grabbing;
+	struct saa7146_pgtable  pt;
+	struct tasklet_struct   vpe_tasklet;
+	bool			full_ts;
+
+	int			fe_synced;
+	struct mutex		pid_mutex;
+
+	int			video_blank;
+	struct video_status	videostate;
+	u16			display_panscan;
+	int			display_ar;
+	int			trickmode;
+#define TRICK_NONE   0
+#define TRICK_FAST   1
+#define TRICK_SLOW   2
+#define TRICK_FREEZE 3
+	struct audio_status	audiostate;
+
+	struct dvb_demux_filter *handle2filter[32];
+	struct av7110_p2t	 p2t_filter[MAXFILT];
+	struct dvb_filter_pes2ts p2t[2];
+	struct ipack		 ipack[2];
+	u8			*kbuf[2];
+
+	int sinfo;
+	int feeding;
+
+	int arm_errors;
+	int registered;
+
+
+	/* AV711X */
+
+	u32		    arm_fw;
+	u32		    arm_rtsl;
+	u32		    arm_vid;
+	u32		    arm_app;
+	u32		    avtype;
+	int		    arm_ready;
+	struct task_struct *arm_thread;
+	wait_queue_head_t   arm_wait;
+	u16		    arm_loops;
+
+	void		   *debi_virt;
+	dma_addr_t	    debi_bus;
+
+	u16		    pids[DMX_PES_OTHER];
+
+	struct dvb_ringbuffer	 ci_rbuffer;
+	struct dvb_ringbuffer	 ci_wbuffer;
+
+	struct audio_mixer	mixer;
+
+	struct dvb_adapter	 dvb_adapter;
+	struct dvb_device	 *video_dev;
+	struct dvb_device	 *audio_dev;
+	struct dvb_device	 *ca_dev;
+	struct dvb_device	 *osd_dev;
+
+	struct dvb_video_events  video_events;
+	video_size_t		 video_size;
+
+	u16			wssMode;
+	u16			wssData;
+
+	struct infrared		ir;
+
+	/* firmware stuff */
+	unsigned char *bin_fw;
+	unsigned long size_fw;
+
+	unsigned char *bin_dpram;
+	unsigned long size_dpram;
+
+	unsigned char *bin_root;
+	unsigned long size_root;
+
+	struct dvb_frontend* fe;
+	fe_status_t fe_status;
+
+	/* crash recovery */
+	void				(*recover)(struct av7110* av7110);
+	fe_sec_voltage_t		saved_voltage;
+	fe_sec_tone_mode_t		saved_tone;
+	struct dvb_diseqc_master_cmd	saved_master_cmd;
+	fe_sec_mini_cmd_t		saved_minicmd;
+
+	int (*fe_init)(struct dvb_frontend* fe);
+	int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status);
+	int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe);
+	int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
+	int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
+	int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
+	int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+	int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
+	int (*fe_set_frontend)(struct dvb_frontend *fe);
+};
+
+
+extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+		       u16 subpid, u16 pcrpid);
+
+extern int av7110_check_ir_config(struct av7110 *av7110, int force);
+extern int av7110_ir_init(struct av7110 *av7110);
+extern void av7110_ir_exit(struct av7110 *av7110);
+
+/* msp3400 i2c subaddresses */
+#define MSP_WR_DEM 0x10
+#define MSP_RD_DEM 0x11
+#define MSP_WR_DSP 0x12
+#define MSP_RD_DSP 0x13
+
+extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val);
+extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg);
+extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val);
+
+
+extern int av7110_init_analog_module(struct av7110 *av7110);
+extern int av7110_init_v4l(struct av7110 *av7110);
+extern int av7110_exit_v4l(struct av7110 *av7110);
+
+#endif /* _AV7110_H_ */
diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c
new file mode 100644
index 000000000000..952b33dbac4f
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_av.c
@@ -0,0 +1,1626 @@
+/*
+ * av7110_av.c: audio and video MPEG decoder stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+#include "av7110_ipack.h"
+
+/* MPEG-2 (ISO 13818 / H.222.0) stream types */
+#define PROG_STREAM_MAP  0xBC
+#define PRIVATE_STREAM1  0xBD
+#define PADDING_STREAM	 0xBE
+#define PRIVATE_STREAM2  0xBF
+#define AUDIO_STREAM_S	 0xC0
+#define AUDIO_STREAM_E	 0xDF
+#define VIDEO_STREAM_S	 0xE0
+#define VIDEO_STREAM_E	 0xEF
+#define ECM_STREAM	 0xF0
+#define EMM_STREAM	 0xF1
+#define DSM_CC_STREAM	 0xF2
+#define ISO13522_STREAM  0xF3
+#define PROG_STREAM_DIR  0xFF
+
+#define PTS_DTS_FLAGS	 0xC0
+
+//pts_dts flags
+#define PTS_ONLY	 0x80
+#define PTS_DTS		 0xC0
+#define TS_SIZE		 188
+#define TRANS_ERROR	 0x80
+#define PAY_START	 0x40
+#define TRANS_PRIO	 0x20
+#define PID_MASK_HI	 0x1F
+//flags
+#define TRANS_SCRMBL1	 0x80
+#define TRANS_SCRMBL2	 0x40
+#define ADAPT_FIELD	 0x20
+#define PAYLOAD		 0x10
+#define COUNT_MASK	 0x0F
+
+// adaptation flags
+#define DISCON_IND	 0x80
+#define RAND_ACC_IND	 0x40
+#define ES_PRI_IND	 0x20
+#define PCR_FLAG	 0x10
+#define OPCR_FLAG	 0x08
+#define SPLICE_FLAG	 0x04
+#define TRANS_PRIV	 0x02
+#define ADAP_EXT_FLAG	 0x01
+
+// adaptation extension flags
+#define LTW_FLAG	 0x80
+#define PIECE_RATE	 0x40
+#define SEAM_SPLICE	 0x20
+
+
+static void p_to_t(u8 const *buf, long int length, u16 pid,
+		   u8 *counter, struct dvb_demux_feed *feed);
+static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len);
+
+
+int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv;
+
+	if (!(dvbdmxfeed->ts_type & TS_PACKET))
+		return 0;
+	if (buf[3] == 0xe0)	 // video PES do not have a length in TS
+		buf[4] = buf[5] = 0;
+	if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
+		return dvbdmxfeed->cb.ts(buf, len, NULL, 0,
+					 &dvbdmxfeed->feed.ts, DMX_OK);
+	else
+		return dvb_filter_pes2ts(p2t, buf, len, 1);
+}
+
+static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv;
+
+	dvbdmxfeed->cb.ts(data, 188, NULL, 0,
+			  &dvbdmxfeed->feed.ts, DMX_OK);
+	return 0;
+}
+
+int av7110_av_start_record(struct av7110 *av7110, int av,
+			   struct dvb_demux_feed *dvbdmxfeed)
+{
+	int ret = 0;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed);
+
+	if (av7110->playing || (av7110->rec_mode & av))
+		return -EBUSY;
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+	dvbdmx->recording = 1;
+	av7110->rec_mode |= av;
+
+	switch (av7110->rec_mode) {
+	case RP_AUDIO:
+		dvb_filter_pes2ts_init(&av7110->p2t[0],
+				       dvbdmx->pesfilter[0]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[0]);
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+		break;
+
+	case RP_VIDEO:
+		dvb_filter_pes2ts_init(&av7110->p2t[1],
+				       dvbdmx->pesfilter[1]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[1]);
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+		break;
+
+	case RP_AV:
+		dvb_filter_pes2ts_init(&av7110->p2t[0],
+				       dvbdmx->pesfilter[0]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[0]);
+		dvb_filter_pes2ts_init(&av7110->p2t[1],
+				       dvbdmx->pesfilter[1]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[1]);
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0);
+		break;
+	}
+	return ret;
+}
+
+int av7110_av_start_play(struct av7110 *av7110, int av)
+{
+	int ret = 0;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->rec_mode)
+		return -EBUSY;
+	if (av7110->playing & av)
+		return -EBUSY;
+
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+
+	if (av7110->playing == RP_NONE) {
+		av7110_ipack_reset(&av7110->ipack[0]);
+		av7110_ipack_reset(&av7110->ipack[1]);
+	}
+
+	av7110->playing |= av;
+	switch (av7110->playing) {
+	case RP_AUDIO:
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+		break;
+	case RP_VIDEO:
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+		av7110->sinfo = 0;
+		break;
+	case RP_AV:
+		av7110->sinfo = 0;
+		ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0);
+		break;
+	}
+	return ret;
+}
+
+int av7110_av_stop(struct av7110 *av7110, int av)
+{
+	int ret = 0;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!(av7110->playing & av) && !(av7110->rec_mode & av))
+		return 0;
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+	if (av7110->playing) {
+		av7110->playing &= ~av;
+		switch (av7110->playing) {
+		case RP_AUDIO:
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+			break;
+		case RP_VIDEO:
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+			break;
+		case RP_NONE:
+			ret = av7110_set_vidmode(av7110, av7110->vidmode);
+			break;
+		}
+	} else {
+		av7110->rec_mode &= ~av;
+		switch (av7110->rec_mode) {
+		case RP_AUDIO:
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+			break;
+		case RP_VIDEO:
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+			break;
+		case RP_NONE:
+			break;
+		}
+	}
+	return ret;
+}
+
+
+int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen)
+{
+	int len;
+	u32 sync;
+	u16 blen;
+
+	if (!dlen) {
+		wake_up(&buf->queue);
+		return -1;
+	}
+	while (1) {
+		len = dvb_ringbuffer_avail(buf);
+		if (len < 6) {
+			wake_up(&buf->queue);
+			return -1;
+		}
+		sync =  DVB_RINGBUFFER_PEEK(buf, 0) << 24;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 3);
+
+		if (((sync &~ 0x0f) == 0x000001e0) ||
+		    ((sync &~ 0x1f) == 0x000001c0) ||
+		    (sync == 0x000001bd))
+			break;
+		printk("resync\n");
+		DVB_RINGBUFFER_SKIP(buf, 1);
+	}
+	blen =  DVB_RINGBUFFER_PEEK(buf, 4) << 8;
+	blen |= DVB_RINGBUFFER_PEEK(buf, 5);
+	blen += 6;
+	if (len < blen || blen > dlen) {
+		//printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen);
+		wake_up(&buf->queue);
+		return -1;
+	}
+
+	dvb_ringbuffer_read(buf, dest, (size_t) blen);
+
+	dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n",
+	       (unsigned long) buf->pread, (unsigned long) buf->pwrite);
+	wake_up(&buf->queue);
+	return blen;
+}
+
+
+int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
+{
+	int err, vol, val, balance = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	av7110->mixer.volume_left = volleft;
+	av7110->mixer.volume_right = volright;
+
+	switch (av7110->adac_type) {
+	case DVB_ADAC_TI:
+		volleft = (volleft * 256) / 1036;
+		volright = (volright * 256) / 1036;
+		if (volleft > 0x3f)
+			volleft = 0x3f;
+		if (volright > 0x3f)
+			volright = 0x3f;
+		if ((err = SendDAC(av7110, 3, 0x80 + volleft)))
+			return err;
+		return SendDAC(av7110, 4, volright);
+
+	case DVB_ADAC_CRYSTAL:
+		volleft = 127 - volleft / 2;
+		volright = 127 - volright / 2;
+		i2c_writereg(av7110, 0x20, 0x03, volleft);
+		i2c_writereg(av7110, 0x20, 0x04, volright);
+		return 0;
+
+	case DVB_ADAC_MSP34x0:
+		vol  = (volleft > volright) ? volleft : volright;
+		val	= (vol * 0x73 / 255) << 8;
+		if (vol > 0)
+		       balance = ((volright - volleft) * 127) / vol;
+		msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */
+		msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */
+		return 0;
+
+	case DVB_ADAC_MSP34x5:
+		vol = (volleft > volright) ? volleft : volright;
+		val = (vol * 0x73 / 255) << 8;
+		if (vol > 0)
+			balance = ((volright - volleft) * 127) / vol;
+		msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */
+		return 0;
+	}
+
+	return 0;
+}
+
+int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode)
+{
+	int ret;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode);
+
+	if (!ret && !av7110->playing) {
+		ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO],
+			   av7110->pids[DMX_PES_AUDIO],
+			   av7110->pids[DMX_PES_TELETEXT],
+			   0, av7110->pids[DMX_PES_PCR]);
+		if (!ret)
+			ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+	}
+	return ret;
+}
+
+
+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)
+{
+	int i;
+	int hsize, vsize;
+	int sw;
+	u8 *p;
+	int ret = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->sinfo)
+		return 0;
+	for (i = 7; i < count - 10; i++) {
+		p = buf + i;
+		if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3)
+			continue;
+		p += 4;
+		hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4);
+		vsize = ((p[1] &0x0F) << 8) | (p[2]);
+		sw = (p[3] & 0x0F);
+		ret = av7110_set_vidmode(av7110, sw2mode[sw]);
+		if (!ret) {
+			dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw);
+			av7110->sinfo = 1;
+		}
+		break;
+	}
+	return ret;
+}
+
+
+/****************************************************************************
+ * I/O buffer management and control
+ ****************************************************************************/
+
+static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf,
+					 const u8 *buf, unsigned long count)
+{
+	unsigned long todo = count;
+	int free;
+
+	while (todo > 0) {
+		if (dvb_ringbuffer_free(rbuf) < 2048) {
+			if (wait_event_interruptible(rbuf->queue,
+						     (dvb_ringbuffer_free(rbuf) >= 2048)))
+				return count - todo;
+		}
+		free = dvb_ringbuffer_free(rbuf);
+		if (free > todo)
+			free = todo;
+		dvb_ringbuffer_write(rbuf, buf, free);
+		todo -= free;
+		buf += free;
+	}
+
+	return count - todo;
+}
+
+static void play_video_cb(u8 *buf, int count, void *priv)
+{
+	struct av7110 *av7110 = (struct av7110 *) priv;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((buf[3] & 0xe0) == 0xe0) {
+		get_video_format(av7110, buf, count);
+		aux_ring_buffer_write(&av7110->avout, buf, count);
+	} else
+		aux_ring_buffer_write(&av7110->aout, buf, count);
+}
+
+static void play_audio_cb(u8 *buf, int count, void *priv)
+{
+	struct av7110 *av7110 = (struct av7110 *) priv;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	aux_ring_buffer_write(&av7110->aout, buf, count);
+}
+
+
+#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096)
+
+static ssize_t ts_play(struct av7110 *av7110, const char __user *buf,
+		       unsigned long count, int nonblock, int type)
+{
+	struct dvb_ringbuffer *rb;
+	u8 *kb;
+	unsigned long todo = count;
+
+	dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count);
+
+	rb = (type) ? &av7110->avout : &av7110->aout;
+	kb = av7110->kbuf[type];
+
+	if (!kb)
+		return -ENOBUFS;
+
+	if (nonblock && !FREE_COND_TS)
+		return -EWOULDBLOCK;
+
+	while (todo >= TS_SIZE) {
+		if (!FREE_COND_TS) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(rb->queue, FREE_COND_TS))
+				return count - todo;
+		}
+		if (copy_from_user(kb, buf, TS_SIZE))
+			return -EFAULT;
+		write_ts_to_decoder(av7110, type, kb, TS_SIZE);
+		todo -= TS_SIZE;
+		buf += TS_SIZE;
+	}
+
+	return count - todo;
+}
+
+
+#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \
+		   dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)
+
+static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf,
+			unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+
+	if (nonblock && !FREE_COND)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (!FREE_COND) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->avout.queue,
+						     FREE_COND))
+				return count - todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		if (copy_from_user(av7110->kbuf[type], buf, n))
+			return -EFAULT;
+		av7110_ipack_instant_repack(av7110->kbuf[type], n,
+					    &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf,
+			unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+
+	if (nonblock && !FREE_COND)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (!FREE_COND) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->avout.queue,
+						     FREE_COND))
+				return count - todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf,
+			 unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+	if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->aout.queue,
+					(dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)))
+				return count-todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		if (copy_from_user(av7110->kbuf[type], buf, n))
+			return -EFAULT;
+		av7110_ipack_instant_repack(av7110->kbuf[type], n,
+					    &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed)
+{
+	memset(p->pes, 0, TS_SIZE);
+	p->counter = 0;
+	p->pos = 0;
+	p->frags = 0;
+	if (feed)
+		p->feed = feed;
+}
+
+static void clear_p2t(struct av7110_p2t *p)
+{
+	memset(p->pes, 0, TS_SIZE);
+//	p->counter = 0;
+	p->pos = 0;
+	p->frags = 0;
+}
+
+
+static int find_pes_header(u8 const *buf, long int length, int *frags)
+{
+	int c = 0;
+	int found = 0;
+
+	*frags = 0;
+
+	while (c < length - 3 && !found) {
+		if (buf[c] == 0x00 && buf[c + 1] == 0x00 &&
+		    buf[c + 2] == 0x01) {
+			switch ( buf[c + 3] ) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				found = 1;
+				break;
+
+			default:
+				c++;
+				break;
+			}
+		} else
+			c++;
+	}
+	if (c == length - 3 && !found) {
+		if (buf[length - 1] == 0x00)
+			*frags = 1;
+		if (buf[length - 2] == 0x00 &&
+		    buf[length - 1] == 0x00)
+			*frags = 2;
+		if (buf[length - 3] == 0x00 &&
+		    buf[length - 2] == 0x00 &&
+		    buf[length - 1] == 0x01)
+			*frags = 3;
+		return -1;
+	}
+
+	return c;
+}
+
+void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p)
+{
+	int c, c2, l, add;
+	int check, rest;
+
+	c = 0;
+	c2 = 0;
+	if (p->frags){
+		check = 0;
+		switch(p->frags) {
+		case 1:
+			if (buf[c] == 0x00 && buf[c + 1] == 0x01) {
+				check = 1;
+				c += 2;
+			}
+			break;
+		case 2:
+			if (buf[c] == 0x01) {
+				check = 1;
+				c++;
+			}
+			break;
+		case 3:
+			check = 1;
+		}
+		if (check) {
+			switch (buf[c]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				p->pes[0] = 0x00;
+				p->pes[1] = 0x00;
+				p->pes[2] = 0x01;
+				p->pes[3] = buf[c];
+				p->pos = 4;
+				memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos);
+				c += (TS_SIZE - 4) - p->pos;
+				p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed);
+				clear_p2t(p);
+				break;
+
+			default:
+				c = 0;
+				break;
+			}
+		}
+		p->frags = 0;
+	}
+
+	if (p->pos) {
+		c2 = find_pes_header(buf + c, length - c, &p->frags);
+		if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos)
+			l = c2+c;
+		else
+			l = (TS_SIZE - 4) - p->pos;
+		memcpy(p->pes + p->pos, buf, l);
+		c += l;
+		p->pos += l;
+		p_to_t(p->pes, p->pos, pid, &p->counter, p->feed);
+		clear_p2t(p);
+	}
+
+	add = 0;
+	while (c < length) {
+		c2 = find_pes_header(buf + c + add, length - c - add, &p->frags);
+		if (c2 >= 0) {
+			c2 += c + add;
+			if (c2 > c){
+				p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed);
+				c = c2;
+				clear_p2t(p);
+				add = 0;
+			} else
+				add = 1;
+		} else {
+			l = length - c;
+			rest = l % (TS_SIZE - 4);
+			l -= rest;
+			p_to_t(buf + c, l, pid, &p->counter, p->feed);
+			memcpy(p->pes, buf + c + l, rest);
+			p->pos = rest;
+			c = length;
+		}
+	}
+}
+
+
+static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length)
+{
+	int i;
+	int c = 0;
+	int fill;
+	u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 };
+
+	fill = (TS_SIZE - 4) - length;
+	if (pes_start)
+		tshead[1] = 0x40;
+	if (fill)
+		tshead[3] = 0x30;
+	tshead[1] |= (u8)((pid & 0x1F00) >> 8);
+	tshead[2] |= (u8)(pid & 0x00FF);
+	tshead[3] |= ((*counter)++ & 0x0F);
+	memcpy(buf, tshead, 4);
+	c += 4;
+
+	if (fill) {
+		buf[4] = fill - 1;
+		c++;
+		if (fill > 1) {
+			buf[5] = 0x00;
+			c++;
+		}
+		for (i = 6; i < fill + 4; i++) {
+			buf[i] = 0xFF;
+			c++;
+		}
+	}
+
+	return c;
+}
+
+
+static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter,
+		   struct dvb_demux_feed *feed)
+{
+	int l, pes_start;
+	u8 obuf[TS_SIZE];
+	long c = 0;
+
+	pes_start = 0;
+	if (length > 3 &&
+	     buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01)
+		switch (buf[3]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				pes_start = 1;
+				break;
+
+			default:
+				break;
+		}
+
+	while (c < length) {
+		memset(obuf, 0, TS_SIZE);
+		if (length - c >= (TS_SIZE - 4)){
+			l = write_ts_header2(pid, counter, pes_start,
+					     obuf, (TS_SIZE - 4));
+			memcpy(obuf + l, buf + c, TS_SIZE - l);
+			c += TS_SIZE - l;
+		} else {
+			l = write_ts_header2(pid, counter, pes_start,
+					     obuf, length - c);
+			memcpy(obuf + l, buf + c, TS_SIZE - l);
+			c = length;
+		}
+		feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		pes_start = 0;
+	}
+}
+
+
+static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len)
+{
+	struct ipack *ipack = &av7110->ipack[type];
+
+	if (buf[1] & TRANS_ERROR) {
+		av7110_ipack_reset(ipack);
+		return -1;
+	}
+
+	if (!(buf[3] & PAYLOAD))
+		return -1;
+
+	if (buf[1] & PAY_START)
+		av7110_ipack_flush(ipack);
+
+	if (buf[3] & ADAPT_FIELD) {
+		len -= buf[4] + 1;
+		buf += buf[4] + 1;
+		if (!len)
+			return 0;
+	}
+
+	av7110_ipack_instant_repack(buf + 4, len - 4, ipack);
+	return 0;
+}
+
+
+int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = (struct av7110 *) demux->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE)
+		return 0;
+
+	switch (feed->pes_type) {
+	case 0:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			return -EINVAL;
+		break;
+	case 1:
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
+			return -EINVAL;
+		break;
+	default:
+		return -1;
+	}
+
+	return write_ts_to_decoder(av7110, feed->pes_type, buf, len);
+}
+
+
+
+/******************************************************************************
+ * Video MPEG decoder events
+ ******************************************************************************/
+void dvb_video_add_event(struct av7110 *av7110, struct video_event *event)
+{
+	struct dvb_video_events *events = &av7110->video_events;
+	int wp;
+
+	spin_lock_bh(&events->lock);
+
+	wp = (events->eventw + 1) % MAX_VIDEO_EVENT;
+	if (wp == events->eventr) {
+		events->overflow = 1;
+		events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT;
+	}
+
+	//FIXME: timestamp?
+	memcpy(&events->events[events->eventw], event, sizeof(struct video_event));
+	events->eventw = wp;
+
+	spin_unlock_bh(&events->lock);
+
+	wake_up_interruptible(&events->wait_queue);
+}
+
+
+static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags)
+{
+	struct dvb_video_events *events = &av7110->video_events;
+
+	if (events->overflow) {
+		events->overflow = 0;
+		return -EOVERFLOW;
+	}
+	if (events->eventw == events->eventr) {
+		int ret;
+
+		if (flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		ret = wait_event_interruptible(events->wait_queue,
+					       events->eventw != events->eventr);
+		if (ret < 0)
+			return ret;
+	}
+
+	spin_lock_bh(&events->lock);
+
+	memcpy(event, &events->events[events->eventr],
+	       sizeof(struct video_event));
+	events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT;
+
+	spin_unlock_bh(&events->lock);
+
+	return 0;
+}
+
+
+/******************************************************************************
+ * DVB device file operations
+ ******************************************************************************/
+
+static unsigned int dvb_video_poll(struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		poll_wait(file, &av7110->avout.queue, wait);
+
+	poll_wait(file, &av7110->video_events.wait_queue, wait);
+
+	if (av7110->video_events.eventw != av7110->video_events.eventr)
+		mask = POLLPRI;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		if (av7110->playing) {
+			if (FREE_COND)
+				mask |= (POLLOUT | POLLWRNORM);
+			} else /* if not playing: may play if asked for */
+				mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	return mask;
+}
+
+static ssize_t dvb_video_write(struct file *file, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned char c;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+		return -EPERM;
+
+	if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY)
+		return -EPERM;
+
+	if (get_user(c, buf))
+		return -EFAULT;
+	if (c == 0x47 && count % TS_SIZE == 0)
+		return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1);
+	else
+		return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1);
+}
+
+static unsigned int dvb_audio_poll(struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	poll_wait(file, &av7110->aout.queue, wait);
+
+	if (av7110->playing) {
+		if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)
+			mask |= (POLLOUT | POLLWRNORM);
+	} else /* if not playing: may play if asked for */
+		mask = (POLLOUT | POLLWRNORM);
+
+	return mask;
+}
+
+static ssize_t dvb_audio_write(struct file *file, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned char c;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) {
+		printk(KERN_ERR "not audio source memory\n");
+		return -EPERM;
+	}
+
+	if (get_user(c, buf))
+		return -EFAULT;
+	if (c == 0x47 && count % TS_SIZE == 0)
+		return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0);
+	else
+		return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0);
+}
+
+static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 };
+
+#define MIN_IFRAME 400000
+
+static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock)
+{
+	unsigned i, n;
+	int progressive = 0;
+	int match = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!(av7110->playing & RP_VIDEO)) {
+		if (av7110_av_start_play(av7110, RP_VIDEO) < 0)
+			return -EBUSY;
+	}
+
+	/* search in buf for instances of 00 00 01 b5 1? */
+	for (i = 0; i < len; i++) {
+		unsigned char c;
+		if (get_user(c, buf + i))
+			return -EFAULT;
+		if (match == 5) {
+			progressive = c & 0x08;
+			match = 0;
+		}
+		if (c == 0x00) {
+			match = (match == 1 || match == 2) ? 2 : 1;
+			continue;
+		}
+		switch (match++) {
+		case 2: if (c == 0x01)
+				continue;
+			break;
+		case 3: if (c == 0xb5)
+				continue;
+			break;
+		case 4: if ((c & 0xf0) == 0x10)
+				continue;
+			break;
+		}
+		match = 0;
+	}
+
+	/* setting n always > 1, fixes problems when playing stillframes
+	   consisting of I- and P-Frames */
+	n = MIN_IFRAME / len + 1;
+
+	/* FIXME: nonblock? */
+	dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1);
+
+	for (i = 0; i < n; i++)
+		dvb_play(av7110, buf, len, 0, 1);
+
+	av7110_ipack_flush(&av7110->ipack[1]);
+
+	if (progressive)
+		return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1);
+	else
+		return 0;
+}
+
+
+static int dvb_video_ioctl(struct file *file,
+			   unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+	int ret = 0;
+
+	dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd);
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT &&
+		     cmd != VIDEO_GET_SIZE ) {
+			return -EPERM;
+		}
+	}
+
+	switch (cmd) {
+	case VIDEO_STOP:
+		av7110->videostate.play_state = VIDEO_STOPPED;
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
+			ret = av7110_av_stop(av7110, RP_VIDEO);
+		else
+			ret = vidcom(av7110, AV_VIDEO_CMD_STOP,
+			       av7110->videostate.video_blank ? 0 : 1);
+		if (!ret)
+			av7110->trickmode = TRICK_NONE;
+		break;
+
+	case VIDEO_PLAY:
+		av7110->trickmode = TRICK_NONE;
+		if (av7110->videostate.play_state == VIDEO_FREEZED) {
+			av7110->videostate.play_state = VIDEO_PLAYING;
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
+			if (ret)
+				break;
+		}
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) {
+			if (av7110->playing == RP_AV) {
+				ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+				if (ret)
+					break;
+				av7110->playing &= ~RP_VIDEO;
+			}
+			ret = av7110_av_start_play(av7110, RP_VIDEO);
+		}
+		if (!ret)
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
+		if (!ret)
+			av7110->videostate.play_state = VIDEO_PLAYING;
+		break;
+
+	case VIDEO_FREEZE:
+		av7110->videostate.play_state = VIDEO_FREEZED;
+		if (av7110->playing & RP_VIDEO)
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
+		else
+			ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1);
+		if (!ret)
+			av7110->trickmode = TRICK_FREEZE;
+		break;
+
+	case VIDEO_CONTINUE:
+		if (av7110->playing & RP_VIDEO)
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
+		if (!ret)
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
+		if (!ret) {
+			av7110->videostate.play_state = VIDEO_PLAYING;
+			av7110->trickmode = TRICK_NONE;
+		}
+		break;
+
+	case VIDEO_SELECT_SOURCE:
+		av7110->videostate.stream_source = (video_stream_source_t) arg;
+		break;
+
+	case VIDEO_SET_BLANK:
+		av7110->videostate.video_blank = (int) arg;
+		break;
+
+	case VIDEO_GET_STATUS:
+		memcpy(parg, &av7110->videostate, sizeof(struct video_status));
+		break;
+
+	case VIDEO_GET_EVENT:
+		ret = dvb_video_get_event(av7110, parg, file->f_flags);
+		break;
+
+	case VIDEO_GET_SIZE:
+		memcpy(parg, &av7110->video_size, sizeof(video_size_t));
+		break;
+
+	case VIDEO_SET_DISPLAY_FORMAT:
+	{
+		video_displayformat_t format = (video_displayformat_t) arg;
+		switch (format) {
+		case VIDEO_PAN_SCAN:
+			av7110->display_panscan = VID_PAN_SCAN_PREF;
+			break;
+		case VIDEO_LETTER_BOX:
+			av7110->display_panscan = VID_VC_AND_PS_PREF;
+			break;
+		case VIDEO_CENTER_CUT_OUT:
+			av7110->display_panscan = VID_CENTRE_CUT_PREF;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		if (ret < 0)
+			break;
+		av7110->videostate.display_format = format;
+		ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType,
+				    1, av7110->display_panscan);
+		break;
+	}
+
+	case VIDEO_SET_FORMAT:
+		if (arg > 1) {
+			ret = -EINVAL;
+			break;
+		}
+		av7110->display_ar = arg;
+		ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType,
+				    1, (u16) arg);
+		break;
+
+	case VIDEO_STILLPICTURE:
+	{
+		struct video_still_picture *pic =
+			(struct video_still_picture *) parg;
+		av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY;
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		ret = play_iframe(av7110, pic->iFrame, pic->size,
+				  file->f_flags & O_NONBLOCK);
+		break;
+	}
+
+	case VIDEO_FAST_FORWARD:
+		//note: arg is ignored by firmware
+		if (av7110->playing & RP_VIDEO)
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+					    __Scan_I, 2, AV_PES, 0);
+		else
+			ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg);
+		if (!ret) {
+			av7110->trickmode = TRICK_FAST;
+			av7110->videostate.play_state = VIDEO_PLAYING;
+		}
+		break;
+
+	case VIDEO_SLOWMOTION:
+		if (av7110->playing&RP_VIDEO) {
+			if (av7110->trickmode != TRICK_SLOW)
+				ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
+			if (!ret)
+				ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
+		} else {
+			ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
+			if (!ret)
+				ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0);
+			if (!ret)
+				ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
+		}
+		if (!ret) {
+			av7110->trickmode = TRICK_SLOW;
+			av7110->videostate.play_state = VIDEO_PLAYING;
+		}
+		break;
+
+	case VIDEO_GET_CAPABILITIES:
+		*(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 |
+			VIDEO_CAP_SYS | VIDEO_CAP_PROG;
+		break;
+
+	case VIDEO_CLEAR_BUFFER:
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		av7110_ipack_reset(&av7110->ipack[1]);
+		if (av7110->playing == RP_AV) {
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+					    __Play, 2, AV_PES, 0);
+			if (ret)
+				break;
+			if (av7110->trickmode == TRICK_FAST)
+				ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+						    __Scan_I, 2, AV_PES, 0);
+			if (av7110->trickmode == TRICK_SLOW) {
+				ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+						    __Slow, 2, 0, 0);
+				if (!ret)
+					ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
+			}
+			if (av7110->trickmode == TRICK_FREEZE)
+				ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1);
+		}
+		break;
+
+	case VIDEO_SET_STREAMTYPE:
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+static int dvb_audio_ioctl(struct file *file,
+			   unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+	int ret = 0;
+
+	dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd);
+
+	if (((file->f_flags & O_ACCMODE) == O_RDONLY) &&
+	    (cmd != AUDIO_GET_STATUS))
+		return -EPERM;
+
+	switch (cmd) {
+	case AUDIO_STOP:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			ret = av7110_av_stop(av7110, RP_AUDIO);
+		else
+			ret = audcom(av7110, AUDIO_CMD_MUTE);
+		if (!ret)
+			av7110->audiostate.play_state = AUDIO_STOPPED;
+		break;
+
+	case AUDIO_PLAY:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			ret = av7110_av_start_play(av7110, RP_AUDIO);
+		if (!ret)
+			ret = audcom(av7110, AUDIO_CMD_UNMUTE);
+		if (!ret)
+			av7110->audiostate.play_state = AUDIO_PLAYING;
+		break;
+
+	case AUDIO_PAUSE:
+		ret = audcom(av7110, AUDIO_CMD_MUTE);
+		if (!ret)
+			av7110->audiostate.play_state = AUDIO_PAUSED;
+		break;
+
+	case AUDIO_CONTINUE:
+		if (av7110->audiostate.play_state == AUDIO_PAUSED) {
+			av7110->audiostate.play_state = AUDIO_PLAYING;
+			ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16);
+		}
+		break;
+
+	case AUDIO_SELECT_SOURCE:
+		av7110->audiostate.stream_source = (audio_stream_source_t) arg;
+		break;
+
+	case AUDIO_SET_MUTE:
+	{
+		ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE);
+		if (!ret)
+			av7110->audiostate.mute_state = (int) arg;
+		break;
+	}
+
+	case AUDIO_SET_AV_SYNC:
+		av7110->audiostate.AV_sync_state = (int) arg;
+		ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF);
+		break;
+
+	case AUDIO_SET_BYPASS_MODE:
+		if (FW_VERSION(av7110->arm_app) < 0x2621)
+			ret = -EINVAL;
+		av7110->audiostate.bypass_mode = (int)arg;
+		break;
+
+	case AUDIO_CHANNEL_SELECT:
+		av7110->audiostate.channel_select = (audio_channel_select_t) arg;
+		switch(av7110->audiostate.channel_select) {
+		case AUDIO_STEREO:
+			ret = audcom(av7110, AUDIO_CMD_STEREO);
+			if (!ret) {
+				if (av7110->adac_type == DVB_ADAC_CRYSTAL)
+					i2c_writereg(av7110, 0x20, 0x02, 0x49);
+				else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+					msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220);
+			}
+			break;
+		case AUDIO_MONO_LEFT:
+			ret = audcom(av7110, AUDIO_CMD_MONO_L);
+			if (!ret) {
+				if (av7110->adac_type == DVB_ADAC_CRYSTAL)
+					i2c_writereg(av7110, 0x20, 0x02, 0x4a);
+				else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+					msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200);
+			}
+			break;
+		case AUDIO_MONO_RIGHT:
+			ret = audcom(av7110, AUDIO_CMD_MONO_R);
+			if (!ret) {
+				if (av7110->adac_type == DVB_ADAC_CRYSTAL)
+					i2c_writereg(av7110, 0x20, 0x02, 0x45);
+				else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+					msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210);
+			}
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+		break;
+
+	case AUDIO_GET_STATUS:
+		memcpy(parg, &av7110->audiostate, sizeof(struct audio_status));
+		break;
+
+	case AUDIO_GET_CAPABILITIES:
+		if (FW_VERSION(av7110->arm_app) < 0x2621)
+			*(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2;
+		else
+			*(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 |
+						AUDIO_CAP_MP1 | AUDIO_CAP_MP2;
+		break;
+
+	case AUDIO_CLEAR_BUFFER:
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+		av7110_ipack_reset(&av7110->ipack[0]);
+		if (av7110->playing == RP_AV)
+			ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+					    __Play, 2, AV_PES, 0);
+		break;
+
+	case AUDIO_SET_ID:
+		break;
+
+	case AUDIO_SET_MIXER:
+	{
+		struct audio_mixer *amix = (struct audio_mixer *)parg;
+		ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right);
+		break;
+	}
+
+	case AUDIO_SET_STREAMTYPE:
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+	}
+
+	return ret;
+}
+
+
+static int dvb_video_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	int err;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((err = dvb_generic_open(inode, file)) < 0)
+		return err;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		av7110->video_blank = 1;
+		av7110->audiostate.AV_sync_state = 1;
+		av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX;
+
+		/*  empty event queue */
+		av7110->video_events.eventr = av7110->video_events.eventw = 0;
+	}
+
+	return 0;
+}
+
+static int dvb_video_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		av7110_av_stop(av7110, RP_VIDEO);
+	}
+
+	return dvb_generic_release(inode, file);
+}
+
+static int dvb_audio_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	int err = dvb_generic_open(inode, file);
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (err < 0)
+		return err;
+	dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+	av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX;
+	return 0;
+}
+
+static int dvb_audio_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	av7110_av_stop(av7110, RP_AUDIO);
+	return dvb_generic_release(inode, file);
+}
+
+
+
+/******************************************************************************
+ * driver registration
+ ******************************************************************************/
+
+static const struct file_operations dvb_video_fops = {
+	.owner		= THIS_MODULE,
+	.write		= dvb_video_write,
+	.unlocked_ioctl	= dvb_generic_ioctl,
+	.open		= dvb_video_open,
+	.release	= dvb_video_release,
+	.poll		= dvb_video_poll,
+	.llseek		= noop_llseek,
+};
+
+static struct dvb_device dvbdev_video = {
+	.priv		= NULL,
+	.users		= 6,
+	.readers	= 5,	/* arbitrary */
+	.writers	= 1,
+	.fops		= &dvb_video_fops,
+	.kernel_ioctl	= dvb_video_ioctl,
+};
+
+static const struct file_operations dvb_audio_fops = {
+	.owner		= THIS_MODULE,
+	.write		= dvb_audio_write,
+	.unlocked_ioctl	= dvb_generic_ioctl,
+	.open		= dvb_audio_open,
+	.release	= dvb_audio_release,
+	.poll		= dvb_audio_poll,
+	.llseek		= noop_llseek,
+};
+
+static struct dvb_device dvbdev_audio = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_audio_fops,
+	.kernel_ioctl	= dvb_audio_ioctl,
+};
+
+
+int av7110_av_register(struct av7110 *av7110)
+{
+	av7110->audiostate.AV_sync_state = 0;
+	av7110->audiostate.mute_state = 0;
+	av7110->audiostate.play_state = AUDIO_STOPPED;
+	av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX;
+	av7110->audiostate.channel_select = AUDIO_STEREO;
+	av7110->audiostate.bypass_mode = 0;
+
+	av7110->videostate.video_blank = 0;
+	av7110->videostate.play_state = VIDEO_STOPPED;
+	av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX;
+	av7110->videostate.video_format = VIDEO_FORMAT_4_3;
+	av7110->videostate.display_format = VIDEO_LETTER_BOX;
+	av7110->display_ar = VIDEO_FORMAT_4_3;
+	av7110->display_panscan = VID_VC_AND_PS_PREF;
+
+	init_waitqueue_head(&av7110->video_events.wait_queue);
+	spin_lock_init(&av7110->video_events.lock);
+	av7110->video_events.eventw = av7110->video_events.eventr = 0;
+	av7110->video_events.overflow = 0;
+	memset(&av7110->video_size, 0, sizeof (video_size_t));
+
+	dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev,
+			    &dvbdev_video, av7110, DVB_DEVICE_VIDEO);
+
+	dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev,
+			    &dvbdev_audio, av7110, DVB_DEVICE_AUDIO);
+
+	return 0;
+}
+
+void av7110_av_unregister(struct av7110 *av7110)
+{
+	dvb_unregister_device(av7110->audio_dev);
+	dvb_unregister_device(av7110->video_dev);
+}
+
+int av7110_av_init(struct av7110 *av7110)
+{
+	void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb };
+	int i, ret;
+
+	for (i = 0; i < 2; i++) {
+		struct ipack *ipack = av7110->ipack + i;
+
+		ret = av7110_ipack_init(ipack, IPACKS, play[i]);
+		if (ret < 0) {
+			if (i)
+				av7110_ipack_free(--ipack);
+			goto out;
+		}
+		ipack->data = av7110;
+	}
+
+	dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN);
+	dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN);
+
+	av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN);
+	av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS;
+out:
+	return ret;
+}
+
+void av7110_av_exit(struct av7110 *av7110)
+{
+	av7110_ipack_free(&av7110->ipack[0]);
+	av7110_ipack_free(&av7110->ipack[1]);
+}
diff --git a/drivers/media/pci/ttpci/av7110_av.h b/drivers/media/pci/ttpci/av7110_av.h
new file mode 100644
index 000000000000..5f02ef85e47d
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_av.h
@@ -0,0 +1,30 @@
+#ifndef _AV7110_AV_H_
+#define _AV7110_AV_H_
+
+struct av7110;
+
+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);
+extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len);
+
+extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright);
+extern int av7110_av_stop(struct av7110 *av7110, int av);
+extern int av7110_av_start_record(struct av7110 *av7110, int av,
+			  struct dvb_demux_feed *dvbdmxfeed);
+extern int av7110_av_start_play(struct av7110 *av7110, int av);
+
+extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event);
+
+extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed);
+extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p);
+
+extern int av7110_av_register(struct av7110 *av7110);
+extern void av7110_av_unregister(struct av7110 *av7110);
+extern int av7110_av_init(struct av7110 *av7110);
+extern void av7110_av_exit(struct av7110 *av7110);
+
+
+#endif /* _AV7110_AV_H_ */
diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c
new file mode 100644
index 000000000000..9fc1dd0ba4c3
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_ca.c
@@ -0,0 +1,387 @@
+/*
+ * av7110_ca.c: CA and CI stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+#include <linux/gfp.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_ca.h"
+
+
+void CI_handle(struct av7110 *av7110, u8 *data, u16 len)
+{
+	dprintk(8, "av7110:%p\n",av7110);
+
+	if (len < 3)
+		return;
+	switch (data[0]) {
+	case CI_MSG_CI_INFO:
+		if (data[2] != 1 && data[2] != 2)
+			break;
+		switch (data[1]) {
+		case 0:
+			av7110->ci_slot[data[2] - 1].flags = 0;
+			break;
+		case 1:
+			av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT;
+			break;
+		case 2:
+			av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY;
+			break;
+		}
+		break;
+	case CI_SWITCH_PRG_REPLY:
+		//av7110->ci_stat=data[1];
+		break;
+	default:
+		break;
+	}
+}
+
+
+void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len)
+{
+	if (dvb_ringbuffer_free(cibuf) < len + 2)
+		return;
+
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff);
+	dvb_ringbuffer_write(cibuf, data, len);
+	wake_up_interruptible(&cibuf->queue);
+}
+
+
+/******************************************************************************
+ * CI link layer file ops
+ ******************************************************************************/
+
+static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size)
+{
+	struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p;
+	void *data;
+
+	for (p = tab; *p; p++) {
+		data = vmalloc(size);
+		if (!data) {
+			while (p-- != tab) {
+				vfree(p[0]->data);
+				p[0]->data = NULL;
+			}
+			return -ENOMEM;
+		}
+		dvb_ringbuffer_init(*p, data, size);
+	}
+	return 0;
+}
+
+static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
+{
+	dvb_ringbuffer_flush_spinlock_wakeup(cirbuf);
+	dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf);
+}
+
+static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
+{
+	vfree(cirbuf->data);
+	cirbuf->data = NULL;
+	vfree(ciwbuf->data);
+	ciwbuf->data = NULL;
+}
+
+static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file,
+		       int slots, ca_slot_info_t *slot)
+{
+	int i;
+	int len = 0;
+	u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 };
+
+	for (i = 0; i < 2; i++) {
+		if (slots & (1 << i))
+			len += 8;
+	}
+
+	if (dvb_ringbuffer_free(cibuf) < len)
+		return -EBUSY;
+
+	for (i = 0; i < 2; i++) {
+		if (slots & (1 << i)) {
+			msg[2] = i;
+			dvb_ringbuffer_write(cibuf, msg, 8);
+			slot[i].flags = 0;
+		}
+	}
+
+	return 0;
+}
+
+static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file,
+			   const char __user *buf, size_t count, loff_t *ppos)
+{
+	int free;
+	int non_blocking = file->f_flags & O_NONBLOCK;
+	u8 *page = (u8 *)__get_free_page(GFP_USER);
+	int res;
+
+	if (!page)
+		return -ENOMEM;
+
+	res = -EINVAL;
+	if (count > 2048)
+		goto out;
+
+	res = -EFAULT;
+	if (copy_from_user(page, buf, count))
+		goto out;
+
+	free = dvb_ringbuffer_free(cibuf);
+	if (count + 2 > free) {
+		res = -EWOULDBLOCK;
+		if (non_blocking)
+			goto out;
+		res = -ERESTARTSYS;
+		if (wait_event_interruptible(cibuf->queue,
+					     (dvb_ringbuffer_free(cibuf) >= count + 2)))
+			goto out;
+	}
+
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff);
+
+	res = dvb_ringbuffer_write(cibuf, page, count);
+out:
+	free_page((unsigned long)page);
+	return res;
+}
+
+static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file,
+			  char __user *buf, size_t count, loff_t *ppos)
+{
+	int avail;
+	int non_blocking = file->f_flags & O_NONBLOCK;
+	ssize_t len;
+
+	if (!cibuf->data || !count)
+		return 0;
+	if (non_blocking && (dvb_ringbuffer_empty(cibuf)))
+		return -EWOULDBLOCK;
+	if (wait_event_interruptible(cibuf->queue,
+				     !dvb_ringbuffer_empty(cibuf)))
+		return -ERESTARTSYS;
+	avail = dvb_ringbuffer_avail(cibuf);
+	if (avail < 4)
+		return 0;
+	len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;
+	len |= DVB_RINGBUFFER_PEEK(cibuf, 1);
+	if (avail < len + 2 || count < len)
+		return -EINVAL;
+	DVB_RINGBUFFER_SKIP(cibuf, 2);
+
+	return dvb_ringbuffer_read_user(cibuf, buf, len);
+}
+
+static int dvb_ca_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	int err = dvb_generic_open(inode, file);
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	if (err < 0)
+		return err;
+	ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
+	return 0;
+}
+
+static unsigned int dvb_ca_poll (struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer;
+	struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer;
+	unsigned int mask = 0;
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	poll_wait(file, &rbuf->queue, wait);
+	poll_wait(file, &wbuf->queue, wait);
+
+	if (!dvb_ringbuffer_empty(rbuf))
+		mask |= (POLLIN | POLLRDNORM);
+
+	if (dvb_ringbuffer_free(wbuf) > 1024)
+		mask |= (POLLOUT | POLLWRNORM);
+
+	return mask;
+}
+
+static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	switch (cmd) {
+	case CA_RESET:
+		return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]);
+		break;
+	case CA_GET_CAP:
+	{
+		ca_caps_t cap;
+
+		cap.slot_num = 2;
+		cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ?
+				 CA_CI_LINK : CA_CI) | CA_DESCR;
+		cap.descr_num = 16;
+		cap.descr_type = CA_ECD;
+		memcpy(parg, &cap, sizeof(cap));
+		break;
+	}
+
+	case CA_GET_SLOT_INFO:
+	{
+		ca_slot_info_t *info=(ca_slot_info_t *)parg;
+
+		if (info->num < 0 || info->num > 1)
+			return -EINVAL;
+		av7110->ci_slot[info->num].num = info->num;
+		av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
+							CA_CI_LINK : CA_CI;
+		memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
+		break;
+	}
+
+	case CA_GET_MSG:
+		break;
+
+	case CA_SEND_MSG:
+		break;
+
+	case CA_GET_DESCR_INFO:
+	{
+		ca_descr_info_t info;
+
+		info.num = 16;
+		info.type = CA_ECD;
+		memcpy(parg, &info, sizeof (info));
+		break;
+	}
+
+	case CA_SET_DESCR:
+	{
+		ca_descr_t *descr = (ca_descr_t*) parg;
+
+		if (descr->index >= 16)
+			return -EINVAL;
+		if (descr->parity > 1)
+			return -EINVAL;
+		av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5,
+			      (descr->index<<8)|descr->parity,
+			      (descr->cw[0]<<8)|descr->cw[1],
+			      (descr->cw[2]<<8)|descr->cw[3],
+			      (descr->cw[4]<<8)|descr->cw[5],
+			      (descr->cw[6]<<8)|descr->cw[7]);
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t dvb_ca_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+
+	dprintk(8, "av7110:%p\n",av7110);
+	return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos);
+}
+
+static ssize_t dvb_ca_read(struct file *file, char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct av7110 *av7110 = dvbdev->priv;
+
+	dprintk(8, "av7110:%p\n",av7110);
+	return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos);
+}
+
+static const struct file_operations dvb_ca_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_ca_read,
+	.write		= dvb_ca_write,
+	.unlocked_ioctl	= dvb_generic_ioctl,
+	.open		= dvb_ca_open,
+	.release	= dvb_generic_release,
+	.poll		= dvb_ca_poll,
+	.llseek		= default_llseek,
+};
+
+static struct dvb_device dvbdev_ca = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_ca_fops,
+	.kernel_ioctl	= dvb_ca_ioctl,
+};
+
+
+int av7110_ca_register(struct av7110 *av7110)
+{
+	return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev,
+				   &dvbdev_ca, av7110, DVB_DEVICE_CA);
+}
+
+void av7110_ca_unregister(struct av7110 *av7110)
+{
+	dvb_unregister_device(av7110->ca_dev);
+}
+
+int av7110_ca_init(struct av7110* av7110)
+{
+	return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192);
+}
+
+void av7110_ca_exit(struct av7110* av7110)
+{
+	ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
+}
diff --git a/drivers/media/pci/ttpci/av7110_ca.h b/drivers/media/pci/ttpci/av7110_ca.h
new file mode 100644
index 000000000000..70ee855ece1b
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_ca.h
@@ -0,0 +1,14 @@
+#ifndef _AV7110_CA_H_
+#define _AV7110_CA_H_
+
+struct av7110;
+
+extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len);
+extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len);
+
+extern int av7110_ca_register(struct av7110 *av7110);
+extern void av7110_ca_unregister(struct av7110 *av7110);
+extern int av7110_ca_init(struct av7110* av7110);
+extern void av7110_ca_exit(struct av7110* av7110);
+
+#endif /* _AV7110_CA_H_ */
diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
new file mode 100644
index 000000000000..f1cbfe526989
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_hw.c
@@ -0,0 +1,1208 @@
+/*
+ * av7110_hw.c: av7110 low level hardware access and firmware interface
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+/* for debugging ARM communication: */
+//#define COM_DEBUG
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+
+#define _NOHANDSHAKE
+
+/****************************************************************************
+ * DEBI functions
+ ****************************************************************************/
+
+/* This DEBI code is based on the Stradis driver
+   by Nathan Laredo <laredo@gnu.org> */
+
+int av7110_debiwrite(struct av7110 *av7110, u32 config,
+		     int addr, u32 val, int count)
+{
+	struct saa7146_dev *dev = av7110->dev;
+
+	if (count <= 0 || count > 32764) {
+		printk("%s: invalid count %d\n", __func__, count);
+		return -1;
+	}
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done failed\n", __func__);
+		return -1;
+	}
+	saa7146_write(dev, DEBI_CONFIG, config);
+	if (count <= 4)		/* immediate transfer */
+		saa7146_write(dev, DEBI_AD, val);
+	else			/* block transfer */
+		saa7146_write(dev, DEBI_AD, av7110->debi_bus);
+	saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
+	saa7146_write(dev, MC2, (2 << 16) | 2);
+	return 0;
+}
+
+u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
+{
+	struct saa7146_dev *dev = av7110->dev;
+	u32 result = 0;
+
+	if (count > 32764 || count <= 0) {
+		printk("%s: invalid count %d\n", __func__, count);
+		return 0;
+	}
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done #1 failed\n", __func__);
+		return 0;
+	}
+	saa7146_write(dev, DEBI_AD, av7110->debi_bus);
+	saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+
+	saa7146_write(dev, DEBI_CONFIG, config);
+	saa7146_write(dev, MC2, (2 << 16) | 2);
+	if (count > 4)
+		return count;
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done #2 failed\n", __func__);
+		return 0;
+	}
+
+	result = saa7146_read(dev, DEBI_AD);
+	result &= (0xffffffffUL >> ((4 - count) * 8));
+	return result;
+}
+
+
+
+/* av7110 ARM core boot stuff */
+#if 0
+void av7110_reset_arm(struct av7110 *av7110)
+{
+	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+
+	/* Disable DEBI and GPIO irq */
+	SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+
+	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	msleep(30);	/* the firmware needs some time to initialize */
+
+	ARM_ResetMailBox(av7110);
+
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+
+	av7110->arm_ready = 1;
+	dprintk(1, "reset ARM\n");
+}
+#endif  /*  0  */
+
+static int waitdebi(struct av7110 *av7110, int adr, int state)
+{
+	int k;
+
+	dprintk(4, "%p\n", av7110);
+
+	for (k = 0; k < 100; k++) {
+		if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
+			return 0;
+		udelay(5);
+	}
+	return -ETIMEDOUT;
+}
+
+static int load_dram(struct av7110 *av7110, u32 *data, int len)
+{
+	int i;
+	int blocks, rest;
+	u32 base, bootblock = AV7110_BOOT_BLOCK;
+
+	dprintk(4, "%p\n", av7110);
+
+	blocks = len / AV7110_BOOT_MAX_SIZE;
+	rest = len % AV7110_BOOT_MAX_SIZE;
+	base = DRAM_START_CODE;
+
+	for (i = 0; i < blocks; i++) {
+		if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
+			return -ETIMEDOUT;
+		}
+		dprintk(4, "writing DRAM block %d\n", i);
+		mwdebi(av7110, DEBISWAB, bootblock,
+		       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE);
+		bootblock ^= 0x1400;
+		iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);
+		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2);
+		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+		base += AV7110_BOOT_MAX_SIZE;
+	}
+
+	if (rest > 0) {
+		if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
+			return -ETIMEDOUT;
+		}
+		if (rest > 4)
+			mwdebi(av7110, DEBISWAB, bootblock,
+			       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest);
+		else
+			mwdebi(av7110, DEBISWAB, bootblock,
+			       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4);
+
+		iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);
+		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2);
+		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+	}
+	if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+		printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
+		return -ETIMEDOUT;
+	}
+	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2);
+	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+	if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) {
+		printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+
+/* we cannot write av7110 DRAM directly, so load a bootloader into
+ * the DPRAM which implements a simple boot protocol */
+int av7110_bootarm(struct av7110 *av7110)
+{
+	const struct firmware *fw;
+	const char *fw_name = "av7110/bootcode.bin";
+	struct saa7146_dev *dev = av7110->dev;
+	u32 ret;
+	int i;
+
+	dprintk(4, "%p\n", av7110);
+
+	av7110->arm_ready = 0;
+
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+
+	/* Disable DEBI and GPIO irq */
+	SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+
+	/* enable DEBI */
+	saa7146_write(av7110->dev, MC1, 0x08800880);
+	saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	/* test DEBI */
+	iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
+	/* FIXME: Why does Nexus CA require 2x iwdebi for first init? */
+	iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
+
+	if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
+		printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "
+		       "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
+		       ret, 0x10325476);
+		return -1;
+	}
+	for (i = 0; i < 8192; i += 4)
+		iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
+	dprintk(2, "debi test OK\n");
+
+	/* boot */
+	dprintk(1, "load boot code\n");
+	saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
+	//saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
+	//saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
+
+	ret = request_firmware(&fw, fw_name, &dev->pci->dev);
+	if (ret) {
+		printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n",
+			fw_name);
+		return ret;
+	}
+
+	mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size);
+	release_firmware(fw);
+	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "saa7146_wait_for_debi_done() timed out\n");
+		return -ETIMEDOUT;
+	}
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	mdelay(1);
+
+	dprintk(1, "load dram code\n");
+	if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "load_dram() failed\n");
+		return -1;
+	}
+
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+	mdelay(1);
+
+	dprintk(1, "load dpram code\n");
+	mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "saa7146_wait_for_debi_done() timed out after loading DRAM\n");
+		return -ETIMEDOUT;
+	}
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	msleep(30);	/* the firmware needs some time to initialize */
+
+	//ARM_ClearIrq(av7110);
+	ARM_ResetMailBox(av7110);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+
+	av7110->arm_errors = 0;
+	av7110->arm_ready = 1;
+	return 0;
+}
+MODULE_FIRMWARE("av7110/bootcode.bin");
+
+/****************************************************************************
+ * DEBI command polling
+ ****************************************************************************/
+
+int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
+{
+	unsigned long start;
+	u32 stat;
+	int err;
+
+	if (FW_VERSION(av7110->arm_app) <= 0x261c) {
+		/* not supported by old firmware */
+		msleep(50);
+		return 0;
+	}
+
+	/* new firmware */
+	start = jiffies;
+	for (;;) {
+		err = time_after(jiffies, start + ARM_WAIT_FREE);
+		if (mutex_lock_interruptible(&av7110->dcomlock))
+			return -ERESTARTSYS;
+		stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+		mutex_unlock(&av7110->dcomlock);
+		if ((stat & flags) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
+				__func__, stat & flags);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+	return 0;
+}
+
+static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
+{
+	int i;
+	unsigned long start;
+	char *type = NULL;
+	u16 flags[2] = {0, 0};
+	u32 stat;
+	int err;
+
+//	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -ENXIO;
+	}
+
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_FREE);
+		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__);
+			av7110->arm_errors++;
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+
+	if (FW_VERSION(av7110->arm_app) <= 0x261f)
+		wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
+
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_SHAKE);
+		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+#endif
+
+	switch ((buf[0] >> 8) & 0xff) {
+	case COMTYPE_PIDFILTER:
+	case COMTYPE_ENCODER:
+	case COMTYPE_REC_PLAY:
+	case COMTYPE_MPEGDECODER:
+		type = "MSG";
+		flags[0] = GPMQOver;
+		flags[1] = GPMQFull;
+		break;
+	case COMTYPE_OSD:
+		type = "OSD";
+		flags[0] = OSDQOver;
+		flags[1] = OSDQFull;
+		break;
+	case COMTYPE_MISC:
+		if (FW_VERSION(av7110->arm_app) >= 0x261d) {
+			type = "MSG";
+			flags[0] = GPMQOver;
+			flags[1] = GPMQBusy;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (type != NULL) {
+		/* non-immediate COMMAND type */
+		start = jiffies;
+		for (;;) {
+			err = time_after(jiffies, start + ARM_WAIT_FREE);
+			stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+			if (stat & flags[0]) {
+				printk(KERN_ERR "%s: %s QUEUE overflow\n",
+					__func__, type);
+				return -1;
+			}
+			if ((stat & flags[1]) == 0)
+				break;
+			if (err) {
+				printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
+					__func__, type);
+				av7110->arm_errors++;
+				return -ETIMEDOUT;
+			}
+			msleep(1);
+		}
+	}
+
+	for (i = 2; i < length; i++)
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
+
+	if (length)
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
+	else
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
+
+	wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
+
+	if (FW_VERSION(av7110->arm_app) <= 0x261f)
+		wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
+
+#ifdef COM_DEBUG
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_FREE);
+		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
+			       __func__, (buf[0] >> 8) & 0xff);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+
+	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+	if (stat & GPMQOver) {
+		printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__);
+		return -ENOSPC;
+	}
+	else if (stat & OSDQOver) {
+		printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__);
+		return -ENOSPC;
+	}
+#endif
+
+	return 0;
+}
+
+static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
+{
+	int ret;
+
+//	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -1;
+	}
+	if (mutex_lock_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	ret = __av7110_send_fw_cmd(av7110, buf, length);
+	mutex_unlock(&av7110->dcomlock);
+	if (ret && ret!=-ERESTARTSYS)
+		printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
+		       __func__, ret);
+	return ret;
+}
+
+int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
+{
+	va_list args;
+	u16 buf[num + 2];
+	int i, ret;
+
+//	dprintk(4, "%p\n", av7110);
+
+	buf[0] = ((type << 8) | com);
+	buf[1] = num;
+
+	if (num) {
+		va_start(args, num);
+		for (i = 0; i < num; i++)
+			buf[i + 2] = va_arg(args, u32);
+		va_end(args);
+	}
+
+	ret = av7110_send_fw_cmd(av7110, buf, num + 2);
+	if (ret && ret != -ERESTARTSYS)
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
+	return ret;
+}
+
+#if 0
+int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
+{
+	int i, ret;
+	u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
+		16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	dprintk(4, "%p\n", av7110);
+
+	for(i = 0; i < len && i < 32; i++)
+	{
+		if(i % 2 == 0)
+			cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
+		else
+			cmd[(i / 2) + 2] |= buf[i];
+	}
+
+	ret = av7110_send_fw_cmd(av7110, cmd, 18);
+	if (ret && ret != -ERESTARTSYS)
+		printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
+	return ret;
+}
+#endif  /*  0  */
+
+int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
+		      int request_buf_len, u16 *reply_buf, int reply_buf_len)
+{
+	int err;
+	s16 i;
+	unsigned long start;
+#ifdef COM_DEBUG
+	u32 stat;
+#endif
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -1;
+	}
+
+	if (mutex_lock_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
+		mutex_unlock(&av7110->dcomlock);
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
+		return err;
+	}
+
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_FREE);
+		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__);
+			mutex_unlock(&av7110->dcomlock);
+			return -ETIMEDOUT;
+		}
+#ifdef _NOHANDSHAKE
+		msleep(1);
+#endif
+	}
+
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_SHAKE);
+		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__);
+			mutex_unlock(&av7110->dcomlock);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+#endif
+
+#ifdef COM_DEBUG
+	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+	if (stat & GPMQOver) {
+		printk(KERN_ERR "%s: GPMQOver\n", __func__);
+		mutex_unlock(&av7110->dcomlock);
+		return -1;
+	}
+	else if (stat & OSDQOver) {
+		printk(KERN_ERR "%s: OSDQOver\n", __func__);
+		mutex_unlock(&av7110->dcomlock);
+		return -1;
+	}
+#endif
+
+	for (i = 0; i < reply_buf_len; i++)
+		reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
+
+	mutex_unlock(&av7110->dcomlock);
+	return 0;
+}
+
+static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
+{
+	int ret;
+	ret = av7110_fw_request(av7110, &tag, 0, buf, length);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
+	return ret;
+}
+
+
+/****************************************************************************
+ * Firmware commands
+ ****************************************************************************/
+
+/* get version of the firmware ROM, RTSL, video ucode and ARM application  */
+int av7110_firmversion(struct av7110 *av7110)
+{
+	u16 buf[20];
+	u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110_fw_query(av7110, tag, buf, 16)) {
+		printk("dvb-ttpci: failed to boot firmware @ card %d\n",
+		       av7110->dvb_adapter.num);
+		return -EIO;
+	}
+
+	av7110->arm_fw = (buf[0] << 16) + buf[1];
+	av7110->arm_rtsl = (buf[2] << 16) + buf[3];
+	av7110->arm_vid = (buf[4] << 16) + buf[5];
+	av7110->arm_app = (buf[6] << 16) + buf[7];
+	av7110->avtype = (buf[8] << 16) + buf[9];
+
+	printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
+	       av7110->dvb_adapter.num, av7110->arm_fw,
+	       av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
+
+	/* print firmware capabilities */
+	if (FW_CI_LL_SUPPORT(av7110->arm_app))
+		printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
+		       av7110->dvb_adapter.num);
+	else
+		printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
+		       av7110->dvb_adapter.num);
+
+	return 0;
+}
+
+
+int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
+{
+	int i, ret;
+	u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
+			16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	dprintk(4, "%p\n", av7110);
+
+	if (len > 10)
+		len = 10;
+
+	buf[1] = len + 2;
+	buf[2] = len;
+
+	if (burst != -1)
+		buf[3] = burst ? 0x01 : 0x00;
+	else
+		buf[3] = 0xffff;
+
+	for (i = 0; i < len; i++)
+		buf[i + 4] = msg[i];
+
+	ret = av7110_send_fw_cmd(av7110, buf, 18);
+	if (ret && ret!=-ERESTARTSYS)
+		printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
+	return ret;
+}
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+
+static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
+}
+
+static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
+		     enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
+			     windownr, colordepth, index, blending);
+}
+
+static inline int SetColor_(struct av7110 *av7110, u8 windownr,
+		     enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
+			     windownr, colordepth, index, colorhi, colorlo);
+}
+
+static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
+			  u16 colorfg, u16 colorbg)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
+			     windownr, fontsize, colorfg, colorbg);
+}
+
+static int FlushText(struct av7110 *av7110)
+{
+	unsigned long start;
+	int err;
+
+	if (mutex_lock_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+	start = jiffies;
+	while (1) {
+		err = time_after(jiffies, start + ARM_WAIT_OSD);
+		if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
+			break;
+		if (err) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
+			       __func__);
+			mutex_unlock(&av7110->dcomlock);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+	mutex_unlock(&av7110->dcomlock);
+	return 0;
+}
+
+static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)
+{
+	int i, ret;
+	unsigned long start;
+	int length = strlen(buf) + 1;
+	u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
+
+	if (mutex_lock_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	start = jiffies;
+	while (1) {
+		ret = time_after(jiffies, start + ARM_WAIT_OSD);
+		if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
+			break;
+		if (ret) {
+			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
+			       __func__);
+			mutex_unlock(&av7110->dcomlock);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (1) {
+		ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
+		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
+			break;
+		if (ret) {
+			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
+			       __func__);
+			mutex_unlock(&av7110->dcomlock);
+			return -ETIMEDOUT;
+		}
+		msleep(1);
+	}
+#endif
+	for (i = 0; i < length / 2; i++)
+		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
+		      swab16(*(u16 *)(buf + 2 * i)), 2);
+	if (length & 1)
+		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
+	ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
+	mutex_unlock(&av7110->dcomlock);
+	if (ret && ret!=-ERESTARTSYS)
+		printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
+	return ret;
+}
+
+static inline int DrawLine(struct av7110 *av7110, u8 windownr,
+			   u16 x, u16 y, u16 dx, u16 dy, u16 color)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,
+			     windownr, x, y, dx, dy, color);
+}
+
+static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
+			    u16 x, u16 y, u16 dx, u16 dy, u16 color)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
+			     windownr, x, y, dx, dy, color);
+}
+
+static inline int HideWindow(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
+}
+
+static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
+}
+
+static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
+}
+
+static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
+}
+
+static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
+				  osd_raw_window_t disptype,
+				  u16 width, u16 height)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
+			     windownr, disptype, width, height);
+}
+
+
+static enum av7110_osd_palette_type bpp2pal[8] = {
+	Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
+};
+static osd_raw_window_t bpp2bit[8] = {
+	OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
+};
+
+static inline int WaitUntilBmpLoaded(struct av7110 *av7110)
+{
+	int ret = wait_event_timeout(av7110->bmpq,
+				av7110->bmp_state != BMP_LOADING, 10*HZ);
+	if (ret == 0) {
+		printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
+		       ret, av7110->bmp_state);
+		av7110->bmp_state = BMP_NONE;
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static inline int LoadBitmap(struct av7110 *av7110,
+			     u16 dx, u16 dy, int inc, u8 __user * data)
+{
+	u16 format;
+	int bpp;
+	int i;
+	int d, delta;
+	u8 c;
+	int ret;
+
+	dprintk(4, "%p\n", av7110);
+
+	format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
+
+	av7110->bmp_state = BMP_LOADING;
+	if	(format == OSD_BITMAP8) {
+		bpp=8; delta = 1;
+	} else if (format == OSD_BITMAP4) {
+		bpp=4; delta = 2;
+	} else if (format == OSD_BITMAP2) {
+		bpp=2; delta = 4;
+	} else if (format == OSD_BITMAP1) {
+		bpp=1; delta = 8;
+	} else {
+		av7110->bmp_state = BMP_NONE;
+		return -EINVAL;
+	}
+	av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
+	av7110->bmpp = 0;
+	if (av7110->bmplen > 32768) {
+		av7110->bmp_state = BMP_NONE;
+		return -EINVAL;
+	}
+	for (i = 0; i < dy; i++) {
+		if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
+			av7110->bmp_state = BMP_NONE;
+			return -EINVAL;
+		}
+	}
+	if (format != OSD_BITMAP8) {
+		for (i = 0; i < dx * dy / delta; i++) {
+			c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
+			for (d = delta - 2; d >= 0; d--) {
+				c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
+				      << ((delta - d - 1) * bpp));
+				((u8 *)av7110->bmpbuf)[1024 + i] = c;
+			}
+		}
+	}
+	av7110->bmplen += 1024;
+	dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
+	ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
+	if (!ret)
+		ret = WaitUntilBmpLoaded(av7110);
+	return ret;
+}
+
+static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
+{
+	dprintk(4, "%p\n", av7110);
+
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);
+}
+
+static inline int ReleaseBitmap(struct av7110 *av7110)
+{
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
+		return -1;
+	if (av7110->bmp_state == BMP_LOADING)
+		dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
+	av7110->bmp_state = BMP_NONE;
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
+}
+
+static u32 RGB2YUV(u16 R, u16 G, u16 B)
+{
+	u16 y, u, v;
+	u16 Y, Cr, Cb;
+
+	y = R * 77 + G * 150 + B * 29;	/* Luma=0.299R+0.587G+0.114B 0..65535 */
+	u = 2048 + B * 8 -(y >> 5);	/* Cr 0..4095 */
+	v = 2048 + R * 8 -(y >> 5);	/* Cb 0..4095 */
+
+	Y = y / 256;
+	Cb = u / 16;
+	Cr = v / 16;
+
+	return Cr | (Cb << 16) | (Y << 8);
+}
+
+static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
+{
+	int ret;
+
+	u16 ch, cl;
+	u32 yuv;
+
+	yuv = blend ? RGB2YUV(r,g,b) : 0;
+	cl = (yuv & 0xffff);
+	ch = ((yuv >> 16) & 0xffff);
+	ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
+			color, ch, cl);
+	if (!ret)
+		ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
+				color, ((blend >> 4) & 0x0f));
+	return ret;
+}
+
+static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
+{
+	int i;
+	int length = last - first + 1;
+
+	if (length * 4 > DATA_BUFF3_SIZE)
+		return -EINVAL;
+
+	for (i = 0; i < length; i++) {
+		u32 color, blend, yuv;
+
+		if (get_user(color, colors + i))
+			return -EFAULT;
+		blend = (color & 0xF0000000) >> 4;
+		yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
+				     (color >> 16) & 0xFF) | blend : 0;
+		yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
+		wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
+	}
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
+			    av7110->osdwin,
+			    bpp2pal[av7110->osdbpp[av7110->osdwin]],
+			    first, last);
+}
+
+static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
+		       int x1, int y1, int inc, u8 __user * data)
+{
+	uint w, h, bpp, bpl, size, lpb, bnum, brest;
+	int i;
+	int rc,release_rc;
+
+	w = x1 - x0 + 1;
+	h = y1 - y0 + 1;
+	if (inc <= 0)
+		inc = w;
+	if (w <= 0 || w > 720 || h <= 0 || h > 576)
+		return -EINVAL;
+	bpp = av7110->osdbpp[av7110->osdwin] + 1;
+	bpl = ((w * bpp + 7) & ~7) / 8;
+	size = h * bpl;
+	lpb = (32 * 1024) / bpl;
+	bnum = size / (lpb * bpl);
+	brest = size - bnum * lpb * bpl;
+
+	if (av7110->bmp_state == BMP_LOADING) {
+		/* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
+		BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);
+		rc = WaitUntilBmpLoaded(av7110);
+		if (rc)
+			return rc;
+		/* just continue. This should work for all fw versions
+		 * if bnum==1 && !brest && LoadBitmap was successful
+		 */
+	}
+
+	rc = 0;
+	for (i = 0; i < bnum; i++) {
+		rc = LoadBitmap(av7110, w, lpb, inc, data);
+		if (rc)
+			break;
+		rc = BlitBitmap(av7110, x0, y0 + i * lpb);
+		if (rc)
+			break;
+		data += lpb * inc;
+	}
+	if (!rc && brest) {
+		rc = LoadBitmap(av7110, w, brest / bpl, inc, data);
+		if (!rc)
+			rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);
+	}
+	release_rc = ReleaseBitmap(av7110);
+	if (!rc)
+		rc = release_rc;
+	if (rc)
+		dprintk(1,"returns %d\n",rc);
+	return rc;
+}
+
+int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
+{
+	int ret;
+
+	if (mutex_lock_interruptible(&av7110->osd_mutex))
+		return -ERESTARTSYS;
+
+	switch (dc->cmd) {
+	case OSD_Close:
+		ret = DestroyOSDWindow(av7110, av7110->osdwin);
+		break;
+	case OSD_Open:
+		av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
+		ret = CreateOSDWindow(av7110, av7110->osdwin,
+				bpp2bit[av7110->osdbpp[av7110->osdwin]],
+				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
+		if (ret)
+			break;
+		if (!dc->data) {
+			ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+			if (ret)
+				break;
+			ret = SetColorBlend(av7110, av7110->osdwin);
+		}
+		break;
+	case OSD_Show:
+		ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);
+		break;
+	case OSD_Hide:
+		ret = HideWindow(av7110, av7110->osdwin);
+		break;
+	case OSD_Clear:
+		ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
+		break;
+	case OSD_Fill:
+		ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
+		break;
+	case OSD_SetColor:
+		ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
+		break;
+	case OSD_SetPalette:
+		if (FW_VERSION(av7110->arm_app) >= 0x2618)
+			ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
+		else {
+			int i, len = dc->x0-dc->color+1;
+			u8 __user *colors = (u8 __user *)dc->data;
+			u8 r, g = 0, b = 0, blend = 0;
+			ret = 0;
+			for (i = 0; i<len; i++) {
+				if (get_user(r, colors + i * 4) ||
+				    get_user(g, colors + i * 4 + 1) ||
+				    get_user(b, colors + i * 4 + 2) ||
+				    get_user(blend, colors + i * 4 + 3)) {
+					ret = -EFAULT;
+					break;
+				    }
+				ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);
+				if (ret)
+					break;
+			}
+		}
+		break;
+	case OSD_SetPixel:
+		ret = DrawLine(av7110, av7110->osdwin,
+			 dc->x0, dc->y0, 0, 0, dc->color);
+		break;
+	case OSD_SetRow:
+		dc->y1 = dc->y0;
+		/* fall through */
+	case OSD_SetBlock:
+		ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
+		break;
+	case OSD_FillRow:
+		ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
+			  dc->x1-dc->x0+1, dc->y1, dc->color);
+		break;
+	case OSD_FillBlock:
+		ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
+			  dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
+		break;
+	case OSD_Line:
+		ret = DrawLine(av7110, av7110->osdwin,
+			 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
+		break;
+	case OSD_Text:
+	{
+		char textbuf[240];
+
+		if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
+			ret = -EFAULT;
+			break;
+		}
+		textbuf[239] = 0;
+		if (dc->x1 > 3)
+			dc->x1 = 3;
+		ret = SetFont(av7110, av7110->osdwin, dc->x1,
+			(u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
+		if (!ret)
+			ret = FlushText(av7110);
+		if (!ret)
+			ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
+		break;
+	}
+	case OSD_SetWindow:
+		if (dc->x0 < 1 || dc->x0 > 7)
+			ret = -EINVAL;
+		else {
+			av7110->osdwin = dc->x0;
+			ret = 0;
+		}
+		break;
+	case OSD_MoveWindow:
+		ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+		if (!ret)
+			ret = SetColorBlend(av7110, av7110->osdwin);
+		break;
+	case OSD_OpenRaw:
+		if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
+			av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
+		else
+			av7110->osdbpp[av7110->osdwin] = 0;
+		ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
+				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
+		if (ret)
+			break;
+		if (!dc->data) {
+			ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+			if (!ret)
+				ret = SetColorBlend(av7110, av7110->osdwin);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&av7110->osd_mutex);
+	if (ret==-ERESTARTSYS)
+		dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
+	else if (ret)
+		dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
+
+	return ret;
+}
+
+int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
+{
+	switch (cap->cmd) {
+	case OSD_CAP_MEMSIZE:
+		if (FW_4M_SDRAM(av7110->arm_app))
+			cap->val = 1000000;
+		else
+			cap->val = 92000;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+#endif /* CONFIG_DVB_AV7110_OSD */
diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h
new file mode 100644
index 000000000000..1634aba5cb84
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_hw.h
@@ -0,0 +1,495 @@
+#ifndef _AV7110_HW_H_
+#define _AV7110_HW_H_
+
+#include "av7110.h"
+
+/* DEBI transfer mode defs */
+
+#define DEBINOSWAP 0x000e0000
+#define DEBISWAB   0x001e0000
+#define DEBISWAP   0x002e0000
+
+#define ARM_WAIT_FREE  (HZ)
+#define ARM_WAIT_SHAKE (HZ/5)
+#define ARM_WAIT_OSD (HZ)
+
+
+enum av7110_bootstate
+{
+	BOOTSTATE_BUFFER_EMPTY	= 0,
+	BOOTSTATE_BUFFER_FULL	= 1,
+	BOOTSTATE_AV7110_BOOT_COMPLETE	= 2
+};
+
+enum av7110_type_rec_play_format
+{	RP_None,
+	AudioPES,
+	AudioMp2,
+	AudioPCM,
+	VideoPES,
+	AV_PES
+};
+
+enum av7110_osd_palette_type
+{
+	NoPalet =  0,	   /* No palette */
+	Pal1Bit =  2,	   /* 2 colors for 1 Bit Palette    */
+	Pal2Bit =  4,	   /* 4 colors for 2 bit palette    */
+	Pal4Bit =  16,	   /* 16 colors for 4 bit palette   */
+	Pal8Bit =  256	   /* 256 colors for 16 bit palette */
+};
+
+/* switch defines */
+#define SB_GPIO 3
+#define SB_OFF	SAA7146_GPIO_OUTLO  /* SlowBlank off (TV-Mode) */
+#define SB_ON	SAA7146_GPIO_INPUT  /* SlowBlank on  (AV-Mode) */
+#define SB_WIDE SAA7146_GPIO_OUTHI  /* SlowBlank 6V  (16/9-Mode) (not implemented) */
+
+#define FB_GPIO 1
+#define FB_OFF	SAA7146_GPIO_LO     /* FastBlank off (CVBS-Mode) */
+#define FB_ON	SAA7146_GPIO_OUTHI  /* FastBlank on  (RGB-Mode) */
+#define FB_LOOP	SAA7146_GPIO_INPUT  /* FastBlank loop-through (PC graphics ???) */
+
+enum av7110_video_output_mode
+{
+	NO_OUT	     = 0,		/* disable analog output */
+	CVBS_RGB_OUT = 1,
+	CVBS_YC_OUT  = 2,
+	YC_OUT	     = 3
+};
+
+/* firmware internal msg q status: */
+#define GPMQFull	0x0001		/* Main Message Queue Full */
+#define GPMQOver	0x0002		/* Main Message Queue Overflow */
+#define HPQFull		0x0004		/* High Priority Msg Queue Full */
+#define HPQOver		0x0008
+#define OSDQFull	0x0010		/* OSD Queue Full */
+#define OSDQOver	0x0020
+#define GPMQBusy	0x0040		/* Queue not empty, FW >= 261d */
+#define HPQBusy		0x0080
+#define OSDQBusy	0x0100
+
+/* hw section filter flags */
+#define	SECTION_EIT		0x01
+#define	SECTION_SINGLE		0x00
+#define	SECTION_CYCLE		0x02
+#define	SECTION_CONTINUOS	0x04
+#define	SECTION_MODE		0x06
+#define SECTION_IPMPE		0x0C	/* size up to 4k */
+#define SECTION_HIGH_SPEED	0x1C	/* larger buffer */
+#define DATA_PIPING_FLAG	0x20	/* for Data Piping Filter */
+
+#define	PBUFSIZE_NONE 0x0000
+#define	PBUFSIZE_1P   0x0100
+#define	PBUFSIZE_2P   0x0200
+#define	PBUFSIZE_1K   0x0300
+#define	PBUFSIZE_2K   0x0400
+#define	PBUFSIZE_4K   0x0500
+#define	PBUFSIZE_8K   0x0600
+#define PBUFSIZE_16K  0x0700
+#define PBUFSIZE_32K  0x0800
+
+
+/* firmware command codes */
+enum av7110_osd_command {
+	WCreate,
+	WDestroy,
+	WMoveD,
+	WMoveA,
+	WHide,
+	WTop,
+	DBox,
+	DLine,
+	DText,
+	Set_Font,
+	SetColor,
+	SetBlend,
+	SetWBlend,
+	SetCBlend,
+	SetNonBlend,
+	LoadBmp,
+	BlitBmp,
+	ReleaseBmp,
+	SetWTrans,
+	SetWNoTrans,
+	Set_Palette
+};
+
+enum av7110_pid_command {
+	MultiPID,
+	VideoPID,
+	AudioPID,
+	InitFilt,
+	FiltError,
+	NewVersion,
+	CacheError,
+	AddPIDFilter,
+	DelPIDFilter,
+	Scan,
+	SetDescr,
+	SetIR,
+	FlushTSQueue
+};
+
+enum av7110_mpeg_command {
+	SelAudChannels
+};
+
+enum av7110_audio_command {
+	AudioDAC,
+	CabADAC,
+	ON22K,
+	OFF22K,
+	MainSwitch,
+	ADSwitch,
+	SendDiSEqC,
+	SetRegister,
+	SpdifSwitch
+};
+
+enum av7110_request_command {
+	AudioState,
+	AudioBuffState,
+	VideoState1,
+	VideoState2,
+	VideoState3,
+	CrashCounter,
+	ReqVersion,
+	ReqVCXO,
+	ReqRegister,
+	ReqSecFilterError,
+	ReqSTC
+};
+
+enum av7110_encoder_command {
+	SetVidMode,
+	SetTestMode,
+	LoadVidCode,
+	SetMonitorType,
+	SetPanScanType,
+	SetFreezeMode,
+	SetWSSConfig
+};
+
+enum av7110_rec_play_state {
+	__Record,
+	__Stop,
+	__Play,
+	__Pause,
+	__Slow,
+	__FF_IP,
+	__Scan_I,
+	__Continue
+};
+
+enum av7110_fw_cmd_misc {
+	AV7110_FW_VIDEO_ZOOM = 1,
+	AV7110_FW_VIDEO_COMMAND,
+	AV7110_FW_AUDIO_COMMAND
+};
+
+enum av7110_command_type {
+	COMTYPE_NOCOM,
+	COMTYPE_PIDFILTER,
+	COMTYPE_MPEGDECODER,
+	COMTYPE_OSD,
+	COMTYPE_BMP,
+	COMTYPE_ENCODER,
+	COMTYPE_AUDIODAC,
+	COMTYPE_REQUEST,
+	COMTYPE_SYSTEM,
+	COMTYPE_REC_PLAY,
+	COMTYPE_COMMON_IF,
+	COMTYPE_PID_FILTER,
+	COMTYPE_PES,
+	COMTYPE_TS,
+	COMTYPE_VIDEO,
+	COMTYPE_AUDIO,
+	COMTYPE_CI_LL,
+	COMTYPE_MISC = 0x80
+};
+
+#define VID_NONE_PREF		0x00	/* No aspect ration processing preferred */
+#define VID_PAN_SCAN_PREF	0x01	/* Pan and Scan Display preferred */
+#define VID_VERT_COMP_PREF	0x02	/* Vertical compression display preferred */
+#define VID_VC_AND_PS_PREF	0x03	/* PanScan and vertical Compression if allowed */
+#define VID_CENTRE_CUT_PREF	0x05	/* PanScan with zero vector */
+
+/* MPEG video decoder commands */
+#define AV_VIDEO_CMD_STOP	0x000e
+#define AV_VIDEO_CMD_PLAY	0x000d
+#define AV_VIDEO_CMD_FREEZE	0x0102
+#define AV_VIDEO_CMD_FFWD	0x0016
+#define AV_VIDEO_CMD_SLOW	0x0022
+
+/* MPEG audio decoder commands */
+#define AUDIO_CMD_MUTE		0x0001
+#define AUDIO_CMD_UNMUTE	0x0002
+#define AUDIO_CMD_PCM16		0x0010
+#define AUDIO_CMD_STEREO	0x0080
+#define AUDIO_CMD_MONO_L	0x0100
+#define AUDIO_CMD_MONO_R	0x0200
+#define AUDIO_CMD_SYNC_OFF	0x000e
+#define AUDIO_CMD_SYNC_ON	0x000f
+
+/* firmware data interface codes */
+#define DATA_NONE		 0x00
+#define DATA_FSECTION		 0x01
+#define DATA_IPMPE		 0x02
+#define DATA_MPEG_RECORD	 0x03
+#define DATA_DEBUG_MESSAGE	 0x04
+#define DATA_COMMON_INTERFACE	 0x05
+#define DATA_MPEG_PLAY		 0x06
+#define DATA_BMP_LOAD		 0x07
+#define DATA_IRCOMMAND		 0x08
+#define DATA_PIPING		 0x09
+#define DATA_STREAMING		 0x0a
+#define DATA_CI_GET		 0x0b
+#define DATA_CI_PUT		 0x0c
+#define DATA_MPEG_VIDEO_EVENT	 0x0d
+
+#define DATA_PES_RECORD		 0x10
+#define DATA_PES_PLAY		 0x11
+#define DATA_TS_RECORD		 0x12
+#define DATA_TS_PLAY		 0x13
+
+/* ancient CI command codes, only two are actually still used
+ * by the link level CI firmware */
+#define CI_CMD_ERROR		 0x00
+#define CI_CMD_ACK		 0x01
+#define CI_CMD_SYSTEM_READY	 0x02
+#define CI_CMD_KEYPRESS		 0x03
+#define CI_CMD_ON_TUNED		 0x04
+#define CI_CMD_ON_SWITCH_PROGRAM 0x05
+#define CI_CMD_SECTION_ARRIVED	 0x06
+#define CI_CMD_SECTION_TIMEOUT	 0x07
+#define CI_CMD_TIME		 0x08
+#define CI_CMD_ENTER_MENU	 0x09
+#define CI_CMD_FAST_PSI		 0x0a
+#define CI_CMD_GET_SLOT_INFO	 0x0b
+
+#define CI_MSG_NONE		 0x00
+#define CI_MSG_CI_INFO		 0x01
+#define CI_MSG_MENU		 0x02
+#define CI_MSG_LIST		 0x03
+#define CI_MSG_TEXT		 0x04
+#define CI_MSG_REQUEST_INPUT	 0x05
+#define CI_MSG_INPUT_COMPLETE	 0x06
+#define CI_MSG_LIST_MORE	 0x07
+#define CI_MSG_MENU_MORE	 0x08
+#define CI_MSG_CLOSE_MMI_IMM	 0x09
+#define CI_MSG_SECTION_REQUEST	 0x0a
+#define CI_MSG_CLOSE_FILTER	 0x0b
+#define CI_PSI_COMPLETE		 0x0c
+#define CI_MODULE_READY		 0x0d
+#define CI_SWITCH_PRG_REPLY	 0x0e
+#define CI_MSG_TEXT_MORE	 0x0f
+
+#define CI_MSG_CA_PMT		 0xe0
+#define CI_MSG_ERROR		 0xf0
+
+
+/* base address of the dual ported RAM which serves as communication
+ * area between PCI bus and av7110,
+ * as seen by the DEBI bus of the saa7146 */
+#define	DPRAM_BASE 0x4000
+
+/* boot protocol area */
+#define AV7110_BOOT_STATE	(DPRAM_BASE + 0x3F8)
+#define AV7110_BOOT_SIZE	(DPRAM_BASE + 0x3FA)
+#define AV7110_BOOT_BASE	(DPRAM_BASE + 0x3FC)
+#define AV7110_BOOT_BLOCK	(DPRAM_BASE + 0x400)
+#define AV7110_BOOT_MAX_SIZE	0xc00
+
+/* firmware command protocol area */
+#define IRQ_STATE	(DPRAM_BASE + 0x0F4)
+#define IRQ_STATE_EXT	(DPRAM_BASE + 0x0F6)
+#define MSGSTATE	(DPRAM_BASE + 0x0F8)
+#define COMMAND		(DPRAM_BASE + 0x0FC)
+#define COM_BUFF	(DPRAM_BASE + 0x100)
+#define COM_BUFF_SIZE	0x20
+
+/* various data buffers */
+#define BUFF1_BASE	(DPRAM_BASE + 0x120)
+#define BUFF1_SIZE	0xE0
+
+#define DATA_BUFF0_BASE	(DPRAM_BASE + 0x200)
+#define DATA_BUFF0_SIZE	0x0800
+
+#define DATA_BUFF1_BASE	(DATA_BUFF0_BASE+DATA_BUFF0_SIZE)
+#define DATA_BUFF1_SIZE	0x0800
+
+#define DATA_BUFF2_BASE	(DATA_BUFF1_BASE+DATA_BUFF1_SIZE)
+#define DATA_BUFF2_SIZE	0x0800
+
+#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE)
+#define DATA_BUFF3_SIZE 0x0400
+
+#define Reserved	(DPRAM_BASE + 0x1E00)
+#define Reserved_SIZE	0x1C0
+
+
+/* firmware status area */
+#define STATUS_BASE	(DPRAM_BASE + 0x1FC0)
+#define STATUS_LOOPS	(STATUS_BASE + 0x08)
+
+#define STATUS_MPEG_WIDTH     (STATUS_BASE + 0x0C)
+/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */
+#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E)
+
+/* firmware data protocol area */
+#define RX_TYPE		(DPRAM_BASE + 0x1FE8)
+#define RX_LEN		(DPRAM_BASE + 0x1FEA)
+#define TX_TYPE		(DPRAM_BASE + 0x1FEC)
+#define TX_LEN		(DPRAM_BASE + 0x1FEE)
+
+#define RX_BUFF		(DPRAM_BASE + 0x1FF4)
+#define TX_BUFF		(DPRAM_BASE + 0x1FF6)
+
+#define HANDSHAKE_REG	(DPRAM_BASE + 0x1FF8)
+#define COM_IF_LOCK	(DPRAM_BASE + 0x1FFA)
+
+#define IRQ_RX		(DPRAM_BASE + 0x1FFC)
+#define IRQ_TX		(DPRAM_BASE + 0x1FFE)
+
+/* used by boot protocol to load firmware into av7110 DRAM */
+#define DRAM_START_CODE		0x2e000404
+#define DRAM_MAX_CODE_SIZE	0x00100000
+
+/* saa7146 gpio lines */
+#define RESET_LINE		2
+#define DEBI_DONE_LINE		1
+#define ARM_IRQ_LINE		0
+
+
+
+extern int av7110_bootarm(struct av7110 *av7110);
+extern int av7110_firmversion(struct av7110 *av7110);
+#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000)
+#define FW_4M_SDRAM(arm_app)      ((arm_app) & 0x40000000)
+#define FW_VERSION(arm_app)	  ((arm_app) & 0x0000FFFF)
+
+extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags);
+extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...);
+extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
+			     int request_buf_len, u16 *reply_buf, int reply_buf_len);
+
+
+/* DEBI (saa7146 data extension bus interface) access */
+extern int av7110_debiwrite(struct av7110 *av7110, u32 config,
+			    int addr, u32 val, int count);
+extern u32 av7110_debiread(struct av7110 *av7110, u32 config,
+			   int addr, int count);
+
+
+/* DEBI during interrupt */
+/* single word writes */
+static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	av7110_debiwrite(av7110, config, addr, val, count);
+}
+
+/* buffer writes */
+static inline void mwdebi(struct av7110 *av7110, u32 config, int addr,
+			  const u8 *val, int count)
+{
+	memcpy(av7110->debi_virt, val, count);
+	av7110_debiwrite(av7110, config, addr, 0, count);
+}
+
+static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	u32 res;
+
+	res=av7110_debiread(av7110, config, addr, count);
+	if (count<=4)
+		memcpy(av7110->debi_virt, (char *) &res, count);
+	return res;
+}
+
+/* DEBI outside interrupts, only for count <= 4! */
+static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	av7110_debiwrite(av7110, config, addr, val, count);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+}
+
+static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	unsigned long flags;
+	u32 res;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	res=av7110_debiread(av7110, config, addr, count);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+	return res;
+}
+
+/* handle mailbox registers of the dual ported RAM */
+static inline void ARM_ResetMailBox(struct av7110 *av7110)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2);
+	av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+}
+
+static inline void ARM_ClearMailBox(struct av7110 *av7110)
+{
+	iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+}
+
+static inline void ARM_ClearIrq(struct av7110 *av7110)
+{
+	irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+}
+
+/****************************************************************************
+ * Firmware commands
+ ****************************************************************************/
+
+static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data);
+}
+
+static inline int av7710_set_video_mode(struct av7110 *av7110, int mode)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode);
+}
+
+static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4,
+			     (com>>16), (com&0xffff),
+			     (arg>>16), (arg&0xffff));
+}
+
+static inline int audcom(struct av7110 *av7110, u32 com)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2,
+			     (com>>16), (com&0xffff));
+}
+
+static inline int Set22K(struct av7110 *av7110, int state)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0);
+}
+
+
+extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst);
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc);
+extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap);
+#endif /* CONFIG_DVB_AV7110_OSD */
+
+
+
+#endif /* _AV7110_HW_H_ */
diff --git a/drivers/media/pci/ttpci/av7110_ipack.c b/drivers/media/pci/ttpci/av7110_ipack.c
new file mode 100644
index 000000000000..699ef8b5b99a
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_ipack.c
@@ -0,0 +1,403 @@
+#include "dvb_filter.h"
+#include "av7110_ipack.h"
+#include <linux/string.h>	/* for memcpy() */
+#include <linux/vmalloc.h>
+
+
+void av7110_ipack_reset(struct ipack *p)
+{
+	p->found = 0;
+	p->cid = 0;
+	p->plength = 0;
+	p->flag1 = 0;
+	p->flag2 = 0;
+	p->hlength = 0;
+	p->mpeg = 0;
+	p->check = 0;
+	p->which = 0;
+	p->done = 0;
+	p->count = 0;
+}
+
+
+int av7110_ipack_init(struct ipack *p, int size,
+		      void (*func)(u8 *buf, int size, void *priv))
+{
+	if (!(p->buf = vmalloc(size*sizeof(u8)))) {
+		printk(KERN_WARNING "Couldn't allocate memory for ipack\n");
+		return -ENOMEM;
+	}
+	p->size = size;
+	p->func = func;
+	p->repack_subids = 0;
+	av7110_ipack_reset(p);
+	return 0;
+}
+
+
+void av7110_ipack_free(struct ipack *p)
+{
+	vfree(p->buf);
+}
+
+
+static void send_ipack(struct ipack *p)
+{
+	int off;
+	struct dvb_audio_info ai;
+	int ac3_off = 0;
+	int streamid = 0;
+	int nframes = 0;
+	int f = 0;
+
+	switch (p->mpeg) {
+	case 2:
+		if (p->count < 10)
+			return;
+		p->buf[3] = p->cid;
+		p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+		p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+		if (p->repack_subids && p->cid == PRIVATE_STREAM1) {
+			off = 9 + p->buf[8];
+			streamid = p->buf[off];
+			if ((streamid & 0xf8) == 0x80) {
+				ai.off = 0;
+				ac3_off = ((p->buf[off + 2] << 8)|
+					   p->buf[off + 3]);
+				if (ac3_off < p->count)
+					f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off,
+								   p->count - ac3_off, &ai, 0);
+				if (!f) {
+					nframes = (p->count - off - 3 - ac3_off) /
+						ai.framesize + 1;
+					p->buf[off + 2] = (ac3_off >> 8) & 0xff;
+					p->buf[off + 3] = (ac3_off) & 0xff;
+					p->buf[off + 1] = nframes;
+					ac3_off +=  nframes * ai.framesize - p->count;
+				}
+			}
+		}
+		p->func(p->buf, p->count, p->data);
+
+		p->buf[6] = 0x80;
+		p->buf[7] = 0x00;
+		p->buf[8] = 0x00;
+		p->count = 9;
+		if (p->repack_subids && p->cid == PRIVATE_STREAM1
+		    && (streamid & 0xf8) == 0x80) {
+			p->count += 4;
+			p->buf[9] = streamid;
+			p->buf[10] = (ac3_off >> 8) & 0xff;
+			p->buf[11] = (ac3_off) & 0xff;
+			p->buf[12] = 0;
+		}
+		break;
+
+	case 1:
+		if (p->count < 8)
+			return;
+		p->buf[3] = p->cid;
+		p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+		p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+		p->func(p->buf, p->count, p->data);
+
+		p->buf[6] = 0x0f;
+		p->count = 7;
+		break;
+	}
+}
+
+
+void av7110_ipack_flush(struct ipack *p)
+{
+	if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6)
+		return;
+	p->plength = p->found - 6;
+	p->found = 0;
+	send_ipack(p);
+	av7110_ipack_reset(p);
+}
+
+
+static void write_ipack(struct ipack *p, const u8 *data, int count)
+{
+	u8 headr[3] = { 0x00, 0x00, 0x01 };
+
+	if (p->count < 6) {
+		memcpy(p->buf, headr, 3);
+		p->count = 6;
+	}
+
+	if (p->count + count < p->size){
+		memcpy(p->buf+p->count, data, count);
+		p->count += count;
+	} else {
+		int rest = p->size - p->count;
+		memcpy(p->buf+p->count, data, rest);
+		p->count += rest;
+		send_ipack(p);
+		if (count - rest > 0)
+			write_ipack(p, data + rest, count - rest);
+	}
+}
+
+
+int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p)
+{
+	int l;
+	int c = 0;
+
+	while (c < count && (p->mpeg == 0 ||
+			     (p->mpeg == 1 && p->found < 7) ||
+			     (p->mpeg == 2 && p->found < 9))
+	       &&  (p->found < 5 || !p->done)) {
+		switch (p->found) {
+		case 0:
+		case 1:
+			if (buf[c] == 0x00)
+				p->found++;
+			else
+				p->found = 0;
+			c++;
+			break;
+		case 2:
+			if (buf[c] == 0x01)
+				p->found++;
+			else if (buf[c] == 0)
+				p->found = 2;
+			else
+				p->found = 0;
+			c++;
+			break;
+		case 3:
+			p->cid = 0;
+			switch (buf[c]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+				p->done = 1;
+				/* fall through */
+			case PRIVATE_STREAM1:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+				p->found++;
+				p->cid = buf[c];
+				c++;
+				break;
+			default:
+				p->found = 0;
+				break;
+			}
+			break;
+
+		case 4:
+			if (count-c > 1) {
+				p->plen[0] = buf[c];
+				c++;
+				p->plen[1] = buf[c];
+				c++;
+				p->found += 2;
+				p->plength = (p->plen[0] << 8) | p->plen[1];
+			} else {
+				p->plen[0] = buf[c];
+				p->found++;
+				return count;
+			}
+			break;
+		case 5:
+			p->plen[1] = buf[c];
+			c++;
+			p->found++;
+			p->plength = (p->plen[0] << 8) | p->plen[1];
+			break;
+		case 6:
+			if (!p->done) {
+				p->flag1 = buf[c];
+				c++;
+				p->found++;
+				if ((p->flag1 & 0xc0) == 0x80)
+					p->mpeg = 2;
+				else {
+					p->hlength = 0;
+					p->which = 0;
+					p->mpeg = 1;
+					p->flag2 = 0;
+				}
+			}
+			break;
+
+		case 7:
+			if (!p->done && p->mpeg == 2) {
+				p->flag2 = buf[c];
+				c++;
+				p->found++;
+			}
+			break;
+
+		case 8:
+			if (!p->done && p->mpeg == 2) {
+				p->hlength = buf[c];
+				c++;
+				p->found++;
+			}
+			break;
+		}
+	}
+
+	if (c == count)
+		return count;
+
+	if (!p->plength)
+		p->plength = MMAX_PLENGTH - 6;
+
+	if (p->done || ((p->mpeg == 2 && p->found >= 9) ||
+			(p->mpeg == 1 && p->found >= 7))) {
+		switch (p->cid) {
+		case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+		case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+		case PRIVATE_STREAM1:
+			if (p->mpeg == 2 && p->found == 9) {
+				write_ipack(p, &p->flag1, 1);
+				write_ipack(p, &p->flag2, 1);
+				write_ipack(p, &p->hlength, 1);
+			}
+
+			if (p->mpeg == 1 && p->found == 7)
+				write_ipack(p, &p->flag1, 1);
+
+			if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+			    p->found < 14) {
+				while (c < count && p->found < 14) {
+					p->pts[p->found - 9] = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+				}
+				if (c == count)
+					return count;
+			}
+
+			if (p->mpeg == 1 && p->which < 2000) {
+
+				if (p->found == 7) {
+					p->check = p->flag1;
+					p->hlength = 1;
+				}
+
+				while (!p->which && c < count &&
+				       p->check == 0xff){
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+				}
+
+				if (c == count)
+					return count;
+
+				if ((p->check & 0xc0) == 0x40 && !p->which) {
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+
+					p->which = 1;
+					if (c == count)
+						return count;
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+					p->which = 2;
+					if (c == count)
+						return count;
+				}
+
+				if (p->which == 1) {
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+					p->which = 2;
+					if (c == count)
+						return count;
+				}
+
+				if ((p->check & 0x30) && p->check != 0xff) {
+					p->flag2 = (p->check & 0xf0) << 2;
+					p->pts[0] = p->check;
+					p->which = 3;
+				}
+
+				if (c == count)
+					return count;
+				if (p->which > 2){
+					if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) {
+						while (c < count && p->which < 7) {
+							p->pts[p->which - 2] = buf[c];
+							write_ipack(p, buf + c, 1);
+							c++;
+							p->found++;
+							p->which++;
+							p->hlength++;
+						}
+						if (c == count)
+							return count;
+					} else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) {
+						while (c < count && p->which < 12) {
+							if (p->which < 7)
+								p->pts[p->which - 2] = buf[c];
+							write_ipack(p, buf + c, 1);
+							c++;
+							p->found++;
+							p->which++;
+							p->hlength++;
+						}
+						if (c == count)
+							return count;
+					}
+					p->which = 2000;
+				}
+
+			}
+
+			while (c < count && p->found < p->plength + 6) {
+				l = count - c;
+				if (l + p->found > p->plength + 6)
+					l = p->plength + 6 - p->found;
+				write_ipack(p, buf + c, l);
+				p->found += l;
+				c += l;
+			}
+			break;
+		}
+
+
+		if (p->done) {
+			if (p->found + count - c < p->plength + 6) {
+				p->found += count - c;
+				c = count;
+			} else {
+				c += p->plength + 6 - p->found;
+				p->found = p->plength + 6;
+			}
+		}
+
+		if (p->plength && p->found == p->plength + 6) {
+			send_ipack(p);
+			av7110_ipack_reset(p);
+			if (c < count)
+				av7110_ipack_instant_repack(buf + c, count - c, p);
+		}
+	}
+	return count;
+}
diff --git a/drivers/media/pci/ttpci/av7110_ipack.h b/drivers/media/pci/ttpci/av7110_ipack.h
new file mode 100644
index 000000000000..becf94d3fdfa
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_ipack.h
@@ -0,0 +1,12 @@
+#ifndef _AV7110_IPACK_H_
+#define _AV7110_IPACK_H_
+
+extern int av7110_ipack_init(struct ipack *p, int size,
+			     void (*func)(u8 *buf,  int size, void *priv));
+
+extern void av7110_ipack_reset(struct ipack *p);
+extern int  av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p);
+extern void av7110_ipack_free(struct ipack * p);
+extern void av7110_ipack_flush(struct ipack *p);
+
+#endif
diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c
new file mode 100644
index 000000000000..908f272fe26c
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_ir.c
@@ -0,0 +1,415 @@
+/*
+ * Driver for the remote control of SAA7146 based AV7110 cards
+ *
+ * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
+ * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+
+
+#define AV_CNT		4
+
+#define IR_RC5		0
+#define IR_RCMM		1
+#define IR_RC5_EXT	2 /* internal only */
+
+#define IR_ALL		0xffffffff
+
+#define UP_TIMEOUT	(HZ*7/25)
+
+
+/* Note: enable ir debugging by or'ing debug with 16 */
+
+static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM};
+module_param_array(ir_protocol, int, NULL, 0644);
+MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)");
+
+static int ir_inversion[AV_CNT];
+module_param_array(ir_inversion, int, NULL, 0644);
+MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted");
+
+static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL };
+module_param_array(ir_device_mask, uint, NULL, 0644);
+MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)");
+
+
+static int av_cnt;
+static struct av7110 *av_list[AV_CNT];
+
+static u16 default_key_map [256] = {
+	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
+	KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
+	KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0,
+	0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0,
+	KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0,
+	KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW,
+	KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN,
+	0, 0, 0, 0, KEY_EPG, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR
+};
+
+
+/* key-up timer */
+static void av7110_emit_keyup(unsigned long parm)
+{
+	struct infrared *ir = (struct infrared *) parm;
+
+	if (!ir || !test_bit(ir->last_key, ir->input_dev->key))
+		return;
+
+	input_report_key(ir->input_dev, ir->last_key, 0);
+	input_sync(ir->input_dev);
+}
+
+
+/* tasklet */
+static void av7110_emit_key(unsigned long parm)
+{
+	struct infrared *ir = (struct infrared *) parm;
+	u32 ircom = ir->ir_command;
+	u8 data;
+	u8 addr;
+	u16 toggle;
+	u16 keycode;
+
+	/* extract device address and data */
+	switch (ir->protocol) {
+	case IR_RC5: /* RC5: 5 bits device address, 6 bits data */
+		data = ircom & 0x3f;
+		addr = (ircom >> 6) & 0x1f;
+		toggle = ircom & 0x0800;
+		break;
+
+	case IR_RCMM: /* RCMM: ? bits device address, ? bits data */
+		data = ircom & 0xff;
+		addr = (ircom >> 8) & 0x1f;
+		toggle = ircom & 0x8000;
+		break;
+
+	case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */
+		data = ircom & 0x3f;
+		addr = (ircom >> 6) & 0x1f;
+		/* invert 7th data bit for backward compatibility with RC5 keymaps */
+		if (!(ircom & 0x1000))
+			data |= 0x40;
+		toggle = ircom & 0x0800;
+		break;
+
+	default:
+		printk("%s invalid protocol %x\n", __func__, ir->protocol);
+		return;
+	}
+
+	input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data);
+	input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
+
+	keycode = ir->key_map[data];
+
+	dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n",
+		__func__, ircom, addr, data, keycode);
+
+	/* check device address */
+	if (!(ir->device_mask & (1 << addr)))
+		return;
+
+	if (!keycode) {
+		printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n",
+			__func__, ircom, addr, data);
+		return;
+	}
+
+	if (timer_pending(&ir->keyup_timer)) {
+		del_timer(&ir->keyup_timer);
+		if (ir->last_key != keycode || toggle != ir->last_toggle) {
+			ir->delay_timer_finished = 0;
+			input_event(ir->input_dev, EV_KEY, ir->last_key, 0);
+			input_event(ir->input_dev, EV_KEY, keycode, 1);
+			input_sync(ir->input_dev);
+		} else if (ir->delay_timer_finished) {
+			input_event(ir->input_dev, EV_KEY, keycode, 2);
+			input_sync(ir->input_dev);
+		}
+	} else {
+		ir->delay_timer_finished = 0;
+		input_event(ir->input_dev, EV_KEY, keycode, 1);
+		input_sync(ir->input_dev);
+	}
+
+	ir->last_key = keycode;
+	ir->last_toggle = toggle;
+
+	ir->keyup_timer.expires = jiffies + UP_TIMEOUT;
+	add_timer(&ir->keyup_timer);
+
+}
+
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
+{
+	int i;
+
+	set_bit(EV_KEY, ir->input_dev->evbit);
+	set_bit(EV_REP, ir->input_dev->evbit);
+	set_bit(EV_MSC, ir->input_dev->evbit);
+
+	set_bit(MSC_RAW, ir->input_dev->mscbit);
+	set_bit(MSC_SCAN, ir->input_dev->mscbit);
+
+	memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+	for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) {
+		if (ir->key_map[i] > KEY_MAX)
+			ir->key_map[i] = 0;
+		else if (ir->key_map[i] > KEY_RESERVED)
+			set_bit(ir->key_map[i], ir->input_dev->keybit);
+	}
+
+	ir->input_dev->keycode = ir->key_map;
+	ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+	ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
+}
+
+
+/* called by the input driver after rep[REP_DELAY] ms */
+static void input_repeat_key(unsigned long parm)
+{
+	struct infrared *ir = (struct infrared *) parm;
+
+	ir->delay_timer_finished = 1;
+}
+
+
+/* check for configuration changes */
+int av7110_check_ir_config(struct av7110 *av7110, int force)
+{
+	int i;
+	int modified = force;
+	int ret = -ENODEV;
+
+	for (i = 0; i < av_cnt; i++)
+		if (av7110 == av_list[i])
+			break;
+
+	if (i < av_cnt && av7110) {
+		if ((av7110->ir.protocol & 1) != ir_protocol[i] ||
+		    av7110->ir.inversion != ir_inversion[i])
+			modified = true;
+
+		if (modified) {
+			/* protocol */
+			if (ir_protocol[i]) {
+				ir_protocol[i] = 1;
+				av7110->ir.protocol = IR_RCMM;
+				av7110->ir.ir_config = 0x0001;
+			} else if (FW_VERSION(av7110->arm_app) >= 0x2620) {
+				av7110->ir.protocol = IR_RC5_EXT;
+				av7110->ir.ir_config = 0x0002;
+			} else {
+				av7110->ir.protocol = IR_RC5;
+				av7110->ir.ir_config = 0x0000;
+			}
+			/* inversion */
+			if (ir_inversion[i]) {
+				ir_inversion[i] = 1;
+				av7110->ir.ir_config |= 0x8000;
+			}
+			av7110->ir.inversion = ir_inversion[i];
+			/* update ARM */
+			ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
+						av7110->ir.ir_config);
+		} else
+			ret = 0;
+
+		/* address */
+		if (av7110->ir.device_mask != ir_device_mask[i])
+			av7110->ir.device_mask = ir_device_mask[i];
+	}
+
+	return ret;
+}
+
+
+/* /proc/av7110_ir interface */
+static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer,
+				    size_t count, loff_t *pos)
+{
+	char *page;
+	u32 ir_config;
+	int size = sizeof ir_config + sizeof av_list[0]->ir.key_map;
+	int i;
+
+	if (count < size)
+		return -EINVAL;
+
+	page = vmalloc(size);
+	if (!page)
+		return -ENOMEM;
+
+	if (copy_from_user(page, buffer, size)) {
+		vfree(page);
+		return -EFAULT;
+	}
+
+	memcpy(&ir_config, page, sizeof ir_config);
+
+	for (i = 0; i < av_cnt; i++) {
+		/* keymap */
+		memcpy(av_list[i]->ir.key_map, page + sizeof ir_config,
+			sizeof(av_list[i]->ir.key_map));
+		/* protocol, inversion, address */
+		ir_protocol[i] = ir_config & 0x0001;
+		ir_inversion[i] = ir_config & 0x8000 ? 1 : 0;
+		if (ir_config & 0x4000)
+			ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f);
+		else
+			ir_device_mask[i] = IR_ALL;
+		/* update configuration */
+		av7110_check_ir_config(av_list[i], false);
+		input_register_keys(&av_list[i]->ir);
+	}
+	vfree(page);
+	return count;
+}
+
+static const struct file_operations av7110_ir_proc_fops = {
+	.owner		= THIS_MODULE,
+	.write		= av7110_ir_proc_write,
+	.llseek		= noop_llseek,
+};
+
+/* interrupt handler */
+static void ir_handler(struct av7110 *av7110, u32 ircom)
+{
+	dprintk(4, "ir command = %08x\n", ircom);
+	av7110->ir.ir_command = ircom;
+	tasklet_schedule(&av7110->ir.ir_tasklet);
+}
+
+
+int __devinit av7110_ir_init(struct av7110 *av7110)
+{
+	struct input_dev *input_dev;
+	static struct proc_dir_entry *e;
+	int err;
+
+	if (av_cnt >= ARRAY_SIZE(av_list))
+		return -ENOSPC;
+
+	av_list[av_cnt++] = av7110;
+	av7110_check_ir_config(av7110, true);
+
+	init_timer(&av7110->ir.keyup_timer);
+	av7110->ir.keyup_timer.function = av7110_emit_keyup;
+	av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	av7110->ir.input_dev = input_dev;
+	snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
+		"pci-%s/ir0", pci_name(av7110->dev->pci));
+
+	input_dev->name = "DVB on-card IR receiver";
+
+	input_dev->phys = av7110->ir.input_phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 2;
+	if (av7110->dev->pci->subsystem_vendor) {
+		input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
+		input_dev->id.product = av7110->dev->pci->subsystem_device;
+	} else {
+		input_dev->id.vendor = av7110->dev->pci->vendor;
+		input_dev->id.product = av7110->dev->pci->device;
+	}
+	input_dev->dev.parent = &av7110->dev->pci->dev;
+	/* initial keymap */
+	memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map);
+	input_register_keys(&av7110->ir);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+	input_dev->timer.function = input_repeat_key;
+	input_dev->timer.data = (unsigned long) &av7110->ir;
+
+	if (av_cnt == 1) {
+		e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops);
+		if (e)
+			e->size = 4 + 256 * sizeof(u16);
+	}
+
+	tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
+	av7110->ir.ir_handler = ir_handler;
+
+	return 0;
+}
+
+
+void __devexit av7110_ir_exit(struct av7110 *av7110)
+{
+	int i;
+
+	if (av_cnt == 0)
+		return;
+
+	del_timer_sync(&av7110->ir.keyup_timer);
+	av7110->ir.ir_handler = NULL;
+	tasklet_kill(&av7110->ir.ir_tasklet);
+
+	for (i = 0; i < av_cnt; i++)
+		if (av_list[i] == av7110) {
+			av_list[i] = av_list[av_cnt-1];
+			av_list[av_cnt-1] = NULL;
+			break;
+		}
+
+	if (av_cnt == 1)
+		remove_proc_entry("av7110_ir", NULL);
+
+	input_unregister_device(av7110->ir.input_dev);
+
+	av_cnt--;
+}
+
+//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
+//MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c
new file mode 100644
index 000000000000..730e906ea912
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_v4l.c
@@ -0,0 +1,966 @@
+/*
+ * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+
+int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
+{
+	u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
+	struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg };
+
+	switch (av7110->adac_type) {
+	case DVB_ADAC_MSP34x0:
+		msgs.addr = 0x40;
+		break;
+	case DVB_ADAC_MSP34x5:
+		msgs.addr = 0x42;
+		break;
+	default:
+		return 0;
+	}
+
+	if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
+		dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
+		       av7110->dvb_adapter.num, reg, val);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
+{
+	u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
+	u8 msg2[2];
+	struct i2c_msg msgs[2] = {
+		{ .flags = 0	   , .len = 3, .buf = msg1 },
+		{ .flags = I2C_M_RD, .len = 2, .buf = msg2 }
+	};
+
+	switch (av7110->adac_type) {
+	case DVB_ADAC_MSP34x0:
+		msgs[0].addr = 0x40;
+		msgs[1].addr = 0x40;
+		break;
+	case DVB_ADAC_MSP34x5:
+		msgs[0].addr = 0x42;
+		msgs[1].addr = 0x42;
+		break;
+	default:
+		return 0;
+	}
+
+	if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
+		dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
+		       av7110->dvb_adapter.num, reg);
+		return -EIO;
+	}
+	*val = (msg2[0] << 8) | msg2[1];
+	return 0;
+}
+
+static struct v4l2_input inputs[4] = {
+	{
+		.index		= 0,
+		.name		= "DVB",
+		.type		= V4L2_INPUT_TYPE_CAMERA,
+		.audioset	= 1,
+		.tuner		= 0, /* ignored */
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+		.capabilities	= V4L2_IN_CAP_STD,
+	}, {
+		.index		= 1,
+		.name		= "Television",
+		.type		= V4L2_INPUT_TYPE_TUNER,
+		.audioset	= 1,
+		.tuner		= 0,
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+		.capabilities	= V4L2_IN_CAP_STD,
+	}, {
+		.index		= 2,
+		.name		= "Video",
+		.type		= V4L2_INPUT_TYPE_CAMERA,
+		.audioset	= 0,
+		.tuner		= 0,
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+		.capabilities	= V4L2_IN_CAP_STD,
+	}, {
+		.index		= 3,
+		.name		= "Y/C",
+		.type		= V4L2_INPUT_TYPE_CAMERA,
+		.audioset	= 0,
+		.tuner		= 0,
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+		.capabilities	= V4L2_IN_CAP_STD,
+	}
+};
+
+static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
+{
+	struct av7110 *av7110 = dev->ext_priv;
+	u8 buf[] = { 0x00, reg, data };
+	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 };
+
+	dprintk(4, "dev: %p\n", dev);
+
+	if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
+		return -1;
+	return 0;
+}
+
+static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4])
+{
+	struct av7110 *av7110 = dev->ext_priv;
+	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
+
+	dprintk(4, "dev: %p\n", dev);
+
+	if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
+		return -1;
+	return 0;
+}
+
+static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+	u32 div;
+	u8 config;
+	u8 buf[4];
+
+	dprintk(4, "freq: 0x%08x\n", freq);
+
+	/* magic number: 614. tuning with the frequency given by v4l2
+	   is always off by 614*62.5 = 38375 kHz...*/
+	div = freq + 614;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x8e;
+
+	if (freq < (u32) (16 * 168.25))
+		config = 0xa0;
+	else if (freq < (u32) (16 * 447.25))
+		config = 0x90;
+	else
+		config = 0x30;
+	config &= ~0x02;
+
+	buf[3] = config;
+
+	return tuner_write(dev, 0x61, buf);
+}
+
+static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+	struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
+	u32 div;
+	u8 data[4];
+
+	div = (freq + 38900000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0xce;
+
+	if (freq < 45000000)
+		return -EINVAL;
+	else if (freq < 137000000)
+		data[3] = 0x01;
+	else if (freq < 403000000)
+		data[3] = 0x02;
+	else if (freq < 860000000)
+		data[3] = 0x04;
+	else
+		return -EINVAL;
+
+	if (av7110->fe->ops.i2c_gate_ctrl)
+		av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1);
+	return tuner_write(dev, 0x63, data);
+}
+
+
+
+static struct saa7146_standard analog_standard[];
+static struct saa7146_standard dvb_standard[];
+static struct saa7146_standard standard[];
+
+static struct v4l2_audio msp3400_v4l2_audio = {
+	.index = 0,
+	.name = "Television",
+	.capability = V4L2_AUDCAP_STEREO
+};
+
+static int av7110_dvb_c_switch(struct saa7146_fh *fh)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
+	u16 adswitch;
+	int source, sync, err;
+
+	dprintk(4, "%p\n", av7110);
+
+	if ((vv->video_status & STATUS_OVERLAY) != 0) {
+		vv->ov_suspend = vv->video_fh;
+		err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+		if (err != 0) {
+			dprintk(2, "suspending video failed\n");
+			vv->ov_suspend = NULL;
+		}
+	}
+
+	if (0 != av7110->current_input) {
+		dprintk(1, "switching to analog TV:\n");
+		adswitch = 1;
+		source = SAA7146_HPS_SOURCE_PORT_B;
+		sync = SAA7146_HPS_SYNC_PORT_B;
+		memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
+
+		switch (av7110->current_input) {
+		case 1:
+			dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
+			msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
+			msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
+			msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
+			msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+			msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
+			msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
+
+			if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+				if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
+					dprintk(1, "setting band in demodulator failed\n");
+			} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+				saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
+				saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
+			}
+			if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1)
+				dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+			break;
+		case 2:
+			dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
+			if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
+				dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+			break;
+		case 3:
+			dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
+			if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
+				dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+			break;
+		default:
+			dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
+		}
+	} else {
+		adswitch = 0;
+		source = SAA7146_HPS_SOURCE_PORT_A;
+		sync = SAA7146_HPS_SYNC_PORT_A;
+		memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+		dprintk(1, "switching DVB mode\n");
+		msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+		msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
+				dprintk(1, "setting band in demodulator failed\n");
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
+		}
+	}
+
+	/* hmm, this does not do anything!? */
+	if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch))
+		dprintk(1, "ADSwitch error\n");
+
+	saa7146_set_hps_source_and_sync(dev, source, sync);
+
+	if (vv->ov_suspend != NULL) {
+		saa7146_start_preview(vv->ov_suspend);
+		vv->ov_suspend = NULL;
+	}
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+	u16 stereo_det;
+	s8 stereo;
+
+	dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
+
+	if (!av7110->analog_tuner_flags || t->index != 0)
+		return -EINVAL;
+
+	memset(t, 0, sizeof(*t));
+	strcpy((char *)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 | V4L2_TUNER_CAP_SAP;
+	t->rangelow = 772;	/* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+	t->rangehigh = 13684;	/* 855.25 MHz / 62.5 kHz = 13684 */
+	/* FIXME: add the real signal strength here */
+	t->signal = 0xffff;
+	t->afc = 0;
+
+	/* FIXME: standard / stereo detection is still broken */
+	msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
+	dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
+	msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
+	dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
+	stereo = (s8)(stereo_det >> 8);
+	if (stereo > 0x10) {
+		/* stereo */
+		t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+		t->audmode = V4L2_TUNER_MODE_STEREO;
+	} else if (stereo < -0x10) {
+		/* bilingual */
+		t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+		t->audmode = V4L2_TUNER_MODE_LANG1;
+	} else /* mono */
+		t->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+	u16 fm_matrix, src;
+	dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
+
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
+
+	switch (t->audmode) {
+	case V4L2_TUNER_MODE_STEREO:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
+		fm_matrix = 0x3001; /* stereo */
+		src = 0x0020;
+		break;
+	case V4L2_TUNER_MODE_LANG1_LANG2:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
+		fm_matrix = 0x3000; /* bilingual */
+		src = 0x0020;
+		break;
+	case V4L2_TUNER_MODE_LANG1:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0000;
+		break;
+	case V4L2_TUNER_MODE_LANG2:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0010;
+		break;
+	default: /* case V4L2_TUNER_MODE_MONO: */
+		dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0030;
+		break;
+	}
+	msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
+	msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
+
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
+
+	memset(f, 0, sizeof(*f));
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency =	av7110->current_freq;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
+
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
+
+	if (V4L2_TUNER_ANALOG_TV != f->type)
+		return -EINVAL;
+
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
+
+	/* tune in desired frequency */
+	if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
+		ves1820_set_tv_freq(dev, f->frequency);
+	else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
+		stv0297_set_tv_freq(dev, f->frequency);
+	av7110->current_freq = f->frequency;
+
+	msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
+
+	if (av7110->analog_tuner_flags) {
+		if (i->index >= 4)
+			return -EINVAL;
+	} else {
+		if (i->index != 0)
+			return -EINVAL;
+	}
+
+	memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	*input = av7110->current_input;
+	dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
+
+	if (!av7110->analog_tuner_flags)
+		return input ? -EINVAL : 0;
+
+	if (input >= 4)
+		return -EINVAL;
+
+	av7110->current_input = input;
+	return av7110_dvb_c_switch(fh);
+}
+
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+	if (a->index != 0)
+		return -EINVAL;
+	*a = msp3400_v4l2_audio;
+	return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+	if (a->index != 0)
+		return -EINVAL;
+	if (av7110->current_input >= 2)
+		return -EINVAL;
+	*a = msp3400_v4l2_audio;
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
+	if (av7110->current_input >= 2)
+		return -EINVAL;
+	return a->index ? -EINVAL : 0;
+}
+
+static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_sliced_vbi_cap *cap)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
+	if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+		return -EINVAL;
+	if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+		cap->service_set = V4L2_SLICED_WSS_625;
+		cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+	}
+	return 0;
+}
+
+static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_FMT:\n");
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return -EINVAL;
+	memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+	if (av7110->wssMode) {
+		f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+	}
+	return 0;
+}
+
+static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_FMT\n");
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return -EINVAL;
+	if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
+	    f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
+		memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+		/* WSS controlled by firmware */
+		av7110->wssMode = 0;
+		av7110->wssData = 0;
+		return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+				     SetWSSConfig, 1, 0);
+	} else {
+		memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+		f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+		/* WSS controlled by userspace */
+		av7110->wssMode = 1;
+		av7110->wssData = 0;
+	}
+	return 0;
+}
+
+static int av7110_vbi_reset(struct file *file)
+{
+	struct saa7146_fh *fh = file->private_data;
+	struct saa7146_dev *dev = fh->dev;
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+
+	dprintk(2, "%s\n", __func__);
+	av7110->wssMode = 0;
+	av7110->wssData = 0;
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return 0;
+	else
+		return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
+}
+
+static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+	struct saa7146_fh *fh = file->private_data;
+	struct saa7146_dev *dev = fh->dev;
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+	struct v4l2_sliced_vbi_data d;
+	int rc;
+
+	dprintk(2, "%s\n", __func__);
+	if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d)
+		return -EINVAL;
+	if (copy_from_user(&d, data, count))
+		return -EFAULT;
+	if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
+		return -EINVAL;
+	if (d.id)
+		av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
+	else
+		av7110->wssData = 0x8000;
+	rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData);
+	return (rc < 0) ? rc : count;
+}
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static u8 saa7113_init_regs[] = {
+	0x02, 0xd0,
+	0x03, 0x23,
+	0x04, 0x00,
+	0x05, 0x00,
+	0x06, 0xe9,
+	0x07, 0x0d,
+	0x08, 0x98,
+	0x09, 0x02,
+	0x0a, 0x80,
+	0x0b, 0x40,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x01,
+	0x0f, 0x7c,
+	0x10, 0x48,
+	0x11, 0x0c,
+	0x12, 0x8b,
+	0x13, 0x1a,
+	0x14, 0x00,
+	0x15, 0x00,
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1b, 0x00,
+	0x1c, 0x00,
+	0x1d, 0x00,
+	0x1e, 0x00,
+
+	0x41, 0x77,
+	0x42, 0x77,
+	0x43, 0x77,
+	0x44, 0x77,
+	0x45, 0x77,
+	0x46, 0x77,
+	0x47, 0x77,
+	0x48, 0x77,
+	0x49, 0x77,
+	0x4a, 0x77,
+	0x4b, 0x77,
+	0x4c, 0x77,
+	0x4d, 0x77,
+	0x4e, 0x77,
+	0x4f, 0x77,
+	0x50, 0x77,
+	0x51, 0x77,
+	0x52, 0x77,
+	0x53, 0x77,
+	0x54, 0x77,
+	0x55, 0x77,
+	0x56, 0x77,
+	0x57, 0xff,
+
+	0xff
+};
+
+
+static struct saa7146_ext_vv av7110_vv_data_st;
+static struct saa7146_ext_vv av7110_vv_data_c;
+
+int av7110_init_analog_module(struct av7110 *av7110)
+{
+	u16 version1, version2;
+
+	if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
+	    i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
+		pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
+			av7110->dvb_adapter.num);
+		av7110->adac_type = DVB_ADAC_MSP34x0;
+	} else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
+		   i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
+		pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
+			av7110->dvb_adapter.num);
+		av7110->adac_type = DVB_ADAC_MSP34x5;
+	} else
+		return -ENODEV;
+
+	msleep(100); // the probing above resets the msp...
+	msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
+	msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
+	dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n",
+		av7110->dvb_adapter.num, version1, version2);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+	msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
+	msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+	msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
+
+	if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
+		pr_info("saa7113 not accessible\n");
+	} else {
+		u8 *i = saa7113_init_regs;
+
+		if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
+			/* Fujitsu/Siemens DVB-Cable */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+		} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) {
+			/* Hauppauge/TT DVB-C premium */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+		} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) {
+			/* Hauppauge/TT DVB-C premium */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297;
+		}
+
+		/* setup for DVB by default */
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
+				dprintk(1, "setting band in demodulator failed\n");
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
+			saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
+		}
+
+		/* init the saa7113 */
+		while (*i != 0xff) {
+			if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) {
+				dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num);
+				break;
+			}
+			i += 2;
+		}
+		/* setup msp for analog sound: B/G Dual-FM */
+		msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001,  3); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  4); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  0); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  3); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG
+		msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz
+		msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI
+		msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz
+		msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI
+		msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2
+	}
+
+	memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+	/* set dd1 stream a & b */
+	saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(av7110->dev, DD1_INIT, 0x03000700);
+	saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	return 0;
+}
+
+int av7110_init_v4l(struct av7110 *av7110)
+{
+	struct saa7146_dev* dev = av7110->dev;
+	struct saa7146_ext_vv *vv_data;
+	int ret;
+
+	/* special case DVB-C: these cards have an analog tuner
+	   plus need some special handling, so we have separate
+	   saa7146_ext_vv data for these... */
+	if (av7110->analog_tuner_flags)
+		vv_data = &av7110_vv_data_c;
+	else
+		vv_data = &av7110_vv_data_st;
+	ret = saa7146_vv_init(dev, vv_data);
+
+	if (ret) {
+		ERR("cannot init capture device. skipping\n");
+		return -ENODEV;
+	}
+	vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data->vid_ops.vidioc_g_input = vidioc_g_input;
+	vv_data->vid_ops.vidioc_s_input = vidioc_s_input;
+	vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+	vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+	vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+	vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+	vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+	vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio;
+	vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio;
+	vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL;
+
+	vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner;
+	vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner;
+	vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency;
+	vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency;
+	vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL;
+	vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
+	vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
+	vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
+
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT;
+
+	if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
+		ERR("cannot register capture device. skipping\n");
+		saa7146_vv_release(dev);
+		return -ENODEV;
+	}
+	if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+		if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
+			ERR("cannot register vbi v4l2 device. skipping\n");
+	}
+	return 0;
+}
+
+int av7110_exit_v4l(struct av7110 *av7110)
+{
+	struct saa7146_dev* dev = av7110->dev;
+
+	saa7146_unregister_device(&av7110->v4l_dev, av7110->dev);
+	saa7146_unregister_device(&av7110->vbi_dev, av7110->dev);
+
+	saa7146_vv_release(dev);
+
+	return 0;
+}
+
+
+
+/* FIXME: these values are experimental values that look better than the
+   values from the latest "official" driver -- at least for me... (MiHu) */
+static struct saa7146_standard standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x15,	.v_field	= 288,
+		.h_offset	= 0x48,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+static struct saa7146_standard analog_standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x1b,	.v_field	= 288,
+		.h_offset	= 0x08,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+static struct saa7146_standard dvb_standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x14,	.v_field	= 288,
+		.h_offset	= 0x48,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+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 = AV7110_VIDEO_MODE_PAL;
+		av7110_set_vidmode(av7110, av7110->vidmode);
+	}
+	else if (std->id & V4L2_STD_NTSC) {
+		av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
+		av7110_set_vidmode(av7110, av7110->vidmode);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+
+static struct saa7146_ext_vv av7110_vv_data_st = {
+	.inputs		= 1,
+	.audios		= 1,
+	.capabilities	= V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
+	.flags		= 0,
+
+	.stds		= &standard[0],
+	.num_stds	= ARRAY_SIZE(standard),
+	.std_callback	= &std_callback,
+
+	.vbi_fops.open	= av7110_vbi_reset,
+	.vbi_fops.release = av7110_vbi_reset,
+	.vbi_fops.write	= av7110_vbi_write,
+};
+
+static struct saa7146_ext_vv av7110_vv_data_c = {
+	.inputs		= 1,
+	.audios		= 1,
+	.capabilities	= V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
+	.flags		= SAA7146_USE_PORT_B_FOR_VBI,
+
+	.stds		= &standard[0],
+	.num_stds	= ARRAY_SIZE(standard),
+	.std_callback	= &std_callback,
+
+	.vbi_fops.open	= av7110_vbi_reset,
+	.vbi_fops.release = av7110_vbi_reset,
+	.vbi_fops.write	= av7110_vbi_write,
+};
+
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
new file mode 100644
index 000000000000..12ddb53c58dc
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -0,0 +1,1640 @@
+/*
+ * budget-av.c: driver for the SAA7146 based Budget DVB cards
+ *              with analog video in
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ *                               Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "budget.h"
+#include "stv0299.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "tda8261.h"
+#include "tda8261_cfg.h"
+#include "tda1002x.h"
+#include "tda1004x.h"
+#include "tua6100.h"
+#include "dvb-pll.h"
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+
+#include "dvb_ca_en50221.h"
+
+#define DEBICICAM		0x02420000
+
+#define SLOTSTATUS_NONE         1
+#define SLOTSTATUS_PRESENT      2
+#define SLOTSTATUS_RESET        4
+#define SLOTSTATUS_READY        8
+#define SLOTSTATUS_OCCUPIED     (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct budget_av {
+	struct budget budget;
+	struct video_device *vd;
+	int cur_input;
+	int has_saa7113;
+	struct tasklet_struct ciintf_irq_tasklet;
+	int slot_status;
+	struct dvb_ca_en50221 ca;
+	u8 reinitialise_demod:1;
+};
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
+
+
+/* GPIO Connections:
+ * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*!
+ * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory
+ * 2 - CI Card Enable (Active Low)
+ * 3 - CI Card Detect
+ */
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg)
+{
+	u8 mm1[] = { 0x00 };
+	u8 mm2[] = { 0x00 };
+	struct i2c_msg msgs[2];
+
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+	msgs[0].addr = msgs[1].addr = id / 2;
+	mm1[0] = reg;
+	msgs[0].len = 1;
+	msgs[1].len = 1;
+	msgs[0].buf = mm1;
+	msgs[1].buf = mm2;
+
+	i2c_transfer(i2c, msgs, 2);
+
+	return mm2[0];
+}
+
+static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len)
+{
+	u8 mm1[] = { reg };
+	struct i2c_msg msgs[2] = {
+		{.addr = id / 2,.flags = 0,.buf = mm1,.len = 1},
+		{.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len}
+	};
+
+	if (i2c_transfer(i2c, msgs, 2) != 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val)
+{
+	u8 msg[2] = { reg, val };
+	struct i2c_msg msgs;
+
+	msgs.flags = 0;
+	msgs.addr = id / 2;
+	msgs.len = 2;
+	msgs.buf = msg;
+	return i2c_transfer(i2c, &msgs, 1);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+	udelay(1);
+
+	result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1);
+	if (result == -ETIMEDOUT) {
+		ciintf_slot_shutdown(ca, slot);
+		pr_info("cam ejected 1\n");
+	}
+	return result;
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+	udelay(1);
+
+	result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1);
+	if (result == -ETIMEDOUT) {
+		ciintf_slot_shutdown(ca, slot);
+		pr_info("cam ejected 2\n");
+	}
+	return result;
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+	udelay(1);
+
+	result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
+	if (result == -ETIMEDOUT) {
+		ciintf_slot_shutdown(ca, slot);
+		pr_info("cam ejected 3\n");
+		return -ETIMEDOUT;
+	}
+	return result;
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+	udelay(1);
+
+	result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
+	if (result == -ETIMEDOUT) {
+		ciintf_slot_shutdown(ca, slot);
+		pr_info("cam ejected 5\n");
+	}
+	return result;
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_reset\n");
+	budget_av->slot_status = SLOTSTATUS_RESET;
+
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */
+
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */
+	msleep(2);
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */
+	msleep(20); /* 20 ms Vcc settling time */
+
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	msleep(20);
+
+	/* reinitialise the frontend if necessary */
+	if (budget_av->reinitialise_demod)
+		dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
+
+	return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_shutdown\n");
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	budget_av->slot_status = SLOTSTATUS_NONE;
+
+	return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status);
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+
+	return 0;
+}
+
+static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	/* test the card detect line - needs to be done carefully
+	 * since it never goes high for some CAMs on this interface (e.g. topuptv) */
+	if (budget_av->slot_status == SLOTSTATUS_NONE) {
+		saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+		udelay(1);
+		if (saa7146_read(saa, PSR) & MASK_06) {
+			if (budget_av->slot_status == SLOTSTATUS_NONE) {
+				budget_av->slot_status = SLOTSTATUS_PRESENT;
+				pr_info("cam inserted A\n");
+			}
+		}
+		saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+	}
+
+	/* We also try and read from IO memory to work round the above detection bug. If
+	 * there is no CAM, we will get a timeout. Only done if there is no cam
+	 * present, since this test actually breaks some cams :(
+	 *
+	 * if the CI interface is not open, we also do the above test since we
+	 * don't care if the cam has problems - we'll be resetting it on open() anyway */
+	if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) {
+		saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+		result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1);
+		if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) {
+			budget_av->slot_status = SLOTSTATUS_PRESENT;
+			pr_info("cam inserted B\n");
+		} else if (result < 0) {
+			if (budget_av->slot_status != SLOTSTATUS_NONE) {
+				ciintf_slot_shutdown(ca, slot);
+				pr_info("cam ejected 5\n");
+				return 0;
+			}
+		}
+	}
+
+	/* read from attribute memory in reset/ready state to know when the CAM is ready */
+	if (budget_av->slot_status == SLOTSTATUS_RESET) {
+		result = ciintf_read_attribute_mem(ca, slot, 0);
+		if (result == 0x1d) {
+			budget_av->slot_status = SLOTSTATUS_READY;
+		}
+	}
+
+	/* work out correct return code */
+	if (budget_av->slot_status != SLOTSTATUS_NONE) {
+		if (budget_av->slot_status & SLOTSTATUS_READY) {
+			return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+		}
+		return DVB_CA_EN50221_POLL_CAM_PRESENT;
+	}
+	return 0;
+}
+
+static int ciintf_init(struct budget_av *budget_av)
+{
+	struct saa7146_dev *saa = budget_av->budget.dev;
+	int result;
+
+	memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221));
+
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+
+	/* Enable DEBI pins */
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
+
+	/* register CI interface */
+	budget_av->ca.owner = THIS_MODULE;
+	budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem;
+	budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem;
+	budget_av->ca.read_cam_control = ciintf_read_cam_control;
+	budget_av->ca.write_cam_control = ciintf_write_cam_control;
+	budget_av->ca.slot_reset = ciintf_slot_reset;
+	budget_av->ca.slot_shutdown = ciintf_slot_shutdown;
+	budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable;
+	budget_av->ca.poll_slot_status = ciintf_poll_slot_status;
+	budget_av->ca.data = budget_av;
+	budget_av->budget.ci_present = 1;
+	budget_av->slot_status = SLOTSTATUS_NONE;
+
+	if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter,
+					  &budget_av->ca, 0, 1)) != 0) {
+		pr_err("ci initialisation failed\n");
+		goto error;
+	}
+
+	pr_info("ci interface initialised\n");
+	return 0;
+
+error:
+	saa7146_write(saa, MC1, MASK_27);
+	return result;
+}
+
+static void ciintf_deinit(struct budget_av *budget_av)
+{
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+
+	/* release the CA device */
+	dvb_ca_en50221_release(&budget_av->ca);
+
+	/* disable DEBI pins */
+	saa7146_write(saa, MC1, MASK_27);
+}
+
+
+static const u8 saa7113_tab[] = {
+	0x01, 0x08,
+	0x02, 0xc0,
+	0x03, 0x33,
+	0x04, 0x00,
+	0x05, 0x00,
+	0x06, 0xeb,
+	0x07, 0xe0,
+	0x08, 0x28,
+	0x09, 0x00,
+	0x0a, 0x80,
+	0x0b, 0x47,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x01,
+	0x0f, 0x44,
+
+	0x10, 0x08,
+	0x11, 0x0c,
+	0x12, 0x7b,
+	0x13, 0x00,
+	0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+
+	0x57, 0xff,
+	0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07,
+	0x5b, 0x83, 0x5e, 0x00,
+	0xff
+};
+
+static int saa7113_init(struct budget_av *budget_av)
+{
+	struct budget *budget = &budget_av->budget;
+	struct saa7146_dev *saa = budget->dev;
+	const u8 *data = saa7113_tab;
+
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI);
+	msleep(200);
+
+	if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) {
+		dprintk(1, "saa7113 not found on KNC card\n");
+		return -ENODEV;
+	}
+
+	dprintk(1, "saa7113 detected and initializing\n");
+
+	while (*data != 0xff) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1));
+		data += 2;
+	}
+
+	dprintk(1, "saa7113  status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f));
+
+	return 0;
+}
+
+static int saa7113_setinput(struct budget_av *budget_av, int input)
+{
+	struct budget *budget = &budget_av->budget;
+
+	if (1 != budget_av->has_saa7113)
+		return -ENODEV;
+
+	if (input == 1) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7);
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80);
+	} else if (input == 0) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0);
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00);
+	} else
+		return -EINVAL;
+
+	budget_av->cur_input = input;
+	return 0;
+}
+
+
+static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+	u8 m1;
+
+	aclk = 0xb5;
+	if (srate < 2000000)
+		bclk = 0x86;
+	else if (srate < 5000000)
+		bclk = 0x89;
+	else if (srate < 15000000)
+		bclk = 0x8f;
+	else if (srate < 45000000)
+		bclk = 0x95;
+
+	m1 = 0x14;
+	if (srate < 4000000)
+		m1 = 0x10;
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+	stv0299_writereg(fe, 0x0f, 0x80 | m1);
+
+	return 0;
+}
+
+static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u32 div;
+	u8 buf[4];
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((c->frequency < 950000) || (c->frequency > 2150000))
+		return -EINVAL;
+
+	div = (c->frequency + (125 - 1)) / 125;	/* round correctly */
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	buf[3] = 0x20;
+
+	if (c->symbol_rate < 4000000)
+		buf[3] |= 1;
+
+	if (c->frequency < 1250000)
+		buf[3] |= 0;
+	else if (c->frequency < 1550000)
+		buf[3] |= 0x40;
+	else if (c->frequency < 2050000)
+		buf[3] |= 0x80;
+	else if (c->frequency < 2150000)
+		buf[3] |= 0xC0;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static u8 typhoon_cinergy1200s_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x7d,		/* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,		/* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,		/* DAC not used, set to high impendance mode */
+	0x07, 0x00,		/* DAC LSB */
+	0x08, 0x40,		/* DiSEqC off */
+	0x09, 0x00,		/* FIFO */
+	0x0c, 0x51,		/* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,		/* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,		/* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,		// AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb9,
+	0x15, 0xc9,		// lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,		// out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,		// 1/2 threshold
+	0x2a, 0x14,		// 2/3 threshold
+	0x2b, 0x0f,		// 3/4 threshold
+	0x2c, 0x09,		// 5/6 threshold
+	0x2d, 0x05,		// 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,		// test all FECs
+	0x32, 0x19,		// viterbi and synchro search
+	0x33, 0xfc,		// rs control
+	0x34, 0x93,		// error control
+	0x0f, 0x92,
+	0xff, 0xff
+};
+
+static struct stv0299_config typhoon_config = {
+	.demod_address = 0x68,
+	.inittab = typhoon_cinergy1200s_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+
+static struct stv0299_config cinergy_1200s_config = {
+	.demod_address = 0x68,
+	.inittab = typhoon_cinergy1200s_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_0,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+static struct stv0299_config cinergy_1200s_1894_0010_config = {
+	.demod_address = 0x68,
+	.inittab = typhoon_cinergy1200s_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+};
+
+static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	u8 buf[6];
+	struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+	int i;
+
+#define CU1216_IF 36125000
+#define TUNER_MUL 62500
+
+	u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0xce;
+	buf[3] = (c->frequency < 150000000 ? 0x01 :
+		  c->frequency < 445000000 ? 0x02 : 0x04);
+	buf[4] = 0xde;
+	buf[5] = 0x20;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
+	/* wait for the pll lock */
+	msg.flags = I2C_M_RD;
+	msg.len = 1;
+	for (i = 0; i < 20; i++) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40))
+			break;
+		msleep(10);
+	}
+
+	/* switch the charge pump to the lower current */
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = &buf[2];
+	buf[2] &= ~0x40;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static struct tda1002x_config philips_cu1216_config = {
+	.demod_address = 0x0c,
+	.invert = 1,
+};
+
+static struct tda1002x_config philips_cu1216_config_altaddress = {
+	.demod_address = 0x0d,
+	.invert = 0,
+};
+
+static struct tda10023_config philips_cu1216_tda10023_config = {
+	.demod_address = 0x0c,
+	.invert = 1,
+};
+
+static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+	// setup PLL configuration
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	return 0;
+}
+
+static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
+			sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = c->frequency + 36166000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (c->frequency < 49000000)
+		return -EINVAL;
+	else if (c->frequency < 161000000)
+		band = 1;
+	else if (c->frequency < 444000000)
+		band = 2;
+	else if (c->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter
+	switch (c->bandwidth_hz) {
+	case 6000000:
+		filter = 0;
+		break;
+
+	case 7000000:
+		filter = 0;
+		break;
+
+	case 8000000:
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+	return 0;
+}
+
+static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
+					   const struct firmware **fw, char *name)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+
+	return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tu1216_config = {
+
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 1,
+	.xtal_freq = TDA10046_XTAL_4M,
+	.agc_config = TDA10046_AGC_DEFAULT,
+	.if_freq = TDA10046_FREQ_3617,
+	.request_firmware = philips_tu1216_request_firmware,
+};
+
+static u8 philips_sd1878_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x7d,
+	0x05, 0x35,
+	0x06, 0x40,
+	0x07, 0x00,
+	0x08, 0x43,
+	0x09, 0x02,
+	0x0C, 0x51,
+	0x0D, 0x82,
+	0x0E, 0x23,
+	0x10, 0x3f,
+	0x11, 0x84,
+	0x12, 0xb9,
+	0x15, 0xc9,
+	0x16, 0x19,
+	0x17, 0x8c,
+	0x18, 0x59,
+	0x19, 0xf8,
+	0x1a, 0xfe,
+	0x1c, 0x7f,
+	0x1d, 0x00,
+	0x1e, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,
+	0x29, 0x28,
+	0x2a, 0x14,
+	0x2b, 0x0f,
+	0x2c, 0x09,
+	0x2d, 0x09,
+	0x31, 0x1f,
+	0x32, 0x19,
+	0x33, 0xfc,
+	0x34, 0x93,
+	0xff, 0xff
+};
+
+static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe,
+		u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+	u8 m1;
+
+	aclk = 0xb5;
+	if (srate < 2000000)
+		bclk = 0x86;
+	else if (srate < 5000000)
+		bclk = 0x89;
+	else if (srate < 15000000)
+		bclk = 0x8f;
+	else if (srate < 45000000)
+		bclk = 0x95;
+
+	m1 = 0x14;
+	if (srate < 4000000)
+		m1 = 0x10;
+
+	stv0299_writereg(fe, 0x0e, 0x23);
+	stv0299_writereg(fe, 0x0f, 0x94);
+	stv0299_writereg(fe, 0x10, 0x39);
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x15, 0xc9);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+	stv0299_writereg(fe, 0x0f, 0x80 | m1);
+
+	return 0;
+}
+
+static struct stv0299_config philips_sd1878_config = {
+	.demod_address = 0x68,
+     .inittab = philips_sd1878_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0299_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_sd1878_ci_set_symbol_rate,
+};
+
+/* KNC1 DVB-S (STB0899) Inittab	*/
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
+
+	{ STB0899_DEV_ID		, 0x81 },
+	{ STB0899_DISCNTRL1		, 0x32 },
+	{ STB0899_DISCNTRL2		, 0x80 },
+	{ STB0899_DISRX_ST0		, 0x04 },
+	{ STB0899_DISRX_ST1		, 0x00 },
+	{ STB0899_DISPARITY		, 0x00 },
+	{ STB0899_DISSTATUS		, 0x20 },
+	{ STB0899_DISF22		, 0x8c },
+	{ STB0899_DISF22RX		, 0x9a },
+	{ STB0899_SYSREG		, 0x0b },
+	{ STB0899_ACRPRESC		, 0x11 },
+	{ STB0899_ACRDIV1		, 0x0a },
+	{ STB0899_ACRDIV2		, 0x05 },
+	{ STB0899_DACR1			, 0x00 },
+	{ STB0899_DACR2			, 0x00 },
+	{ STB0899_OUTCFG		, 0x00 },
+	{ STB0899_MODECFG		, 0x00 },
+	{ STB0899_IRQSTATUS_3		, 0x30 },
+	{ STB0899_IRQSTATUS_2		, 0x00 },
+	{ STB0899_IRQSTATUS_1		, 0x00 },
+	{ STB0899_IRQSTATUS_0		, 0x00 },
+	{ STB0899_IRQMSK_3		, 0xf3 },
+	{ STB0899_IRQMSK_2		, 0xfc },
+	{ STB0899_IRQMSK_1		, 0xff },
+	{ STB0899_IRQMSK_0		, 0xff },
+	{ STB0899_IRQCFG		, 0x00 },
+	{ STB0899_I2CCFG		, 0x88 },
+	{ STB0899_I2CRPT		, 0x58 }, /* Repeater=8, Stop=disabled */
+	{ STB0899_IOPVALUE5		, 0x00 },
+	{ STB0899_IOPVALUE4		, 0x20 },
+	{ STB0899_IOPVALUE3		, 0xc9 },
+	{ STB0899_IOPVALUE2		, 0x90 },
+	{ STB0899_IOPVALUE1		, 0x40 },
+	{ STB0899_IOPVALUE0		, 0x00 },
+	{ STB0899_GPIO00CFG		, 0x82 },
+	{ STB0899_GPIO01CFG		, 0x82 },
+	{ STB0899_GPIO02CFG		, 0x82 },
+	{ STB0899_GPIO03CFG		, 0x82 },
+	{ STB0899_GPIO04CFG		, 0x82 },
+	{ STB0899_GPIO05CFG		, 0x82 },
+	{ STB0899_GPIO06CFG		, 0x82 },
+	{ STB0899_GPIO07CFG		, 0x82 },
+	{ STB0899_GPIO08CFG		, 0x82 },
+	{ STB0899_GPIO09CFG		, 0x82 },
+	{ STB0899_GPIO10CFG		, 0x82 },
+	{ STB0899_GPIO11CFG		, 0x82 },
+	{ STB0899_GPIO12CFG		, 0x82 },
+	{ STB0899_GPIO13CFG		, 0x82 },
+	{ STB0899_GPIO14CFG		, 0x82 },
+	{ STB0899_GPIO15CFG		, 0x82 },
+	{ STB0899_GPIO16CFG		, 0x82 },
+	{ STB0899_GPIO17CFG		, 0x82 },
+	{ STB0899_GPIO18CFG		, 0x82 },
+	{ STB0899_GPIO19CFG		, 0x82 },
+	{ STB0899_GPIO20CFG		, 0x82 },
+	{ STB0899_SDATCFG		, 0xb8 },
+	{ STB0899_SCLTCFG		, 0xba },
+	{ STB0899_AGCRFCFG		, 0x08 }, /* 0x1c */
+	{ STB0899_GPIO22		, 0x82 }, /* AGCBB2CFG */
+	{ STB0899_GPIO21		, 0x91 }, /* AGCBB1CFG */
+	{ STB0899_DIRCLKCFG		, 0x82 },
+	{ STB0899_CLKOUT27CFG		, 0x7e },
+	{ STB0899_STDBYCFG		, 0x82 },
+	{ STB0899_CS0CFG		, 0x82 },
+	{ STB0899_CS1CFG		, 0x82 },
+	{ STB0899_DISEQCOCFG		, 0x20 },
+	{ STB0899_GPIO32CFG		, 0x82 },
+	{ STB0899_GPIO33CFG		, 0x82 },
+	{ STB0899_GPIO34CFG		, 0x82 },
+	{ STB0899_GPIO35CFG		, 0x82 },
+	{ STB0899_GPIO36CFG		, 0x82 },
+	{ STB0899_GPIO37CFG		, 0x82 },
+	{ STB0899_GPIO38CFG		, 0x82 },
+	{ STB0899_GPIO39CFG		, 0x82 },
+	{ STB0899_NCOARSE		, 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+	{ STB0899_SYNTCTRL		, 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+	{ STB0899_FILTCTRL		, 0x00 },
+	{ STB0899_SYSCTRL		, 0x00 },
+	{ STB0899_STOPCLK1		, 0x20 },
+	{ STB0899_STOPCLK2		, 0x00 },
+	{ STB0899_INTBUFSTATUS		, 0x00 },
+	{ STB0899_INTBUFCTRL		, 0x0a },
+	{ 0xffff			, 0xff },
+};
+
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
+	{ STB0899_DEMOD			, 0x00 },
+	{ STB0899_RCOMPC		, 0xc9 },
+	{ STB0899_AGC1CN		, 0x41 },
+	{ STB0899_AGC1REF		, 0x08 },
+	{ STB0899_RTC			, 0x7a },
+	{ STB0899_TMGCFG		, 0x4e },
+	{ STB0899_AGC2REF		, 0x33 },
+	{ STB0899_TLSR			, 0x84 },
+	{ STB0899_CFD			, 0xee },
+	{ STB0899_ACLC			, 0x87 },
+	{ STB0899_BCLC			, 0x94 },
+	{ STB0899_EQON			, 0x41 },
+	{ STB0899_LDT			, 0xdd },
+	{ STB0899_LDT2			, 0xc9 },
+	{ STB0899_EQUALREF		, 0xb4 },
+	{ STB0899_TMGRAMP		, 0x10 },
+	{ STB0899_TMGTHD		, 0x30 },
+	{ STB0899_IDCCOMP		, 0xfb },
+	{ STB0899_QDCCOMP		, 0x03 },
+	{ STB0899_POWERI		, 0x3b },
+	{ STB0899_POWERQ		, 0x3d },
+	{ STB0899_RCOMP			, 0x81 },
+	{ STB0899_AGCIQIN		, 0x80 },
+	{ STB0899_AGC2I1		, 0x04 },
+	{ STB0899_AGC2I2		, 0xf5 },
+	{ STB0899_TLIR			, 0x25 },
+	{ STB0899_RTF			, 0x80 },
+	{ STB0899_DSTATUS		, 0x00 },
+	{ STB0899_LDI			, 0xca },
+	{ STB0899_CFRM			, 0xf1 },
+	{ STB0899_CFRL			, 0xf3 },
+	{ STB0899_NIRM			, 0x2a },
+	{ STB0899_NIRL			, 0x05 },
+	{ STB0899_ISYMB			, 0x17 },
+	{ STB0899_QSYMB			, 0xfa },
+	{ STB0899_SFRH			, 0x2f },
+	{ STB0899_SFRM			, 0x68 },
+	{ STB0899_SFRL			, 0x40 },
+	{ STB0899_SFRUPH		, 0x2f },
+	{ STB0899_SFRUPM		, 0x68 },
+	{ STB0899_SFRUPL		, 0x40 },
+	{ STB0899_EQUAI1		, 0xfd },
+	{ STB0899_EQUAQ1		, 0x04 },
+	{ STB0899_EQUAI2		, 0x0f },
+	{ STB0899_EQUAQ2		, 0xff },
+	{ STB0899_EQUAI3		, 0xdf },
+	{ STB0899_EQUAQ3		, 0xfa },
+	{ STB0899_EQUAI4		, 0x37 },
+	{ STB0899_EQUAQ4		, 0x0d },
+	{ STB0899_EQUAI5		, 0xbd },
+	{ STB0899_EQUAQ5		, 0xf7 },
+	{ STB0899_DSTATUS2		, 0x00 },
+	{ STB0899_VSTATUS		, 0x00 },
+	{ STB0899_VERROR		, 0xff },
+	{ STB0899_IQSWAP		, 0x2a },
+	{ STB0899_ECNT1M		, 0x00 },
+	{ STB0899_ECNT1L		, 0x00 },
+	{ STB0899_ECNT2M		, 0x00 },
+	{ STB0899_ECNT2L		, 0x00 },
+	{ STB0899_ECNT3M		, 0x00 },
+	{ STB0899_ECNT3L		, 0x00 },
+	{ STB0899_FECAUTO1		, 0x06 },
+	{ STB0899_FECM			, 0x01 },
+	{ STB0899_VTH12			, 0xf0 },
+	{ STB0899_VTH23			, 0xa0 },
+	{ STB0899_VTH34			, 0x78 },
+	{ STB0899_VTH56			, 0x4e },
+	{ STB0899_VTH67			, 0x48 },
+	{ STB0899_VTH78			, 0x38 },
+	{ STB0899_PRVIT			, 0xff },
+	{ STB0899_VITSYNC		, 0x19 },
+	{ STB0899_RSULC			, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+	{ STB0899_TSULC			, 0x42 },
+	{ STB0899_RSLLC			, 0x40 },
+	{ STB0899_TSLPL			, 0x12 },
+	{ STB0899_TSCFGH		, 0x0c },
+	{ STB0899_TSCFGM		, 0x00 },
+	{ STB0899_TSCFGL		, 0x0c },
+	{ STB0899_TSOUT			, 0x4d }, /* 0x0d for CAM */
+	{ STB0899_RSSYNCDEL		, 0x00 },
+	{ STB0899_TSINHDELH		, 0x02 },
+	{ STB0899_TSINHDELM		, 0x00 },
+	{ STB0899_TSINHDELL		, 0x00 },
+	{ STB0899_TSLLSTKM		, 0x00 },
+	{ STB0899_TSLLSTKL		, 0x00 },
+	{ STB0899_TSULSTKM		, 0x00 },
+	{ STB0899_TSULSTKL		, 0xab },
+	{ STB0899_PCKLENUL		, 0x00 },
+	{ STB0899_PCKLENLL		, 0xcc },
+	{ STB0899_RSPCKLEN		, 0xcc },
+	{ STB0899_TSSTATUS		, 0x80 },
+	{ STB0899_ERRCTRL1		, 0xb6 },
+	{ STB0899_ERRCTRL2		, 0x96 },
+	{ STB0899_ERRCTRL3		, 0x89 },
+	{ STB0899_DMONMSK1		, 0x27 },
+	{ STB0899_DMONMSK0		, 0x03 },
+	{ STB0899_DEMAPVIT		, 0x5c },
+	{ STB0899_PLPARM		, 0x1f },
+	{ STB0899_PDELCTRL		, 0x48 },
+	{ STB0899_PDELCTRL2		, 0x00 },
+	{ STB0899_BBHCTRL1		, 0x00 },
+	{ STB0899_BBHCTRL2		, 0x00 },
+	{ STB0899_HYSTTHRESH		, 0x77 },
+	{ STB0899_MATCSTM		, 0x00 },
+	{ STB0899_MATCSTL		, 0x00 },
+	{ STB0899_UPLCSTM		, 0x00 },
+	{ STB0899_UPLCSTL		, 0x00 },
+	{ STB0899_DFLCSTM		, 0x00 },
+	{ STB0899_DFLCSTL		, 0x00 },
+	{ STB0899_SYNCCST		, 0x00 },
+	{ STB0899_SYNCDCSTM		, 0x00 },
+	{ STB0899_SYNCDCSTL		, 0x00 },
+	{ STB0899_ISI_ENTRY		, 0x00 },
+	{ STB0899_ISI_BIT_EN		, 0x00 },
+	{ STB0899_MATSTRM		, 0x00 },
+	{ STB0899_MATSTRL		, 0x00 },
+	{ STB0899_UPLSTRM		, 0x00 },
+	{ STB0899_UPLSTRL		, 0x00 },
+	{ STB0899_DFLSTRM		, 0x00 },
+	{ STB0899_DFLSTRL		, 0x00 },
+	{ STB0899_SYNCSTR		, 0x00 },
+	{ STB0899_SYNCDSTRM		, 0x00 },
+	{ STB0899_SYNCDSTRL		, 0x00 },
+	{ STB0899_CFGPDELSTATUS1	, 0x10 },
+	{ STB0899_CFGPDELSTATUS2	, 0x00 },
+	{ STB0899_BBFERRORM		, 0x00 },
+	{ STB0899_BBFERRORL		, 0x00 },
+	{ STB0899_UPKTERRORM		, 0x00 },
+	{ STB0899_UPKTERRORL		, 0x00 },
+	{ 0xffff			, 0xff },
+};
+
+/* STB0899 demodulator config for the KNC1 and clones */
+static struct stb0899_config knc1_dvbs2_config = {
+	.init_dev		= knc1_stb0899_s1_init_1,
+	.init_s2_demod		= stb0899_s2_init_2,
+	.init_s1_demod		= knc1_stb0899_s1_init_3,
+	.init_s2_fec		= stb0899_s2_init_4,
+	.init_tst		= stb0899_s1_init_5,
+
+	.postproc		= NULL,
+
+	.demod_address		= 0x68,
+//	.ts_output_mode		= STB0899_OUT_PARALLEL,	/* types = SERIAL/PARALLEL	*/
+	.block_sync_mode	= STB0899_SYNC_FORCED,	/* DSS, SYNC_FORCED/UNSYNCED	*/
+//	.ts_pfbit_toggle	= STB0899_MPEG_NORMAL,	/* DirecTV, MPEG toggling seq	*/
+
+	.xtal_freq		= 27000000,
+	.inversion		= IQ_SWAP_OFF, /* 1 */
+
+	.lo_clk			= 76500000,
+	.hi_clk			= 90000000,
+
+	.esno_ave		= STB0899_DVBS2_ESNO_AVE,
+	.esno_quant		= STB0899_DVBS2_ESNO_QUANT,
+	.avframes_coarse	= STB0899_DVBS2_AVFRAMES_COARSE,
+	.avframes_fine		= STB0899_DVBS2_AVFRAMES_FINE,
+	.miss_threshold		= STB0899_DVBS2_MISS_THRESHOLD,
+	.uwp_threshold_acq	= STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+	.uwp_threshold_track	= STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+	.uwp_threshold_sof	= STB0899_DVBS2_UWP_THRESHOLD_SOF,
+	.sof_search_timeout	= STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+	.btr_nco_bits		= STB0899_DVBS2_BTR_NCO_BITS,
+	.btr_gain_shift_offset	= STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+	.crl_nco_bits		= STB0899_DVBS2_CRL_NCO_BITS,
+	.ldpc_max_iter		= STB0899_DVBS2_LDPC_MAX_ITER,
+
+	.tuner_get_frequency	= tda8261_get_frequency,
+	.tuner_set_frequency	= tda8261_set_frequency,
+	.tuner_set_bandwidth	= NULL,
+	.tuner_get_bandwidth	= tda8261_get_bandwidth,
+	.tuner_set_rfsiggain	= NULL
+};
+
+/*
+ * SD1878/SHA tuner config
+ * 1F, Single I/P, Horizontal mount, High Sensitivity
+ */
+static const struct tda8261_config sd1878c_config = {
+//	.name		= "SD1878/SHA",
+	.addr		= 0x60,
+	.step_size	= TDA8261_STEP_1000 /* kHz */
+};
+
+static u8 read_pwm(struct budget_av *budget_av)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
+	{.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
+	};
+
+	if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2)
+	    || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+#define SUBID_DVBS_KNC1			0x0010
+#define SUBID_DVBS_KNC1_PLUS		0x0011
+#define SUBID_DVBS_TYPHOON		0x4f56
+#define SUBID_DVBS_CINERGY1200		0x1154
+#define SUBID_DVBS_CYNERGY1200N 	0x1155
+#define SUBID_DVBS_TV_STAR		0x0014
+#define SUBID_DVBS_TV_STAR_PLUS_X4	0x0015
+#define SUBID_DVBS_TV_STAR_CI		0x0016
+#define SUBID_DVBS2_KNC1		0x0018
+#define SUBID_DVBS2_KNC1_OEM		0x0019
+#define SUBID_DVBS_EASYWATCH_1  	0x001a
+#define SUBID_DVBS_EASYWATCH_2  	0x001b
+#define SUBID_DVBS2_EASYWATCH		0x001d
+#define SUBID_DVBS_EASYWATCH		0x001e
+
+#define SUBID_DVBC_EASYWATCH		0x002a
+#define SUBID_DVBC_EASYWATCH_MK3	0x002c
+#define SUBID_DVBC_KNC1			0x0020
+#define SUBID_DVBC_KNC1_PLUS		0x0021
+#define SUBID_DVBC_KNC1_MK3		0x0022
+#define SUBID_DVBC_KNC1_TDA10024	0x0028
+#define SUBID_DVBC_KNC1_PLUS_MK3	0x0023
+#define SUBID_DVBC_CINERGY1200		0x1156
+#define SUBID_DVBC_CINERGY1200_MK3	0x1176
+
+#define SUBID_DVBT_EASYWATCH		0x003a
+#define SUBID_DVBT_KNC1_PLUS		0x0031
+#define SUBID_DVBT_KNC1			0x0030
+#define SUBID_DVBT_CINERGY1200		0x1157
+
+static void frontend_init(struct budget_av *budget_av)
+{
+	struct saa7146_dev * saa = budget_av->budget.dev;
+	struct dvb_frontend * fe = NULL;
+
+	/* Enable / PowerON Frontend */
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+
+	/* Wait for PowerON */
+	msleep(100);
+
+	/* additional setup necessary for the PLUS cards */
+	switch (saa->pci->subsystem_device) {
+		case SUBID_DVBS_KNC1_PLUS:
+		case SUBID_DVBC_KNC1_PLUS:
+		case SUBID_DVBT_KNC1_PLUS:
+		case SUBID_DVBC_EASYWATCH:
+		case SUBID_DVBC_KNC1_PLUS_MK3:
+		case SUBID_DVBS2_KNC1:
+		case SUBID_DVBS2_KNC1_OEM:
+		case SUBID_DVBS2_EASYWATCH:
+			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
+			break;
+	}
+
+	switch (saa->pci->subsystem_device) {
+
+	case SUBID_DVBS_KNC1:
+		/*
+		 * maybe that setting is needed for other dvb-s cards as well,
+		 * but so far it has been only confirmed for this type
+		 */
+		budget_av->reinitialise_demod = 1;
+		/* fall through */
+	case SUBID_DVBS_KNC1_PLUS:
+	case SUBID_DVBS_EASYWATCH_1:
+		if (saa->pci->subsystem_vendor == 0x1894) {
+			fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config,
+					     &budget_av->budget.i2c_adap);
+			if (fe) {
+				dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap);
+			}
+		} else {
+			fe = dvb_attach(stv0299_attach, &typhoon_config,
+					     &budget_av->budget.i2c_adap);
+			if (fe) {
+				fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+			}
+		}
+		break;
+
+	case SUBID_DVBS_TV_STAR:
+	case SUBID_DVBS_TV_STAR_PLUS_X4:
+	case SUBID_DVBS_TV_STAR_CI:
+	case SUBID_DVBS_CYNERGY1200N:
+	case SUBID_DVBS_EASYWATCH:
+	case SUBID_DVBS_EASYWATCH_2:
+		fe = dvb_attach(stv0299_attach, &philips_sd1878_config,
+				&budget_av->budget.i2c_adap);
+		if (fe) {
+			dvb_attach(dvb_pll_attach, fe, 0x60,
+				   &budget_av->budget.i2c_adap,
+				   DVB_PLL_PHILIPS_SD1878_TDA8261);
+		}
+		break;
+
+	case SUBID_DVBS_TYPHOON:
+		fe = dvb_attach(stv0299_attach, &typhoon_config,
+				    &budget_av->budget.i2c_adap);
+		if (fe) {
+			fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+		}
+		break;
+	case SUBID_DVBS2_KNC1:
+	case SUBID_DVBS2_KNC1_OEM:
+	case SUBID_DVBS2_EASYWATCH:
+		budget_av->reinitialise_demod = 1;
+		if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap)))
+			dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap);
+
+		break;
+	case SUBID_DVBS_CINERGY1200:
+		fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
+				    &budget_av->budget.i2c_adap);
+		if (fe) {
+			fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
+		}
+		break;
+
+	case SUBID_DVBC_KNC1:
+	case SUBID_DVBC_KNC1_PLUS:
+	case SUBID_DVBC_CINERGY1200:
+	case SUBID_DVBC_EASYWATCH:
+		budget_av->reinitialise_demod = 1;
+		budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+		fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
+				     &budget_av->budget.i2c_adap,
+				     read_pwm(budget_av));
+		if (fe == NULL)
+			fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress,
+					     &budget_av->budget.i2c_adap,
+					     read_pwm(budget_av));
+		if (fe) {
+			fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+		}
+		break;
+
+	case SUBID_DVBC_EASYWATCH_MK3:
+	case SUBID_DVBC_CINERGY1200_MK3:
+	case SUBID_DVBC_KNC1_MK3:
+	case SUBID_DVBC_KNC1_TDA10024:
+	case SUBID_DVBC_KNC1_PLUS_MK3:
+		budget_av->reinitialise_demod = 1;
+		budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+		fe = dvb_attach(tda10023_attach,
+			&philips_cu1216_tda10023_config,
+			&budget_av->budget.i2c_adap,
+			read_pwm(budget_av));
+		if (fe) {
+			fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+		}
+		break;
+
+	case SUBID_DVBT_EASYWATCH:
+	case SUBID_DVBT_KNC1:
+	case SUBID_DVBT_KNC1_PLUS:
+	case SUBID_DVBT_CINERGY1200:
+		budget_av->reinitialise_demod = 1;
+		fe = dvb_attach(tda10046_attach, &philips_tu1216_config,
+				     &budget_av->budget.i2c_adap);
+		if (fe) {
+			fe->ops.tuner_ops.init = philips_tu1216_tuner_init;
+			fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params;
+		}
+		break;
+	}
+
+	if (fe == NULL) {
+		pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       saa->pci->vendor,
+		       saa->pci->device,
+		       saa->pci->subsystem_vendor,
+		       saa->pci->subsystem_device);
+		return;
+	}
+
+	budget_av->budget.dvb_frontend = fe;
+
+	if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
+				  budget_av->budget.dvb_frontend)) {
+		pr_err("Frontend registration failed!\n");
+		dvb_frontend_detach(budget_av->budget.dvb_frontend);
+		budget_av->budget.dvb_frontend = NULL;
+	}
+}
+
+
+static void budget_av_irq(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av);
+
+	if (*isr & MASK_10)
+		ttpci_budget_irq10_handler(dev, isr);
+}
+
+static int budget_av_detach(struct saa7146_dev *dev)
+{
+	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+	int err;
+
+	dprintk(2, "dev: %p\n", dev);
+
+	if (1 == budget_av->has_saa7113) {
+		saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+
+		msleep(200);
+
+		saa7146_unregister_device(&budget_av->vd, dev);
+
+		saa7146_vv_release(dev);
+	}
+
+	if (budget_av->budget.ci_present)
+		ciintf_deinit(budget_av);
+
+	if (budget_av->budget.dvb_frontend != NULL) {
+		dvb_unregister_frontend(budget_av->budget.dvb_frontend);
+		dvb_frontend_detach(budget_av->budget.dvb_frontend);
+	}
+	err = ttpci_budget_deinit(&budget_av->budget);
+
+	kfree(budget_av);
+
+	return err;
+}
+
+#define KNC1_INPUTS 2
+static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+	{ 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0,
+		V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+	{ 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0,
+		V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+};
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index);
+	if (i->index >= KNC1_INPUTS)
+		return -EINVAL;
+	memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+	*i = budget_av->cur_input;
+
+	dprintk(1, "VIDIOC_G_INPUT %d\n", *i);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+	dprintk(1, "VIDIOC_S_INPUT %d\n", input);
+	return saa7113_setinput(budget_av, input);
+}
+
+static struct saa7146_ext_vv vv_data;
+
+static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget_av *budget_av;
+	u8 *mac;
+	int err;
+
+	dprintk(2, "dev: %p\n", dev);
+
+	if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL)))
+		return -ENOMEM;
+
+	budget_av->has_saa7113 = 0;
+	budget_av->budget.ci_present = 0;
+
+	dev->ext_priv = budget_av;
+
+	err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE,
+				adapter_nr);
+	if (err) {
+		kfree(budget_av);
+		return err;
+	}
+
+	/* knc1 initialization */
+	saa7146_write(dev, DD1_STREAM_B, 0x04000000);
+	saa7146_write(dev, DD1_INIT, 0x07000600);
+	saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26);
+
+	if (saa7113_init(budget_av) == 0) {
+		budget_av->has_saa7113 = 1;
+
+		if (0 != saa7146_vv_init(dev, &vv_data)) {
+			/* fixme: proper cleanup here */
+			ERR("cannot init vv subsystem\n");
+			return err;
+		}
+		vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+		vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+		vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+
+		if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
+			/* fixme: proper cleanup here */
+			ERR("cannot register capture v4l2 device\n");
+			saa7146_vv_release(dev);
+			return err;
+		}
+
+		/* beware: this modifies dev->vv ... */
+		saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A,
+						SAA7146_HPS_SYNC_PORT_A);
+
+		saa7113_setinput(budget_av, 0);
+	}
+
+	/* fixme: find some sane values here... */
+	saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+	mac = budget_av->budget.dvb_adapter.proposed_mac;
+	if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
+		pr_err("KNC1-%d: Could not read MAC from KNC1 card\n",
+		       budget_av->budget.dvb_adapter.num);
+		memset(mac, 0, 6);
+	} else {
+		pr_info("KNC1-%d: MAC addr = %pM\n",
+			budget_av->budget.dvb_adapter.num, mac);
+	}
+
+	budget_av->budget.dvb_adapter.priv = budget_av;
+	frontend_init(budget_av);
+	ciintf_init(budget_av);
+
+	ttpci_budget_init_hooks(&budget_av->budget);
+
+	return 0;
+}
+
+static struct saa7146_standard standard[] = {
+	{.name = "PAL",.id = V4L2_STD_PAL,
+	 .v_offset = 0x17,.v_field = 288,
+	 .h_offset = 0x14,.h_pixels = 680,
+	 .v_max_out = 576,.h_max_out = 768 },
+
+	{.name = "NTSC",.id = V4L2_STD_NTSC,
+	 .v_offset = 0x16,.v_field = 240,
+	 .h_offset = 0x06,.h_pixels = 708,
+	 .v_max_out = 480,.h_max_out = 640, },
+};
+
+static struct saa7146_ext_vv vv_data = {
+	.inputs = 2,
+	.capabilities = 0,	// perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113
+	.flags = 0,
+	.stds = &standard[0],
+	.num_stds = ARRAY_SIZE(standard),
+};
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
+MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
+MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
+MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
+MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T);
+MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
+MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP);
+MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024);
+MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
+MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
+MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
+MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
+MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
+MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3);
+MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56),
+	MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010),
+	MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010),
+	MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011),
+	MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011),
+	MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
+	MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015),
+	MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
+	MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018),
+	MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019),
+	MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d),
+	MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
+	MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
+	MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b),
+	MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
+	MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c),
+	MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a),
+	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
+	MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
+	MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+	MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028),
+	MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
+	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
+	MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
+	MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
+	MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155),
+	MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
+	MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176),
+	MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
+	{
+	 .vendor = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name = "budget_av",
+	.flags = SAA7146_USE_I2C_IRQ,
+
+	.pci_tbl = pci_tbl,
+
+	.module = THIS_MODULE,
+	.attach = budget_av_attach,
+	.detach = budget_av_detach,
+
+	.irq_mask = MASK_10,
+	.irq_func = budget_av_irq,
+};
+
+static int __init budget_av_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_av_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_av_init);
+module_exit(budget_av_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)");
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
new file mode 100644
index 000000000000..98e524178765
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -0,0 +1,1591 @@
+/*
+ * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ *     msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
+ *     partially based on the Siemens DVB driver by Ralph+Marcus Metzler
+ *
+ * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+
+#include "budget.h"
+
+#include "dvb_ca_en50221.h"
+#include "stv0299.h"
+#include "stv0297.h"
+#include "tda1004x.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+#include "lnbp21.h"
+#include "bsbe1.h"
+#include "bsru6.h"
+#include "tda1002x.h"
+#include "tda827x.h"
+#include "bsbe1-d01a.h"
+
+#define MODULE_NAME "budget_ci"
+
+/*
+ * Regarding DEBIADDR_IR:
+ * Some CI modules hang if random addresses are read.
+ * Using address 0x4000 for the IR read means that we
+ * use the same address as for CI version, which should
+ * be a safe default.
+ */
+#define DEBIADDR_IR		0x4000
+#define DEBIADDR_CICONTROL	0x0000
+#define DEBIADDR_CIVERSION	0x4000
+#define DEBIADDR_IO		0x1000
+#define DEBIADDR_ATTR		0x3000
+
+#define CICONTROL_RESET		0x01
+#define CICONTROL_ENABLETS	0x02
+#define CICONTROL_CAMDETECT	0x08
+
+#define DEBICICTL		0x00420000
+#define DEBICICAM		0x02420000
+
+#define SLOTSTATUS_NONE		1
+#define SLOTSTATUS_PRESENT	2
+#define SLOTSTATUS_RESET	4
+#define SLOTSTATUS_READY	8
+#define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+
+/* RC5 device wildcard */
+#define IR_DEVICE_ANY		255
+
+static int rc5_device = -1;
+module_param(rc5_device, int, 0644);
+MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct budget_ci_ir {
+	struct rc_dev *dev;
+	struct tasklet_struct msp430_irq_tasklet;
+	char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
+	char phys[32];
+	int rc5_device;
+	u32 ir_key;
+	bool have_command;
+	bool full_rc5;		/* Outputs a full RC5 code */
+};
+
+struct budget_ci {
+	struct budget budget;
+	struct tasklet_struct ciintf_irq_tasklet;
+	int slot_status;
+	int ci_irq;
+	struct dvb_ca_en50221 ca;
+	struct budget_ci_ir ir;
+	u8 tuner_pll_address; /* used for philips_tdm1316l configs */
+};
+
+static void msp430_ir_interrupt(unsigned long data)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) data;
+	struct rc_dev *dev = budget_ci->ir.dev;
+	u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+
+	/*
+	 * The msp430 chip can generate two different bytes, command and device
+	 *
+	 * type1: X1CCCCCC, C = command bits (0 - 63)
+	 * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
+	 *
+	 * Each signal from the remote control can generate one or more command
+	 * bytes and one or more device bytes. For the repeated bytes, the
+	 * highest bit (X) is set. The first command byte is always generated
+	 * before the first device byte. Other than that, no specific order
+	 * seems to apply. To make life interesting, bytes can also be lost.
+	 *
+	 * Only when we have a command and device byte, a keypress is
+	 * generated.
+	 */
+
+	if (ir_debug)
+		printk("budget_ci: received byte 0x%02x\n", command);
+
+	/* Remove repeat bit, we use every command */
+	command = command & 0x7f;
+
+	/* Is this a RC5 command byte? */
+	if (command & 0x40) {
+		budget_ci->ir.have_command = true;
+		budget_ci->ir.ir_key = command & 0x3f;
+		return;
+	}
+
+	/* It's a RC5 device byte */
+	if (!budget_ci->ir.have_command)
+		return;
+	budget_ci->ir.have_command = false;
+
+	if (budget_ci->ir.rc5_device != IR_DEVICE_ANY &&
+	    budget_ci->ir.rc5_device != (command & 0x1f))
+		return;
+
+	if (budget_ci->ir.full_rc5) {
+		rc_keydown(dev,
+			   budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key,
+			   (command & 0x20) ? 1 : 0);
+		return;
+	}
+
+	/* FIXME: We should generate complete scancodes for all devices */
+	rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0);
+}
+
+static int msp430_ir_init(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	struct rc_dev *dev;
+	int error;
+
+	dev = rc_allocate_device();
+	if (!dev) {
+		printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+		return -ENOMEM;
+	}
+
+	snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
+		 "Budget-CI dvb ir receiver %s", saa->name);
+	snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys),
+		 "pci-%s/ir0", pci_name(saa->pci));
+
+	dev->driver_name = MODULE_NAME;
+	dev->input_name = budget_ci->ir.name;
+	dev->input_phys = budget_ci->ir.phys;
+	dev->input_id.bustype = BUS_PCI;
+	dev->input_id.version = 1;
+	if (saa->pci->subsystem_vendor) {
+		dev->input_id.vendor = saa->pci->subsystem_vendor;
+		dev->input_id.product = saa->pci->subsystem_device;
+	} else {
+		dev->input_id.vendor = saa->pci->vendor;
+		dev->input_id.product = saa->pci->device;
+	}
+	dev->dev.parent = &saa->pci->dev;
+
+	if (rc5_device < 0)
+		budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+	else
+		budget_ci->ir.rc5_device = rc5_device;
+
+	/* Select keymap and address */
+	switch (budget_ci->budget.dev->pci->subsystem_device) {
+	case 0x100c:
+	case 0x100f:
+	case 0x1011:
+	case 0x1012:
+		/* The hauppauge keymap is a superset of these remotes */
+		dev->map_name = RC_MAP_HAUPPAUGE;
+		budget_ci->ir.full_rc5 = true;
+
+		if (rc5_device < 0)
+			budget_ci->ir.rc5_device = 0x1f;
+		break;
+	case 0x1010:
+	case 0x1017:
+	case 0x1019:
+	case 0x101a:
+	case 0x101b:
+		/* for the Technotrend 1500 bundled remote */
+		dev->map_name = RC_MAP_TT_1500;
+		break;
+	default:
+		/* unknown remote */
+		dev->map_name = RC_MAP_BUDGET_CI_OLD;
+		break;
+	}
+	if (!budget_ci->ir.full_rc5)
+		dev->scanmask = 0xff;
+
+	error = rc_register_device(dev);
+	if (error) {
+		printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+		rc_free_device(dev);
+		return error;
+	}
+
+	budget_ci->ir.dev = dev;
+
+	tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
+		     (unsigned long) budget_ci);
+
+	SAA7146_IER_ENABLE(saa, MASK_06);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
+
+	return 0;
+}
+
+static void msp430_ir_deinit(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	SAA7146_IER_DISABLE(saa, MASK_06);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+	tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
+
+	rc_unregister_device(budget_ci->ir.dev);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+				     DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0);
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+				      DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0);
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+				     DEBIADDR_IO | (address & 3), 1, 1, 0);
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+				      DEBIADDR_IO | (address & 3), 1, value, 1, 0);
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	if (budget_ci->ci_irq) {
+		// trigger on RISING edge during reset so we know when READY is re-asserted
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+	}
+	budget_ci->slot_status = SLOTSTATUS_RESET;
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+	msleep(1);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int tmp;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+
+	tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       tmp | CICONTROL_ENABLETS, 1, 0);
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+	return 0;
+}
+
+static void ciintf_interrupt(unsigned long data)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	unsigned int flags;
+
+	// ensure we don't get spurious IRQs during initialisation
+	if (!budget_ci->budget.ci_present)
+		return;
+
+	// read the CAM status
+	flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	if (flags & CICONTROL_CAMDETECT) {
+
+		// GPIO should be set to trigger on falling edge if a CAM is present
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+
+		if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+			// CAM insertion IRQ
+			budget_ci->slot_status = SLOTSTATUS_PRESENT;
+			dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+						     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+
+		} else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+			// CAM ready (reset completed)
+			budget_ci->slot_status = SLOTSTATUS_READY;
+			dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
+
+		} else if (budget_ci->slot_status & SLOTSTATUS_READY) {
+			// FR/DA IRQ
+			dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
+		}
+	} else {
+
+		// trigger on rising edge if a CAM is not present - when a CAM is inserted, we
+		// only want to get the IRQ when it sets READY. If we trigger on the falling edge,
+		// the CAM might not actually be ready yet.
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+
+		// generate a CAM removal IRQ if we haven't already
+		if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
+			// CAM removal IRQ
+			budget_ci->slot_status = SLOTSTATUS_NONE;
+			dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+						     DVB_CA_EN50221_CAMCHANGE_REMOVED);
+		}
+	}
+}
+
+static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	unsigned int flags;
+
+	// ensure we don't get spurious IRQs during initialisation
+	if (!budget_ci->budget.ci_present)
+		return -EINVAL;
+
+	// read the CAM status
+	flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	if (flags & CICONTROL_CAMDETECT) {
+		// mark it as present if it wasn't before
+		if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+			budget_ci->slot_status = SLOTSTATUS_PRESENT;
+		}
+
+		// during a RESET, we check if we can read from IO memory to see when CAM is ready
+		if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+			if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) {
+				budget_ci->slot_status = SLOTSTATUS_READY;
+			}
+		}
+	} else {
+		budget_ci->slot_status = SLOTSTATUS_NONE;
+	}
+
+	if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+		if (budget_ci->slot_status & SLOTSTATUS_READY) {
+			return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+		}
+		return DVB_CA_EN50221_POLL_CAM_PRESENT;
+	}
+
+	return 0;
+}
+
+static int ciintf_init(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int flags;
+	int result;
+	int ci_version;
+	int ca_flags;
+
+	memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
+
+	// enable DEBI pins
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
+
+	// test if it is there
+	ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
+	if ((ci_version & 0xa0) != 0xa0) {
+		result = -ENODEV;
+		goto error;
+	}
+
+	// determine whether a CAM is present or not
+	flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	budget_ci->slot_status = SLOTSTATUS_NONE;
+	if (flags & CICONTROL_CAMDETECT)
+		budget_ci->slot_status = SLOTSTATUS_PRESENT;
+
+	// version 0xa2 of the CI firmware doesn't generate interrupts
+	if (ci_version == 0xa2) {
+		ca_flags = 0;
+		budget_ci->ci_irq = 0;
+	} else {
+		ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
+				DVB_CA_EN50221_FLAG_IRQ_FR |
+				DVB_CA_EN50221_FLAG_IRQ_DA;
+		budget_ci->ci_irq = 1;
+	}
+
+	// register CI interface
+	budget_ci->ca.owner = THIS_MODULE;
+	budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
+	budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
+	budget_ci->ca.read_cam_control = ciintf_read_cam_control;
+	budget_ci->ca.write_cam_control = ciintf_write_cam_control;
+	budget_ci->ca.slot_reset = ciintf_slot_reset;
+	budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
+	budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
+	budget_ci->ca.poll_slot_status = ciintf_poll_slot_status;
+	budget_ci->ca.data = budget_ci;
+	if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
+					  &budget_ci->ca,
+					  ca_flags, 1)) != 0) {
+		printk("budget_ci: CI interface detected, but initialisation failed.\n");
+		goto error;
+	}
+
+	// Setup CI slot IRQ
+	if (budget_ci->ci_irq) {
+		tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
+		if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+		} else {
+			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+		}
+		SAA7146_IER_ENABLE(saa, MASK_03);
+	}
+
+	// enable interface
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	// success!
+	printk("budget_ci: CI interface initialised\n");
+	budget_ci->budget.ci_present = 1;
+
+	// forge a fake CI IRQ so the CAM state is setup correctly
+	if (budget_ci->ci_irq) {
+		flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+		if (budget_ci->slot_status != SLOTSTATUS_NONE)
+			flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+		dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
+	}
+
+	return 0;
+
+error:
+	saa7146_write(saa, MC1, MASK_27);
+	return result;
+}
+
+static void ciintf_deinit(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	// disable CI interrupts
+	if (budget_ci->ci_irq) {
+		SAA7146_IER_DISABLE(saa, MASK_03);
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+		tasklet_kill(&budget_ci->ciintf_irq_tasklet);
+	}
+
+	// reset interface
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+	msleep(1);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	// disable TS data stream to CI interface
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+
+	// release the CA device
+	dvb_ca_en50221_release(&budget_ci->ca);
+
+	// disable DEBI pins
+	saa7146_write(saa, MC1, MASK_27);
+}
+
+static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
+
+	if (*isr & MASK_06)
+		tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet);
+
+	if (*isr & MASK_10)
+		ttpci_budget_irq10_handler(dev, isr);
+
+	if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq))
+		tasklet_schedule(&budget_ci->ciintf_irq_tasklet);
+}
+
+static u8 philips_su1278_tt_inittab[] = {
+	0x01, 0x0f,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x5b,
+	0x05, 0x85,
+	0x06, 0x02,
+	0x07, 0x00,
+	0x08, 0x02,
+	0x09, 0x00,
+	0x0C, 0x01,
+	0x0D, 0x81,
+	0x0E, 0x44,
+	0x0f, 0x14,
+	0x10, 0x3c,
+	0x11, 0x84,
+	0x12, 0xda,
+	0x13, 0x97,
+	0x14, 0x95,
+	0x15, 0xc9,
+	0x16, 0x19,
+	0x17, 0x8c,
+	0x18, 0x59,
+	0x19, 0xf8,
+	0x1a, 0xfe,
+	0x1c, 0x7f,
+	0x1d, 0x00,
+	0x1e, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,
+	0x29, 0x28,
+	0x2a, 0x14,
+	0x2b, 0x0f,
+	0x2c, 0x09,
+	0x2d, 0x09,
+	0x31, 0x1f,
+	0x32, 0x19,
+	0x33, 0xfc,
+	0x34, 0x93,
+	0xff, 0xff
+};
+
+static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	stv0299_writereg(fe, 0x0e, 0x44);
+	if (srate >= 10000000) {
+		stv0299_writereg(fe, 0x13, 0x97);
+		stv0299_writereg(fe, 0x14, 0x95);
+		stv0299_writereg(fe, 0x15, 0xc9);
+		stv0299_writereg(fe, 0x17, 0x8c);
+		stv0299_writereg(fe, 0x1a, 0xfe);
+		stv0299_writereg(fe, 0x1c, 0x7f);
+		stv0299_writereg(fe, 0x2d, 0x09);
+	} else {
+		stv0299_writereg(fe, 0x13, 0x99);
+		stv0299_writereg(fe, 0x14, 0x8d);
+		stv0299_writereg(fe, 0x15, 0xce);
+		stv0299_writereg(fe, 0x17, 0x43);
+		stv0299_writereg(fe, 0x1a, 0x1d);
+		stv0299_writereg(fe, 0x1c, 0x12);
+		stv0299_writereg(fe, 0x2d, 0x05);
+	}
+	stv0299_writereg(fe, 0x0e, 0x23);
+	stv0299_writereg(fe, 0x0f, 0x94);
+	stv0299_writereg(fe, 0x10, 0x39);
+	stv0299_writereg(fe, 0x15, 0xc9);
+
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+
+	return 0;
+}
+
+static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u32 div;
+	u8 buf[4];
+	struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((p->frequency < 950000) || (p->frequency > 2150000))
+		return -EINVAL;
+
+	div = (p->frequency + (500 - 1)) / 500;	/* round correctly */
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2;
+	buf[3] = 0x20;
+
+	if (p->symbol_rate < 4000000)
+		buf[3] |= 1;
+
+	if (p->frequency < 1250000)
+		buf[3] |= 0;
+	else if (p->frequency < 1550000)
+		buf[3] |= 0x40;
+	else if (p->frequency < 2050000)
+		buf[3] |= 0x80;
+	else if (p->frequency < 2150000)
+		buf[3] |= 0xC0;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct stv0299_config philips_su1278_tt_config = {
+
+	.demod_address = 0x68,
+	.inittab = philips_su1278_tt_inittab,
+	.mclk = 64000000UL,
+	.invert = 0,
+	.skip_reinit = 1,
+	.lock_output = STV0299_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 50,
+	.set_symbol_rate = philips_su1278_tt_set_symbol_rate,
+};
+
+
+
+static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
+	struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len =
+			sizeof(td1316_init) };
+
+	// setup PLL configuration
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	// disable the mc44BC374c (do not check for errors)
+	tuner_msg.addr = 0x65;
+	tuner_msg.buf = disable_mc44BC374c;
+	tuner_msg.len = sizeof(disable_mc44BC374c);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1);
+	}
+
+	return 0;
+}
+
+static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = p->frequency + 36130000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (p->frequency < 49000000)
+		return -EINVAL;
+	else if (p->frequency < 159000000)
+		band = 1;
+	else if (p->frequency < 444000000)
+		band = 2;
+	else if (p->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter and TDA9889
+	switch (p->bandwidth_hz) {
+	case 6000000:
+		tda1004x_writereg(fe, 0x0C, 0x14);
+		filter = 0;
+		break;
+
+	case 7000000:
+		tda1004x_writereg(fe, 0x0C, 0x80);
+		filter = 0;
+		break;
+
+	case 8000000:
+		tda1004x_writereg(fe, 0x0C, 0x14);
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = tuner_frequency >> 8;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+	return 0;
+}
+
+static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe,
+					     const struct firmware **fw, char *name)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+
+	return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tdm1316l_config = {
+
+	.demod_address = 0x8,
+	.invert = 0,
+	.invert_oclk = 0,
+	.xtal_freq = TDA10046_XTAL_4M,
+	.agc_config = TDA10046_AGC_DEFAULT,
+	.if_freq = TDA10046_FREQ_3617,
+	.request_firmware = philips_tdm1316l_request_firmware,
+};
+
+static struct tda1004x_config philips_tdm1316l_config_invert = {
+
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 0,
+	.xtal_freq = TDA10046_XTAL_4M,
+	.agc_config = TDA10046_AGC_DEFAULT,
+	.if_freq = TDA10046_FREQ_3617,
+	.request_firmware = philips_tdm1316l_request_firmware,
+};
+
+static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u8 tuner_buf[5];
+	struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,
+				    .flags = 0,
+				    .buf = tuner_buf,
+				    .len = sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = p->frequency + 36125000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000) {
+		cp = 3;
+		band = 1;
+	} else if (tuner_frequency < 160000000) {
+		cp = 5;
+		band = 1;
+	} else if (tuner_frequency < 200000000) {
+		cp = 6;
+		band = 1;
+	} else if (tuner_frequency < 290000000) {
+		cp = 3;
+		band = 2;
+	} else if (tuner_frequency < 420000000) {
+		cp = 5;
+		band = 2;
+	} else if (tuner_frequency < 480000000) {
+		cp = 6;
+		band = 2;
+	} else if (tuner_frequency < 620000000) {
+		cp = 3;
+		band = 4;
+	} else if (tuner_frequency < 830000000) {
+		cp = 5;
+		band = 4;
+	} else if (tuner_frequency < 895000000) {
+		cp = 7;
+		band = 4;
+	} else
+		return -EINVAL;
+
+	// assume PLL filter should always be 8MHz for the moment.
+	filter = 1;
+
+	// calculate divisor
+	tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500;
+
+	// setup tuner buffer
+	tuner_buf[0] = tuner_frequency >> 8;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xc8;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+	tuner_buf[4] = 0x80;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(50);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+
+	return 0;
+}
+
+static u8 dvbc_philips_tdm1316l_inittab[] = {
+	0x80, 0x01,
+	0x80, 0x00,
+	0x81, 0x01,
+	0x81, 0x00,
+	0x00, 0x09,
+	0x01, 0x69,
+	0x03, 0x00,
+	0x04, 0x00,
+	0x07, 0x00,
+	0x08, 0x00,
+	0x20, 0x00,
+	0x21, 0x40,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x24, 0x40,
+	0x25, 0x88,
+	0x30, 0xff,
+	0x31, 0x00,
+	0x32, 0xff,
+	0x33, 0x00,
+	0x34, 0x50,
+	0x35, 0x7f,
+	0x36, 0x00,
+	0x37, 0x20,
+	0x38, 0x00,
+	0x40, 0x1c,
+	0x41, 0xff,
+	0x42, 0x29,
+	0x43, 0x20,
+	0x44, 0xff,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x49, 0x04,
+	0x4a, 0x00,
+	0x4b, 0x7b,
+	0x52, 0x30,
+	0x55, 0xae,
+	0x56, 0x47,
+	0x57, 0xe1,
+	0x58, 0x3a,
+	0x5a, 0x1e,
+	0x5b, 0x34,
+	0x60, 0x00,
+	0x63, 0x00,
+	0x64, 0x00,
+	0x65, 0x00,
+	0x66, 0x00,
+	0x67, 0x00,
+	0x68, 0x00,
+	0x69, 0x00,
+	0x6a, 0x02,
+	0x6b, 0x00,
+	0x70, 0xff,
+	0x71, 0x00,
+	0x72, 0x00,
+	0x73, 0x00,
+	0x74, 0x0c,
+	0x80, 0x00,
+	0x81, 0x00,
+	0x82, 0x00,
+	0x83, 0x00,
+	0x84, 0x04,
+	0x85, 0x80,
+	0x86, 0x24,
+	0x87, 0x78,
+	0x88, 0x10,
+	0x89, 0x00,
+	0x90, 0x01,
+	0x91, 0x01,
+	0xa0, 0x04,
+	0xa1, 0x00,
+	0xa2, 0x00,
+	0xb0, 0x91,
+	0xb1, 0x0b,
+	0xc0, 0x53,
+	0xc1, 0x70,
+	0xc2, 0x12,
+	0xd0, 0x00,
+	0xd1, 0x00,
+	0xd2, 0x00,
+	0xd3, 0x00,
+	0xd4, 0x00,
+	0xd5, 0x00,
+	0xde, 0x00,
+	0xdf, 0x00,
+	0x61, 0x38,
+	0x62, 0x0a,
+	0x53, 0x13,
+	0x59, 0x08,
+	0xff, 0xff,
+};
+
+static struct stv0297_config dvbc_philips_tdm1316l_config = {
+	.demod_address = 0x1c,
+	.inittab = dvbc_philips_tdm1316l_inittab,
+	.invert = 0,
+	.stop_during_read = 1,
+};
+
+static struct tda10023_config tda10023_config = {
+	.demod_address = 0xc,
+	.invert = 0,
+	.xtal = 16000000,
+	.pll_m = 11,
+	.pll_p = 3,
+	.pll_n = 1,
+	.deltaf = 0xa511,
+};
+
+static struct tda827x_config tda827x_config = {
+	.config = 0,
+};
+
+/* TT S2-3200 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
+
+	{ STB0899_DEV_ID		, 0x81 },
+	{ STB0899_DISCNTRL1		, 0x32 },
+	{ STB0899_DISCNTRL2     	, 0x80 },
+	{ STB0899_DISRX_ST0     	, 0x04 },
+	{ STB0899_DISRX_ST1     	, 0x00 },
+	{ STB0899_DISPARITY     	, 0x00 },
+	{ STB0899_DISSTATUS		, 0x20 },
+	{ STB0899_DISF22        	, 0x8c },
+	{ STB0899_DISF22RX      	, 0x9a },
+	{ STB0899_SYSREG		, 0x0b },
+	{ STB0899_ACRPRESC      	, 0x11 },
+	{ STB0899_ACRDIV1       	, 0x0a },
+	{ STB0899_ACRDIV2       	, 0x05 },
+	{ STB0899_DACR1         	, 0x00 },
+	{ STB0899_DACR2         	, 0x00 },
+	{ STB0899_OUTCFG        	, 0x00 },
+	{ STB0899_MODECFG       	, 0x00 },
+	{ STB0899_IRQSTATUS_3		, 0x30 },
+	{ STB0899_IRQSTATUS_2		, 0x00 },
+	{ STB0899_IRQSTATUS_1		, 0x00 },
+	{ STB0899_IRQSTATUS_0		, 0x00 },
+	{ STB0899_IRQMSK_3      	, 0xf3 },
+	{ STB0899_IRQMSK_2      	, 0xfc },
+	{ STB0899_IRQMSK_1      	, 0xff },
+	{ STB0899_IRQMSK_0		, 0xff },
+	{ STB0899_IRQCFG		, 0x00 },
+	{ STB0899_I2CCFG        	, 0x88 },
+	{ STB0899_I2CRPT        	, 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
+	{ STB0899_IOPVALUE5		, 0x00 },
+	{ STB0899_IOPVALUE4		, 0x20 },
+	{ STB0899_IOPVALUE3		, 0xc9 },
+	{ STB0899_IOPVALUE2		, 0x90 },
+	{ STB0899_IOPVALUE1		, 0x40 },
+	{ STB0899_IOPVALUE0		, 0x00 },
+	{ STB0899_GPIO00CFG     	, 0x82 },
+	{ STB0899_GPIO01CFG     	, 0x82 },
+	{ STB0899_GPIO02CFG     	, 0x82 },
+	{ STB0899_GPIO03CFG     	, 0x82 },
+	{ STB0899_GPIO04CFG     	, 0x82 },
+	{ STB0899_GPIO05CFG     	, 0x82 },
+	{ STB0899_GPIO06CFG     	, 0x82 },
+	{ STB0899_GPIO07CFG     	, 0x82 },
+	{ STB0899_GPIO08CFG     	, 0x82 },
+	{ STB0899_GPIO09CFG     	, 0x82 },
+	{ STB0899_GPIO10CFG     	, 0x82 },
+	{ STB0899_GPIO11CFG     	, 0x82 },
+	{ STB0899_GPIO12CFG     	, 0x82 },
+	{ STB0899_GPIO13CFG     	, 0x82 },
+	{ STB0899_GPIO14CFG     	, 0x82 },
+	{ STB0899_GPIO15CFG     	, 0x82 },
+	{ STB0899_GPIO16CFG     	, 0x82 },
+	{ STB0899_GPIO17CFG     	, 0x82 },
+	{ STB0899_GPIO18CFG     	, 0x82 },
+	{ STB0899_GPIO19CFG     	, 0x82 },
+	{ STB0899_GPIO20CFG     	, 0x82 },
+	{ STB0899_SDATCFG       	, 0xb8 },
+	{ STB0899_SCLTCFG       	, 0xba },
+	{ STB0899_AGCRFCFG      	, 0x1c }, /* 0x11 */
+	{ STB0899_GPIO22        	, 0x82 }, /* AGCBB2CFG */
+	{ STB0899_GPIO21        	, 0x91 }, /* AGCBB1CFG */
+	{ STB0899_DIRCLKCFG     	, 0x82 },
+	{ STB0899_CLKOUT27CFG   	, 0x7e },
+	{ STB0899_STDBYCFG      	, 0x82 },
+	{ STB0899_CS0CFG        	, 0x82 },
+	{ STB0899_CS1CFG        	, 0x82 },
+	{ STB0899_DISEQCOCFG    	, 0x20 },
+	{ STB0899_GPIO32CFG		, 0x82 },
+	{ STB0899_GPIO33CFG		, 0x82 },
+	{ STB0899_GPIO34CFG		, 0x82 },
+	{ STB0899_GPIO35CFG		, 0x82 },
+	{ STB0899_GPIO36CFG		, 0x82 },
+	{ STB0899_GPIO37CFG		, 0x82 },
+	{ STB0899_GPIO38CFG		, 0x82 },
+	{ STB0899_GPIO39CFG		, 0x82 },
+	{ STB0899_NCOARSE       	, 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+	{ STB0899_SYNTCTRL      	, 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+	{ STB0899_FILTCTRL      	, 0x00 },
+	{ STB0899_SYSCTRL       	, 0x00 },
+	{ STB0899_STOPCLK1      	, 0x20 },
+	{ STB0899_STOPCLK2      	, 0x00 },
+	{ STB0899_INTBUFSTATUS		, 0x00 },
+	{ STB0899_INTBUFCTRL    	, 0x0a },
+	{ 0xffff			, 0xff },
+};
+
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
+	{ STB0899_DEMOD         	, 0x00 },
+	{ STB0899_RCOMPC        	, 0xc9 },
+	{ STB0899_AGC1CN        	, 0x41 },
+	{ STB0899_AGC1REF       	, 0x10 },
+	{ STB0899_RTC			, 0x7a },
+	{ STB0899_TMGCFG        	, 0x4e },
+	{ STB0899_AGC2REF       	, 0x34 },
+	{ STB0899_TLSR          	, 0x84 },
+	{ STB0899_CFD           	, 0xc7 },
+	{ STB0899_ACLC			, 0x87 },
+	{ STB0899_BCLC          	, 0x94 },
+	{ STB0899_EQON          	, 0x41 },
+	{ STB0899_LDT           	, 0xdd },
+	{ STB0899_LDT2          	, 0xc9 },
+	{ STB0899_EQUALREF      	, 0xb4 },
+	{ STB0899_TMGRAMP       	, 0x10 },
+	{ STB0899_TMGTHD        	, 0x30 },
+	{ STB0899_IDCCOMP		, 0xfb },
+	{ STB0899_QDCCOMP		, 0x03 },
+	{ STB0899_POWERI		, 0x3b },
+	{ STB0899_POWERQ		, 0x3d },
+	{ STB0899_RCOMP			, 0x81 },
+	{ STB0899_AGCIQIN		, 0x80 },
+	{ STB0899_AGC2I1		, 0x04 },
+	{ STB0899_AGC2I2		, 0xf5 },
+	{ STB0899_TLIR			, 0x25 },
+	{ STB0899_RTF			, 0x80 },
+	{ STB0899_DSTATUS		, 0x00 },
+	{ STB0899_LDI			, 0xca },
+	{ STB0899_CFRM			, 0xf1 },
+	{ STB0899_CFRL			, 0xf3 },
+	{ STB0899_NIRM			, 0x2a },
+	{ STB0899_NIRL			, 0x05 },
+	{ STB0899_ISYMB			, 0x17 },
+	{ STB0899_QSYMB			, 0xfa },
+	{ STB0899_SFRH          	, 0x2f },
+	{ STB0899_SFRM          	, 0x68 },
+	{ STB0899_SFRL          	, 0x40 },
+	{ STB0899_SFRUPH        	, 0x2f },
+	{ STB0899_SFRUPM        	, 0x68 },
+	{ STB0899_SFRUPL        	, 0x40 },
+	{ STB0899_EQUAI1		, 0xfd },
+	{ STB0899_EQUAQ1		, 0x04 },
+	{ STB0899_EQUAI2		, 0x0f },
+	{ STB0899_EQUAQ2		, 0xff },
+	{ STB0899_EQUAI3		, 0xdf },
+	{ STB0899_EQUAQ3		, 0xfa },
+	{ STB0899_EQUAI4		, 0x37 },
+	{ STB0899_EQUAQ4		, 0x0d },
+	{ STB0899_EQUAI5		, 0xbd },
+	{ STB0899_EQUAQ5		, 0xf7 },
+	{ STB0899_DSTATUS2		, 0x00 },
+	{ STB0899_VSTATUS       	, 0x00 },
+	{ STB0899_VERROR		, 0xff },
+	{ STB0899_IQSWAP		, 0x2a },
+	{ STB0899_ECNT1M		, 0x00 },
+	{ STB0899_ECNT1L		, 0x00 },
+	{ STB0899_ECNT2M		, 0x00 },
+	{ STB0899_ECNT2L		, 0x00 },
+	{ STB0899_ECNT3M		, 0x00 },
+	{ STB0899_ECNT3L		, 0x00 },
+	{ STB0899_FECAUTO1      	, 0x06 },
+	{ STB0899_FECM			, 0x01 },
+	{ STB0899_VTH12         	, 0xf0 },
+	{ STB0899_VTH23         	, 0xa0 },
+	{ STB0899_VTH34			, 0x78 },
+	{ STB0899_VTH56         	, 0x4e },
+	{ STB0899_VTH67         	, 0x48 },
+	{ STB0899_VTH78         	, 0x38 },
+	{ STB0899_PRVIT         	, 0xff },
+	{ STB0899_VITSYNC       	, 0x19 },
+	{ STB0899_RSULC         	, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+	{ STB0899_TSULC         	, 0x42 },
+	{ STB0899_RSLLC         	, 0x40 },
+	{ STB0899_TSLPL			, 0x12 },
+	{ STB0899_TSCFGH        	, 0x0c },
+	{ STB0899_TSCFGM        	, 0x00 },
+	{ STB0899_TSCFGL        	, 0x0c },
+	{ STB0899_TSOUT			, 0x4d }, /* 0x0d for CAM */
+	{ STB0899_RSSYNCDEL     	, 0x00 },
+	{ STB0899_TSINHDELH     	, 0x02 },
+	{ STB0899_TSINHDELM		, 0x00 },
+	{ STB0899_TSINHDELL		, 0x00 },
+	{ STB0899_TSLLSTKM		, 0x00 },
+	{ STB0899_TSLLSTKL		, 0x00 },
+	{ STB0899_TSULSTKM		, 0x00 },
+	{ STB0899_TSULSTKL		, 0xab },
+	{ STB0899_PCKLENUL		, 0x00 },
+	{ STB0899_PCKLENLL		, 0xcc },
+	{ STB0899_RSPCKLEN		, 0xcc },
+	{ STB0899_TSSTATUS		, 0x80 },
+	{ STB0899_ERRCTRL1      	, 0xb6 },
+	{ STB0899_ERRCTRL2      	, 0x96 },
+	{ STB0899_ERRCTRL3      	, 0x89 },
+	{ STB0899_DMONMSK1		, 0x27 },
+	{ STB0899_DMONMSK0		, 0x03 },
+	{ STB0899_DEMAPVIT      	, 0x5c },
+	{ STB0899_PLPARM		, 0x1f },
+	{ STB0899_PDELCTRL      	, 0x48 },
+	{ STB0899_PDELCTRL2     	, 0x00 },
+	{ STB0899_BBHCTRL1      	, 0x00 },
+	{ STB0899_BBHCTRL2      	, 0x00 },
+	{ STB0899_HYSTTHRESH    	, 0x77 },
+	{ STB0899_MATCSTM		, 0x00 },
+	{ STB0899_MATCSTL		, 0x00 },
+	{ STB0899_UPLCSTM		, 0x00 },
+	{ STB0899_UPLCSTL		, 0x00 },
+	{ STB0899_DFLCSTM		, 0x00 },
+	{ STB0899_DFLCSTL		, 0x00 },
+	{ STB0899_SYNCCST		, 0x00 },
+	{ STB0899_SYNCDCSTM		, 0x00 },
+	{ STB0899_SYNCDCSTL		, 0x00 },
+	{ STB0899_ISI_ENTRY		, 0x00 },
+	{ STB0899_ISI_BIT_EN		, 0x00 },
+	{ STB0899_MATSTRM		, 0x00 },
+	{ STB0899_MATSTRL		, 0x00 },
+	{ STB0899_UPLSTRM		, 0x00 },
+	{ STB0899_UPLSTRL		, 0x00 },
+	{ STB0899_DFLSTRM		, 0x00 },
+	{ STB0899_DFLSTRL		, 0x00 },
+	{ STB0899_SYNCSTR		, 0x00 },
+	{ STB0899_SYNCDSTRM		, 0x00 },
+	{ STB0899_SYNCDSTRL		, 0x00 },
+	{ STB0899_CFGPDELSTATUS1	, 0x10 },
+	{ STB0899_CFGPDELSTATUS2	, 0x00 },
+	{ STB0899_BBFERRORM		, 0x00 },
+	{ STB0899_BBFERRORL		, 0x00 },
+	{ STB0899_UPKTERRORM		, 0x00 },
+	{ STB0899_UPKTERRORL		, 0x00 },
+	{ 0xffff			, 0xff },
+};
+
+static struct stb0899_config tt3200_config = {
+	.init_dev		= tt3200_stb0899_s1_init_1,
+	.init_s2_demod		= stb0899_s2_init_2,
+	.init_s1_demod		= tt3200_stb0899_s1_init_3,
+	.init_s2_fec		= stb0899_s2_init_4,
+	.init_tst		= stb0899_s1_init_5,
+
+	.postproc		= NULL,
+
+	.demod_address 		= 0x68,
+
+	.xtal_freq		= 27000000,
+	.inversion		= IQ_SWAP_ON, /* 1 */
+
+	.lo_clk			= 76500000,
+	.hi_clk			= 99000000,
+
+	.esno_ave		= STB0899_DVBS2_ESNO_AVE,
+	.esno_quant		= STB0899_DVBS2_ESNO_QUANT,
+	.avframes_coarse	= STB0899_DVBS2_AVFRAMES_COARSE,
+	.avframes_fine		= STB0899_DVBS2_AVFRAMES_FINE,
+	.miss_threshold		= STB0899_DVBS2_MISS_THRESHOLD,
+	.uwp_threshold_acq	= STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+	.uwp_threshold_track	= STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+	.uwp_threshold_sof	= STB0899_DVBS2_UWP_THRESHOLD_SOF,
+	.sof_search_timeout	= STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+	.btr_nco_bits		= STB0899_DVBS2_BTR_NCO_BITS,
+	.btr_gain_shift_offset	= STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+	.crl_nco_bits		= STB0899_DVBS2_CRL_NCO_BITS,
+	.ldpc_max_iter		= STB0899_DVBS2_LDPC_MAX_ITER,
+
+	.tuner_get_frequency	= stb6100_get_frequency,
+	.tuner_set_frequency	= stb6100_set_frequency,
+	.tuner_set_bandwidth	= stb6100_set_bandwidth,
+	.tuner_get_bandwidth	= stb6100_get_bandwidth,
+	.tuner_set_rfsiggain	= NULL
+};
+
+static struct stb6100_config tt3200_stb6100_config = {
+	.tuner_address	= 0x60,
+	.refclock	= 27000000,
+};
+
+static void frontend_init(struct budget_ci *budget_ci)
+{
+	switch (budget_ci->budget.dev->pci->subsystem_device) {
+	case 0x100c:		// Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
+		budget_ci->budget.dvb_frontend =
+			dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+			budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
+			break;
+		}
+		break;
+
+	case 0x100f:		// Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
+		budget_ci->budget.dvb_frontend =
+			dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params;
+			break;
+		}
+		break;
+
+	case 0x1010:		// TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt))
+		budget_ci->tuner_pll_address = 0x61;
+		budget_ci->budget.dvb_frontend =
+			dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params;
+			break;
+		}
+		break;
+
+	case 0x1011:		// Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
+		budget_ci->tuner_pll_address = 0x63;
+		budget_ci->budget.dvb_frontend =
+			dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
+			break;
+		}
+		break;
+
+	case 0x1012:		// TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt))
+		budget_ci->tuner_pll_address = 0x60;
+		budget_ci->budget.dvb_frontend =
+			dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
+			break;
+		}
+		break;
+
+	case 0x1017:		// TT S-1500 PCI
+		budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+			budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
+
+			budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+			if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) {
+				printk("%s: No LNBP21 found!\n", __func__);
+				dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+				budget_ci->budget.dvb_frontend = NULL;
+			}
+		}
+		break;
+
+	case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */
+		budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48);
+		if (budget_ci->budget.dvb_frontend) {
+			if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) {
+				printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+				dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+				budget_ci->budget.dvb_frontend = NULL;
+			}
+		}
+		break;
+
+	case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */
+		budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) {
+				if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+					printk(KERN_ERR "%s: No LNBP21 found!\n", __func__);
+					dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+					budget_ci->budget.dvb_frontend = NULL;
+				}
+			} else {
+				printk(KERN_ERR "%s: No STB6000 found!\n", __func__);
+				dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+				budget_ci->budget.dvb_frontend = NULL;
+			}
+		}
+		break;
+
+	case 0x1019:		// TT S2-3200 PCI
+		/*
+		 * NOTE! on some STB0899 versions, the internal PLL takes a longer time
+		 * to settle, aka LOCK. On the older revisions of the chip, we don't see
+		 * this, as a result on the newer chips the entire clock tree, will not
+		 * be stable after a freshly POWER 'ed up situation.
+		 * In this case, we should RESET the STB0899 (Active LOW) and wait for
+		 * PLL stabilization.
+		 *
+		 * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is
+		 * connected to the SAA7146 GPIO, GPIO2, Pin 142
+		 */
+		/* Reset Demodulator */
+		saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO);
+		/* Wait for everything to die */
+		msleep(50);
+		/* Pull it up out of Reset state */
+		saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI);
+		/* Wait for PLL to stabilize */
+		msleep(250);
+		/*
+		 * PLL state should be stable now. Ideally, we should check
+		 * for PLL LOCK status. But well, never mind!
+		 */
+		budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
+				if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+					printk("%s: No LNBP21 found!\n", __func__);
+					dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+					budget_ci->budget.dvb_frontend = NULL;
+				}
+			} else {
+					dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+					budget_ci->budget.dvb_frontend = NULL;
+			}
+		}
+		break;
+
+	}
+
+	if (budget_ci->budget.dvb_frontend == NULL) {
+		printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       budget_ci->budget.dev->pci->vendor,
+		       budget_ci->budget.dev->pci->device,
+		       budget_ci->budget.dev->pci->subsystem_vendor,
+		       budget_ci->budget.dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend
+		    (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
+			printk("budget-ci: Frontend registration failed!\n");
+			dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+			budget_ci->budget.dvb_frontend = NULL;
+		}
+	}
+}
+
+static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget_ci *budget_ci;
+	int err;
+
+	budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL);
+	if (!budget_ci) {
+		err = -ENOMEM;
+		goto out1;
+	}
+
+	dprintk(2, "budget_ci: %p\n", budget_ci);
+
+	dev->ext_priv = budget_ci;
+
+	err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
+				adapter_nr);
+	if (err)
+		goto out2;
+
+	err = msp430_ir_init(budget_ci);
+	if (err)
+		goto out3;
+
+	ciintf_init(budget_ci);
+
+	budget_ci->budget.dvb_adapter.priv = budget_ci;
+	frontend_init(budget_ci);
+
+	ttpci_budget_init_hooks(&budget_ci->budget);
+
+	return 0;
+
+out3:
+	ttpci_budget_deinit(&budget_ci->budget);
+out2:
+	kfree(budget_ci);
+out1:
+	return err;
+}
+
+static int budget_ci_detach(struct saa7146_dev *dev)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int err;
+
+	if (budget_ci->budget.ci_present)
+		ciintf_deinit(budget_ci);
+	msp430_ir_deinit(budget_ci);
+	if (budget_ci->budget.dvb_frontend) {
+		dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
+		dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+	}
+	err = ttpci_budget_deinit(&budget_ci->budget);
+
+	// disable frontend and CI interface
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+
+	kfree(budget_ci);
+
+	return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T	 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
+	MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
+	MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010),
+	MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
+	MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
+	MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
+	MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a),
+	MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019),
+	MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b),
+	{
+	 .vendor = 0,
+	 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name = "budget_ci dvb",
+	.flags = SAA7146_USE_I2C_IRQ,
+
+	.module = THIS_MODULE,
+	.pci_tbl = &pci_tbl[0],
+	.attach = budget_ci_attach,
+	.detach = budget_ci_detach,
+
+	.irq_mask = MASK_03 | MASK_06 | MASK_10,
+	.irq_func = budget_ci_irq,
+};
+
+static int __init budget_ci_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_ci_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_ci_init);
+module_exit(budget_ci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB cards w/ CI-module produced by "
+		   "Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
new file mode 100644
index 000000000000..37d02fe09137
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-core.c
@@ -0,0 +1,602 @@
+/*
+ * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *			 & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ *	     Michael Dreher <michael@5dot1.de>,
+ *	     Oliver Endriss <o.endriss@gmx.de>,
+ *	     Andreas 'randy' Weinberger
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+
+#include "budget.h"
+#include "ttpci-eeprom.h"
+
+#define TS_WIDTH		(2 * TS_SIZE)
+#define TS_WIDTH_ACTIVY		TS_SIZE
+#define TS_WIDTH_DVBC		TS_SIZE
+#define TS_HEIGHT_MASK		0xf00
+#define TS_HEIGHT_MASK_ACTIVY	0xc00
+#define TS_HEIGHT_MASK_DVBC	0xe00
+#define TS_MIN_BUFSIZE_K	188
+#define TS_MAX_BUFSIZE_K	1410
+#define TS_MAX_BUFSIZE_K_ACTIVY	564
+#define TS_MAX_BUFSIZE_K_DVBC	1316
+#define BUFFER_WARNING_WAIT	(30*HZ)
+
+int budget_debug;
+static int dma_buffer_size = TS_MIN_BUFSIZE_K;
+module_param_named(debug, budget_debug, int, 0644);
+module_param_named(bufsize, dma_buffer_size, int, 0444);
+MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
+MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
+
+/****************************************************************************
+ * TT budget / WinTV Nova
+ ****************************************************************************/
+
+static int stop_ts_capture(struct budget *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_write(budget->dev, MC1, MASK_20);	// DMA3 off
+	SAA7146_IER_DISABLE(budget->dev, MASK_10);
+	return 0;
+}
+
+static int start_ts_capture(struct budget *budget)
+{
+	struct saa7146_dev *dev = budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	if (!budget->feeding || !budget->fe_synced)
+		return 0;
+
+	saa7146_write(dev, MC1, MASK_20);	// DMA3 off
+
+	memset(budget->grabbing, 0x00, budget->buffer_size);
+
+	saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+
+	budget->ttbp = 0;
+
+	/*
+	 *  Signal path on the Activy:
+	 *
+	 *  tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory
+	 *
+	 *  Since the tuner feeds 204 bytes packets into the SAA7146,
+	 *  DMA3 is configured to strip the trailing 16 FEC bytes:
+	 *      Pitch: 188, NumBytes3: 188, NumLines3: 1024
+	 */
+
+	switch(budget->card->type) {
+	case BUDGET_FS_ACTIVY:
+		saa7146_write(dev, DD1_INIT, 0x04000000);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+		saa7146_write(dev, BRS_CTRL, 0x00000000);
+		break;
+	case BUDGET_PATCH:
+		saa7146_write(dev, DD1_INIT, 0x00000200);
+		saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		break;
+	case BUDGET_CIN1200C_MK3:
+	case BUDGET_KNC1C_MK3:
+	case BUDGET_KNC1C_TDA10024:
+	case BUDGET_KNC1CP_MK3:
+		if (budget->video_port == BUDGET_VIDEO_PORTA) {
+			saa7146_write(dev, DD1_INIT, 0x06000200);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x00000000);
+		} else {
+			saa7146_write(dev, DD1_INIT, 0x00000600);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x60000000);
+		}
+		break;
+	default:
+		if (budget->video_port == BUDGET_VIDEO_PORTA) {
+			saa7146_write(dev, DD1_INIT, 0x06000200);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x00000000);
+		} else {
+			saa7146_write(dev, DD1_INIT, 0x02000600);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x60000000);
+		}
+	}
+
+	saa7146_write(dev, MC2, (MASK_08 | MASK_24));
+	mdelay(10);
+
+	saa7146_write(dev, BASE_ODD3, 0);
+	if (budget->buffer_size > budget->buffer_height * budget->buffer_width) {
+		// using odd/even buffers
+		saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width);
+	} else {
+		// using a single buffer
+		saa7146_write(dev, BASE_EVEN3, 0);
+	}
+	saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
+	saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
+
+	saa7146_write(dev, PITCH3, budget->buffer_width);
+	saa7146_write(dev, NUM_LINE_BYTE3,
+			(budget->buffer_height << 16) | budget->buffer_width);
+
+	saa7146_write(dev, MC2, (MASK_04 | MASK_20));
+
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);	/* VPE */
+	SAA7146_IER_ENABLE(budget->dev, MASK_10);	/* VPE */
+	saa7146_write(dev, MC1, (MASK_04 | MASK_20));	/* DMA3 on */
+
+	return 0;
+}
+
+static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	int synced;
+	int ret;
+
+	if (budget->read_fe_status)
+		ret = budget->read_fe_status(fe, status);
+	else
+		ret = -EINVAL;
+
+	if (!ret) {
+		synced = (*status & FE_HAS_LOCK);
+		if (synced != budget->fe_synced) {
+			budget->fe_synced = synced;
+			spin_lock(&budget->feedlock);
+			if (synced)
+				start_ts_capture(budget);
+			else
+				stop_ts_capture(budget);
+			spin_unlock(&budget->feedlock);
+		}
+	}
+	return ret;
+}
+
+static void vpeirq(unsigned long data)
+{
+	struct budget *budget = (struct budget *) data;
+	u8 *mem = (u8 *) (budget->grabbing);
+	u32 olddma = budget->ttbp;
+	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+	u32 count;
+
+	/* Ensure streamed PCI data is synced to CPU */
+	pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE);
+
+	/* nearest lower position divisible by 188 */
+	newdma -= newdma % 188;
+
+	if (newdma >= budget->buffer_size)
+		return;
+
+	budget->ttbp = newdma;
+
+	if (budget->feeding == 0 || newdma == olddma)
+		return;
+
+	if (newdma > olddma) {	/* no wraparound, dump olddma..newdma */
+		count = newdma - olddma;
+		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
+	} else {		/* wraparound, dump olddma..buflen and 0..newdma */
+		count = budget->buffer_size - olddma;
+		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188);
+		count += newdma;
+		dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
+	}
+
+	if (count > budget->buffer_warning_threshold)
+		budget->buffer_warnings++;
+
+	if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) {
+		printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n",
+			budget->dev->name, __func__, budget->buffer_warnings, count);
+		budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT;
+		budget->buffer_warnings = 0;
+	}
+}
+
+
+int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+			  int uselocks, int nobusyloop)
+{
+	struct saa7146_dev *saa = budget->dev;
+	int result = 0;
+	unsigned long flags = 0;
+
+	if (count > 4 || count <= 0)
+		return 0;
+
+	if (uselocks)
+		spin_lock_irqsave(&budget->debilock, flags);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+	saa7146_write(saa, DEBI_CONFIG, config);
+	saa7146_write(saa, DEBI_PAGE, 0);
+	saa7146_write(saa, MC2, (2 << 16) | 2);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	result = saa7146_read(saa, DEBI_AD);
+	result &= (0xffffffffUL >> ((4 - count) * 8));
+
+	if (uselocks)
+		spin_unlock_irqrestore(&budget->debilock, flags);
+
+	return result;
+}
+
+int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
+			   int count, u32 value, int uselocks, int nobusyloop)
+{
+	struct saa7146_dev *saa = budget->dev;
+	unsigned long flags = 0;
+	int result;
+
+	if (count > 4 || count <= 0)
+		return 0;
+
+	if (uselocks)
+		spin_lock_irqsave(&budget->debilock, flags);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff));
+	saa7146_write(saa, DEBI_CONFIG, config);
+	saa7146_write(saa, DEBI_PAGE, 0);
+	saa7146_write(saa, DEBI_AD, value);
+	saa7146_write(saa, MC2, (2 << 16) | 2);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	if (uselocks)
+		spin_unlock_irqrestore(&budget->debilock, flags);
+	return 0;
+}
+
+
+/****************************************************************************
+ * DVB API SECTION
+ ****************************************************************************/
+
+static int budget_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct budget *budget = (struct budget *) demux->priv;
+	int status = 0;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	spin_lock(&budget->feedlock);
+	feed->pusi_seen = 0; /* have a clean section start */
+	if (budget->feeding++ == 0)
+		status = start_ts_capture(budget);
+	spin_unlock(&budget->feedlock);
+	return status;
+}
+
+static int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct budget *budget = (struct budget *) demux->priv;
+	int status = 0;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	spin_lock(&budget->feedlock);
+	if (--budget->feeding == 0)
+		status = stop_ts_capture(budget);
+	spin_unlock(&budget->feedlock);
+	return status;
+}
+
+static int budget_register(struct budget *budget)
+{
+	struct dvb_demux *dvbdemux = &budget->demux;
+	int ret;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	dvbdemux->priv = (void *) budget;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = budget_start_feed;
+	dvbdemux->stop_feed = budget_stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+
+	dvb_dmx_init(&budget->demux);
+
+	budget->dmxdev.filternum = 256;
+	budget->dmxdev.demux = &dvbdemux->dmx;
+	budget->dmxdev.capabilities = 0;
+
+	dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter);
+
+	budget->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	budget->mem_frontend.source = DMX_MEMORY_FE;
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+	if (ret < 0)
+		return ret;
+
+	ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+	if (ret < 0)
+		return ret;
+
+	dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
+
+	return 0;
+}
+
+static void budget_unregister(struct budget *budget)
+{
+	struct dvb_demux *dvbdemux = &budget->demux;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	dvb_net_release(&budget->dvb_net);
+
+	dvbdemux->dmx.close(&dvbdemux->dmx);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+
+	dvb_dmxdev_release(&budget->dmxdev);
+	dvb_dmx_release(&budget->demux);
+}
+
+int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+		      struct saa7146_pci_extension_data *info,
+		      struct module *owner, short *adapter_nums)
+{
+	int ret = 0;
+	struct budget_info *bi = info->ext_priv;
+	int max_bufsize;
+	int height_mask;
+
+	memset(budget, 0, sizeof(struct budget));
+
+	dprintk(2, "dev: %p, budget: %p\n", dev, budget);
+
+	budget->card = bi;
+	budget->dev = (struct saa7146_dev *) dev;
+
+	switch(budget->card->type) {
+	case BUDGET_FS_ACTIVY:
+		budget->buffer_width = TS_WIDTH_ACTIVY;
+		max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
+		height_mask = TS_HEIGHT_MASK_ACTIVY;
+		break;
+
+	case BUDGET_KNC1C:
+	case BUDGET_KNC1CP:
+	case BUDGET_CIN1200C:
+	case BUDGET_KNC1C_MK3:
+	case BUDGET_KNC1C_TDA10024:
+	case BUDGET_KNC1CP_MK3:
+	case BUDGET_CIN1200C_MK3:
+		budget->buffer_width = TS_WIDTH_DVBC;
+		max_bufsize = TS_MAX_BUFSIZE_K_DVBC;
+		height_mask = TS_HEIGHT_MASK_DVBC;
+		break;
+
+	default:
+		budget->buffer_width = TS_WIDTH;
+		max_bufsize = TS_MAX_BUFSIZE_K;
+		height_mask = TS_HEIGHT_MASK;
+	}
+
+	if (dma_buffer_size < TS_MIN_BUFSIZE_K)
+		dma_buffer_size = TS_MIN_BUFSIZE_K;
+	else if (dma_buffer_size > max_bufsize)
+		dma_buffer_size = max_bufsize;
+
+	budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
+	if (budget->buffer_height > 0xfff) {
+		budget->buffer_height /= 2;
+		budget->buffer_height &= height_mask;
+		budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width;
+	} else {
+		budget->buffer_height &= height_mask;
+		budget->buffer_size = budget->buffer_height * budget->buffer_width;
+	}
+	budget->buffer_warning_threshold = budget->buffer_size * 80/100;
+	budget->buffer_warnings = 0;
+	budget->buffer_warning_time = jiffies;
+
+	dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n",
+		budget->dev->name,
+		budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
+		budget->buffer_width, budget->buffer_height);
+	printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
+
+	ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
+				   owner, &budget->dev->pci->dev, adapter_nums);
+	if (ret < 0)
+		return ret;
+
+	/* set dd1 stream a & b */
+	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+	saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+	saa7146_write(dev, DD1_INIT, 0x02000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	if (bi->type != BUDGET_FS_ACTIVY)
+		budget->video_port = BUDGET_VIDEO_PORTB;
+	else
+		budget->video_port = BUDGET_VIDEO_PORTA;
+	spin_lock_init(&budget->feedlock);
+	spin_lock_init(&budget->debilock);
+
+	/* the Siemens DVB needs this if you want to have the i2c chips
+	   get recognized before the main driver is loaded */
+	if (bi->type != BUDGET_FS_ACTIVY)
+		saa7146_write(dev, GPIO_CTRL, 0x500000);	/* GPIO 3 = 1 */
+
+	strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name));
+
+	saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120);
+	strcpy(budget->i2c_adap.name, budget->card->name);
+
+	if (i2c_add_adapter(&budget->i2c_adap) < 0) {
+		ret = -ENOMEM;
+		goto err_dvb_unregister;
+	}
+
+	ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
+
+	budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt);
+	if (NULL == budget->grabbing) {
+		ret = -ENOMEM;
+		goto err_del_i2c;
+	}
+
+	saa7146_write(dev, PCI_BT_V1, 0x001c0000);
+	/* upload all */
+	saa7146_write(dev, GPIO_CTRL, 0x000000);
+
+	tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
+
+	/* frontend power on */
+	if (bi->type != BUDGET_FS_ACTIVY)
+		saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+	if ((ret = budget_register(budget)) == 0)
+		return 0; /* Everything OK */
+
+	/* An error occurred, cleanup resources */
+	saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
+
+err_del_i2c:
+	i2c_del_adapter(&budget->i2c_adap);
+
+err_dvb_unregister:
+	dvb_unregister_adapter(&budget->dvb_adapter);
+
+	return ret;
+}
+
+void ttpci_budget_init_hooks(struct budget *budget)
+{
+	if (budget->dvb_frontend && !budget->read_fe_status) {
+		budget->read_fe_status = budget->dvb_frontend->ops.read_status;
+		budget->dvb_frontend->ops.read_status = budget_read_fe_status;
+	}
+}
+
+int ttpci_budget_deinit(struct budget *budget)
+{
+	struct saa7146_dev *dev = budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	budget_unregister(budget);
+
+	tasklet_kill(&budget->vpe_tasklet);
+
+	saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt);
+
+	i2c_del_adapter(&budget->i2c_adap);
+
+	dvb_unregister_adapter(&budget->dvb_adapter);
+
+	return 0;
+}
+
+void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget *budget = (struct budget *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget: %p\n", dev, budget);
+
+	if (*isr & MASK_10)
+		tasklet_schedule(&budget->vpe_tasklet);
+}
+
+void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
+{
+	struct budget *budget = (struct budget *) dev->ext_priv;
+
+	spin_lock(&budget->feedlock);
+	budget->video_port = video_port;
+	if (budget->feeding) {
+		stop_ts_capture(budget);
+		start_ts_capture(budget);
+	}
+	spin_unlock(&budget->feedlock);
+}
+
+EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
+EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
+EXPORT_SYMBOL_GPL(ttpci_budget_init);
+EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks);
+EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
+EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
+EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
+EXPORT_SYMBOL_GPL(budget_debug);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c
new file mode 100644
index 000000000000..2cb35c23d2ac
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget-patch.c
@@ -0,0 +1,680 @@
+/*
+ * budget-patch.c: driver for Budget Patch,
+ * hardware modification of DVB-S cards enabling full TS
+ *
+ * Written by Emard <emard@softhome.net>
+ *
+ * Original idea by Roberto Deza <rdeza@unav.es>
+ *
+ * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic
+ * and Metzlerbros
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "tda8083.h"
+
+#include "bsru6.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define budget_patch budget
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH);
+//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000),
+//        MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+	{
+		.vendor    = 0,
+	}
+};
+
+/* those lines are for budget-patch to be tried
+** on a true budget card and observe the
+** behaviour of VSYNC generated by rps1.
+** this code was shamelessly copy/pasted from budget.c
+*/
+static void gpio_Set22K (struct budget *budget, int state)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+	saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+   Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	udelay(data ? 500 : 1000);
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+	int i, par=1, d;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	for (i=7; i>=0; i--) {
+		d = (data>>i)&1;
+		par ^= d;
+		DiseqcSendBit(budget, d);
+	}
+
+	DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+	struct saa7146_dev *dev=budget->dev;
+	int i;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	mdelay(16);
+
+	for (i=0; i<len; i++)
+		DiseqcSendByte(budget, msg[i]);
+
+	mdelay(16);
+
+	if (burst!=-1) {
+		if (burst)
+			DiseqcSendByte(budget, 0xff);
+		else {
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+			mdelay(12);
+			udelay(500);
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+/* shamelessly copy/pasted from budget.c
+*/
+static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		gpio_Set22K (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		gpio_Set22K (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length)
+{
+	int i;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	for (i = 2; i < length; i++)
+	{
+		  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0);
+		  msleep(5);
+	}
+	if (length)
+		  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0);
+	else
+		  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0);
+	msleep(5);
+	ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0);
+	msleep(5);
+	return 0;
+}
+
+static void av7110_set22k(struct budget_patch *budget, int state)
+{
+	u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0};
+
+	dprintk(2, "budget: %p\n", budget);
+	budget_av7110_send_fw_cmd(budget, buf, 2);
+}
+
+static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst)
+{
+	int i;
+	u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC),
+		16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	dprintk(2, "budget: %p\n", budget);
+
+	if (len>10)
+		len=10;
+
+	buf[1] = len+2;
+	buf[2] = len;
+
+	if (burst != -1)
+		buf[3]=burst ? 0x01 : 0x00;
+	else
+		buf[3]=0xffff;
+
+	for (i=0; i<len; i++)
+		buf[i+4]=msg[i];
+
+	budget_av7110_send_fw_cmd(budget, buf, 18);
+	return 0;
+}
+
+static int budget_patch_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		av7110_set22k (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		av7110_set22k (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	av7110_send_diseqc_msg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (p->frequency + 479500) / 125;
+
+	if (p->frequency > 2000000)
+		pwr = 3;
+	else if (p->frequency > 1800000)
+		pwr = 2;
+	else if (p->frequency > 1600000)
+		pwr = 1;
+	else if (p->frequency > 1200000)
+		pwr = 0;
+	else if (p->frequency >= 1100000)
+		pwr = 1;
+	else pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+	// NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config = {
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+};
+
+static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = p->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+};
+
+static void frontend_init(struct budget_patch* budget)
+{
+	switch(budget->dev->pci->subsystem_device) {
+	case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X
+	case 0x1013: // SATELCO Multimedia PCI
+
+		// try the ALPS BSRV2 first of all
+		budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+			budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst;
+			budget->dvb_frontend->ops.set_tone = budget_patch_set_tone;
+			break;
+		}
+
+		// try the ALPS BSRU6 now
+		budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+			budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+
+			budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops.set_tone = budget_set_tone;
+			break;
+		}
+
+		// Try the grundig 29504-451
+		budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+			budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops.set_tone = budget_set_tone;
+			break;
+		}
+		break;
+	}
+
+	if (budget->dvb_frontend == NULL) {
+		printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       budget->dev->pci->vendor,
+		       budget->dev->pci->device,
+		       budget->dev->pci->subsystem_vendor,
+		       budget->dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) {
+			printk("budget-av: Frontend registration failed!\n");
+			dvb_frontend_detach(budget->dvb_frontend);
+			budget->dvb_frontend = NULL;
+		}
+	}
+}
+
+/* written by Emard */
+static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget_patch *budget;
+	int err;
+	int count = 0;
+	int detected = 0;
+
+#define PATCH_RESET 0
+#define RPS_IRQ 0
+#define HPS_SETUP 0
+#if PATCH_RESET
+	saa7146_write(dev, MC1, MASK_31);
+	msleep(40);
+#endif
+#if HPS_SETUP
+	// initialize registers. Better to have it like this
+	// than leaving something unconfigured
+	saa7146_write(dev, DD1_STREAM_B, 0);
+	// port B VSYNC at rising edge
+	saa7146_write(dev, DD1_INIT, 0x00000200);  // have this in budget-core too!
+	saa7146_write(dev, BRS_CTRL, 0x00000000);  // VBI
+
+	// debi config
+	// saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18);
+
+	// zero all HPS registers
+	saa7146_write(dev, HPS_H_PRESCALE, 0);                  // r68
+	saa7146_write(dev, HPS_H_SCALE, 0);                     // r6c
+	saa7146_write(dev, BCS_CTRL, 0);                        // r70
+	saa7146_write(dev, HPS_V_SCALE, 0);                     // r60
+	saa7146_write(dev, HPS_V_GAIN, 0);                      // r64
+	saa7146_write(dev, CHROMA_KEY_RANGE, 0);                // r74
+	saa7146_write(dev, CLIP_FORMAT_CTRL, 0);                // r78
+	// Set HPS prescaler for port B input
+	saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) );
+	saa7146_write(dev, MC2,
+	  0 * (MASK_08 | MASK_24)  |   // BRS control
+	  0 * (MASK_09 | MASK_25)  |   // a
+	  0 * (MASK_10 | MASK_26)  |   // b
+	  1 * (MASK_06 | MASK_22)  |   // HPS_CTRL1
+	  1 * (MASK_05 | MASK_21)  |   // HPS_CTRL2
+	  0 * (MASK_01 | MASK_15)      // DEBI
+	   );
+#endif
+	// Disable RPS1 and RPS0
+	saa7146_write(dev, MC1, ( MASK_29 | MASK_28));
+	// RPS1 timeout disable
+	saa7146_write(dev, RPS_TOV1, 0);
+
+	// code for autodetection
+	// will wait for VBI_B event (vertical blank at port B)
+	// and will reset GPIO3 after VBI_B is detected.
+	// (GPIO3 should be raised high by CPU to
+	// test if GPIO3 will generate vertical blank signal
+	// in budget patch GPIO3 is connected to VSYNC_B
+	count = 0;
+#if 0
+	WRITE_RPS1(CMD_UPLOAD |
+	  MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 );
+#endif
+	WRITE_RPS1(CMD_PAUSE | EVT_VBI_B);
+	WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+	WRITE_RPS1(GPIO3_MSK);
+	WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
+#if RPS_IRQ
+	// issue RPS1 interrupt to increment counter
+	WRITE_RPS1(CMD_INTERRUPT);
+	// at least a NOP is neede between two interrupts
+	WRITE_RPS1(CMD_NOP);
+	// interrupt again
+	WRITE_RPS1(CMD_INTERRUPT);
+#endif
+	WRITE_RPS1(CMD_STOP);
+
+#if RPS_IRQ
+	// set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+	// use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+	// use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+	saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+	// set event counter 1 threshold to maximum allowed value        (rEC p55)
+	saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+	// Fix VSYNC level
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	// Set RPS1 Address register to point to RPS code               (r108 p42)
+	saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+	// Enable RPS1,                                                 (rFC p33)
+	saa7146_write(dev, MC1, (MASK_13 | MASK_29 ));
+
+
+	mdelay(50);
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	mdelay(150);
+
+
+	if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0)
+		detected = 1;
+
+#if RPS_IRQ
+	printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff );
+#endif
+	// Disable RPS1
+	saa7146_write(dev, MC1, ( MASK_29 ));
+
+	if(detected == 0)
+		printk("budget-patch not detected or saa7146 in non-default state.\n"
+		       "try enabling ressetting of 7146 with MASK_31 in MC1 register\n");
+
+	else
+		printk("BUDGET-PATCH DETECTED.\n");
+
+
+/*      OLD (Original design by Roberto Deza):
+**      This code will setup the SAA7146_RPS1 to generate a square
+**      wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of
+**      TS_WIDTH packets) has been acquired on SAA7146_D1B video port;
+**      then, this GPIO3 output which is connected to the D1B_VSYNC
+**      input, will trigger the acquisition of the alternate field
+**      and so on.
+**      Currently, the TT_budget / WinTV_Nova cards have two ICs
+**      (74HCT4040, LVC74) for the generation of this VSYNC signal,
+**      which seems that can be done perfectly without this :-)).
+*/
+
+/*      New design (By Emard)
+**      this rps1 code will copy internal HS event to GPIO3 pin.
+**      GPIO3 is in budget-patch hardware connected to port B VSYNC
+
+**      HS is an internal event of 7146, accessible with RPS
+**      and temporarily raised high every n lines
+**      (n in defined in the RPS_THRESH1 counter threshold)
+**      I think HS is raised high on the beginning of the n-th line
+**      and remains high until this n-th line that triggered
+**      it is completely received. When the reception of n-th line
+**      ends, HS is lowered.
+
+**      To transmit data over DMA, 7146 needs changing state at
+**      port B VSYNC pin. Any changing of port B VSYNC will
+**      cause some DMA data transfer, with more or less packets loss.
+**      It depends on the phase and frequency of VSYNC and
+**      the way of 7146 is instructed to trigger on port B (defined
+**      in DD1_INIT register, 3rd nibble from the right valid
+**      numbers are 0-7, see datasheet)
+**
+**      The correct triggering can minimize packet loss,
+**      dvbtraffic should give this stable bandwidths:
+**        22k transponder = 33814 kbit/s
+**      27.5k transponder = 38045 kbit/s
+**      by experiment it is found that the best results
+**      (stable bandwidths and almost no packet loss)
+**      are obtained using DD1_INIT triggering number 2
+**      (Va at rising edge of VS Fa = HS x VS-failing forced toggle)
+**      and a VSYNC phase that occurs in the middle of DMA transfer
+**      (about byte 188*512=96256 in the DMA window).
+**
+**      Phase of HS is still not clear to me how to control,
+**      It just happens to be so. It can be seen if one enables
+**      RPS_IRQ and print Event Counter 1 in vpeirq(). Every
+**      time RPS_INTERRUPT is called, the Event Counter 1 will
+**      increment. That's how the 7146 is programmed to do event
+**      counting in this budget-patch.c
+**      I *think* HPS setting has something to do with the phase
+**      of HS but I can't be 100% sure in that.
+
+**      hardware debug note: a working budget card (including budget patch)
+**      with vpeirq() interrupt setup in mode "0x90" (every 64K) will
+**      generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
+**      and that means 3*25=75 Hz of interrupt frequency, as seen by
+**      watch cat /proc/interrupts
+**
+**      If this frequency is 3x lower (and data received in the DMA
+**      buffer don't start with 0x47, but in the middle of packets,
+**      whose lengths appear to be like 188 292 188 104 etc.
+**      this means VSYNC line is not connected in the hardware.
+**      (check soldering pcb and pins)
+**      The same behaviour of missing VSYNC can be duplicated on budget
+**      cards, by setting DD1_INIT trigger mode 7 in 3rd nibble.
+*/
+
+	// Setup RPS1 "program" (p35)
+	count = 0;
+
+
+	// Wait Source Line Counter Threshold                           (p36)
+	WRITE_RPS1(CMD_PAUSE | EVT_HS);
+	// Set GPIO3=1                                                  (p42)
+	WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+	WRITE_RPS1(GPIO3_MSK);
+	WRITE_RPS1(SAA7146_GPIO_OUTHI<<24);
+#if RPS_IRQ
+	// issue RPS1 interrupt
+	WRITE_RPS1(CMD_INTERRUPT);
+#endif
+	// Wait reset Source Line Counter Threshold                     (p36)
+	WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS);
+	// Set GPIO3=0                                                  (p42)
+	WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+	WRITE_RPS1(GPIO3_MSK);
+	WRITE_RPS1(SAA7146_GPIO_OUTLO<<24);
+#if RPS_IRQ
+	// issue RPS1 interrupt
+	WRITE_RPS1(CMD_INTERRUPT);
+#endif
+	// Jump to begin of RPS program                                 (p37)
+	WRITE_RPS1(CMD_JUMP);
+	WRITE_RPS1(dev->d_rps1.dma_handle);
+
+	// Fix VSYNC level
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	// Set RPS1 Address register to point to RPS code               (r108 p42)
+	saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+	if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
+		return -ENOMEM;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+	if (err) {
+		kfree(budget);
+		return err;
+	}
+
+	// Set Source Line Counter Threshold, using BRS                 (rCC p43)
+	// It generates HS event every TS_HEIGHT lines
+	// this is related to TS_WIDTH set in register
+	// NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE
+	// low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188
+	//,then RPS_THRESH1
+	// should be set to trigger every TS_HEIGHT (512) lines.
+	//
+	saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 );
+
+	// saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 );
+	// Enable RPS1                                                  (rFC p33)
+	saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+
+	dev->ext_priv = budget;
+
+	budget->dvb_adapter.priv = budget;
+	frontend_init(budget);
+
+	ttpci_budget_init_hooks(budget);
+
+	return 0;
+}
+
+static int budget_patch_detach (struct saa7146_dev* dev)
+{
+	struct budget_patch *budget = (struct budget_patch*) dev->ext_priv;
+	int err;
+
+	if (budget->dvb_frontend) {
+		dvb_unregister_frontend(budget->dvb_frontend);
+		dvb_frontend_detach(budget->dvb_frontend);
+	}
+	err = ttpci_budget_deinit (budget);
+
+	kfree (budget);
+
+	return err;
+}
+
+static int __init budget_patch_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_patch_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+static struct saa7146_extension budget_extension = {
+	.name           = "budget_patch dvb",
+	.flags          = 0,
+
+	.module         = THIS_MODULE,
+	.pci_tbl        = pci_tbl,
+	.attach         = budget_patch_attach,
+	.detach         = budget_patch_detach,
+
+	.irq_mask       = MASK_10,
+	.irq_func       = ttpci_budget_irq10_handler,
+};
+
+module_init(budget_patch_init);
+module_exit(budget_patch_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others");
+MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 "
+		   "based so-called Budget Patch cards");
diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
new file mode 100644
index 000000000000..7e6e43ae5c51
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget.c
@@ -0,0 +1,871 @@
+/*
+ * budget.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ *           Michael Dreher <michael@5dot1.de>,
+ *           Oliver Endriss <o.endriss@gmx.de> and
+ *           Andreas 'randy' Weinberger
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/ 
+ */
+
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "ves1820.h"
+#include "l64781.h"
+#include "tda8083.h"
+#include "s5h1420.h"
+#include "tda10086.h"
+#include "tda826x.h"
+#include "lnbp21.h"
+#include "bsru6.h"
+#include "bsbe1.h"
+#include "tdhd1.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "isl6423.h"
+#include "lnbh24.h"
+
+
+static int diseqc_method;
+module_param(diseqc_method, int, 0444);
+MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static void Set22K (struct budget *budget, int state)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+	saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+   Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	udelay(data ? 500 : 1000);
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+	int i, par=1, d;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	for (i=7; i>=0; i--) {
+		d = (data>>i)&1;
+		par ^= d;
+		DiseqcSendBit(budget, d);
+	}
+
+	DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+	struct saa7146_dev *dev=budget->dev;
+	int i;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	mdelay(16);
+
+	for (i=0; i<len; i++)
+		DiseqcSendByte(budget, msg[i]);
+
+	mdelay(16);
+
+	if (burst!=-1) {
+		if (burst)
+			DiseqcSendByte(budget, 0xff);
+		else {
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+			mdelay(12);
+			udelay(500);
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+/*
+ *   Routines for the Fujitsu Siemens Activy budget card
+ *   22 kHz tone and DiSEqC are handled by the frontend.
+ *   Voltage must be set here.
+ *   GPIO 1: LNBP EN, GPIO 2: LNBP VSEL
+ */
+static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
+{
+	struct saa7146_dev *dev=budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+			saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+			break;
+		case SEC_VOLTAGE_18:
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+			saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+			break;
+		case SEC_VOLTAGE_OFF:
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	return SetVoltage_Activy (budget, voltage);
+}
+
+static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		Set22K (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		Set22K (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (c->frequency + 479500) / 125;
+
+	if (c->frequency > 2000000)
+		pwr = 3;
+	else if (c->frequency > 1800000)
+		pwr = 2;
+	else if (c->frequency > 1600000)
+		pwr = 1;
+	else if (c->frequency > 1200000)
+		pwr = 0;
+	else if (c->frequency >= 1100000)
+		pwr = 1;
+	else pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+	// NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config =
+{
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+};
+
+static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (c->frequency + 35937500 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85 | ((div >> 10) & 0x60);
+	data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+};
+
+static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget *budget = fe->dvb->priv;
+	u8 *tuner_addr = fe->tuner_priv;
+	u32 div;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) };
+
+	if (tuner_addr)
+		msg.addr = *tuner_addr;
+	else
+		msg.addr = 0x61;
+
+	div = (36125000 + c->frequency) / 166666;
+
+	cfg = 0x88;
+
+	if (c->frequency < 175000000)
+		cpump = 2;
+	else if (c->frequency < 390000000)
+		cpump = 1;
+	else if (c->frequency < 470000000)
+		cpump = 2;
+	else if (c->frequency < 750000000)
+		cpump = 1;
+	else
+		cpump = 3;
+
+	if (c->frequency < 175000000)
+		band_select = 0x0e;
+	else if (c->frequency < 470000000)
+		band_select = 0x05;
+	else
+		band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = (cpump << 6) | band_select;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+	.demod_address = 0x55,
+};
+
+static struct l64781_config grundig_29504_401_config_activy = {
+	.demod_address = 0x54,
+};
+
+static u8 tuner_address_grundig_29504_401_activy = 0x60;
+
+static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = c->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+};
+
+static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = c->frequency / 1000;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0xc2;
+
+	if (div < 1450)
+		data[3] = 0x00;
+	else if (div < 1850)
+		data[3] = 0x40;
+	else if (div < 2000)
+		data[3] = 0x80;
+	else
+		data[3] = 0xc0;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+
+	return 0;
+}
+
+static struct s5h1420_config s5h1420_config = {
+	.demod_address = 0x53,
+	.invert = 1,
+	.cdclk_polarity = 1,
+};
+
+static struct tda10086_config tda10086_config = {
+	.demod_address = 0x0e,
+	.invert = 0,
+	.diseqc_tone = 1,
+	.xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct stv0299_config alps_bsru6_config_activy = {
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.op0_off = 1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsru6_set_symbol_rate,
+};
+
+static struct stv0299_config alps_bsbe1_config_activy = {
+	.demod_address = 0x68,
+	.inittab = alps_bsbe1_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.op0_off = 1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsbe1_set_symbol_rate,
+};
+
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
+{
+	struct budget *budget = (struct budget *)fe->dvb->priv;
+
+	return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+
+static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
+{
+	u8 val;
+	struct i2c_msg msg[] = {
+		{ .addr = adr, .flags = 0, .buf = &reg, .len = 1 },
+		{ .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+	};
+
+	return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
+}
+
+static u8 read_pwm(struct budget* budget)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+				 { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+	if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static struct stv090x_config tt1600_stv090x_config = {
+	.device			= STV0903,
+	.demod_mode		= STV090x_SINGLE,
+	.clk_mode		= STV090x_CLK_EXT,
+
+	.xtal			= 13500000,
+	.address		= 0x68,
+
+	.ts1_mode		= STV090x_TSMODE_DVBCI,
+	.ts2_mode		= STV090x_TSMODE_SERIAL_CONTINUOUS,
+
+	.repeater_level		= STV090x_RPTLEVEL_16,
+
+	.tuner_init		= NULL,
+	.tuner_sleep		= NULL,
+	.tuner_set_mode		= NULL,
+	.tuner_set_frequency	= NULL,
+	.tuner_get_frequency	= NULL,
+	.tuner_set_bandwidth	= NULL,
+	.tuner_get_bandwidth	= NULL,
+	.tuner_set_bbgain	= NULL,
+	.tuner_get_bbgain	= NULL,
+	.tuner_set_refclk	= NULL,
+	.tuner_get_status	= NULL,
+};
+
+static struct stv6110x_config tt1600_stv6110x_config = {
+	.addr			= 0x60,
+	.refclk			= 27000000,
+	.clk_div		= 2,
+};
+
+static struct isl6423_config tt1600_isl6423_config = {
+	.current_max		= SEC_CURRENT_515m,
+	.curlim			= SEC_CURRENT_LIM_ON,
+	.mod_extern		= 1,
+	.addr			= 0x08,
+};
+
+static void frontend_init(struct budget *budget)
+{
+	(void)alps_bsbe1_config; /* avoid warning */
+
+	switch(budget->dev->pci->subsystem_device) {
+	case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
+	case 0x1013:
+		// try the ALPS BSRV2 first of all
+		budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+			budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops.set_tone = budget_set_tone;
+			break;
+		}
+
+		// try the ALPS BSRU6 now
+		budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+			budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+			if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) {
+				budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+				budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+				budget->dvb_frontend->ops.set_tone = budget_set_tone;
+			}
+			break;
+		}
+		break;
+
+	case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
+
+		budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
+			break;
+		}
+		break;
+
+	case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
+
+		budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+			budget->dvb_frontend->tuner_priv = NULL;
+			break;
+		}
+		break;
+
+	case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */
+	{
+		int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67);
+
+		if (subtype < 0)
+			break;
+		/* fixme: find a better way to identify the card */
+		if (subtype < 0x36) {
+			/* assume ALPS BSRU6 */
+			budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
+			if (budget->dvb_frontend) {
+				printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
+				budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+				budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+				budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+				budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+				break;
+			}
+		} else {
+			/* assume ALPS BSBE1 */
+			/* reset tuner */
+			saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO);
+			msleep(50);
+			saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI);
+			msleep(250);
+			budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
+			if (budget->dvb_frontend) {
+				printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
+				budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+				budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+				budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+				budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+				break;
+			}
+		}
+		break;
+	}
+
+	case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
+		budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+			budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+			budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+		}
+		break;
+
+	case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
+		budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
+			budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+		}
+		break;
+
+	case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
+		budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy;
+			budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+		}
+		break;
+
+	case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
+		budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
+			if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+				printk("%s: No LNBP21 found!\n", __func__);
+				goto error_out;
+			}
+			break;
+		}
+
+	case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
+		// gpio2 is connected to CLB - reset it + leave it high
+		saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+		msleep(1);
+		saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+		msleep(1);
+
+		budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
+				printk("%s: No tda826x found!\n", __func__);
+			if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+				printk("%s: No LNBP21 found!\n", __func__);
+				goto error_out;
+			}
+			break;
+		}
+
+	case 0x101c: { /* TT S2-1600 */
+			struct stv6110x_devctl *ctl;
+			saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+			msleep(50);
+			saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+			msleep(250);
+
+			budget->dvb_frontend = dvb_attach(stv090x_attach,
+							  &tt1600_stv090x_config,
+							  &budget->i2c_adap,
+							  STV090x_DEMODULATOR_0);
+
+			if (budget->dvb_frontend) {
+
+				ctl = dvb_attach(stv6110x_attach,
+						 budget->dvb_frontend,
+						 &tt1600_stv6110x_config,
+						 &budget->i2c_adap);
+
+				if (ctl) {
+					tt1600_stv090x_config.tuner_init	  = ctl->tuner_init;
+					tt1600_stv090x_config.tuner_sleep	  = ctl->tuner_sleep;
+					tt1600_stv090x_config.tuner_set_mode	  = ctl->tuner_set_mode;
+					tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+					tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+					tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+					tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+					tt1600_stv090x_config.tuner_set_bbgain	  = ctl->tuner_set_bbgain;
+					tt1600_stv090x_config.tuner_get_bbgain	  = ctl->tuner_get_bbgain;
+					tt1600_stv090x_config.tuner_set_refclk	  = ctl->tuner_set_refclk;
+					tt1600_stv090x_config.tuner_get_status	  = ctl->tuner_get_status;
+
+					/* call the init function once to initialize
+					   tuner's clock output divider and demod's
+					   master clock */
+					if (budget->dvb_frontend->ops.init)
+						budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+					if (dvb_attach(isl6423_attach,
+						       budget->dvb_frontend,
+						       &budget->i2c_adap,
+						       &tt1600_isl6423_config) == NULL) {
+						printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
+						goto error_out;
+					}
+				} else {
+					printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+					goto error_out;
+				}
+			}
+		}
+		break;
+
+	case 0x1020: { /* Omicom S2 */
+			struct stv6110x_devctl *ctl;
+			saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+			msleep(50);
+			saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+			msleep(250);
+
+			budget->dvb_frontend = dvb_attach(stv090x_attach,
+							  &tt1600_stv090x_config,
+							  &budget->i2c_adap,
+							  STV090x_DEMODULATOR_0);
+
+			if (budget->dvb_frontend) {
+				printk(KERN_INFO "budget: Omicom S2 detected\n");
+
+				ctl = dvb_attach(stv6110x_attach,
+						 budget->dvb_frontend,
+						 &tt1600_stv6110x_config,
+						 &budget->i2c_adap);
+
+				if (ctl) {
+					tt1600_stv090x_config.tuner_init	  = ctl->tuner_init;
+					tt1600_stv090x_config.tuner_sleep	  = ctl->tuner_sleep;
+					tt1600_stv090x_config.tuner_set_mode	  = ctl->tuner_set_mode;
+					tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+					tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+					tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+					tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+					tt1600_stv090x_config.tuner_set_bbgain	  = ctl->tuner_set_bbgain;
+					tt1600_stv090x_config.tuner_get_bbgain	  = ctl->tuner_get_bbgain;
+					tt1600_stv090x_config.tuner_set_refclk	  = ctl->tuner_set_refclk;
+					tt1600_stv090x_config.tuner_get_status	  = ctl->tuner_get_status;
+
+					/* call the init function once to initialize
+					   tuner's clock output divider and demod's
+					   master clock */
+					if (budget->dvb_frontend->ops.init)
+						budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+					if (dvb_attach(lnbh24_attach,
+							budget->dvb_frontend,
+							&budget->i2c_adap,
+							LNBH24_PCL | LNBH24_TTX,
+							LNBH24_TEN, 0x14>>1) == NULL) {
+						printk(KERN_ERR
+						"No LNBH24 found!\n");
+						goto error_out;
+					}
+				} else {
+					printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+					goto error_out;
+				}
+			}
+		}
+		break;
+	}
+
+	if (budget->dvb_frontend == NULL) {
+		printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+		       budget->dev->pci->vendor,
+		       budget->dev->pci->device,
+		       budget->dev->pci->subsystem_vendor,
+		       budget->dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend))
+			goto error_out;
+	}
+	return;
+
+error_out:
+	printk("budget: Frontend registration failed!\n");
+	dvb_frontend_detach(budget->dvb_frontend);
+	budget->dvb_frontend = NULL;
+	return;
+}
+
+static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget *budget = NULL;
+	int err;
+
+	budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
+	if( NULL == budget ) {
+		return -ENOMEM;
+	}
+
+	dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
+
+	dev->ext_priv = budget;
+
+	err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+	if (err) {
+		printk("==> failed\n");
+		kfree (budget);
+		return err;
+	}
+
+	budget->dvb_adapter.priv = budget;
+	frontend_init(budget);
+
+	ttpci_budget_init_hooks(budget);
+
+	return 0;
+}
+
+static int budget_detach (struct saa7146_dev* dev)
+{
+	struct budget *budget = (struct budget*) dev->ext_priv;
+	int err;
+
+	if (budget->dvb_frontend) {
+		dvb_unregister_frontend(budget->dvb_frontend);
+		dvb_frontend_detach(budget->dvb_frontend);
+	}
+
+	err = ttpci_budget_deinit (budget);
+
+	kfree (budget);
+	dev->ext_priv = NULL;
+
+	return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs,	"TT-Budget/WinTV-NOVA-S  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(ttbc,	"TT-Budget/WinTV-NOVA-C  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt,	"TT-Budget/WinTV-NOVA-T  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(satel,	"SATELCO Multimedia PCI",	BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact,	 "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(ttbs,  0x13c2, 0x1003),
+	MAKE_EXTENSION_PCI(ttbc,  0x13c2, 0x1004),
+	MAKE_EXTENSION_PCI(ttbt,  0x13c2, 0x1005),
+	MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+	MAKE_EXTENSION_PCI(ttbs,  0x13c2, 0x1016),
+	MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
+	MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
+	MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
+	MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+	MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
+	MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
+	MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020),
+	{
+		.vendor    = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name		= "budget dvb",
+	.flags		= SAA7146_USE_I2C_IRQ,
+
+	.module		= THIS_MODULE,
+	.pci_tbl	= pci_tbl,
+	.attach		= budget_attach,
+	.detach		= budget_detach,
+
+	.irq_mask	= MASK_10,
+	.irq_func	= ttpci_budget_irq10_handler,
+};
+
+static int __init budget_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_init);
+module_exit(budget_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
new file mode 100644
index 000000000000..3d8a806c20bb
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget.h
@@ -0,0 +1,124 @@
+
+#ifndef __BUDGET_DVB__
+#define __BUDGET_DVB__
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <media/saa7146.h>
+
+extern int budget_debug;
+
+#ifdef dprintk
+#undef dprintk
+#endif
+
+#define dprintk(level,args...) \
+	    do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0)
+
+struct budget_info {
+	char *name;
+	int type;
+};
+
+/* place to store all the necessary device information */
+struct budget {
+
+	/* devices */
+	struct dvb_device dvb_dev;
+	struct dvb_net dvb_net;
+
+	struct saa7146_dev *dev;
+
+	struct i2c_adapter i2c_adap;
+	struct budget_info *card;
+
+	unsigned char *grabbing;
+	struct saa7146_pgtable pt;
+
+	struct tasklet_struct fidb_tasklet;
+	struct tasklet_struct vpe_tasklet;
+
+	struct dmxdev dmxdev;
+	struct dvb_demux demux;
+
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+
+	int ci_present;
+	int video_port;
+
+	u32 buffer_width;
+	u32 buffer_height;
+	u32 buffer_size;
+	u32 buffer_warning_threshold;
+	u32 buffer_warnings;
+	unsigned long buffer_warning_time;
+
+	u32 ttbp;
+	int feeding;
+
+	spinlock_t feedlock;
+
+	spinlock_t debilock;
+
+	struct dvb_adapter dvb_adapter;
+	struct dvb_frontend *dvb_frontend;
+	int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status);
+	int fe_synced;
+
+	void *priv;
+};
+
+#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
+static struct budget_info x_var ## _info = { \
+	.name=x_name,	\
+	.type=x_type };	\
+static struct saa7146_pci_extension_data x_var = { \
+	.ext_priv = &x_var ## _info, \
+	.ext = &budget_extension };
+
+#define BUDGET_TT		   0
+#define BUDGET_TT_HW_DISEQC	   1
+#define BUDGET_PATCH		   3
+#define BUDGET_FS_ACTIVY	   4
+#define BUDGET_CIN1200S		   5
+#define BUDGET_CIN1200C		   6
+#define BUDGET_CIN1200T		   7
+#define BUDGET_KNC1S		   8
+#define BUDGET_KNC1C		   9
+#define BUDGET_KNC1T		   10
+#define BUDGET_KNC1SP		   11
+#define BUDGET_KNC1CP		   12
+#define BUDGET_KNC1TP		   13
+#define BUDGET_TVSTAR		   14
+#define BUDGET_CIN1200C_MK3	   15
+#define BUDGET_KNC1C_MK3	   16
+#define BUDGET_KNC1CP_MK3	   17
+#define BUDGET_KNC1S2              18
+#define BUDGET_KNC1C_TDA10024	   19
+
+#define BUDGET_VIDEO_PORTA         0
+#define BUDGET_VIDEO_PORTB         1
+
+extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+			     struct saa7146_pci_extension_data *info,
+			     struct module *owner, short *adapter_nums);
+extern void ttpci_budget_init_hooks(struct budget *budget);
+extern int ttpci_budget_deinit(struct budget *budget);
+extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
+extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port);
+extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+				 int uselocks, int nobusyloop);
+extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value,
+				  int uselocks, int nobusyloop);
+
+#endif
diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c
new file mode 100644
index 000000000000..32d43156c548
--- /dev/null
+++ b/drivers/media/pci/ttpci/ttpci-eeprom.c
@@ -0,0 +1,176 @@
+/*
+    Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM,
+    decode it and store it in the associated adapter struct for
+    use by dvb_net.c
+
+    This card appear to have the 24C16 write protect held to ground,
+    thus permitting normal read/write operation. Theoretically it
+    would be possible to write routines to burn a different (encoded)
+    MAC address into the EEPROM.
+
+    Robert Schlabbach	GMX
+    Michael Glaum	KVH Industries
+    Holger Waechtler	Convergence
+
+    Copyright (C) 2002-2003 Ralph Metzler <rjkm@metzlerbros.de>
+			    Metzler Brothers Systementwicklung GbR
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <asm/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/i2c.h>
+
+#include "ttpci-eeprom.h"
+
+#if 1
+#define dprintk(x...) do { printk(x); } while (0)
+#else
+#define dprintk(x...) do { } while (0)
+#endif
+
+
+static int check_mac_tt(u8 *buf)
+{
+	int i;
+	u16 tmp = 0xffff;
+
+	for (i = 0; i < 8; i++) {
+		tmp  = (tmp << 8) | ((tmp >> 8) ^ buf[i]);
+		tmp ^= (tmp >> 4) & 0x0f;
+		tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5);
+	}
+	tmp ^= 0xffff;
+	return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9]));
+}
+
+static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC)
+{
+	u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
+		       0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
+		       0x1d, 0x36, 0x64, 0x78};
+	u8 data[20];
+	int i;
+
+	/* In case there is a sig check failure have the orig contents available */
+	memcpy(data, encodedMAC, 20);
+
+	for (i = 0; i < 20; i++)
+		data[i] ^= xor[i];
+	for (i = 0; i < 10; i++)
+		data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
+			>> ((data[2 * i + 1] >> 6) & 3);
+
+	if (check_mac_tt(data))
+		return -ENODEV;
+
+	decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0];
+	decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4];
+	return 0;
+}
+
+int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC)
+{
+	u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
+		       0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
+		       0x1d, 0x36, 0x64, 0x78};
+	u8 data[20];
+	int i;
+
+	memcpy(data, encodedMAC, 20);
+
+	for (i = 0; i < 20; i++)
+		data[i] ^= xor[i];
+	for (i = 0; i < 10; i++)
+		data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
+			>> ((data[2 * i + 1] >> 6) & 3);
+
+	if (check_mac_tt(data))
+		return -ENODEV;
+
+	decodedMAC[0] = data[2];
+	decodedMAC[1] = data[1];
+	decodedMAC[2] = data[0];
+	decodedMAC[3] = data[6];
+	decodedMAC[4] = data[5];
+	decodedMAC[5] = data[4];
+	return 0;
+}
+EXPORT_SYMBOL(ttpci_eeprom_decode_mac);
+
+static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC)
+{
+	int ret;
+	u8 b0[] = { 0xcc };
+
+	struct i2c_msg msg[] = {
+		{ .addr = 0x50, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 }
+	};
+
+	/* dprintk("%s\n", __func__); */
+
+	ret = i2c_transfer(adapter, msg, 2);
+
+	if (ret != 2)		/* Assume EEPROM isn't there */
+		return (-ENODEV);
+
+	return 0;
+}
+
+
+int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
+{
+	int ret, i;
+	u8 encodedMAC[20];
+	u8 decodedMAC[6];
+
+	ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC);
+
+	if (ret != 0) {		/* Will only be -ENODEV */
+		dprintk("Couldn't read from EEPROM: not there?\n");
+		memset(proposed_mac, 0, 6);
+		return ret;
+	}
+
+	ret = getmac_tt(decodedMAC, encodedMAC);
+	if( ret != 0 ) {
+		dprintk("adapter failed MAC signature check\n");
+		dprintk("encoded MAC from EEPROM was " );
+		for(i=0; i<19; i++) {
+			dprintk( "%.2x:", encodedMAC[i]);
+		}
+		dprintk("%.2x\n", encodedMAC[19]);
+		memset(proposed_mac, 0, 6);
+		return ret;
+	}
+
+	memcpy(proposed_mac, decodedMAC, 6);
+	dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		decodedMAC[0], decodedMAC[1], decodedMAC[2],
+		decodedMAC[3], decodedMAC[4], decodedMAC[5]);
+	return 0;
+}
+
+EXPORT_SYMBOL(ttpci_eeprom_parse_mac);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
+MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards "
+		"made by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.h b/drivers/media/pci/ttpci/ttpci-eeprom.h
new file mode 100644
index 000000000000..dcc33d5a5cb1
--- /dev/null
+++ b/drivers/media/pci/ttpci/ttpci-eeprom.h
@@ -0,0 +1,34 @@
+/*
+    Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM,
+    decode it and store it in associated adapter net device
+
+    Robert Schlabbach	GMX
+    Michael Glaum	KVH Industries
+    Holger Waechtler	Convergence
+
+    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 __TTPCI_EEPROM_H__
+#define __TTPCI_EEPROM_H__
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC);
+extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
+
+#endif
diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig
new file mode 100644
index 000000000000..26ca8702e33f
--- /dev/null
+++ b/drivers/media/pci/zoran/Kconfig
@@ -0,0 +1,74 @@
+config VIDEO_ZORAN
+	tristate "Zoran ZR36057/36067 Video For Linux"
+	depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
+	help
+	  Say Y for support for MJPEG capture cards based on the Zoran
+	  36057/36067 PCI controller chipset. This includes the Iomega
+	  Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
+	  a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
+	  more information, check <file:Documentation/video4linux/Zoran>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zr36067.
+
+config VIDEO_ZORAN_DC30
+	tristate "Pinnacle/Miro DC30(+) support"
+	depends on VIDEO_ZORAN
+	select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
+	  card. This also supports really old DC10 cards based on the
+	  zr36050 MJPEG codec and zr36016 VFE.
+
+config VIDEO_ZORAN_ZR36060
+	tristate "Zoran ZR36060"
+	depends on VIDEO_ZORAN
+	help
+	  Say Y to support Zoran boards based on 36060 chips.
+	  This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
+	  and 33 R10 and AverMedia 6 boards.
+
+config VIDEO_ZORAN_BUZ
+	tristate "Iomega Buz support"
+	depends on VIDEO_ZORAN_ZR36060
+	select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for the Iomega Buz MJPEG capture/playback card.
+
+config VIDEO_ZORAN_DC10
+	tristate "Pinnacle/Miro DC10(+) support"
+	depends on VIDEO_ZORAN_ZR36060
+	select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
+	  card.
+
+config VIDEO_ZORAN_LML33
+	tristate "Linux Media Labs LML33 support"
+	depends on VIDEO_ZORAN_ZR36060
+	select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for the Linux Media Labs LML33 MJPEG capture/playback
+	  card.
+
+config VIDEO_ZORAN_LML33R10
+	tristate "Linux Media Labs LML33R10 support"
+	depends on VIDEO_ZORAN_ZR36060
+	select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  support for the Linux Media Labs LML33R10 MJPEG capture/playback
+	  card.
+
+config VIDEO_ZORAN_AVS6EYES
+	tristate "AverMedia 6 Eyes support"
+	depends on VIDEO_ZORAN_ZR36060
+	select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT
+	select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/pci/zoran/Makefile b/drivers/media/pci/zoran/Makefile
new file mode 100644
index 000000000000..44cc13352c88
--- /dev/null
+++ b/drivers/media/pci/zoran/Makefile
@@ -0,0 +1,6 @@
+zr36067-objs	:=	zoran_procfs.o zoran_device.o \
+			zoran_driver.o zoran_card.o
+
+obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
+obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
+obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c
new file mode 100644
index 000000000000..c01071635290
--- /dev/null
+++ b/drivers/media/pci/zoran/videocodec.c
@@ -0,0 +1,407 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define VIDEOCODEC_VERSION "v0.2"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+// kernel config is here (procfs flag)
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+#endif
+
+#include "videocodec.h"
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+struct attached_list {
+	struct videocodec *codec;
+	struct attached_list *next;
+};
+
+struct codec_list {
+	const struct videocodec *codec;
+	int attached;
+	struct attached_list *list;
+	struct codec_list *next;
+};
+
+static struct codec_list *codeclist_top = NULL;
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+struct videocodec *
+videocodec_attach (struct videocodec_master *master)
+{
+	struct codec_list *h = codeclist_top;
+	struct attached_list *a, *ptr;
+	struct videocodec *codec;
+	int res;
+
+	if (!master) {
+		dprintk(1, KERN_ERR "videocodec_attach: no data\n");
+		return NULL;
+	}
+
+	dprintk(2,
+		"videocodec_attach: '%s', flags %lx, magic %lx\n",
+		master->name, master->flags, master->magic);
+
+	if (!h) {
+		dprintk(1,
+			KERN_ERR
+			"videocodec_attach: no device available\n");
+		return NULL;
+	}
+
+	while (h) {
+		// attach only if the slave has at least the flags
+		// expected by the master
+		if ((master->flags & h->codec->flags) == master->flags) {
+			dprintk(4, "videocodec_attach: try '%s'\n",
+				h->codec->name);
+
+			if (!try_module_get(h->codec->owner))
+				return NULL;
+
+			codec = kmemdup(h->codec, sizeof(struct videocodec),
+					GFP_KERNEL);
+			if (!codec) {
+				dprintk(1,
+					KERN_ERR
+					"videocodec_attach: no mem\n");
+				goto out_module_put;
+			}
+
+			snprintf(codec->name, sizeof(codec->name),
+				 "%s[%d]", codec->name, h->attached);
+			codec->master_data = master;
+			res = codec->setup(codec);
+			if (res == 0) {
+				dprintk(3, "videocodec_attach '%s'\n",
+					codec->name);
+				ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL);
+				if (!ptr) {
+					dprintk(1,
+						KERN_ERR
+						"videocodec_attach: no memory\n");
+					goto out_kfree;
+				}
+				ptr->codec = codec;
+
+				a = h->list;
+				if (!a) {
+					h->list = ptr;
+					dprintk(4,
+						"videocodec: first element\n");
+				} else {
+					while (a->next)
+						a = a->next;	// find end
+					a->next = ptr;
+					dprintk(4,
+						"videocodec: in after '%s'\n",
+						h->codec->name);
+				}
+
+				h->attached += 1;
+				return codec;
+			} else {
+				kfree(codec);
+			}
+		}
+		h = h->next;
+	}
+
+	dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
+	return NULL;
+
+ out_module_put:
+	module_put(h->codec->owner);
+ out_kfree:
+	kfree(codec);
+	return NULL;
+}
+
+int
+videocodec_detach (struct videocodec *codec)
+{
+	struct codec_list *h = codeclist_top;
+	struct attached_list *a, *prev;
+	int res;
+
+	if (!codec) {
+		dprintk(1, KERN_ERR "videocodec_detach: no data\n");
+		return -EINVAL;
+	}
+
+	dprintk(2,
+		"videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
+		codec->name, codec->type, codec->flags, codec->magic);
+
+	if (!h) {
+		dprintk(1,
+			KERN_ERR "videocodec_detach: no device left...\n");
+		return -ENXIO;
+	}
+
+	while (h) {
+		a = h->list;
+		prev = NULL;
+		while (a) {
+			if (codec == a->codec) {
+				res = a->codec->unset(a->codec);
+				if (res >= 0) {
+					dprintk(3,
+						"videocodec_detach: '%s'\n",
+						a->codec->name);
+					a->codec->master_data = NULL;
+				} else {
+					dprintk(1,
+						KERN_ERR
+						"videocodec_detach: '%s'\n",
+						a->codec->name);
+					a->codec->master_data = NULL;
+				}
+				if (prev == NULL) {
+					h->list = a->next;
+					dprintk(4,
+						"videocodec: delete first\n");
+				} else {
+					prev->next = a->next;
+					dprintk(4,
+						"videocodec: delete middle\n");
+				}
+				module_put(a->codec->owner);
+				kfree(a->codec);
+				kfree(a);
+				h->attached -= 1;
+				return 0;
+			}
+			prev = a;
+			a = a->next;
+		}
+		h = h->next;
+	}
+
+	dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
+	return -EINVAL;
+}
+
+int
+videocodec_register (const struct videocodec *codec)
+{
+	struct codec_list *ptr, *h = codeclist_top;
+
+	if (!codec) {
+		dprintk(1, KERN_ERR "videocodec_register: no data!\n");
+		return -EINVAL;
+	}
+
+	dprintk(2,
+		"videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
+		codec->name, codec->type, codec->flags, codec->magic);
+
+	ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL);
+	if (!ptr) {
+		dprintk(1, KERN_ERR "videocodec_register: no memory\n");
+		return -ENOMEM;
+	}
+	ptr->codec = codec;
+
+	if (!h) {
+		codeclist_top = ptr;
+		dprintk(4, "videocodec: hooked in as first element\n");
+	} else {
+		while (h->next)
+			h = h->next;	// find the end
+		h->next = ptr;
+		dprintk(4, "videocodec: hooked in after '%s'\n",
+			h->codec->name);
+	}
+
+	return 0;
+}
+
+int
+videocodec_unregister (const struct videocodec *codec)
+{
+	struct codec_list *prev = NULL, *h = codeclist_top;
+
+	if (!codec) {
+		dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
+		return -EINVAL;
+	}
+
+	dprintk(2,
+		"videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
+		codec->name, codec->type, codec->flags, codec->magic);
+
+	if (!h) {
+		dprintk(1,
+			KERN_ERR
+			"videocodec_unregister: no device left...\n");
+		return -ENXIO;
+	}
+
+	while (h) {
+		if (codec == h->codec) {
+			if (h->attached) {
+				dprintk(1,
+					KERN_ERR
+					"videocodec: '%s' is used\n",
+					h->codec->name);
+				return -EBUSY;
+			}
+			dprintk(3, "videocodec: unregister '%s' is ok.\n",
+				h->codec->name);
+			if (prev == NULL) {
+				codeclist_top = h->next;
+				dprintk(4,
+					"videocodec: delete first element\n");
+			} else {
+				prev->next = h->next;
+				dprintk(4,
+					"videocodec: delete middle element\n");
+			}
+			kfree(h);
+			return 0;
+		}
+		prev = h;
+		h = h->next;
+	}
+
+	dprintk(1,
+		KERN_ERR
+		"videocodec_unregister: given codec not found!\n");
+	return -EINVAL;
+}
+
+#ifdef CONFIG_PROC_FS
+static int proc_videocodecs_show(struct seq_file *m, void *v)
+{
+	struct codec_list *h = codeclist_top;
+	struct attached_list *a;
+
+	seq_printf(m, "<S>lave or attached <M>aster name  type flags    magic    ");
+	seq_printf(m, "(connected as)\n");
+
+	h = codeclist_top;
+	while (h) {
+		seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n",
+			      h->codec->name, h->codec->type,
+			      h->codec->flags, h->codec->magic);
+		a = h->list;
+		while (a) {
+			seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n",
+				      a->codec->master_data->name,
+				      a->codec->master_data->type,
+				      a->codec->master_data->flags,
+				      a->codec->master_data->magic,
+				      a->codec->name);
+			a = a->next;
+		}
+		h = h->next;
+	}
+
+	return 0;
+}
+
+static int proc_videocodecs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_videocodecs_show, NULL);
+}
+
+static const struct file_operations videocodecs_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= proc_videocodecs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+/* ===================== */
+/* hook in driver module */
+/* ===================== */
+static int __init
+videocodec_init (void)
+{
+#ifdef CONFIG_PROC_FS
+	static struct proc_dir_entry *videocodec_proc_entry;
+#endif
+
+	printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
+	       VIDEOCODEC_VERSION);
+
+#ifdef CONFIG_PROC_FS
+	videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops);
+	if (!videocodec_proc_entry) {
+		dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
+	}
+#endif
+	return 0;
+}
+
+static void __exit
+videocodec_exit (void)
+{
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("videocodecs", NULL);
+#endif
+}
+
+EXPORT_SYMBOL(videocodec_attach);
+EXPORT_SYMBOL(videocodec_detach);
+EXPORT_SYMBOL(videocodec_register);
+EXPORT_SYMBOL(videocodec_unregister);
+
+module_init(videocodec_init);
+module_exit(videocodec_exit);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Intermediate API module for video codecs "
+		   VIDEOCODEC_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h
new file mode 100644
index 000000000000..def55585ad23
--- /dev/null
+++ b/drivers/media/pci/zoran/videocodec.h
@@ -0,0 +1,353 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+/* =================== */
+/* general description */
+/* =================== */
+
+/* Should ease the (re-)usage of drivers supporting cards with (different)
+   video codecs. The codecs register to this module their functionality,
+   and the processors (masters) can attach to them if they fit.
+
+   The codecs are typically have a "strong" binding to their master - so I
+   don't think it makes sense to have a full blown interfacing as with e.g.
+   i2c. If you have an other opinion, let's discuss & implement it :-)))
+
+   Usage:
+
+   The slave has just to setup the videocodec structure and use two functions:
+   videocodec_register(codecdata);
+   videocodec_unregister(codecdata);
+   The best is just calling them at module (de-)initialisation.
+
+   The master sets up the structure videocodec_master and calls:
+   codecdata=videocodec_attach(master_codecdata);
+   videocodec_detach(codecdata);
+
+   The slave is called during attach/detach via functions setup previously
+   during register. At that time, the master_data pointer is set up
+   and the slave can access any io registers of the master device (in the case
+   the slave is bound to it). Otherwise it doesn't need this functions and
+   therfor they may not be initialized.
+
+   The other functions are just for convenience, as they are for sure used by
+   most/all of the codecs. The last ones may be omitted, too.
+
+   See the structure declaration below for more information and which data has
+   to be set up for the master and the slave.
+
+   ----------------------------------------------------------------------------
+   The master should have "knowledge" of the slave and vice versa.  So the data
+   structures sent to/from slave via set_data/get_data set_image/get_image are
+   device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!)
+   ----------------------------------------------------------------------------
+*/
+
+
+/* ========================================== */
+/* description of the videocodec_io structure */
+/* ========================================== */
+
+/*
+   ==== master setup ====
+   name -> name of the device structure for reference and debugging
+   master_data ->  data ref. for the master (e.g. the zr36055,57,67)
+   readreg -> ref. to read-fn from register (setup by master, used by slave)
+   writereg -> ref. to write-fn to register (setup by master, used by slave)
+	       this two functions do the lowlevel I/O job
+
+   ==== slave functionality setup ====
+   slave_data -> data ref. for the slave (e.g. the zr36050,60)
+   check -> fn-ref. checks availability of an device, returns -EIO on failure or
+	    the type on success
+	    this makes espcecially sense if a driver module supports more than
+	    one codec which may be quite similar to access, nevertheless it
+	    is good for a first functionality check
+
+   -- main functions you always need for compression/decompression --
+
+   set_mode -> this fn-ref. resets the entire codec, and sets up the mode
+	       with the last defined norm/size (or device default if not
+	       available) - it returns 0 if the mode is possible
+   set_size -> this fn-ref. sets the norm and image size for
+	       compression/decompression (returns 0 on success)
+	       the norm param is defined in videodev2.h (V4L2_STD_*)
+
+   additional setup may be available, too - but the codec should work with
+   some default values even without this
+
+   set_data -> sets device-specific data (tables, quality etc.)
+   get_data -> query device-specific data (tables, quality etc.)
+
+   if the device delivers interrupts, they may be setup/handled here
+   setup_interrupt -> codec irq setup (not needed for 36050/60)
+   handle_interrupt -> codec irq handling (not needed for 36050/60)
+
+   if the device delivers pictures, they may be handled here
+   put_image -> puts image data to the codec (not needed for 36050/60)
+   get_image -> gets image data from the codec (not needed for 36050/60)
+		the calls include frame numbers and flags (even/odd/...)
+		if needed and a flag which allows blocking until its ready
+*/
+
+/* ============== */
+/* user interface */
+/* ============== */
+
+/*
+   Currently there is only a information display planned, as the layer
+   is not visible for the user space at all.
+
+   Information is available via procfs. The current entry is "/proc/videocodecs"
+   but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--.
+
+A example for such an output is:
+
+<S>lave or attached <M>aster name  type flags    magic    (connected as)
+S                          zr36050 0002 0000d001 00000000 (TEMPLATE)
+M                       zr36055[0] 0001 0000c001 00000000 (zr36050[0])
+M                       zr36055[1] 0001 0000c001 00000000 (zr36050[1])
+
+*/
+
+
+/* =============================================== */
+/* special defines for the videocodec_io structure */
+/* =============================================== */
+
+#ifndef __LINUX_VIDEOCODEC_H
+#define __LINUX_VIDEOCODEC_H
+
+#include <linux/videodev2.h>
+
+#define CODEC_DO_COMPRESSION 0
+#define CODEC_DO_EXPANSION   1
+
+/* this are the current codec flags I think they are needed */
+/*  -> type value in structure */
+#define CODEC_FLAG_JPEG      0x00000001L	// JPEG codec
+#define CODEC_FLAG_MPEG      0x00000002L	// MPEG1/2/4 codec
+#define CODEC_FLAG_DIVX      0x00000004L	// DIVX codec
+#define CODEC_FLAG_WAVELET   0x00000008L	// WAVELET codec
+					  // room for other types
+
+#define CODEC_FLAG_MAGIC     0x00000800L	// magic key must match
+#define CODEC_FLAG_HARDWARE  0x00001000L	// is a hardware codec
+#define CODEC_FLAG_VFE       0x00002000L	// has direct video frontend
+#define CODEC_FLAG_ENCODER   0x00004000L	// compression capability
+#define CODEC_FLAG_DECODER   0x00008000L	// decompression capability
+#define CODEC_FLAG_NEEDIRQ   0x00010000L	// needs irq handling
+#define CODEC_FLAG_RDWRPIC   0x00020000L	// handles picture I/O
+
+/* a list of modes, some are just examples (is there any HW?) */
+#define CODEC_MODE_BJPG      0x0001	// Baseline JPEG
+#define CODEC_MODE_LJPG      0x0002	// Lossless JPEG
+#define CODEC_MODE_MPEG1     0x0003	// MPEG 1
+#define CODEC_MODE_MPEG2     0x0004	// MPEG 2
+#define CODEC_MODE_MPEG4     0x0005	// MPEG 4
+#define CODEC_MODE_MSDIVX    0x0006	// MS DivX
+#define CODEC_MODE_ODIVX     0x0007	// Open DivX
+#define CODEC_MODE_WAVELET   0x0008	// Wavelet
+
+/* this are the current codec types I want to implement */
+/*  -> type value in structure */
+#define CODEC_TYPE_NONE    0
+#define CODEC_TYPE_L64702  1
+#define CODEC_TYPE_ZR36050 2
+#define CODEC_TYPE_ZR36016 3
+#define CODEC_TYPE_ZR36060 4
+
+/* the type of data may be enhanced by future implementations (data-fn.'s) */
+/*  -> used in command                                                     */
+#define CODEC_G_STATUS         0x0000	/* codec status (query only) */
+#define CODEC_S_CODEC_MODE     0x0001	/* codec mode (baseline JPEG, MPEG1,... */
+#define CODEC_G_CODEC_MODE     0x8001
+#define CODEC_S_VFE            0x0002	/* additional video frontend setup */
+#define CODEC_G_VFE            0x8002
+#define CODEC_S_MMAP           0x0003	/* MMAP setup (if available) */
+
+#define CODEC_S_JPEG_TDS_BYTE  0x0010	/* target data size in bytes */
+#define CODEC_G_JPEG_TDS_BYTE  0x8010
+#define CODEC_S_JPEG_SCALE     0x0011	/* scaling factor for quant. tables */
+#define CODEC_G_JPEG_SCALE     0x8011
+#define CODEC_S_JPEG_HDT_DATA  0x0018	/* huffman-tables */
+#define CODEC_G_JPEG_HDT_DATA  0x8018
+#define CODEC_S_JPEG_QDT_DATA  0x0019	/* quantizing-tables */
+#define CODEC_G_JPEG_QDT_DATA  0x8019
+#define CODEC_S_JPEG_APP_DATA  0x001A	/* APP marker */
+#define CODEC_G_JPEG_APP_DATA  0x801A
+#define CODEC_S_JPEG_COM_DATA  0x001B	/* COM marker */
+#define CODEC_G_JPEG_COM_DATA  0x801B
+
+#define CODEC_S_PRIVATE        0x1000	/* "private" commands start here */
+#define CODEC_G_PRIVATE        0x9000
+
+#define CODEC_G_FLAG           0x8000	/* this is how 'get' is detected */
+
+/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */
+/*  -> used in get_image, put_image                                        */
+#define CODEC_TRANSFER_KERNEL 0	/* use "memcopy" */
+#define CODEC_TRANSFER_USER   1	/* use "to/from_user" */
+
+
+/* ========================= */
+/* the structures itself ... */
+/* ========================= */
+
+struct vfe_polarity {
+	unsigned int vsync_pol:1;
+	unsigned int hsync_pol:1;
+	unsigned int field_pol:1;
+	unsigned int blank_pol:1;
+	unsigned int subimg_pol:1;
+	unsigned int poe_pol:1;
+	unsigned int pvalid_pol:1;
+	unsigned int vclk_pol:1;
+};
+
+struct vfe_settings {
+	__u32 x, y;		/* Offsets into image */
+	__u32 width, height;	/* Area to capture */
+	__u16 decimation;	/* Decimation divider */
+	__u16 flags;		/* Flags for capture */
+	__u16 quality;		/* quality of the video */
+};
+
+struct tvnorm {
+	u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+};
+
+struct jpeg_com_marker {
+	int len; /* number of usable bytes in data */
+	char data[60];
+};
+
+struct jpeg_app_marker {
+	int appn; /* number app segment */
+	int len; /* number of usable bytes in data */
+	char data[60];
+};
+
+struct videocodec {
+	struct module *owner;
+	/* -- filled in by slave device during register -- */
+	char name[32];
+	unsigned long magic;	/* may be used for client<->master attaching */
+	unsigned long flags;	/* functionality flags */
+	unsigned int type;	/* codec type */
+
+	/* -- these is filled in later during master device attach -- */
+
+	struct videocodec_master *master_data;
+
+	/* -- these are filled in by the slave device during register -- */
+
+	void *data;		/* private slave data */
+
+	/* attach/detach client functions (indirect call) */
+	int (*setup) (struct videocodec * codec);
+	int (*unset) (struct videocodec * codec);
+
+	/* main functions, every client needs them for sure! */
+	// set compression or decompression (or freeze, stop, standby, etc)
+	int (*set_mode) (struct videocodec * codec,
+			 int mode);
+	// setup picture size and norm (for the codec's video frontend)
+	int (*set_video) (struct videocodec * codec,
+			  struct tvnorm * norm,
+			  struct vfe_settings * cap,
+			  struct vfe_polarity * pol);
+	// other control commands, also mmap setup etc.
+	int (*control) (struct videocodec * codec,
+			int type,
+			int size,
+			void *data);
+
+	/* additional setup/query/processing (may be NULL pointer) */
+	// interrupt setup / handling (for irq's delivered by master)
+	int (*setup_interrupt) (struct videocodec * codec,
+				long mode);
+	int (*handle_interrupt) (struct videocodec * codec,
+				 int source,
+				 long flag);
+	// picture interface (if any)
+	long (*put_image) (struct videocodec * codec,
+			   int tr_type,
+			   int block,
+			   long *fr_num,
+			   long *flag,
+			   long size,
+			   void *buf);
+	long (*get_image) (struct videocodec * codec,
+			   int tr_type,
+			   int block,
+			   long *fr_num,
+			   long *flag,
+			   long size,
+			   void *buf);
+};
+
+struct videocodec_master {
+	/* -- filled in by master device for registration -- */
+	char name[32];
+	unsigned long magic;	/* may be used for client<->master attaching */
+	unsigned long flags;	/* functionality flags */
+	unsigned int type;	/* master type */
+
+	void *data;		/* private master data */
+
+	 __u32(*readreg) (struct videocodec * codec,
+			  __u16 reg);
+	void (*writereg) (struct videocodec * codec,
+			  __u16 reg,
+			  __u32 value);
+};
+
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+/* attach and detach commands for the master */
+// * master structure needs to be kmalloc'ed before calling attach
+//   and free'd after calling detach
+// * returns pointer on success, NULL on failure
+extern struct videocodec *videocodec_attach(struct videocodec_master *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_detach(struct videocodec *);
+
+/* register and unregister commands for the slaves */
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_register(const struct videocodec *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_unregister(const struct videocodec *);
+
+/* the other calls are directly done via the videocodec structure! */
+
+#endif				/*ifndef __LINUX_VIDEOCODEC_H */
diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h
new file mode 100644
index 000000000000..ca2754a3cd63
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran.h
@@ -0,0 +1,403 @@
+/*
+ * zoran - Iomega Buz driver
+ *
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * based on
+ *
+ * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * and
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+ *                        & Marcus Metzler (mocm@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 _BUZ_H_
+#define _BUZ_H_
+
+#include <media/v4l2-device.h>
+
+struct zoran_sync {
+	unsigned long frame;	/* number of buffer that has been free'd */
+	unsigned long length;	/* number of code bytes in buffer (capture only) */
+	unsigned long seq;	/* frame sequence number */
+	struct timeval timestamp;	/* timestamp */
+};
+
+
+#define ZORAN_NAME    "ZORAN"	/* name of the device */
+
+#define ZR_DEVNAME(zr) ((zr)->name)
+
+#define   BUZ_MAX_WIDTH   (zr->timing->Wa)
+#define   BUZ_MAX_HEIGHT  (zr->timing->Ha)
+#define   BUZ_MIN_WIDTH    32	/* never display less than 32 pixels */
+#define   BUZ_MIN_HEIGHT   24	/* never display less than 24 rows */
+
+#define BUZ_NUM_STAT_COM    4
+#define BUZ_MASK_STAT_COM   3
+
+#define BUZ_MAX_FRAME     256	/* Must be a power of 2 */
+#define BUZ_MASK_FRAME    255	/* Must be BUZ_MAX_FRAME-1 */
+
+#define BUZ_MAX_INPUT       16
+
+#if VIDEO_MAX_FRAME <= 32
+#   define   V4L_MAX_FRAME   32
+#elif VIDEO_MAX_FRAME <= 64
+#   define   V4L_MAX_FRAME   64
+#else
+#   error   "Too many video frame buffers to handle"
+#endif
+#define   V4L_MASK_FRAME   (V4L_MAX_FRAME - 1)
+
+#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME)
+
+#include "zr36057.h"
+
+enum card_type {
+	UNKNOWN = -1,
+
+	/* Pinnacle/Miro */
+	DC10_old,		/* DC30 like */
+	DC10_new,		/* DC10plus like */
+	DC10plus,
+	DC30,
+	DC30plus,
+
+	/* Linux Media Labs */
+	LML33,
+	LML33R10,
+
+	/* Iomega */
+	BUZ,
+
+	/* AverMedia */
+	AVS6EYES,
+
+	/* total number of cards */
+	NUM_CARDS
+};
+
+enum zoran_codec_mode {
+	BUZ_MODE_IDLE,		/* nothing going on */
+	BUZ_MODE_MOTION_COMPRESS,	/* grabbing frames */
+	BUZ_MODE_MOTION_DECOMPRESS,	/* playing frames */
+	BUZ_MODE_STILL_COMPRESS,	/* still frame conversion */
+	BUZ_MODE_STILL_DECOMPRESS	/* still frame conversion */
+};
+
+enum zoran_buffer_state {
+	BUZ_STATE_USER,		/* buffer is owned by application */
+	BUZ_STATE_PEND,		/* buffer is queued in pend[] ready to feed to I/O */
+	BUZ_STATE_DMA,		/* buffer is queued in dma[] for I/O */
+	BUZ_STATE_DONE		/* buffer is ready to return to application */
+};
+
+enum zoran_map_mode {
+	ZORAN_MAP_MODE_RAW,
+	ZORAN_MAP_MODE_JPG_REC,
+#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC
+	ZORAN_MAP_MODE_JPG_PLAY,
+};
+
+enum gpio_type {
+	ZR_GPIO_JPEG_SLEEP = 0,
+	ZR_GPIO_JPEG_RESET,
+	ZR_GPIO_JPEG_FRAME,
+	ZR_GPIO_VID_DIR,
+	ZR_GPIO_VID_EN,
+	ZR_GPIO_VID_RESET,
+	ZR_GPIO_CLK_SEL1,
+	ZR_GPIO_CLK_SEL2,
+	ZR_GPIO_MAX,
+};
+
+enum gpcs_type {
+	GPCS_JPEG_RESET = 0,
+	GPCS_JPEG_START,
+	GPCS_MAX,
+};
+
+struct zoran_format {
+	char *name;
+	__u32 fourcc;
+	int colorspace;
+	int depth;
+	__u32 flags;
+	__u32 vfespfr;
+};
+/* flags */
+#define ZORAN_FORMAT_COMPRESSED 1<<0
+#define ZORAN_FORMAT_OVERLAY    1<<1
+#define ZORAN_FORMAT_CAPTURE	1<<2
+#define ZORAN_FORMAT_PLAYBACK	1<<3
+
+/* overlay-settings */
+struct zoran_overlay_settings {
+	int is_set;
+	int x, y, width, height;	/* position */
+	int clipcount;		/* position and number of clips */
+	const struct zoran_format *format;	/* overlay format */
+};
+
+/* v4l-capture settings */
+struct zoran_v4l_settings {
+	int width, height, bytesperline;	/* capture size */
+	const struct zoran_format *format;	/* capture format */
+};
+
+/* jpg-capture/-playback settings */
+struct zoran_jpg_settings {
+	int decimation;		/* this bit is used to set everything to default */
+	int HorDcm, VerDcm, TmpDcm;	/* capture decimation settings (TmpDcm=1 means both fields) */
+	int field_per_buff, odd_even;	/* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */
+	int img_x, img_y, img_width, img_height;	/* crop settings (subframe capture) */
+	struct v4l2_jpegcompression jpg_comp;	/* JPEG-specific capture settings */
+};
+
+struct zoran_fh;
+
+struct zoran_mapping {
+	struct zoran_fh *fh;
+	int count;
+};
+
+struct zoran_buffer {
+	struct zoran_mapping *map;
+	enum zoran_buffer_state state;	/* state: unused/pending/dma/done */
+	struct zoran_sync bs;		/* DONE: info to return to application */
+	union {
+		struct {
+			__le32 *frag_tab;	/* addresses of frag table */
+			u32 frag_tab_bus;	/* same value cached to save time in ISR */
+		} jpg;
+		struct {
+			char *fbuffer;		/* virtual address of frame buffer */
+			unsigned long fbuffer_phys;/* physical address of frame buffer */
+			unsigned long fbuffer_bus;/* bus address of frame buffer */
+		} v4l;
+	};
+};
+
+enum zoran_lock_activity {
+	ZORAN_FREE,		/* free for use */
+	ZORAN_ACTIVE,		/* active but unlocked */
+	ZORAN_LOCKED,		/* locked */
+};
+
+/* buffer collections */
+struct zoran_buffer_col {
+	enum zoran_lock_activity active;	/* feature currently in use? */
+	unsigned int num_buffers, buffer_size;
+	struct zoran_buffer buffer[MAX_FRAME];	/* buffers */
+	u8 allocated;		/* Flag if buffers are allocated  */
+	u8 need_contiguous;	/* Flag if contiguous buffers are needed */
+	/* only applies to jpg buffers, raw buffers are always contiguous */
+};
+
+struct zoran;
+
+/* zoran_fh contains per-open() settings */
+struct zoran_fh {
+	struct zoran *zr;
+
+	enum zoran_map_mode map_mode;		/* Flag which bufferset will map by next mmap() */
+
+	struct zoran_overlay_settings overlay_settings;
+	u32 *overlay_mask;			/* overlay mask */
+	enum zoran_lock_activity overlay_active;/* feature currently in use? */
+
+	struct zoran_buffer_col buffers;	/* buffers' info */
+
+	struct zoran_v4l_settings v4l_settings;	/* structure with a lot of things to play with */
+	struct zoran_jpg_settings jpg_settings;	/* structure with a lot of things to play with */
+};
+
+struct card_info {
+	enum card_type type;
+	char name[32];
+	const char *i2c_decoder;	/* i2c decoder device */
+	const unsigned short *addrs_decoder;
+	const char *i2c_encoder;	/* i2c encoder device */
+	const unsigned short *addrs_encoder;
+	u16 video_vfe, video_codec;			/* videocodec types */
+	u16 audio_chip;					/* audio type */
+
+	int inputs;		/* number of video inputs */
+	struct input {
+		int muxsel;
+		char name[32];
+	} input[BUZ_MAX_INPUT];
+
+	v4l2_std_id norms;
+	struct tvnorm *tvn[3];	/* supported TV norms */
+
+	u32 jpeg_int;		/* JPEG interrupt */
+	u32 vsync_int;		/* VSYNC interrupt */
+	s8 gpio[ZR_GPIO_MAX];
+	u8 gpcs[GPCS_MAX];
+
+	struct vfe_polarity vfe_pol;
+	u8 gpio_pol[ZR_GPIO_MAX];
+
+	/* is the /GWS line connected? */
+	u8 gws_not_connected;
+
+	/* avs6eyes mux setting */
+	u8 input_mux;
+
+	void (*init) (struct zoran * zr);
+};
+
+struct zoran {
+	struct v4l2_device v4l2_dev;
+	struct video_device *video_dev;
+
+	struct i2c_adapter i2c_adapter;	/* */
+	struct i2c_algo_bit_data i2c_algo;	/* */
+	u32 i2cbr;
+
+	struct v4l2_subdev *decoder;	/* video decoder sub-device */
+	struct v4l2_subdev *encoder;	/* video encoder sub-device */
+
+	struct videocodec *codec;	/* video codec */
+	struct videocodec *vfe;	/* video front end */
+
+	struct mutex resource_lock;	/* prevent evil stuff */
+	struct mutex other_lock;	/* please merge with above */
+
+	u8 initialized;		/* flag if zoran has been correctly initialized */
+	int user;		/* number of current users */
+	struct card_info card;
+	struct tvnorm *timing;
+
+	unsigned short id;	/* number of this device */
+	char name[32];		/* name of this device */
+	struct pci_dev *pci_dev;	/* PCI device */
+	unsigned char revision;	/* revision of zr36057 */
+	unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */
+
+	spinlock_t spinlock;	/* Spinlock */
+
+	/* Video for Linux parameters */
+	int input;	/* card's norm and input */
+	v4l2_std_id norm;
+
+	/* Current buffer params */
+	void    *vbuf_base;
+	int     vbuf_height, vbuf_width;
+	int     vbuf_depth;
+	int     vbuf_bytesperline;
+
+	struct zoran_overlay_settings overlay_settings;
+	u32 *overlay_mask;	/* overlay mask */
+	enum zoran_lock_activity overlay_active;	/* feature currently in use? */
+
+	wait_queue_head_t v4l_capq;
+
+	int v4l_overlay_active;	/* Overlay grab is activated */
+	int v4l_memgrab_active;	/* Memory grab is activated */
+
+	int v4l_grab_frame;	/* Frame number being currently grabbed */
+#define NO_GRAB_ACTIVE (-1)
+	unsigned long v4l_grab_seq;	/* Number of frames grabbed */
+	struct zoran_v4l_settings v4l_settings;	/* structure with a lot of things to play with */
+
+	/* V4L grab queue of frames pending */
+	unsigned long v4l_pend_head;
+	unsigned long v4l_pend_tail;
+	unsigned long v4l_sync_tail;
+	int v4l_pend[V4L_MAX_FRAME];
+	struct zoran_buffer_col v4l_buffers;	/* V4L buffers' info */
+
+	/* Buz MJPEG parameters */
+	enum zoran_codec_mode codec_mode;	/* status of codec */
+	struct zoran_jpg_settings jpg_settings;	/* structure with a lot of things to play with */
+
+	wait_queue_head_t jpg_capq;	/* wait here for grab to finish */
+
+	/* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
+	/* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
+	/* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
+	unsigned long jpg_que_head;	/* Index where to put next buffer which is queued */
+	unsigned long jpg_dma_head;	/* Index of next buffer which goes into stat_com  */
+	unsigned long jpg_dma_tail;	/* Index of last buffer in stat_com               */
+	unsigned long jpg_que_tail;	/* Index of last buffer in queue                  */
+	unsigned long jpg_seq_num;	/* count of frames since grab/play started        */
+	unsigned long jpg_err_seq;	/* last seq_num before error                      */
+	unsigned long jpg_err_shift;
+	unsigned long jpg_queued_num;	/* count of frames queued since grab/play started */
+
+	/* zr36057's code buffer table */
+	__le32 *stat_com;		/* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+
+	/* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
+	int jpg_pend[BUZ_MAX_FRAME];
+
+	/* array indexed by frame number */
+	struct zoran_buffer_col jpg_buffers;	/* MJPEG buffers' info */
+
+	/* Additional stuff for testing */
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *zoran_proc;
+#else
+	void *zoran_proc;
+#endif
+	int testing;
+	int jpeg_error;
+	int intr_counter_GIRQ1;
+	int intr_counter_GIRQ0;
+	int intr_counter_CodRepIRQ;
+	int intr_counter_JPEGRepIRQ;
+	int field_counter;
+	int IRQ1_in;
+	int IRQ1_out;
+	int JPEG_in;
+	int JPEG_out;
+	int JPEG_0;
+	int JPEG_1;
+	int END_event_missed;
+	int JPEG_missed;
+	int JPEG_error;
+	int num_errors;
+	int JPEG_max_missed;
+	int JPEG_min_missed;
+
+	u32 last_isr;
+	unsigned long frame_num;
+
+	wait_queue_head_t test_q;
+};
+
+static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct zoran, v4l2_dev);
+}
+
+/* There was something called _ALPHA_BUZ that used the PCI address instead of
+ * the kernel iomapped address for btread/btwrite.  */
+#define btwrite(dat,adr)    writel((dat), zr->zr36057_mem+(adr))
+#define btread(adr)         readl(zr->zr36057_mem+(adr))
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
new file mode 100644
index 000000000000..fffc54b452c8
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -0,0 +1,1528 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/kmod.h>
+#include <linux/wait.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <media/v4l2-common.h>
+#include <media/bt819.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_card.h"
+#include "zoran_device.h"
+#include "zoran_procfs.h"
+
+extern const struct zoran_format zoran_formats[];
+
+static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "Card type");
+
+/*
+   The video mem address of the video card.
+   The driver has a little database for some videocards
+   to determine it from there. If your video card is not in there
+   you have either to give it to the driver as a parameter
+   or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem;	/* default = 0 - Video memory base address */
+module_param(vidmem, ulong, 0444);
+MODULE_PARM_DESC(vidmem, "Default video memory base address");
+
+/*
+   Default input and video norm at startup of the driver.
+*/
+
+static unsigned int default_input;	/* default 0 = Composite, 1 = S-Video */
+module_param(default_input, uint, 0444);
+MODULE_PARM_DESC(default_input,
+		 "Default input (0=Composite, 1=S-Video, 2=Internal)");
+
+static int default_mux = 1;	/* 6 Eyes input selection */
+module_param(default_mux, int, 0644);
+MODULE_PARM_DESC(default_mux,
+		 "Default 6 Eyes mux setting (Input selection)");
+
+static int default_norm;	/* default 0 = PAL, 1 = NTSC 2 = SECAM */
+module_param(default_norm, int, 0444);
+MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
+
+/* /dev/videoN, -1 for autodetect */
+static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
+
+int v4l_nbufs = 4;
+int v4l_bufsize = 864;		/* Everybody should be able to work with this setting */
+module_param(v4l_nbufs, int, 0644);
+MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
+module_param(v4l_bufsize, int, 0644);
+MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
+
+int jpg_nbufs = 32;
+int jpg_bufsize = 512;		/* max size for 100% quality full-PAL frame */
+module_param(jpg_nbufs, int, 0644);
+MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
+module_param(jpg_bufsize, int, 0644);
+MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
+
+int pass_through = 0;		/* 1=Pass through TV signal when device is not used */
+				/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+module_param(pass_through, int, 0644);
+MODULE_PARM_DESC(pass_through,
+		 "Pass TV signal through to TV-out when idling");
+
+int zr36067_debug = 1;
+module_param_named(debug, zr36067_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-5)");
+
+#define ZORAN_VERSION "0.10.1"
+
+MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
+MODULE_AUTHOR("Serguei Miridonov");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ZORAN_VERSION);
+
+#define ZR_DEVICE(subven, subdev, data)	{ \
+	.vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
+	.subvendor = (subven), .subdevice = (subdev), .driver_data = (data) }
+
+static struct pci_device_id zr36067_pci_tbl[] = {
+	ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus),
+	ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus),
+	ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10),
+	ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ),
+	ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS),
+	{0}
+};
+MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
+
+static unsigned int zoran_num;		/* number of cards found */
+
+/* videocodec bus functions ZR36060 */
+static u32
+zr36060_read (struct videocodec *codec,
+	      u16                reg)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+	__u32 data;
+
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 0, 1, reg >> 8)
+	    || post_office_write(zr, 0, 2, reg & 0xff)) {
+		return -1;
+	}
+
+	data = post_office_read(zr, 0, 3) & 0xff;
+	return data;
+}
+
+static void
+zr36060_write (struct videocodec *codec,
+	       u16                reg,
+	       u32                val)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 0, 1, reg >> 8)
+	    || post_office_write(zr, 0, 2, reg & 0xff)) {
+		return;
+	}
+
+	post_office_write(zr, 0, 3, val & 0xff);
+}
+
+/* videocodec bus functions ZR36050 */
+static u32
+zr36050_read (struct videocodec *codec,
+	      u16                reg)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+	__u32 data;
+
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 1, 0, reg >> 2)) {	// reg. HIGHBYTES
+		return -1;
+	}
+
+	data = post_office_read(zr, 0, reg & 0x03) & 0xff;	// reg. LOWBYTES + read
+	return data;
+}
+
+static void
+zr36050_write (struct videocodec *codec,
+	       u16                reg,
+	       u32                val)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 1, 0, reg >> 2)) {	// reg. HIGHBYTES
+		return;
+	}
+
+	post_office_write(zr, 0, reg & 0x03, val & 0xff);	// reg. LOWBYTES + wr. data
+}
+
+/* videocodec bus functions ZR36016 */
+static u32
+zr36016_read (struct videocodec *codec,
+	      u16                reg)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+	__u32 data;
+
+	if (post_office_wait(zr)) {
+		return -1;
+	}
+
+	data = post_office_read(zr, 2, reg & 0x03) & 0xff;	// read
+	return data;
+}
+
+/* hack for in zoran_device.c */
+void
+zr36016_write (struct videocodec *codec,
+	       u16                reg,
+	       u32                val)
+{
+	struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+	if (post_office_wait(zr)) {
+		return;
+	}
+
+	post_office_write(zr, 2, reg & 0x03, val & 0x0ff);	// wr. data
+}
+
+/*
+ * Board specific information
+ */
+
+static void
+dc10_init (struct zoran *zr)
+{
+	dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+	/* Pixel clock selection */
+	GPIO(zr, 4, 0);
+	GPIO(zr, 5, 1);
+	/* Enable the video bus sync signals */
+	GPIO(zr, 7, 0);
+}
+
+static void
+dc10plus_init (struct zoran *zr)
+{
+	dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+}
+
+static void
+buz_init (struct zoran *zr)
+{
+	dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+	/* some stuff from Iomega */
+	pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
+	pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
+	pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
+}
+
+static void
+lml33_init (struct zoran *zr)
+{
+	dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+	GPIO(zr, 2, 1);		// Set Composite input/output
+}
+
+static void
+avs6eyes_init (struct zoran *zr)
+{
+	// AverMedia 6-Eyes original driver by Christer Weinigel
+
+	// Lifted straight from Christer's old driver and
+	// modified slightly by Martin Samuelsson.
+
+	int mux = default_mux; /* 1 = BT866, 7 = VID1 */
+
+	GPIO(zr, 4, 1); /* Bt866 SLEEP on */
+	udelay(2);
+
+	GPIO(zr, 0, 1); /* ZR36060 /RESET on */
+	GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */
+	GPIO(zr, 2, mux & 1);   /* MUX S0 */
+	GPIO(zr, 3, 0); /* /FRAME on */
+	GPIO(zr, 4, 0); /* Bt866 SLEEP off */
+	GPIO(zr, 5, mux & 2);   /* MUX S1 */
+	GPIO(zr, 6, 0); /* ? */
+	GPIO(zr, 7, mux & 4);   /* MUX S2 */
+
+}
+
+static char *
+codecid_to_modulename (u16 codecid)
+{
+	char *name = NULL;
+
+	switch (codecid) {
+	case CODEC_TYPE_ZR36060:
+		name = "zr36060";
+		break;
+	case CODEC_TYPE_ZR36050:
+		name = "zr36050";
+		break;
+	case CODEC_TYPE_ZR36016:
+		name = "zr36016";
+		break;
+	}
+
+	return name;
+}
+
+// struct tvnorm {
+//      u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+// };
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
+
+/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
+static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
+static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
+
+/* FIXME: I cannot swap U and V in saa7114, so i do one
+ * pixel left shift in zoran (75 -> 74)
+ * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
+static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
+
+/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I
+ * copy Maxim's left shift hack for the 6 Eyes.
+ *
+ * Christer's driver used the unshifted norms, though...
+ * /Sam  */
+static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
+
+static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
+static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
+static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
+static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
+static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
+static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
+static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
+static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
+
+static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+	{
+		.type = DC10_old,
+		.name = "DC10(old)",
+		.i2c_decoder = "vpx3220a",
+		.addrs_decoder = vpx3220_addrs,
+		.video_codec = CODEC_TYPE_ZR36050,
+		.video_vfe = CODEC_TYPE_ZR36016,
+
+		.inputs = 3,
+		.input = {
+			{ 1, "Composite" },
+			{ 2, "S-Video" },
+			{ 0, "Internal/comp" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+			&f50sqpixel_dc10,
+			&f60sqpixel_dc10,
+			&f50sqpixel_dc10
+		},
+		.jpeg_int = 0,
+		.vsync_int = ZR36057_ISR_GIRQ1,
+		.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+		.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+		.gpcs = { -1, 0 },
+		.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gws_not_connected = 0,
+		.input_mux = 0,
+		.init = &dc10_init,
+	}, {
+		.type = DC10_new,
+		.name = "DC10(new)",
+		.i2c_decoder = "saa7110",
+		.addrs_decoder = saa7110_addrs,
+		.i2c_encoder = "adv7175",
+		.addrs_encoder = adv717x_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 3,
+		.input = {
+				{ 0, "Composite" },
+				{ 7, "S-Video" },
+				{ 5, "Internal/comp" }
+			},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+				&f50sqpixel,
+				&f60sqpixel,
+				&f50sqpixel},
+		.jpeg_int = ZR36057_ISR_GIRQ0,
+		.vsync_int = ZR36057_ISR_GIRQ1,
+		.gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+		.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gpcs = { -1, 1},
+		.vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+		.gws_not_connected = 0,
+		.input_mux = 0,
+		.init = &dc10plus_init,
+	}, {
+		.type = DC10plus,
+		.name = "DC10plus",
+		.i2c_decoder = "saa7110",
+		.addrs_decoder = saa7110_addrs,
+		.i2c_encoder = "adv7175",
+		.addrs_encoder = adv717x_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 3,
+		.input = {
+			{ 0, "Composite" },
+			{ 7, "S-Video" },
+			{ 5, "Internal/comp" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+			&f50sqpixel,
+			&f60sqpixel,
+			&f50sqpixel
+		},
+		.jpeg_int = ZR36057_ISR_GIRQ0,
+		.vsync_int = ZR36057_ISR_GIRQ1,
+		.gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+		.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gpcs = { -1, 1 },
+		.vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+		.gws_not_connected = 0,
+		.input_mux = 0,
+		.init = &dc10plus_init,
+	}, {
+		.type = DC30,
+		.name = "DC30",
+		.i2c_decoder = "vpx3220a",
+		.addrs_decoder = vpx3220_addrs,
+		.i2c_encoder = "adv7175",
+		.addrs_encoder = adv717x_addrs,
+		.video_codec = CODEC_TYPE_ZR36050,
+		.video_vfe = CODEC_TYPE_ZR36016,
+
+		.inputs = 3,
+		.input = {
+			{ 1, "Composite" },
+			{ 2, "S-Video" },
+			{ 0, "Internal/comp" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+			&f50sqpixel_dc10,
+			&f60sqpixel_dc10,
+			&f50sqpixel_dc10
+		},
+		.jpeg_int = 0,
+		.vsync_int = ZR36057_ISR_GIRQ1,
+		.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+		.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+		.gpcs = { -1, 0 },
+		.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gws_not_connected = 0,
+		.input_mux = 0,
+		.init = &dc10_init,
+	}, {
+		.type = DC30plus,
+		.name = "DC30plus",
+		.i2c_decoder = "vpx3220a",
+		.addrs_decoder = vpx3220_addrs,
+		.i2c_encoder = "adv7175",
+		.addrs_encoder = adv717x_addrs,
+		.video_codec = CODEC_TYPE_ZR36050,
+		.video_vfe = CODEC_TYPE_ZR36016,
+
+		.inputs = 3,
+		.input = {
+			{ 1, "Composite" },
+			{ 2, "S-Video" },
+			{ 0, "Internal/comp" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+			&f50sqpixel_dc10,
+			&f60sqpixel_dc10,
+			&f50sqpixel_dc10
+		},
+		.jpeg_int = 0,
+		.vsync_int = ZR36057_ISR_GIRQ1,
+		.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+		.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+		.gpcs = { -1, 0 },
+		.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gws_not_connected = 0,
+		.input_mux = 0,
+		.init = &dc10_init,
+	}, {
+		.type = LML33,
+		.name = "LML33",
+		.i2c_decoder = "bt819a",
+		.addrs_decoder = bt819_addrs,
+		.i2c_encoder = "bt856",
+		.addrs_encoder = bt856_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 2,
+		.input = {
+			{ 0, "Composite" },
+			{ 7, "S-Video" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+		.tvn = {
+			&f50ccir601_lml33,
+			&f60ccir601_lml33,
+			NULL
+		},
+		.jpeg_int = ZR36057_ISR_GIRQ1,
+		.vsync_int = ZR36057_ISR_GIRQ0,
+		.gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+		.gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+		.gpcs = { 3, 1 },
+		.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+		.gws_not_connected = 1,
+		.input_mux = 0,
+		.init = &lml33_init,
+	}, {
+		.type = LML33R10,
+		.name = "LML33R10",
+		.i2c_decoder = "saa7114",
+		.addrs_decoder = saa7114_addrs,
+		.i2c_encoder = "adv7170",
+		.addrs_encoder = adv717x_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 2,
+		.input = {
+			{ 0, "Composite" },
+			{ 7, "S-Video" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+		.tvn = {
+			&f50ccir601_lm33r10,
+			&f60ccir601_lm33r10,
+			NULL
+		},
+		.jpeg_int = ZR36057_ISR_GIRQ1,
+		.vsync_int = ZR36057_ISR_GIRQ0,
+		.gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+		.gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+		.gpcs = { 3, 1 },
+		.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+		.gws_not_connected = 1,
+		.input_mux = 0,
+		.init = &lml33_init,
+	}, {
+		.type = BUZ,
+		.name = "Buz",
+		.i2c_decoder = "saa7111",
+		.addrs_decoder = saa7111_addrs,
+		.i2c_encoder = "saa7185",
+		.addrs_encoder = saa7185_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 2,
+		.input = {
+			{ 3, "Composite" },
+			{ 7, "S-Video" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+		.tvn = {
+			&f50ccir601,
+			&f60ccir601,
+			&f50ccir601
+		},
+		.jpeg_int = ZR36057_ISR_GIRQ1,
+		.vsync_int = ZR36057_ISR_GIRQ0,
+		.gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
+		.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+		.gpcs = { 3, 1 },
+		.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+		.gws_not_connected = 1,
+		.input_mux = 0,
+		.init = &buz_init,
+	}, {
+		.type = AVS6EYES,
+		.name = "6-Eyes",
+		/* AverMedia chose not to brand the 6-Eyes. Thus it
+		   can't be autodetected, and requires card=x. */
+		.i2c_decoder = "ks0127",
+		.addrs_decoder = ks0127_addrs,
+		.i2c_encoder = "bt866",
+		.addrs_encoder = bt866_addrs,
+		.video_codec = CODEC_TYPE_ZR36060,
+
+		.inputs = 10,
+		.input = {
+			{ 0, "Composite 1" },
+			{ 1, "Composite 2" },
+			{ 2, "Composite 3" },
+			{ 4, "Composite 4" },
+			{ 5, "Composite 5" },
+			{ 6, "Composite 6" },
+			{ 8, "S-Video 1" },
+			{ 9, "S-Video 2" },
+			{10, "S-Video 3" },
+			{15, "YCbCr" }
+		},
+		.norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+		.tvn = {
+			&f50ccir601_avs6eyes,
+			&f60ccir601_avs6eyes,
+			NULL
+		},
+		.jpeg_int = ZR36057_ISR_GIRQ1,
+		.vsync_int = ZR36057_ISR_GIRQ0,
+		.gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam
+		.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam
+		.gpcs = { 3, 1 },			// Validity unknown /Sam
+		.vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 },  // Validity unknown /Sam
+		.gws_not_connected = 1,
+		.input_mux = 1,
+		.init = &avs6eyes_init,
+	}
+
+};
+
+/*
+ * I2C functions
+ */
+/* software I2C functions */
+static int
+zoran_i2c_getsda (void *data)
+{
+	struct zoran *zr = (struct zoran *) data;
+
+	return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static int
+zoran_i2c_getscl (void *data)
+{
+	struct zoran *zr = (struct zoran *) data;
+
+	return btread(ZR36057_I2CBR) & 1;
+}
+
+static void
+zoran_i2c_setsda (void *data,
+		  int   state)
+{
+	struct zoran *zr = (struct zoran *) data;
+
+	if (state)
+		zr->i2cbr |= 2;
+	else
+		zr->i2cbr &= ~2;
+	btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static void
+zoran_i2c_setscl (void *data,
+		  int   state)
+{
+	struct zoran *zr = (struct zoran *) data;
+
+	if (state)
+		zr->i2cbr |= 1;
+	else
+		zr->i2cbr &= ~1;
+	btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+	.setsda = zoran_i2c_setsda,
+	.setscl = zoran_i2c_setscl,
+	.getsda = zoran_i2c_getsda,
+	.getscl = zoran_i2c_getscl,
+	.udelay = 10,
+	.timeout = 100,
+};
+
+static int
+zoran_register_i2c (struct zoran *zr)
+{
+	memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
+	       sizeof(struct i2c_algo_bit_data));
+	zr->i2c_algo.data = zr;
+	strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
+		sizeof(zr->i2c_adapter.name));
+	i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
+	zr->i2c_adapter.algo_data = &zr->i2c_algo;
+	zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
+	return i2c_bit_add_bus(&zr->i2c_adapter);
+}
+
+static void
+zoran_unregister_i2c (struct zoran *zr)
+{
+	i2c_del_adapter(&zr->i2c_adapter);
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+int
+zoran_check_jpg_settings (struct zoran              *zr,
+			  struct zoran_jpg_settings *settings,
+			  int try)
+{
+	int err = 0, err0 = 0;
+
+	dprintk(4,
+		KERN_DEBUG
+		"%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
+		ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
+		settings->VerDcm, settings->TmpDcm);
+	dprintk(4,
+		KERN_DEBUG
+		"%s: %s - x: %d, y: %d, w: %d, y: %d\n",
+		ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
+		settings->img_width, settings->img_height);
+	/* Check decimation, set default values for decimation = 1, 2, 4 */
+	switch (settings->decimation) {
+	case 1:
+
+		settings->HorDcm = 1;
+		settings->VerDcm = 1;
+		settings->TmpDcm = 1;
+		settings->field_per_buff = 2;
+		settings->img_x = 0;
+		settings->img_y = 0;
+		settings->img_width = BUZ_MAX_WIDTH;
+		settings->img_height = BUZ_MAX_HEIGHT / 2;
+		break;
+	case 2:
+
+		settings->HorDcm = 2;
+		settings->VerDcm = 1;
+		settings->TmpDcm = 2;
+		settings->field_per_buff = 1;
+		settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+		settings->img_y = 0;
+		settings->img_width =
+		    (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+		settings->img_height = BUZ_MAX_HEIGHT / 2;
+		break;
+	case 4:
+
+		if (zr->card.type == DC10_new) {
+			dprintk(1,
+				KERN_DEBUG
+				"%s: %s - HDec by 4 is not supported on the DC10\n",
+				ZR_DEVNAME(zr), __func__);
+			err0++;
+			break;
+		}
+
+		settings->HorDcm = 4;
+		settings->VerDcm = 2;
+		settings->TmpDcm = 2;
+		settings->field_per_buff = 1;
+		settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+		settings->img_y = 0;
+		settings->img_width =
+		    (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+		settings->img_height = BUZ_MAX_HEIGHT / 2;
+		break;
+	case 0:
+
+		/* We have to check the data the user has set */
+
+		if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
+		    (zr->card.type == DC10_new || settings->HorDcm != 4)) {
+			settings->HorDcm = clamp(settings->HorDcm, 1, 2);
+			err0++;
+		}
+		if (settings->VerDcm != 1 && settings->VerDcm != 2) {
+			settings->VerDcm = clamp(settings->VerDcm, 1, 2);
+			err0++;
+		}
+		if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
+			settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
+			err0++;
+		}
+		if (settings->field_per_buff != 1 &&
+		    settings->field_per_buff != 2) {
+			settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
+			err0++;
+		}
+		if (settings->img_x < 0) {
+			settings->img_x = 0;
+			err0++;
+		}
+		if (settings->img_y < 0) {
+			settings->img_y = 0;
+			err0++;
+		}
+		if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
+			settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
+			err0++;
+		}
+		if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
+			settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
+			err0++;
+		}
+		if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
+			settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
+			err0++;
+		}
+		if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
+			settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
+			err0++;
+		}
+		if (settings->img_width % (16 * settings->HorDcm) != 0) {
+			settings->img_width -= settings->img_width % (16 * settings->HorDcm);
+			if (settings->img_width == 0)
+				settings->img_width = 16 * settings->HorDcm;
+			err0++;
+		}
+		if (settings->img_height % (8 * settings->VerDcm) != 0) {
+			settings->img_height -= settings->img_height % (8 * settings->VerDcm);
+			if (settings->img_height == 0)
+				settings->img_height = 8 * settings->VerDcm;
+			err0++;
+		}
+
+		if (!try && err0) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - error in params for decimation = 0\n",
+				ZR_DEVNAME(zr), __func__);
+			err++;
+		}
+		break;
+	default:
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
+			ZR_DEVNAME(zr), __func__, settings->decimation);
+		err++;
+		break;
+	}
+
+	if (settings->jpg_comp.quality > 100)
+		settings->jpg_comp.quality = 100;
+	if (settings->jpg_comp.quality < 5)
+		settings->jpg_comp.quality = 5;
+	if (settings->jpg_comp.APPn < 0)
+		settings->jpg_comp.APPn = 0;
+	if (settings->jpg_comp.APPn > 15)
+		settings->jpg_comp.APPn = 15;
+	if (settings->jpg_comp.APP_len < 0)
+		settings->jpg_comp.APP_len = 0;
+	if (settings->jpg_comp.APP_len > 60)
+		settings->jpg_comp.APP_len = 60;
+	if (settings->jpg_comp.COM_len < 0)
+		settings->jpg_comp.COM_len = 0;
+	if (settings->jpg_comp.COM_len > 60)
+		settings->jpg_comp.COM_len = 60;
+	if (err)
+		return -EINVAL;
+	return 0;
+}
+
+void
+zoran_open_init_params (struct zoran *zr)
+{
+	int i;
+
+	/* User must explicitly set a window */
+	zr->overlay_settings.is_set = 0;
+	zr->overlay_mask = NULL;
+	zr->overlay_active = ZORAN_FREE;
+
+	zr->v4l_memgrab_active = 0;
+	zr->v4l_overlay_active = 0;
+	zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+	zr->v4l_grab_seq = 0;
+	zr->v4l_settings.width = 192;
+	zr->v4l_settings.height = 144;
+	zr->v4l_settings.format = &zoran_formats[7];	/* YUY2 - YUV-4:2:2 packed */
+	zr->v4l_settings.bytesperline =
+	    zr->v4l_settings.width *
+	    ((zr->v4l_settings.format->depth + 7) / 8);
+
+	/* DMA ring stuff for V4L */
+	zr->v4l_pend_tail = 0;
+	zr->v4l_pend_head = 0;
+	zr->v4l_sync_tail = 0;
+	zr->v4l_buffers.active = ZORAN_FREE;
+	for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+		zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;	/* nothing going on */
+	}
+	zr->v4l_buffers.allocated = 0;
+
+	for (i = 0; i < BUZ_MAX_FRAME; i++) {
+		zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER;	/* nothing going on */
+	}
+	zr->jpg_buffers.active = ZORAN_FREE;
+	zr->jpg_buffers.allocated = 0;
+	/* Set necessary params and call zoran_check_jpg_settings to set the defaults */
+	zr->jpg_settings.decimation = 1;
+	zr->jpg_settings.jpg_comp.quality = 50;	/* default compression factor 8 */
+	if (zr->card.type != BUZ)
+		zr->jpg_settings.odd_even = 1;
+	else
+		zr->jpg_settings.odd_even = 0;
+	zr->jpg_settings.jpg_comp.APPn = 0;
+	zr->jpg_settings.jpg_comp.APP_len = 0;	/* No APPn marker */
+	memset(zr->jpg_settings.jpg_comp.APP_data, 0,
+	       sizeof(zr->jpg_settings.jpg_comp.APP_data));
+	zr->jpg_settings.jpg_comp.COM_len = 0;	/* No COM marker */
+	memset(zr->jpg_settings.jpg_comp.COM_data, 0,
+	       sizeof(zr->jpg_settings.jpg_comp.COM_data));
+	zr->jpg_settings.jpg_comp.jpeg_markers =
+	    V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT;
+	i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
+	if (i)
+		dprintk(1, KERN_ERR "%s: %s internal error\n",
+			ZR_DEVNAME(zr), __func__);
+
+	clear_interrupt_counters(zr);
+	zr->testing = 0;
+}
+
+static void __devinit
+test_interrupts (struct zoran *zr)
+{
+	DEFINE_WAIT(wait);
+	int timeout, icr;
+
+	clear_interrupt_counters(zr);
+
+	zr->testing = 1;
+	icr = btread(ZR36057_ICR);
+	btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+	prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
+	timeout = schedule_timeout(HZ);
+	finish_wait(&zr->test_q, &wait);
+	btwrite(0, ZR36057_ICR);
+	btwrite(0x78000000, ZR36057_ISR);
+	zr->testing = 0;
+	dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
+	if (timeout) {
+		dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
+	}
+	if (zr36067_debug > 1)
+		print_interrupts(zr);
+	btwrite(icr, ZR36057_ICR);
+}
+
+static int __devinit
+zr36057_init (struct zoran *zr)
+{
+	int j, err;
+
+	dprintk(1,
+		KERN_INFO
+		"%s: %s - initializing card[%d], zr=%p\n",
+		ZR_DEVNAME(zr), __func__, zr->id, zr);
+
+	/* default setup of all parameters which will persist between opens */
+	zr->user = 0;
+
+	init_waitqueue_head(&zr->v4l_capq);
+	init_waitqueue_head(&zr->jpg_capq);
+	init_waitqueue_head(&zr->test_q);
+	zr->jpg_buffers.allocated = 0;
+	zr->v4l_buffers.allocated = 0;
+
+	zr->vbuf_base = (void *) vidmem;
+	zr->vbuf_width = 0;
+	zr->vbuf_height = 0;
+	zr->vbuf_depth = 0;
+	zr->vbuf_bytesperline = 0;
+
+	/* Avoid nonsense settings from user for default input/norm */
+	if (default_norm < 0 || default_norm > 2)
+		default_norm = 0;
+	if (default_norm == 0) {
+		zr->norm = V4L2_STD_PAL;
+		zr->timing = zr->card.tvn[0];
+	} else if (default_norm == 1) {
+		zr->norm = V4L2_STD_NTSC;
+		zr->timing = zr->card.tvn[1];
+	} else {
+		zr->norm = V4L2_STD_SECAM;
+		zr->timing = zr->card.tvn[2];
+	}
+	if (zr->timing == NULL) {
+		dprintk(1,
+			KERN_WARNING
+			"%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
+			ZR_DEVNAME(zr), __func__);
+		zr->norm = V4L2_STD_PAL;
+		zr->timing = zr->card.tvn[0];
+	}
+
+	if (default_input > zr->card.inputs-1) {
+		dprintk(1,
+			KERN_WARNING
+			"%s: default_input value %d out of range (0-%d)\n",
+			ZR_DEVNAME(zr), default_input, zr->card.inputs-1);
+		default_input = 0;
+	}
+	zr->input = default_input;
+
+	/* default setup (will be repeated at every open) */
+	zoran_open_init_params(zr);
+
+	/* allocate memory *before* doing anything to the hardware
+	 * in case allocation fails */
+	zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
+	zr->video_dev = video_device_alloc();
+	if (!zr->stat_com || !zr->video_dev) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - kmalloc (STAT_COM) failed\n",
+			ZR_DEVNAME(zr), __func__);
+		err = -ENOMEM;
+		goto exit_free;
+	}
+	for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+		zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
+	}
+
+	/*
+	 *   Now add the template and register the device unit.
+	 */
+	memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
+	zr->video_dev->parent = &zr->pci_dev->dev;
+	strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
+	/* It's not a mem2mem device, but you can both capture and output from
+	   one and the same device. This should really be split up into two
+	   device nodes, but that's a job for another day. */
+	zr->video_dev->vfl_dir = VFL_DIR_M2M;
+	err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
+	if (err < 0)
+		goto exit_free;
+	video_set_drvdata(zr->video_dev, zr);
+
+	zoran_init_hardware(zr);
+	if (zr36067_debug > 2)
+		detect_guest_activity(zr);
+	test_interrupts(zr);
+	if (!pass_through) {
+		decoder_call(zr, video, s_stream, 0);
+		encoder_call(zr, video, s_routing, 2, 0, 0);
+	}
+
+	zr->zoran_proc = NULL;
+	zr->initialized = 1;
+	return 0;
+
+exit_free:
+	kfree(zr->stat_com);
+	kfree(zr->video_dev);
+	return err;
+}
+
+static void __devexit zoran_remove(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+	struct zoran *zr = to_zoran(v4l2_dev);
+
+	if (!zr->initialized)
+		goto exit_free;
+
+	/* unregister videocodec bus */
+	if (zr->codec) {
+		struct videocodec_master *master = zr->codec->master_data;
+
+		videocodec_detach(zr->codec);
+		kfree(master);
+	}
+	if (zr->vfe) {
+		struct videocodec_master *master = zr->vfe->master_data;
+
+		videocodec_detach(zr->vfe);
+		kfree(master);
+	}
+
+	/* unregister i2c bus */
+	zoran_unregister_i2c(zr);
+	/* disable PCI bus-mastering */
+	zoran_set_pci_master(zr, 0);
+	/* put chip into reset */
+	btwrite(0, ZR36057_SPGPPCR);
+	free_irq(zr->pci_dev->irq, zr);
+	/* unmap and free memory */
+	kfree(zr->stat_com);
+	zoran_proc_cleanup(zr);
+	iounmap(zr->zr36057_mem);
+	pci_disable_device(zr->pci_dev);
+	video_unregister_device(zr->video_dev);
+exit_free:
+	v4l2_device_unregister(&zr->v4l2_dev);
+	kfree(zr);
+}
+
+void
+zoran_vdev_release (struct video_device *vdev)
+{
+	kfree(vdev);
+}
+
+static struct videocodec_master * __devinit
+zoran_setup_videocodec (struct zoran *zr,
+			int           type)
+{
+	struct videocodec_master *m = NULL;
+
+	m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
+	if (!m) {
+		dprintk(1, KERN_ERR "%s: %s - no memory\n",
+			ZR_DEVNAME(zr), __func__);
+		return m;
+	}
+
+	/* magic and type are unused for master struct. Makes sense only at
+	   codec structs.
+	   In the past, .type were initialized to the old V4L1 .hardware
+	   value, as VID_HARDWARE_ZR36067
+	 */
+	m->magic = 0L;
+	m->type = 0;
+
+	m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
+	strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+	m->data = zr;
+
+	switch (type)
+	{
+	case CODEC_TYPE_ZR36060:
+		m->readreg = zr36060_read;
+		m->writereg = zr36060_write;
+		m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
+		break;
+	case CODEC_TYPE_ZR36050:
+		m->readreg = zr36050_read;
+		m->writereg = zr36050_write;
+		m->flags |= CODEC_FLAG_JPEG;
+		break;
+	case CODEC_TYPE_ZR36016:
+		m->readreg = zr36016_read;
+		m->writereg = zr36016_write;
+		m->flags |= CODEC_FLAG_VFE;
+		break;
+	}
+
+	return m;
+}
+
+static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct zoran *zr = to_zoran(sd->v4l2_dev);
+
+	/* Bt819 needs to reset its FIFO buffer using #FRST pin and
+	   LML33 card uses GPIO(7) for that. */
+	if (cmd == BT819_FIFO_RESET_LOW)
+		GPIO(zr, 7, 0);
+	else if (cmd == BT819_FIFO_RESET_HIGH)
+		GPIO(zr, 7, 1);
+}
+
+/*
+ *   Scan for a Buz card (actually for the PCI controller ZR36057),
+ *   request the irq and map the io memory
+ */
+static int __devinit zoran_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
+{
+	unsigned char latency, need_latency;
+	struct zoran *zr;
+	int result;
+	struct videocodec_master *master_vfe = NULL;
+	struct videocodec_master *master_codec = NULL;
+	int card_num;
+	char *codec_name, *vfe_name;
+	unsigned int nr;
+
+
+	nr = zoran_num++;
+	if (nr >= BUZ_MAX) {
+		dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
+			ZORAN_NAME, BUZ_MAX);
+		return -ENOENT;
+	}
+
+	zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
+	if (!zr) {
+		dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
+			ZORAN_NAME, __func__);
+		return -ENOMEM;
+	}
+	zr->v4l2_dev.notify = zoran_subdev_notify;
+	if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
+		goto zr_free_mem;
+	zr->pci_dev = pdev;
+	zr->id = nr;
+	snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
+	spin_lock_init(&zr->spinlock);
+	mutex_init(&zr->resource_lock);
+	mutex_init(&zr->other_lock);
+	if (pci_enable_device(pdev))
+		goto zr_unreg;
+	zr->revision = zr->pci_dev->revision;
+
+	dprintk(1,
+		KERN_INFO
+		"%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n",
+		ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision,
+		zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0));
+	if (zr->revision >= 2) {
+		dprintk(1,
+			KERN_INFO
+			"%s: Subsystem vendor=0x%04x id=0x%04x\n",
+			ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor,
+			zr->pci_dev->subsystem_device);
+	}
+
+	/* Use auto-detected card type? */
+	if (card[nr] == -1) {
+		if (zr->revision < 2) {
+			dprintk(1,
+				KERN_ERR
+				"%s: No card type specified, please use the card=X module parameter\n",
+				ZR_DEVNAME(zr));
+			dprintk(1,
+				KERN_ERR
+				"%s: It is not possible to auto-detect ZR36057 based cards\n",
+				ZR_DEVNAME(zr));
+			goto zr_unreg;
+		}
+
+		card_num = ent->driver_data;
+		if (card_num >= NUM_CARDS) {
+			dprintk(1,
+				KERN_ERR
+				"%s: Unknown card, try specifying card=X module parameter\n",
+				ZR_DEVNAME(zr));
+			goto zr_unreg;
+		}
+		dprintk(3,
+			KERN_DEBUG
+			"%s: %s() - card %s detected\n",
+			ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name);
+	} else {
+		card_num = card[nr];
+		if (card_num >= NUM_CARDS || card_num < 0) {
+			dprintk(1,
+				KERN_ERR
+				"%s: User specified card type %d out of range (0 .. %d)\n",
+				ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
+			goto zr_unreg;
+		}
+	}
+
+	/* even though we make this a non pointer and thus
+	 * theoretically allow for making changes to this struct
+	 * on a per-individual card basis at runtime, this is
+	 * strongly discouraged. This structure is intended to
+	 * keep general card information, no settings or anything */
+	zr->card = zoran_cards[card_num];
+	snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
+		 "%s[%u]", zr->card.name, zr->id);
+
+	zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
+	if (!zr->zr36057_mem) {
+		dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
+			ZR_DEVNAME(zr), __func__);
+		goto zr_unreg;
+	}
+
+	result = request_irq(zr->pci_dev->irq, zoran_irq,
+			     IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr);
+	if (result < 0) {
+		if (result == -EINVAL) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - bad irq number or handler\n",
+				ZR_DEVNAME(zr), __func__);
+		} else if (result == -EBUSY) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
+				ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
+		} else {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - can't assign irq, error code %d\n",
+				ZR_DEVNAME(zr), __func__, result);
+		}
+		goto zr_unmap;
+	}
+
+	/* set PCI latency timer */
+	pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+			     &latency);
+	need_latency = zr->revision > 1 ? 32 : 48;
+	if (latency != need_latency) {
+		dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
+			ZR_DEVNAME(zr), latency, need_latency);
+		pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+				      need_latency);
+	}
+
+	zr36057_restart(zr);
+	/* i2c */
+	dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
+		ZR_DEVNAME(zr));
+
+	if (zoran_register_i2c(zr) < 0) {
+		dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
+			ZR_DEVNAME(zr), __func__);
+		goto zr_free_irq;
+	}
+
+	zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+		&zr->i2c_adapter, zr->card.i2c_decoder,
+		0, zr->card.addrs_decoder);
+
+	if (zr->card.i2c_encoder)
+		zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+			&zr->i2c_adapter, zr->card.i2c_encoder,
+			0, zr->card.addrs_encoder);
+
+	dprintk(2,
+		KERN_INFO "%s: Initializing videocodec bus...\n",
+		ZR_DEVNAME(zr));
+
+	if (zr->card.video_codec) {
+		codec_name = codecid_to_modulename(zr->card.video_codec);
+		if (codec_name) {
+			result = request_module(codec_name);
+			if (result) {
+				dprintk(1,
+					KERN_ERR
+					"%s: failed to load modules %s: %d\n",
+					ZR_DEVNAME(zr), codec_name, result);
+			}
+		}
+	}
+	if (zr->card.video_vfe) {
+		vfe_name = codecid_to_modulename(zr->card.video_vfe);
+		if (vfe_name) {
+			result = request_module(vfe_name);
+			if (result < 0) {
+				dprintk(1,
+					KERN_ERR
+					"%s: failed to load modules %s: %d\n",
+					ZR_DEVNAME(zr), vfe_name, result);
+			}
+		}
+	}
+
+	/* reset JPEG codec */
+	jpeg_codec_sleep(zr, 1);
+	jpeg_codec_reset(zr);
+	/* video bus enabled */
+	/* display codec revision */
+	if (zr->card.video_codec != 0) {
+		master_codec = zoran_setup_videocodec(zr, zr->card.video_codec);
+		if (!master_codec)
+			goto zr_unreg_i2c;
+		zr->codec = videocodec_attach(master_codec);
+		if (!zr->codec) {
+			dprintk(1, KERN_ERR "%s: %s - no codec found\n",
+				ZR_DEVNAME(zr), __func__);
+			goto zr_free_codec;
+		}
+		if (zr->codec->type != zr->card.video_codec) {
+			dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
+				ZR_DEVNAME(zr), __func__);
+			goto zr_detach_codec;
+		}
+	}
+	if (zr->card.video_vfe != 0) {
+		master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe);
+		if (!master_vfe)
+			goto zr_detach_codec;
+		zr->vfe = videocodec_attach(master_vfe);
+		if (!zr->vfe) {
+			dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
+				ZR_DEVNAME(zr), __func__);
+			goto zr_free_vfe;
+		}
+		if (zr->vfe->type != zr->card.video_vfe) {
+			dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
+				ZR_DEVNAME(zr), __func__);
+			goto zr_detach_vfe;
+		}
+	}
+
+	/* take care of Natoma chipset and a revision 1 zr36057 */
+	if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
+		zr->jpg_buffers.need_contiguous = 1;
+		dprintk(1, KERN_INFO
+			"%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+			ZR_DEVNAME(zr));
+	}
+
+	if (zr36057_init(zr) < 0)
+		goto zr_detach_vfe;
+
+	zoran_proc_init(zr);
+
+	return 0;
+
+zr_detach_vfe:
+	videocodec_detach(zr->vfe);
+zr_free_vfe:
+	kfree(master_vfe);
+zr_detach_codec:
+	videocodec_detach(zr->codec);
+zr_free_codec:
+	kfree(master_codec);
+zr_unreg_i2c:
+	zoran_unregister_i2c(zr);
+zr_free_irq:
+	btwrite(0, ZR36057_SPGPPCR);
+	free_irq(zr->pci_dev->irq, zr);
+zr_unmap:
+	iounmap(zr->zr36057_mem);
+zr_unreg:
+	v4l2_device_unregister(&zr->v4l2_dev);
+zr_free_mem:
+	kfree(zr);
+
+	return -ENODEV;
+}
+
+static struct pci_driver zoran_driver = {
+	.name = "zr36067",
+	.id_table = zr36067_pci_tbl,
+	.probe = zoran_probe,
+	.remove = __devexit_p(zoran_remove),
+};
+
+static int __init zoran_init(void)
+{
+	int res;
+
+	printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
+	       ZORAN_VERSION);
+
+	/* check the parameters we have been given, adjust if necessary */
+	if (v4l_nbufs < 2)
+		v4l_nbufs = 2;
+	if (v4l_nbufs > VIDEO_MAX_FRAME)
+		v4l_nbufs = VIDEO_MAX_FRAME;
+	/* The user specfies the in KB, we want them in byte
+	 * (and page aligned) */
+	v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+	if (v4l_bufsize < 32768)
+		v4l_bufsize = 32768;
+	/* 2 MB is arbitrary but sufficient for the maximum possible images */
+	if (v4l_bufsize > 2048 * 1024)
+		v4l_bufsize = 2048 * 1024;
+	if (jpg_nbufs < 4)
+		jpg_nbufs = 4;
+	if (jpg_nbufs > BUZ_MAX_FRAME)
+		jpg_nbufs = BUZ_MAX_FRAME;
+	jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
+	if (jpg_bufsize < 8192)
+		jpg_bufsize = 8192;
+	if (jpg_bufsize > (512 * 1024))
+		jpg_bufsize = 512 * 1024;
+	/* Use parameter for vidmem or try to find a video card */
+	if (vidmem) {
+		dprintk(1,
+			KERN_INFO
+			"%s: Using supplied video memory base address @ 0x%lx\n",
+			ZORAN_NAME, vidmem);
+	}
+
+	/* some mainboards might not do PCI-PCI data transfer well */
+	if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
+		dprintk(1,
+			KERN_WARNING
+			"%s: chipset does not support reliable PCI-PCI DMA\n",
+			ZORAN_NAME);
+	}
+
+	res = pci_register_driver(&zoran_driver);
+	if (res) {
+		dprintk(1,
+			KERN_ERR
+			"%s: Unable to register ZR36057 driver\n",
+			ZORAN_NAME);
+		return res;
+	}
+
+	return 0;
+}
+
+static void __exit zoran_exit(void)
+{
+	pci_unregister_driver(&zoran_driver);
+}
+
+module_init(zoran_init);
+module_exit(zoran_exit);
diff --git a/drivers/media/pci/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h
new file mode 100644
index 000000000000..4936fead73e8
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_card.h
@@ -0,0 +1,54 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_CARD_H__
+#define __ZORAN_CARD_H__
+
+extern int zr36067_debug;
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (zr36067_debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* Anybody who uses more than four? */
+#define BUZ_MAX 4
+
+extern struct video_device zoran_template;
+
+extern int zoran_check_jpg_settings(struct zoran *zr,
+				    struct zoran_jpg_settings *settings,
+				    int try);
+extern void zoran_open_init_params(struct zoran *zr);
+extern void zoran_vdev_release(struct video_device *vdev);
+
+void zr36016_write(struct videocodec *codec, u16 reg, u32 val);
+
+#endif				/* __ZORAN_CARD_H__ */
diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c
new file mode 100644
index 000000000000..a4cd504b8eee
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_device.c
@@ -0,0 +1,1640 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles device access (PCI/I2C/codec/...)
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \
+		   ZR36057_ISR_GIRQ1 | \
+		   ZR36057_ISR_JPEGRepIRQ )
+
+static bool lml33dpath;		/* default = 0
+				 * 1 will use digital path in capture
+				 * mode instead of analog. It can be
+				 * used for picture adjustments using
+				 * tool like xawtv while watching image
+				 * on TV monitor connected to the output.
+				 * However, due to absence of 75 Ohm
+				 * load on Bt819 input, there will be
+				 * some image imperfections */
+
+module_param(lml33dpath, bool, 0644);
+MODULE_PARM_DESC(lml33dpath,
+		 "Use digital path capture mode (on LML33 cards)");
+
+static void
+zr36057_init_vfe (struct zoran *zr);
+
+/*
+ * General Purpose I/O and Guest bus access
+ */
+
+/*
+ * This is a bit tricky. When a board lacks a GPIO function, the corresponding
+ * GPIO bit number in the card_info structure is set to 0.
+ */
+
+void
+GPIO (struct zoran *zr,
+      int           bit,
+      unsigned int  value)
+{
+	u32 reg;
+	u32 mask;
+
+	/* Make sure the bit number is legal
+	 * A bit number of -1 (lacking) gives a mask of 0,
+	 * making it harmless */
+	mask = (1 << (24 + bit)) & 0xff000000;
+	reg = btread(ZR36057_GPPGCR1) & ~mask;
+	if (value) {
+		reg |= mask;
+	}
+	btwrite(reg, ZR36057_GPPGCR1);
+	udelay(1);
+}
+
+/*
+ * Wait til post office is no longer busy
+ */
+
+int
+post_office_wait (struct zoran *zr)
+{
+	u32 por;
+
+//      while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
+	while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
+		/* wait for something to happen */
+	}
+	if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) {
+		/* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
+		dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr),
+			por);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+post_office_write (struct zoran *zr,
+		   unsigned int  guest,
+		   unsigned int  reg,
+		   unsigned int  value)
+{
+	u32 por;
+
+	por =
+	    ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
+	    ((reg & 7) << 16) | (value & 0xFF);
+	btwrite(por, ZR36057_POR);
+
+	return post_office_wait(zr);
+}
+
+int
+post_office_read (struct zoran *zr,
+		  unsigned int  guest,
+		  unsigned int  reg)
+{
+	u32 por;
+
+	por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
+	btwrite(por, ZR36057_POR);
+	if (post_office_wait(zr) < 0) {
+		return -1;
+	}
+
+	return btread(ZR36057_POR) & 0xFF;
+}
+
+/*
+ * detect guests
+ */
+
+static void
+dump_guests (struct zoran *zr)
+{
+	if (zr36067_debug > 2) {
+		int i, guest[8];
+
+		for (i = 1; i < 8; i++) {	// Don't read jpeg codec here
+			guest[i] = post_office_read(zr, i, 0);
+		}
+
+		printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+		for (i = 1; i < 8; i++) {
+			printk(" 0x%02x", guest[i]);
+		}
+		printk("\n");
+	}
+}
+
+static inline unsigned long
+get_time (void)
+{
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+	return (1000000 * tv.tv_sec + tv.tv_usec);
+}
+
+void
+detect_guest_activity (struct zoran *zr)
+{
+	int timeout, i, j, res, guest[8], guest0[8], change[8][3];
+	unsigned long t0, t1;
+
+	dump_guests(zr);
+	printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
+	       ZR_DEVNAME(zr));
+	for (i = 1; i < 8; i++) {	// Don't read jpeg codec here
+		guest0[i] = guest[i] = post_office_read(zr, i, 0);
+	}
+
+	timeout = 0;
+	j = 0;
+	t0 = get_time();
+	while (timeout < 10000) {
+		udelay(10);
+		timeout++;
+		for (i = 1; (i < 8) && (j < 8); i++) {
+			res = post_office_read(zr, i, 0);
+			if (res != guest[i]) {
+				t1 = get_time();
+				change[j][0] = (t1 - t0);
+				t0 = t1;
+				change[j][1] = i;
+				change[j][2] = res;
+				j++;
+				guest[i] = res;
+			}
+		}
+		if (j >= 8)
+			break;
+	}
+	printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+	for (i = 1; i < 8; i++) {
+		printk(" 0x%02x", guest0[i]);
+	}
+	printk("\n");
+	if (j == 0) {
+		printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr));
+		return;
+	}
+	for (i = 0; i < j; i++) {
+		printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr),
+		       change[i][0], change[i][1], change[i][2]);
+	}
+}
+
+/*
+ * JPEG Codec access
+ */
+
+void
+jpeg_codec_sleep (struct zoran *zr,
+		  int           sleep)
+{
+	GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep);
+	if (!sleep) {
+		dprintk(3,
+			KERN_DEBUG
+			"%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n",
+			ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+		udelay(500);
+	} else {
+		dprintk(3,
+			KERN_DEBUG
+			"%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n",
+			ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+		udelay(2);
+	}
+}
+
+int
+jpeg_codec_reset (struct zoran *zr)
+{
+	/* Take the codec out of sleep */
+	jpeg_codec_sleep(zr, 0);
+
+	if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) {
+		post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0,
+				  0);
+		udelay(2);
+	} else {
+		GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0);
+		udelay(2);
+		GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1);
+		udelay(2);
+	}
+
+	return 0;
+}
+
+/*
+ *   Set the registers for the size we have specified. Don't bother
+ *   trying to understand this without the ZR36057 manual in front of
+ *   you [AC].
+ *
+ *   PS: The manual is free for download in .pdf format from
+ *   www.zoran.com - nicely done those folks.
+ */
+
+static void
+zr36057_adjust_vfe (struct zoran          *zr,
+		    enum zoran_codec_mode  mode)
+{
+	u32 reg;
+
+	switch (mode) {
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		reg = btread(ZR36057_VFEHCR);
+		if ((reg & (1 << 10)) && zr->card.type != LML33R10) {
+			reg += ((1 << 10) | 1);
+		}
+		btwrite(reg, ZR36057_VFEHCR);
+		break;
+	case BUZ_MODE_MOTION_COMPRESS:
+	case BUZ_MODE_IDLE:
+	default:
+		if ((zr->norm & V4L2_STD_NTSC) ||
+		    (zr->card.type == LML33R10 &&
+		     (zr->norm & V4L2_STD_PAL)))
+			btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		else
+			btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		reg = btread(ZR36057_VFEHCR);
+		if (!(reg & (1 << 10)) && zr->card.type != LML33R10) {
+			reg -= ((1 << 10) | 1);
+		}
+		btwrite(reg, ZR36057_VFEHCR);
+		break;
+	}
+}
+
+/*
+ * set geometry
+ */
+
+static void
+zr36057_set_vfe (struct zoran              *zr,
+		 int                        video_width,
+		 int                        video_height,
+		 const struct zoran_format *format)
+{
+	struct tvnorm *tvn;
+	unsigned HStart, HEnd, VStart, VEnd;
+	unsigned DispMode;
+	unsigned VidWinWid, VidWinHt;
+	unsigned hcrop1, hcrop2, vcrop1, vcrop2;
+	unsigned Wa, We, Ha, He;
+	unsigned X, Y, HorDcm, VerDcm;
+	u32 reg;
+	unsigned mask_line_size;
+
+	tvn = zr->timing;
+
+	Wa = tvn->Wa;
+	Ha = tvn->Ha;
+
+	dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
+		ZR_DEVNAME(zr), video_width, video_height);
+
+	if (video_width < BUZ_MIN_WIDTH ||
+	    video_height < BUZ_MIN_HEIGHT ||
+	    video_width > Wa || video_height > Ha) {
+		dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
+			ZR_DEVNAME(zr), video_width, video_height);
+		return;
+	}
+
+	/**** zr36057 ****/
+
+	/* horizontal */
+	VidWinWid = video_width;
+	X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa);
+	We = (VidWinWid * 64) / X;
+	HorDcm = 64 - X;
+	hcrop1 = 2 * ((tvn->Wa - We) / 4);
+	hcrop2 = tvn->Wa - We - hcrop1;
+	HStart = tvn->HStart ? tvn->HStart : 1;
+	/* (Ronald) Original comment:
+	 * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+"
+	 * this is false. It inverses chroma values on the LML33R10 (so Cr
+	 * suddenly is shown as Cb and reverse, really cool effect if you
+	 * want to see blue faces, not useful otherwise). So don't use |1.
+	 * However, the DC10 has '0' as HStart, but does need |1, so we
+	 * use a dirty check...
+	 */
+	HEnd = HStart + tvn->Wa - 1;
+	HStart += hcrop1;
+	HEnd -= hcrop2;
+	reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
+	    | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
+	if (zr->card.vfe_pol.hsync_pol)
+		reg |= ZR36057_VFEHCR_HSPol;
+	btwrite(reg, ZR36057_VFEHCR);
+
+	/* Vertical */
+	DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
+	VidWinHt = DispMode ? video_height : video_height / 2;
+	Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha);
+	He = (VidWinHt * 64) / Y;
+	VerDcm = 64 - Y;
+	vcrop1 = (tvn->Ha / 2 - He) / 2;
+	vcrop2 = tvn->Ha / 2 - He - vcrop1;
+	VStart = tvn->VStart;
+	VEnd = VStart + tvn->Ha / 2;	// - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP
+	VStart += vcrop1;
+	VEnd -= vcrop2;
+	reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
+	    | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
+	if (zr->card.vfe_pol.vsync_pol)
+		reg |= ZR36057_VFEVCR_VSPol;
+	btwrite(reg, ZR36057_VFEVCR);
+
+	/* scaler and pixel format */
+	reg = 0;
+	reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
+	reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
+	reg |= (DispMode << ZR36057_VFESPFR_DispMode);
+	/* RJ: I don't know, why the following has to be the opposite
+	 * of the corresponding ZR36060 setting, but only this way
+	 * we get the correct colors when uncompressing to the screen  */
+	//reg |= ZR36057_VFESPFR_VCLKPol; /**/
+	/* RJ: Don't know if that is needed for NTSC also */
+	if (!(zr->norm & V4L2_STD_NTSC))
+		reg |= ZR36057_VFESPFR_ExtFl;	// NEEDED!!!!!!! Wolfgang
+	reg |= ZR36057_VFESPFR_TopField;
+	if (HorDcm >= 48) {
+		reg |= 3 << ZR36057_VFESPFR_HFilter;	/* 5 tap filter */
+	} else if (HorDcm >= 32) {
+		reg |= 2 << ZR36057_VFESPFR_HFilter;	/* 4 tap filter */
+	} else if (HorDcm >= 16) {
+		reg |= 1 << ZR36057_VFESPFR_HFilter;	/* 3 tap filter */
+	}
+	reg |= format->vfespfr;
+	btwrite(reg, ZR36057_VFESPFR);
+
+	/* display configuration */
+	reg = (16 << ZR36057_VDCR_MinPix)
+	    | (VidWinHt << ZR36057_VDCR_VidWinHt)
+	    | (VidWinWid << ZR36057_VDCR_VidWinWid);
+	if (pci_pci_problems & PCIPCI_TRITON)
+		// || zr->revision < 1) // Revision 1 has also Triton support
+		reg &= ~ZR36057_VDCR_Triton;
+	else
+		reg |= ZR36057_VDCR_Triton;
+	btwrite(reg, ZR36057_VDCR);
+
+	/* (Ronald) don't write this if overlay_mask = NULL */
+	if (zr->overlay_mask) {
+		/* Write overlay clipping mask data, but don't enable overlay clipping */
+		/* RJ: since this makes only sense on the screen, we use
+		 * zr->overlay_settings.width instead of video_width */
+
+		mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+		reg = virt_to_bus(zr->overlay_mask);
+		btwrite(reg, ZR36057_MMTR);
+		reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+		btwrite(reg, ZR36057_MMBR);
+		reg =
+		    mask_line_size - (zr->overlay_settings.width +
+				      31) / 32;
+		if (DispMode == 0)
+			reg += mask_line_size;
+		reg <<= ZR36057_OCR_MaskStride;
+		btwrite(reg, ZR36057_OCR);
+	}
+
+	zr36057_adjust_vfe(zr, zr->codec_mode);
+}
+
+/*
+ * Switch overlay on or off
+ */
+
+void
+zr36057_overlay (struct zoran *zr,
+		 int           on)
+{
+	u32 reg;
+
+	if (on) {
+		/* do the necessary settings ... */
+		btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);	/* switch it off first */
+
+		zr36057_set_vfe(zr,
+				zr->overlay_settings.width,
+				zr->overlay_settings.height,
+				zr->overlay_settings.format);
+
+		/* Start and length of each line MUST be 4-byte aligned.
+		 * This should be already checked before the call to this routine.
+		 * All error messages are internal driver checking only! */
+
+		/* video display top and bottom registers */
+		reg = (long) zr->vbuf_base +
+		    zr->overlay_settings.x *
+		    ((zr->overlay_settings.format->depth + 7) / 8) +
+		    zr->overlay_settings.y *
+		    zr->vbuf_bytesperline;
+		btwrite(reg, ZR36057_VDTR);
+		if (reg & 3)
+			dprintk(1,
+				KERN_ERR
+				"%s: zr36057_overlay() - video_address not aligned\n",
+				ZR_DEVNAME(zr));
+		if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+			reg += zr->vbuf_bytesperline;
+		btwrite(reg, ZR36057_VDBR);
+
+		/* video stride, status, and frame grab register */
+		reg = zr->vbuf_bytesperline -
+		    zr->overlay_settings.width *
+		    ((zr->overlay_settings.format->depth + 7) / 8);
+		if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+			reg += zr->vbuf_bytesperline;
+		if (reg & 3)
+			dprintk(1,
+				KERN_ERR
+				"%s: zr36057_overlay() - video_stride not aligned\n",
+				ZR_DEVNAME(zr));
+		reg = (reg << ZR36057_VSSFGR_DispStride);
+		reg |= ZR36057_VSSFGR_VidOvf;	/* clear overflow status */
+		btwrite(reg, ZR36057_VSSFGR);
+
+		/* Set overlay clipping */
+		if (zr->overlay_settings.clipcount > 0)
+			btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
+
+		/* ... and switch it on */
+		btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
+	} else {
+		/* Switch it off */
+		btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+	}
+}
+
+/*
+ * The overlay mask has one bit for each pixel on a scan line,
+ *  and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+
+void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count)
+{
+	struct zoran *zr = fh->zr;
+	unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+	u32 *mask;
+	int x, y, width, height;
+	unsigned i, j, k;
+
+	/* fill mask with one bits */
+	memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
+
+	for (i = 0; i < count; ++i) {
+		/* pick up local copy of clip */
+		x = vp[i].c.left;
+		y = vp[i].c.top;
+		width = vp[i].c.width;
+		height = vp[i].c.height;
+
+		/* trim clips that extend beyond the window */
+		if (x < 0) {
+			width += x;
+			x = 0;
+		}
+		if (y < 0) {
+			height += y;
+			y = 0;
+		}
+		if (x + width > fh->overlay_settings.width) {
+			width = fh->overlay_settings.width - x;
+		}
+		if (y + height > fh->overlay_settings.height) {
+			height = fh->overlay_settings.height - y;
+		}
+
+		/* ignore degenerate clips */
+		if (height <= 0) {
+			continue;
+		}
+		if (width <= 0) {
+			continue;
+		}
+
+		/* apply clip for each scan line */
+		for (j = 0; j < height; ++j) {
+			/* reset bit for each pixel */
+			/* this can be optimized later if need be */
+			mask = fh->overlay_mask + (y + j) * mask_line_size;
+			for (k = 0; k < width; ++k) {
+				mask[(x + k) / 32] &=
+				    ~((u32) 1 << (x + k) % 32);
+			}
+		}
+	}
+}
+
+/* Enable/Disable uncompressed memory grabbing of the 36057 */
+
+void
+zr36057_set_memgrab (struct zoran *zr,
+		     int           mode)
+{
+	if (mode) {
+		/* We only check SnapShot and not FrameGrab here.  SnapShot==1
+		 * means a capture is already in progress, but FrameGrab==1
+		 * doesn't necessary mean that.  It's more correct to say a 1
+		 * to 0 transition indicates a capture completed.  If a
+		 * capture is pending when capturing is tuned off, FrameGrab
+		 * will be stuck at 1 until capturing is turned back on.
+		 */
+		if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot)
+			dprintk(1,
+				KERN_WARNING
+				"%s: zr36057_set_memgrab(1) with SnapShot on!?\n",
+				ZR_DEVNAME(zr));
+
+		/* switch on VSync interrupts */
+		btwrite(IRQ_MASK, ZR36057_ISR);	// Clear Interrupts
+		btor(zr->card.vsync_int, ZR36057_ICR);	// SW
+
+		/* enable SnapShot */
+		btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+
+		/* Set zr36057 video front end  and enable video */
+		zr36057_set_vfe(zr, zr->v4l_settings.width,
+				zr->v4l_settings.height,
+				zr->v4l_settings.format);
+
+		zr->v4l_memgrab_active = 1;
+	} else {
+		/* switch off VSync interrupts */
+		btand(~zr->card.vsync_int, ZR36057_ICR);	// SW
+
+		zr->v4l_memgrab_active = 0;
+		zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+
+		/* reenable grabbing to screen if it was running */
+		if (zr->v4l_overlay_active) {
+			zr36057_overlay(zr, 1);
+		} else {
+			btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+			btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+		}
+	}
+}
+
+int
+wait_grab_pending (struct zoran *zr)
+{
+	unsigned long flags;
+
+	/* wait until all pending grabs are finished */
+
+	if (!zr->v4l_memgrab_active)
+		return 0;
+
+	wait_event_interruptible(zr->v4l_capq,
+			(zr->v4l_pend_tail == zr->v4l_pend_head));
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+	zr36057_set_memgrab(zr, 0);
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	return 0;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *  Set up the Buz-specific MJPEG part                                       *
+ *                                                                           *
+ *****************************************************************************/
+
+static inline void
+set_frame (struct zoran *zr,
+	   int           val)
+{
+	GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val);
+}
+
+static void
+set_videobus_dir (struct zoran *zr,
+		  int           val)
+{
+	switch (zr->card.type) {
+	case LML33:
+	case LML33R10:
+		if (lml33dpath == 0)
+			GPIO(zr, 5, val);
+		else
+			GPIO(zr, 5, 1);
+		break;
+	default:
+		GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR],
+		     zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val);
+		break;
+	}
+}
+
+static void
+init_jpeg_queue (struct zoran *zr)
+{
+	int i;
+
+	/* re-initialize DMA ring stuff */
+	zr->jpg_que_head = 0;
+	zr->jpg_dma_head = 0;
+	zr->jpg_dma_tail = 0;
+	zr->jpg_que_tail = 0;
+	zr->jpg_seq_num = 0;
+	zr->JPEG_error = 0;
+	zr->num_errors = 0;
+	zr->jpg_err_seq = 0;
+	zr->jpg_err_shift = 0;
+	zr->jpg_queued_num = 0;
+	for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
+		zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER;	/* nothing going on */
+	}
+	for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
+		zr->stat_com[i] = cpu_to_le32(1);	/* mark as unavailable to zr36057 */
+	}
+}
+
+static void
+zr36057_set_jpg (struct zoran          *zr,
+		 enum zoran_codec_mode  mode)
+{
+	struct tvnorm *tvn;
+	u32 reg;
+
+	tvn = zr->timing;
+
+	/* assert P_Reset, disable code transfer, deassert Active */
+	btwrite(0, ZR36057_JPC);
+
+	/* MJPEG compression mode */
+	switch (mode) {
+
+	case BUZ_MODE_MOTION_COMPRESS:
+	default:
+		reg = ZR36057_JMC_MJPGCmpMode;
+		break;
+
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		reg = ZR36057_JMC_MJPGExpMode;
+		reg |= ZR36057_JMC_SyncMstr;
+		/* RJ: The following is experimental - improves the output to screen */
+		//if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
+		break;
+
+	case BUZ_MODE_STILL_COMPRESS:
+		reg = ZR36057_JMC_JPGCmpMode;
+		break;
+
+	case BUZ_MODE_STILL_DECOMPRESS:
+		reg = ZR36057_JMC_JPGExpMode;
+		break;
+
+	}
+	reg |= ZR36057_JMC_JPG;
+	if (zr->jpg_settings.field_per_buff == 1)
+		reg |= ZR36057_JMC_Fld_per_buff;
+	btwrite(reg, ZR36057_JMC);
+
+	/* vertical */
+	btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
+	reg = (6 << ZR36057_VSP_VsyncSize) |
+	      (tvn->Ht << ZR36057_VSP_FrmTot);
+	btwrite(reg, ZR36057_VSP);
+	reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) |
+	      (zr->jpg_settings.img_height << ZR36057_FVAP_PAY);
+	btwrite(reg, ZR36057_FVAP);
+
+	/* horizontal */
+	if (zr->card.vfe_pol.hsync_pol)
+		btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+	else
+		btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+	reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) |
+	      (tvn->Wt << ZR36057_HSP_LineTot);
+	btwrite(reg, ZR36057_HSP);
+	reg = ((zr->jpg_settings.img_x +
+		tvn->HStart + 4) << ZR36057_FHAP_NAX) |
+	      (zr->jpg_settings.img_width << ZR36057_FHAP_PAX);
+	btwrite(reg, ZR36057_FHAP);
+
+	/* field process parameters */
+	if (zr->jpg_settings.odd_even)
+		reg = ZR36057_FPP_Odd_Even;
+	else
+		reg = 0;
+
+	btwrite(reg, ZR36057_FPP);
+
+	/* Set proper VCLK Polarity, else colors will be wrong during playback */
+	//btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
+
+	/* code base address */
+	reg = virt_to_bus(zr->stat_com);
+	btwrite(reg, ZR36057_JCBA);
+
+	/* FIFO threshold (FIFO is 160. double words) */
+	/* NOTE: decimal values here */
+	switch (mode) {
+
+	case BUZ_MODE_STILL_COMPRESS:
+	case BUZ_MODE_MOTION_COMPRESS:
+		if (zr->card.type != BUZ)
+			reg = 140;
+		else
+			reg = 60;
+		break;
+
+	case BUZ_MODE_STILL_DECOMPRESS:
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		reg = 20;
+		break;
+
+	default:
+		reg = 80;
+		break;
+
+	}
+	btwrite(reg, ZR36057_JCFT);
+	zr36057_adjust_vfe(zr, mode);
+
+}
+
+void
+print_interrupts (struct zoran *zr)
+{
+	int res, noerr = 0;
+
+	printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr));
+	if ((res = zr->field_counter) < -1 || res > 1) {
+		printk(" FD:%d", res);
+	}
+	if ((res = zr->intr_counter_GIRQ1) != 0) {
+		printk(" GIRQ1:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_GIRQ0) != 0) {
+		printk(" GIRQ0:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_CodRepIRQ) != 0) {
+		printk(" CodRepIRQ:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
+		printk(" JPEGRepIRQ:%d", res);
+		noerr++;
+	}
+	if (zr->JPEG_max_missed) {
+		printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
+		       zr->JPEG_min_missed);
+	}
+	if (zr->END_event_missed) {
+		printk(" ENDs missed: %d", zr->END_event_missed);
+	}
+	//if (zr->jpg_queued_num) {
+	printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
+	       zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
+	//}
+	if (!noerr) {
+		printk(": no interrupts detected.");
+	}
+	printk("\n");
+}
+
+void
+clear_interrupt_counters (struct zoran *zr)
+{
+	zr->intr_counter_GIRQ1 = 0;
+	zr->intr_counter_GIRQ0 = 0;
+	zr->intr_counter_CodRepIRQ = 0;
+	zr->intr_counter_JPEGRepIRQ = 0;
+	zr->field_counter = 0;
+	zr->IRQ1_in = 0;
+	zr->IRQ1_out = 0;
+	zr->JPEG_in = 0;
+	zr->JPEG_out = 0;
+	zr->JPEG_0 = 0;
+	zr->JPEG_1 = 0;
+	zr->END_event_missed = 0;
+	zr->JPEG_missed = 0;
+	zr->JPEG_max_missed = 0;
+	zr->JPEG_min_missed = 0x7fffffff;
+}
+
+static u32
+count_reset_interrupt (struct zoran *zr)
+{
+	u32 isr;
+
+	if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
+		if (isr & ZR36057_ISR_GIRQ1) {
+			btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
+			zr->intr_counter_GIRQ1++;
+		}
+		if (isr & ZR36057_ISR_GIRQ0) {
+			btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
+			zr->intr_counter_GIRQ0++;
+		}
+		if (isr & ZR36057_ISR_CodRepIRQ) {
+			btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
+			zr->intr_counter_CodRepIRQ++;
+		}
+		if (isr & ZR36057_ISR_JPEGRepIRQ) {
+			btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
+			zr->intr_counter_JPEGRepIRQ++;
+		}
+	}
+	return isr;
+}
+
+void
+jpeg_start (struct zoran *zr)
+{
+	int reg;
+
+	zr->frame_num = 0;
+
+	/* deassert P_reset, disable code transfer, deassert Active */
+	btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
+	/* stop flushing the internal code buffer */
+	btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+	/* enable code transfer */
+	btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);
+
+	/* clear IRQs */
+	btwrite(IRQ_MASK, ZR36057_ISR);
+	/* enable the JPEG IRQs */
+	btwrite(zr->card.jpeg_int |
+			ZR36057_ICR_JPEGRepIRQ |
+			ZR36057_ICR_IntPinEn,
+		ZR36057_ICR);
+
+	set_frame(zr, 0);	// \FRAME
+
+	/* set the JPEG codec guest ID */
+	reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) |
+	       (0 << ZR36057_JCGI_JPEGuestReg);
+	btwrite(reg, ZR36057_JCGI);
+
+	if (zr->card.video_vfe == CODEC_TYPE_ZR36016 &&
+	    zr->card.video_codec == CODEC_TYPE_ZR36050) {
+		/* Enable processing on the ZR36016 */
+		if (zr->vfe)
+			zr36016_write(zr->vfe, 0, 1);
+
+		/* load the address of the GO register in the ZR36050 latch */
+		post_office_write(zr, 0, 0, 0);
+	}
+
+	/* assert Active */
+	btor(ZR36057_JPC_Active, ZR36057_JPC);
+
+	/* enable the Go generation */
+	btor(ZR36057_JMC_Go_en, ZR36057_JMC);
+	udelay(30);
+
+	set_frame(zr, 1);	// /FRAME
+
+	dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr));
+}
+
+void
+zr36057_enable_jpg (struct zoran          *zr,
+		    enum zoran_codec_mode  mode)
+{
+	struct vfe_settings cap;
+	int field_size =
+	    zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
+
+	zr->codec_mode = mode;
+
+	cap.x = zr->jpg_settings.img_x;
+	cap.y = zr->jpg_settings.img_y;
+	cap.width = zr->jpg_settings.img_width;
+	cap.height = zr->jpg_settings.img_height;
+	cap.decimation =
+	    zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8);
+	cap.quality = zr->jpg_settings.jpg_comp.quality;
+
+	switch (mode) {
+
+	case BUZ_MODE_MOTION_COMPRESS: {
+		struct jpeg_app_marker app;
+		struct jpeg_com_marker com;
+
+		/* In motion compress mode, the decoder output must be enabled, and
+		 * the video bus direction set to input.
+		 */
+		set_videobus_dir(zr, 0);
+		decoder_call(zr, video, s_stream, 1);
+		encoder_call(zr, video, s_routing, 0, 0, 0);
+
+		/* Take the JPEG codec and the VFE out of sleep */
+		jpeg_codec_sleep(zr, 0);
+
+		/* set JPEG app/com marker */
+		app.appn = zr->jpg_settings.jpg_comp.APPn;
+		app.len = zr->jpg_settings.jpg_comp.APP_len;
+		memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60);
+		zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA,
+				   sizeof(struct jpeg_app_marker), &app);
+
+		com.len = zr->jpg_settings.jpg_comp.COM_len;
+		memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60);
+		zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA,
+				   sizeof(struct jpeg_com_marker), &com);
+
+		/* Setup the JPEG codec */
+		zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE,
+				   sizeof(int), &field_size);
+		zr->codec->set_video(zr->codec, zr->timing, &cap,
+				     &zr->card.vfe_pol);
+		zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION);
+
+		/* Setup the VFE */
+		if (zr->vfe) {
+			zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE,
+					 sizeof(int), &field_size);
+			zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+					   &zr->card.vfe_pol);
+			zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION);
+		}
+
+		init_jpeg_queue(zr);
+		zr36057_set_jpg(zr, mode);	// \P_Reset, ... Video param, FIFO
+
+		clear_interrupt_counters(zr);
+		dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n",
+			ZR_DEVNAME(zr));
+		break;
+	}
+
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		/* In motion decompression mode, the decoder output must be disabled, and
+		 * the video bus direction set to output.
+		 */
+		decoder_call(zr, video, s_stream, 0);
+		set_videobus_dir(zr, 1);
+		encoder_call(zr, video, s_routing, 1, 0, 0);
+
+		/* Take the JPEG codec and the VFE out of sleep */
+		jpeg_codec_sleep(zr, 0);
+		/* Setup the VFE */
+		if (zr->vfe) {
+			zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+					   &zr->card.vfe_pol);
+			zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION);
+		}
+		/* Setup the JPEG codec */
+		zr->codec->set_video(zr->codec, zr->timing, &cap,
+				     &zr->card.vfe_pol);
+		zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION);
+
+		init_jpeg_queue(zr);
+		zr36057_set_jpg(zr, mode);	// \P_Reset, ... Video param, FIFO
+
+		clear_interrupt_counters(zr);
+		dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n",
+			ZR_DEVNAME(zr));
+		break;
+
+	case BUZ_MODE_IDLE:
+	default:
+		/* shut down processing */
+		btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ),
+		      ZR36057_ICR);
+		btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ,
+			ZR36057_ISR);
+		btand(~ZR36057_JMC_Go_en, ZR36057_JMC);	// \Go_en
+
+		msleep(50);
+
+		set_videobus_dir(zr, 0);
+		set_frame(zr, 1);	// /FRAME
+		btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);	// /CFlush
+		btwrite(0, ZR36057_JPC);	// \P_Reset,\CodTrnsEn,\Active
+		btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
+		btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
+		jpeg_codec_reset(zr);
+		jpeg_codec_sleep(zr, 1);
+		zr36057_adjust_vfe(zr, mode);
+
+		decoder_call(zr, video, s_stream, 1);
+		encoder_call(zr, video, s_routing, 0, 0, 0);
+
+		dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
+		break;
+
+	}
+}
+
+/* when this is called the spinlock must be held */
+void
+zoran_feed_stat_com (struct zoran *zr)
+{
+	/* move frames from pending queue to DMA */
+
+	int frame, i, max_stat_com;
+
+	max_stat_com =
+	    (zr->jpg_settings.TmpDcm ==
+	     1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
+
+	while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com &&
+	       zr->jpg_dma_head < zr->jpg_que_head) {
+
+		frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
+		if (zr->jpg_settings.TmpDcm == 1) {
+			/* fill 1 stat_com entry */
+			i = (zr->jpg_dma_head -
+			     zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+			if (!(zr->stat_com[i] & cpu_to_le32(1)))
+				break;
+			zr->stat_com[i] =
+			    cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+		} else {
+			/* fill 2 stat_com entries */
+			i = ((zr->jpg_dma_head -
+			      zr->jpg_err_shift) & 1) * 2;
+			if (!(zr->stat_com[i] & cpu_to_le32(1)))
+				break;
+			zr->stat_com[i] =
+			    cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+			zr->stat_com[i + 1] =
+			    cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+		}
+		zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
+		zr->jpg_dma_head++;
+
+	}
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+		zr->jpg_queued_num++;
+}
+
+/* when this is called the spinlock must be held */
+static void
+zoran_reap_stat_com (struct zoran *zr)
+{
+	/* move frames from DMA queue to done queue */
+
+	int i;
+	u32 stat_com;
+	unsigned int seq;
+	unsigned int dif;
+	struct zoran_buffer *buffer;
+	int frame;
+
+	/* In motion decompress we don't have a hardware frame counter,
+	 * we just count the interrupts here */
+
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+		zr->jpg_seq_num++;
+	}
+	while (zr->jpg_dma_tail < zr->jpg_dma_head) {
+		if (zr->jpg_settings.TmpDcm == 1)
+			i = (zr->jpg_dma_tail -
+			     zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+		else
+			i = ((zr->jpg_dma_tail -
+			      zr->jpg_err_shift) & 1) * 2 + 1;
+
+		stat_com = le32_to_cpu(zr->stat_com[i]);
+
+		if ((stat_com & 1) == 0) {
+			return;
+		}
+		frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+		buffer = &zr->jpg_buffers.buffer[frame];
+		do_gettimeofday(&buffer->bs.timestamp);
+
+		if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+			buffer->bs.length = (stat_com & 0x7fffff) >> 1;
+
+			/* update sequence number with the help of the counter in stat_com */
+
+			seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
+			dif = (seq - zr->jpg_seq_num) & 0xff;
+			zr->jpg_seq_num += dif;
+		} else {
+			buffer->bs.length = 0;
+		}
+		buffer->bs.seq =
+		    zr->jpg_settings.TmpDcm ==
+		    2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
+		buffer->state = BUZ_STATE_DONE;
+
+		zr->jpg_dma_tail++;
+	}
+}
+
+static void zoran_restart(struct zoran *zr)
+{
+	/* Now the stat_comm buffer is ready for restart */
+	unsigned int status = 0;
+	int mode;
+
+	if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+		decoder_call(zr, video, g_input_status, &status);
+		mode = CODEC_DO_COMPRESSION;
+	} else {
+		status = V4L2_IN_ST_NO_SIGNAL;
+		mode = CODEC_DO_EXPANSION;
+	}
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+	    !(status & V4L2_IN_ST_NO_SIGNAL)) {
+		/********** RESTART code *************/
+		jpeg_codec_reset(zr);
+		zr->codec->set_mode(zr->codec, mode);
+		zr36057_set_jpg(zr, zr->codec_mode);
+		jpeg_start(zr);
+
+		if (zr->num_errors <= 8)
+			dprintk(2, KERN_INFO "%s: Restart\n",
+				ZR_DEVNAME(zr));
+
+		zr->JPEG_missed = 0;
+		zr->JPEG_error = 2;
+		/********** End RESTART code ***********/
+	}
+}
+
+static void
+error_handler (struct zoran *zr,
+	       u32           astat,
+	       u32           stat)
+{
+	int i;
+
+	/* This is JPEG error handling part */
+	if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS &&
+	    zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) {
+		return;
+	}
+
+	if ((stat & 1) == 0 &&
+	    zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
+	    zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) {
+		/* No free buffers... */
+		zoran_reap_stat_com(zr);
+		zoran_feed_stat_com(zr);
+		wake_up_interruptible(&zr->jpg_capq);
+		zr->JPEG_missed = 0;
+		return;
+	}
+
+	if (zr->JPEG_error == 1) {
+		zoran_restart(zr);
+		return;
+	}
+
+	/*
+	 * First entry: error just happened during normal operation
+	 *
+	 * In BUZ_MODE_MOTION_COMPRESS:
+	 *
+	 * Possible glitch in TV signal. In this case we should
+	 * stop the codec and wait for good quality signal before
+	 * restarting it to avoid further problems
+	 *
+	 * In BUZ_MODE_MOTION_DECOMPRESS:
+	 *
+	 * Bad JPEG frame: we have to mark it as processed (codec crashed
+	 * and was not able to do it itself), and to remove it from queue.
+	 */
+	btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+	udelay(1);
+	stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
+	btwrite(0, ZR36057_JPC);
+	btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+	jpeg_codec_reset(zr);
+	jpeg_codec_sleep(zr, 1);
+	zr->JPEG_error = 1;
+	zr->num_errors++;
+
+	/* Report error */
+	if (zr36067_debug > 1 && zr->num_errors <= 8) {
+		long frame;
+		int j;
+
+		frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+		printk(KERN_ERR
+		       "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+		       ZR_DEVNAME(zr), stat, zr->last_isr,
+		       zr->jpg_que_tail, zr->jpg_dma_tail,
+		       zr->jpg_dma_head, zr->jpg_que_head,
+		       zr->jpg_seq_num, frame);
+		printk(KERN_INFO "stat_com frames:");
+		for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+			for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
+				if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus)
+					printk(KERN_CONT "% d->%d", j, i);
+			}
+		}
+		printk(KERN_CONT "\n");
+	}
+	/* Find an entry in stat_com and rotate contents */
+	if (zr->jpg_settings.TmpDcm == 1)
+		i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+	else
+		i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+		/* Mimic zr36067 operation */
+		zr->stat_com[i] |= cpu_to_le32(1);
+		if (zr->jpg_settings.TmpDcm != 1)
+			zr->stat_com[i + 1] |= cpu_to_le32(1);
+		/* Refill */
+		zoran_reap_stat_com(zr);
+		zoran_feed_stat_com(zr);
+		wake_up_interruptible(&zr->jpg_capq);
+		/* Find an entry in stat_com again after refill */
+		if (zr->jpg_settings.TmpDcm == 1)
+			i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+		else
+			i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
+	}
+	if (i) {
+		/* Rotate stat_comm entries to make current entry first */
+		int j;
+		__le32 bus_addr[BUZ_NUM_STAT_COM];
+
+		/* Here we are copying the stat_com array, which
+		 * is already in little endian format, so
+		 * no endian conversions here
+		 */
+		memcpy(bus_addr, zr->stat_com, sizeof(bus_addr));
+
+		for (j = 0; j < BUZ_NUM_STAT_COM; j++)
+			zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM];
+
+		zr->jpg_err_shift += i;
+		zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
+	}
+	if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+		zr->jpg_err_seq = zr->jpg_seq_num;	/* + 1; */
+	zoran_restart(zr);
+}
+
+irqreturn_t
+zoran_irq (int             irq,
+	   void           *dev_id)
+{
+	u32 stat, astat;
+	int count;
+	struct zoran *zr;
+	unsigned long flags;
+
+	zr = dev_id;
+	count = 0;
+
+	if (zr->testing) {
+		/* Testing interrupts */
+		spin_lock_irqsave(&zr->spinlock, flags);
+		while ((stat = count_reset_interrupt(zr))) {
+			if (count++ > 100) {
+				btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+				dprintk(1,
+					KERN_ERR
+					"%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
+					ZR_DEVNAME(zr), stat);
+				wake_up_interruptible(&zr->test_q);
+			}
+		}
+		zr->last_isr = stat;
+		spin_unlock_irqrestore(&zr->spinlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+	while (1) {
+		/* get/clear interrupt status bits */
+		stat = count_reset_interrupt(zr);
+		astat = stat & IRQ_MASK;
+		if (!astat) {
+			break;
+		}
+		dprintk(4,
+			KERN_DEBUG
+			"zoran_irq: astat: 0x%08x, mask: 0x%08x\n",
+			astat, btread(ZR36057_ICR));
+		if (astat & zr->card.vsync_int) {	// SW
+
+			if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+			    zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+				/* count missed interrupts */
+				zr->JPEG_missed++;
+			}
+			//post_office_read(zr,1,0);
+			/* Interrupts may still happen when
+			 * zr->v4l_memgrab_active is switched off.
+			 * We simply ignore them */
+
+			if (zr->v4l_memgrab_active) {
+				/* A lot more checks should be here ... */
+				if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0)
+					dprintk(1,
+						KERN_WARNING
+						"%s: BuzIRQ with SnapShot off ???\n",
+						ZR_DEVNAME(zr));
+
+				if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+					/* There is a grab on a frame going on, check if it has finished */
+					if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) {
+						/* it is finished, notify the user */
+
+						zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
+						zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq;
+						do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp);
+						zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+						zr->v4l_pend_tail++;
+					}
+				}
+
+				if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
+					wake_up_interruptible(&zr->v4l_capq);
+
+				/* Check if there is another grab queued */
+
+				if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
+				    zr->v4l_pend_tail != zr->v4l_pend_head) {
+					int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
+					u32 reg;
+
+					zr->v4l_grab_frame = frame;
+
+					/* Set zr36057 video front end and enable video */
+
+					/* Buffer address */
+
+					reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus;
+					btwrite(reg, ZR36057_VDTR);
+					if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
+						reg += zr->v4l_settings.bytesperline;
+					btwrite(reg, ZR36057_VDBR);
+
+					/* video stride, status, and frame grab register */
+					reg = 0;
+					if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
+						reg += zr->v4l_settings.bytesperline;
+					reg = (reg << ZR36057_VSSFGR_DispStride);
+					reg |= ZR36057_VSSFGR_VidOvf;
+					reg |= ZR36057_VSSFGR_SnapShot;
+					reg |= ZR36057_VSSFGR_FrameGrab;
+					btwrite(reg, ZR36057_VSSFGR);
+
+					btor(ZR36057_VDCR_VidEn,
+					     ZR36057_VDCR);
+				}
+			}
+
+			/* even if we don't grab, we do want to increment
+			 * the sequence counter to see lost frames */
+			zr->v4l_grab_seq++;
+		}
+#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+		if (astat & ZR36057_ISR_CodRepIRQ) {
+			zr->intr_counter_CodRepIRQ++;
+			IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+				ZR_DEVNAME(zr)));
+			btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+		}
+#endif				/* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+		if ((astat & ZR36057_ISR_JPEGRepIRQ) &&
+		    (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+		     zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
+			if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) {
+				char sv[BUZ_NUM_STAT_COM + 1];
+				int i;
+
+				printk(KERN_INFO
+				       "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+				       ZR_DEVNAME(zr), stat,
+				       zr->jpg_settings.odd_even,
+				       zr->jpg_settings.field_per_buff,
+				       zr->JPEG_missed);
+
+				for (i = 0; i < BUZ_NUM_STAT_COM; i++)
+					sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0';
+				sv[BUZ_NUM_STAT_COM] = 0;
+				printk(KERN_INFO
+				       "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+				       ZR_DEVNAME(zr), sv,
+				       zr->jpg_que_tail,
+				       zr->jpg_dma_tail,
+				       zr->jpg_dma_head,
+				       zr->jpg_que_head);
+			} else {
+				/* Get statistics */
+				if (zr->JPEG_missed > zr->JPEG_max_missed)
+					zr->JPEG_max_missed = zr->JPEG_missed;
+				if (zr->JPEG_missed < zr->JPEG_min_missed)
+					zr->JPEG_min_missed = zr->JPEG_missed;
+			}
+
+			if (zr36067_debug > 2 && zr->frame_num < 6) {
+				int i;
+
+				printk(KERN_INFO "%s: seq=%ld stat_com:",
+				       ZR_DEVNAME(zr), zr->jpg_seq_num);
+				for (i = 0; i < 4; i++) {
+					printk(KERN_CONT " %08x",
+					       le32_to_cpu(zr->stat_com[i]));
+				}
+				printk(KERN_CONT "\n");
+			}
+			zr->frame_num++;
+			zr->JPEG_missed = 0;
+			zr->JPEG_error = 0;
+			zoran_reap_stat_com(zr);
+			zoran_feed_stat_com(zr);
+			wake_up_interruptible(&zr->jpg_capq);
+		}
+#endif				/* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+		/* DATERR, too many fields missed, error processing */
+		if ((astat & zr->card.jpeg_int) ||
+		    zr->JPEG_missed > 25 ||
+		    zr->JPEG_error == 1	||
+		    ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
+		     (zr->frame_num && (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) {
+			error_handler(zr, astat, stat);
+		}
+
+		count++;
+		if (count > 10) {
+			dprintk(2, KERN_WARNING "%s: irq loop %d\n",
+				ZR_DEVNAME(zr), count);
+			if (count > 20) {
+				btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+				dprintk(2,
+					KERN_ERR
+					"%s: IRQ lockup, cleared int mask\n",
+					ZR_DEVNAME(zr));
+				break;
+			}
+		}
+		zr->last_isr = stat;
+	}
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	return IRQ_HANDLED;
+}
+
+void
+zoran_set_pci_master (struct zoran *zr,
+		      int           set_master)
+{
+	if (set_master) {
+		pci_set_master(zr->pci_dev);
+	} else {
+		u16 command;
+
+		pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command);
+		command &= ~PCI_COMMAND_MASTER;
+		pci_write_config_word(zr->pci_dev, PCI_COMMAND, command);
+	}
+}
+
+void
+zoran_init_hardware (struct zoran *zr)
+{
+	/* Enable bus-mastering */
+	zoran_set_pci_master(zr, 1);
+
+	/* Initialize the board */
+	if (zr->card.init) {
+		zr->card.init(zr);
+	}
+
+	decoder_call(zr, core, init, 0);
+	decoder_call(zr, core, s_std, zr->norm);
+	decoder_call(zr, video, s_routing,
+		zr->card.input[zr->input].muxsel, 0, 0);
+
+	encoder_call(zr, core, init, 0);
+	encoder_call(zr, video, s_std_output, zr->norm);
+	encoder_call(zr, video, s_routing, 0, 0, 0);
+
+	/* toggle JPEG codec sleep to sync PLL */
+	jpeg_codec_sleep(zr, 1);
+	jpeg_codec_sleep(zr, 0);
+
+	/* set individual interrupt enables (without GIRQ1)
+	 * but don't global enable until zoran_open() */
+
+	//btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR);  // SW
+	// It looks like using only JPEGRepIRQEn is not always reliable,
+	// may be when JPEG codec crashes it won't generate IRQ? So,
+	 /*CP*/			//        btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM    WHY ? LP
+	    zr36057_init_vfe(zr);
+
+	zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+
+	btwrite(IRQ_MASK, ZR36057_ISR);	// Clears interrupts
+}
+
+void
+zr36057_restart (struct zoran *zr)
+{
+	btwrite(0, ZR36057_SPGPPCR);
+	mdelay(1);
+	btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
+	mdelay(1);
+
+	/* assert P_Reset */
+	btwrite(0, ZR36057_JPC);
+	/* set up GPIO direction - all output */
+	btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
+
+	/* set up GPIO pins and guest bus timing */
+	btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
+}
+
+/*
+ * initialize video front end
+ */
+
+static void
+zr36057_init_vfe (struct zoran *zr)
+{
+	u32 reg;
+
+	reg = btread(ZR36057_VFESPFR);
+	reg |= ZR36057_VFESPFR_LittleEndian;
+	reg &= ~ZR36057_VFESPFR_VCLKPol;
+	reg |= ZR36057_VFESPFR_ExtFl;
+	reg |= ZR36057_VFESPFR_TopField;
+	btwrite(reg, ZR36057_VFESPFR);
+	reg = btread(ZR36057_VDCR);
+	if (pci_pci_problems & PCIPCI_TRITON)
+		// || zr->revision < 1) // Revision 1 has also Triton support
+		reg &= ~ZR36057_VDCR_Triton;
+	else
+		reg |= ZR36057_VDCR_Triton;
+	btwrite(reg, ZR36057_VDCR);
+}
diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h
new file mode 100644
index 000000000000..07f2c23ff740
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_device.h
@@ -0,0 +1,95 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_DEVICE_H__
+#define __ZORAN_DEVICE_H__
+
+/* general purpose I/O */
+extern void GPIO(struct zoran *zr,
+		 int bit,
+		 unsigned int value);
+
+/* codec (or actually: guest bus) access */
+extern int post_office_wait(struct zoran *zr);
+extern int post_office_write(struct zoran *zr,
+			     unsigned guest,
+			     unsigned reg,
+			     unsigned value);
+extern int post_office_read(struct zoran *zr,
+			    unsigned guest,
+			    unsigned reg);
+
+extern void detect_guest_activity(struct zoran *zr);
+
+extern void jpeg_codec_sleep(struct zoran *zr,
+			     int sleep);
+extern int jpeg_codec_reset(struct zoran *zr);
+
+/* zr360x7 access to raw capture */
+extern void zr36057_overlay(struct zoran *zr,
+			    int on);
+extern void write_overlay_mask(struct zoran_fh *fh,
+			       struct v4l2_clip *vp,
+			       int count);
+extern void zr36057_set_memgrab(struct zoran *zr,
+				int mode);
+extern int wait_grab_pending(struct zoran *zr);
+
+/* interrupts */
+extern void print_interrupts(struct zoran *zr);
+extern void clear_interrupt_counters(struct zoran *zr);
+extern irqreturn_t zoran_irq(int irq, void *dev_id);
+
+/* JPEG codec access */
+extern void jpeg_start(struct zoran *zr);
+extern void zr36057_enable_jpg(struct zoran *zr,
+			       enum zoran_codec_mode mode);
+extern void zoran_feed_stat_com(struct zoran *zr);
+
+/* general */
+extern void zoran_set_pci_master(struct zoran *zr,
+				 int set_master);
+extern void zoran_init_hardware(struct zoran *zr);
+extern void zr36057_restart(struct zoran *zr);
+
+extern const struct zoran_format zoran_formats[];
+
+extern int v4l_nbufs;
+extern int v4l_bufsize;
+extern int jpg_nbufs;
+extern int jpg_bufsize;
+extern int pass_through;
+
+/* i2c */
+#define decoder_call(zr, o, f, args...) \
+	v4l2_subdev_call(zr->decoder, o, f, ##args)
+#define encoder_call(zr, o, f, args...) \
+	v4l2_subdev_call(zr->encoder, o, f, ##args)
+
+#endif				/* __ZORAN_DEVICE_H__ */
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
new file mode 100644
index 000000000000..53f12c7466b0
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -0,0 +1,3090 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Based on
+ *
+ * Miro DC10 driver
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Iomega Buz driver version 1.0
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * buz.0.0.3
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+ *                        & Marcus Metzler (mocm@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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/spinlock.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "videocodec.h"
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include <linux/mutex.h>
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+
+const struct zoran_format zoran_formats[] = {
+	{
+		.name = "15-bit RGB LE",
+		.fourcc = V4L2_PIX_FMT_RGB555,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 15,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif|
+			   ZR36057_VFESPFR_LittleEndian,
+	}, {
+		.name = "15-bit RGB BE",
+		.fourcc = V4L2_PIX_FMT_RGB555X,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 15,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
+	}, {
+		.name = "16-bit RGB LE",
+		.fourcc = V4L2_PIX_FMT_RGB565,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 16,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif|
+			   ZR36057_VFESPFR_LittleEndian,
+	}, {
+		.name = "16-bit RGB BE",
+		.fourcc = V4L2_PIX_FMT_RGB565X,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 16,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
+	}, {
+		.name = "24-bit RGB",
+		.fourcc = V4L2_PIX_FMT_BGR24,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 24,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
+	}, {
+		.name = "32-bit RGB LE",
+		.fourcc = V4L2_PIX_FMT_BGR32,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 32,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
+	}, {
+		.name = "32-bit RGB BE",
+		.fourcc = V4L2_PIX_FMT_RGB32,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.depth = 32,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_RGB888,
+	}, {
+		.name = "4:2:2, packed, YUYV",
+		.fourcc = V4L2_PIX_FMT_YUYV,
+		.colorspace = V4L2_COLORSPACE_SMPTE170M,
+		.depth = 16,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_YUV422,
+	}, {
+		.name = "4:2:2, packed, UYVY",
+		.fourcc = V4L2_PIX_FMT_UYVY,
+		.colorspace = V4L2_COLORSPACE_SMPTE170M,
+		.depth = 16,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_OVERLAY,
+		.vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
+	}, {
+		.name = "Hardware-encoded Motion-JPEG",
+		.fourcc = V4L2_PIX_FMT_MJPEG,
+		.colorspace = V4L2_COLORSPACE_SMPTE170M,
+		.depth = 0,
+		.flags = ZORAN_FORMAT_CAPTURE |
+			 ZORAN_FORMAT_PLAYBACK |
+			 ZORAN_FORMAT_COMPRESSED,
+	}
+};
+#define NUM_FORMATS ARRAY_SIZE(zoran_formats)
+
+	/* small helper function for calculating buffersizes for v4l2
+	 * we calculate the nearest higher power-of-two, which
+	 * will be the recommended buffersize */
+static __u32
+zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
+{
+	__u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
+	__u32 num = (1024 * 512) / (div);
+	__u32 result = 2;
+
+	num--;
+	while (num) {
+		num >>= 1;
+		result <<= 1;
+	}
+
+	if (result > jpg_bufsize)
+		return jpg_bufsize;
+	if (result < 8192)
+		return 8192;
+	return result;
+}
+
+/* forward references */
+static void v4l_fbuffer_free(struct zoran_fh *fh);
+static void jpg_fbuffer_free(struct zoran_fh *fh);
+
+/* Set mapping mode */
+static void map_mode_raw(struct zoran_fh *fh)
+{
+	fh->map_mode = ZORAN_MAP_MODE_RAW;
+	fh->buffers.buffer_size = v4l_bufsize;
+	fh->buffers.num_buffers = v4l_nbufs;
+}
+static void map_mode_jpg(struct zoran_fh *fh, int play)
+{
+	fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
+	fh->buffers.buffer_size = jpg_bufsize;
+	fh->buffers.num_buffers = jpg_nbufs;
+}
+static inline const char *mode_name(enum zoran_map_mode mode)
+{
+	return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
+}
+
+/*
+ *   Allocate the V4L grab buffers
+ *
+ *   These have to be pysically contiguous.
+ */
+
+static int v4l_fbuffer_alloc(struct zoran_fh *fh)
+{
+	struct zoran *zr = fh->zr;
+	int i, off;
+	unsigned char *mem;
+
+	for (i = 0; i < fh->buffers.num_buffers; i++) {
+		if (fh->buffers.buffer[i].v4l.fbuffer)
+			dprintk(2,
+				KERN_WARNING
+				"%s: %s - buffer %d already allocated!?\n",
+				ZR_DEVNAME(zr), __func__, i);
+
+		//udelay(20);
+		mem = kmalloc(fh->buffers.buffer_size,
+			      GFP_KERNEL | __GFP_NOWARN);
+		if (!mem) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - kmalloc for V4L buf %d failed\n",
+				ZR_DEVNAME(zr), __func__, i);
+			v4l_fbuffer_free(fh);
+			return -ENOBUFS;
+		}
+		fh->buffers.buffer[i].v4l.fbuffer = mem;
+		fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
+		fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
+		for (off = 0; off < fh->buffers.buffer_size;
+		     off += PAGE_SIZE)
+			SetPageReserved(virt_to_page(mem + off));
+		dprintk(4,
+			KERN_INFO
+			"%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
+			ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
+			(unsigned long long)virt_to_bus(mem));
+	}
+
+	fh->buffers.allocated = 1;
+
+	return 0;
+}
+
+/* free the V4L grab buffers */
+static void v4l_fbuffer_free(struct zoran_fh *fh)
+{
+	struct zoran *zr = fh->zr;
+	int i, off;
+	unsigned char *mem;
+
+	dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+	for (i = 0; i < fh->buffers.num_buffers; i++) {
+		if (!fh->buffers.buffer[i].v4l.fbuffer)
+			continue;
+
+		mem = fh->buffers.buffer[i].v4l.fbuffer;
+		for (off = 0; off < fh->buffers.buffer_size;
+		     off += PAGE_SIZE)
+			ClearPageReserved(virt_to_page(mem + off));
+		kfree(fh->buffers.buffer[i].v4l.fbuffer);
+		fh->buffers.buffer[i].v4l.fbuffer = NULL;
+	}
+
+	fh->buffers.allocated = 0;
+}
+
+/*
+ *   Allocate the MJPEG grab buffers.
+ *
+ *   If a Natoma chipset is present and this is a revision 1 zr36057,
+ *   each MJPEG buffer needs to be physically contiguous.
+ *   (RJ: This statement is from Dave Perks' original driver,
+ *   I could never check it because I have a zr36067)
+ *
+ *   RJ: The contents grab buffers needs never be accessed in the driver.
+ *       Therefore there is no need to allocate them with vmalloc in order
+ *       to get a contiguous virtual memory space.
+ *       I don't understand why many other drivers first allocate them with
+ *       vmalloc (which uses internally also get_zeroed_page, but delivers you
+ *       virtual addresses) and then again have to make a lot of efforts
+ *       to get the physical address.
+ *
+ *   Ben Capper:
+ *       On big-endian architectures (such as ppc) some extra steps
+ *       are needed. When reading and writing to the stat_com array
+ *       and fragment buffers, the device expects to see little-
+ *       endian values. The use of cpu_to_le32() and le32_to_cpu()
+ *       in this function (and one or two others in zoran_device.c)
+ *       ensure that these values are always stored in little-endian
+ *       form, regardless of architecture. The zr36057 does Very Bad
+ *       Things on big endian architectures if the stat_com array
+ *       and fragment buffers are not little-endian.
+ */
+
+static int jpg_fbuffer_alloc(struct zoran_fh *fh)
+{
+	struct zoran *zr = fh->zr;
+	int i, j, off;
+	u8 *mem;
+
+	for (i = 0; i < fh->buffers.num_buffers; i++) {
+		if (fh->buffers.buffer[i].jpg.frag_tab)
+			dprintk(2,
+				KERN_WARNING
+				"%s: %s - buffer %d already allocated!?\n",
+				ZR_DEVNAME(zr), __func__, i);
+
+		/* Allocate fragment table for this buffer */
+
+		mem = (void *)get_zeroed_page(GFP_KERNEL);
+		if (!mem) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
+				ZR_DEVNAME(zr), __func__, i);
+			jpg_fbuffer_free(fh);
+			return -ENOBUFS;
+		}
+		fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
+		fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
+
+		if (fh->buffers.need_contiguous) {
+			mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
+			if (mem == NULL) {
+				dprintk(1,
+					KERN_ERR
+					"%s: %s - kmalloc failed for buffer %d\n",
+					ZR_DEVNAME(zr), __func__, i);
+				jpg_fbuffer_free(fh);
+				return -ENOBUFS;
+			}
+			fh->buffers.buffer[i].jpg.frag_tab[0] =
+				cpu_to_le32(virt_to_bus(mem));
+			fh->buffers.buffer[i].jpg.frag_tab[1] =
+				cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
+			for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+				SetPageReserved(virt_to_page(mem + off));
+		} else {
+			/* jpg_bufsize is already page aligned */
+			for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+				mem = (void *)get_zeroed_page(GFP_KERNEL);
+				if (mem == NULL) {
+					dprintk(1,
+						KERN_ERR
+						"%s: %s - get_zeroed_page failed for buffer %d\n",
+						ZR_DEVNAME(zr), __func__, i);
+					jpg_fbuffer_free(fh);
+					return -ENOBUFS;
+				}
+
+				fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
+					cpu_to_le32(virt_to_bus(mem));
+				fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
+					cpu_to_le32((PAGE_SIZE >> 2) << 1);
+				SetPageReserved(virt_to_page(mem));
+			}
+
+			fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
+		}
+	}
+
+	dprintk(4,
+		KERN_DEBUG "%s: %s - %d KB allocated\n",
+		ZR_DEVNAME(zr), __func__,
+		(fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
+
+	fh->buffers.allocated = 1;
+
+	return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void jpg_fbuffer_free(struct zoran_fh *fh)
+{
+	struct zoran *zr = fh->zr;
+	int i, j, off;
+	unsigned char *mem;
+	__le32 frag_tab;
+	struct zoran_buffer *buffer;
+
+	dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+	for (i = 0, buffer = &fh->buffers.buffer[0];
+	     i < fh->buffers.num_buffers; i++, buffer++) {
+		if (!buffer->jpg.frag_tab)
+			continue;
+
+		if (fh->buffers.need_contiguous) {
+			frag_tab = buffer->jpg.frag_tab[0];
+
+			if (frag_tab) {
+				mem = bus_to_virt(le32_to_cpu(frag_tab));
+				for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+					ClearPageReserved(virt_to_page(mem + off));
+				kfree(mem);
+				buffer->jpg.frag_tab[0] = 0;
+				buffer->jpg.frag_tab[1] = 0;
+			}
+		} else {
+			for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+				frag_tab = buffer->jpg.frag_tab[2 * j];
+
+				if (!frag_tab)
+					break;
+				ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
+				free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
+				buffer->jpg.frag_tab[2 * j] = 0;
+				buffer->jpg.frag_tab[2 * j + 1] = 0;
+			}
+		}
+
+		free_page((unsigned long)buffer->jpg.frag_tab);
+		buffer->jpg.frag_tab = NULL;
+	}
+
+	fh->buffers.allocated = 0;
+}
+
+/*
+ *   V4L Buffer grabbing
+ */
+
+static int
+zoran_v4l_set_format (struct zoran_fh           *fh,
+		      int                        width,
+		      int                        height,
+		      const struct zoran_format *format)
+{
+	struct zoran *zr = fh->zr;
+	int bpp;
+
+	/* Check size and format of the grab wanted */
+
+	if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
+	    height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - wrong frame size (%dx%d)\n",
+			ZR_DEVNAME(zr), __func__, width, height);
+		return -EINVAL;
+	}
+
+	bpp = (format->depth + 7) / 8;
+
+	/* Check against available buffer size */
+	if (height * width * bpp > fh->buffers.buffer_size) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - video buffer size (%d kB) is too small\n",
+			ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
+		return -EINVAL;
+	}
+
+	/* The video front end needs 4-byte alinged line sizes */
+
+	if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - wrong frame alignment\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	fh->v4l_settings.width = width;
+	fh->v4l_settings.height = height;
+	fh->v4l_settings.format = format;
+	fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
+
+	return 0;
+}
+
+static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
+{
+	struct zoran *zr = fh->zr;
+	unsigned long flags;
+	int res = 0;
+
+	if (!fh->buffers.allocated) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - buffers not yet allocated\n",
+			ZR_DEVNAME(zr), __func__);
+		res = -ENOMEM;
+	}
+
+	/* No grabbing outside the buffer range! */
+	if (num >= fh->buffers.num_buffers || num < 0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - buffer %d is out of range\n",
+			ZR_DEVNAME(zr), __func__, num);
+		res = -EINVAL;
+	}
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+
+	if (fh->buffers.active == ZORAN_FREE) {
+		if (zr->v4l_buffers.active == ZORAN_FREE) {
+			zr->v4l_buffers = fh->buffers;
+			fh->buffers.active = ZORAN_ACTIVE;
+		} else {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - another session is already capturing\n",
+				ZR_DEVNAME(zr), __func__);
+			res = -EBUSY;
+		}
+	}
+
+	/* make sure a grab isn't going on currently with this buffer */
+	if (!res) {
+		switch (zr->v4l_buffers.buffer[num].state) {
+		default:
+		case BUZ_STATE_PEND:
+			if (zr->v4l_buffers.active == ZORAN_FREE) {
+				fh->buffers.active = ZORAN_FREE;
+				zr->v4l_buffers.allocated = 0;
+			}
+			res = -EBUSY;	/* what are you doing? */
+			break;
+		case BUZ_STATE_DONE:
+			dprintk(2,
+				KERN_WARNING
+				"%s: %s - queueing buffer %d in state DONE!?\n",
+				ZR_DEVNAME(zr), __func__, num);
+		case BUZ_STATE_USER:
+			/* since there is at least one unused buffer there's room for at least
+			 * one more pend[] entry */
+			zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
+			zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
+			zr->v4l_buffers.buffer[num].bs.length =
+			    fh->v4l_settings.bytesperline *
+			    zr->v4l_settings.height;
+			fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	if (!res && zr->v4l_buffers.active == ZORAN_FREE)
+		zr->v4l_buffers.active = fh->buffers.active;
+
+	return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int v4l_sync(struct zoran_fh *fh, int frame)
+{
+	struct zoran *zr = fh->zr;
+	unsigned long flags;
+
+	if (fh->buffers.active == ZORAN_FREE) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - no grab active for this session\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	/* check passed-in frame number */
+	if (frame >= fh->buffers.num_buffers || frame < 0) {
+		dprintk(1,
+			KERN_ERR "%s: %s - frame %d is invalid\n",
+			ZR_DEVNAME(zr), __func__, frame);
+		return -EINVAL;
+	}
+
+	/* Check if is buffer was queued at all */
+	if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - attempt to sync on a buffer which was not queued?\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EPROTO;
+	}
+
+	/* wait on this buffer to get ready */
+	if (!wait_event_interruptible_timeout(zr->v4l_capq,
+		(zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ))
+		return -ETIME;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+
+	/* buffer should now be in BUZ_STATE_DONE */
+	if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
+		dprintk(2,
+			KERN_ERR "%s: %s - internal state error\n",
+			ZR_DEVNAME(zr), __func__);
+
+	zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
+	fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+
+	/* Check if streaming capture has finished */
+	if (zr->v4l_pend_tail == zr->v4l_pend_head) {
+		zr36057_set_memgrab(zr, 0);
+		if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
+			fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
+			zr->v4l_buffers.allocated = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	return 0;
+}
+
+/*
+ *   Queue a MJPEG buffer for capture/playback
+ */
+
+static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
+				 enum zoran_codec_mode mode)
+{
+	struct zoran *zr = fh->zr;
+	unsigned long flags;
+	int res = 0;
+
+	/* Check if buffers are allocated */
+	if (!fh->buffers.allocated) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - buffers not yet allocated\n",
+			ZR_DEVNAME(zr), __func__);
+		return -ENOMEM;
+	}
+
+	/* No grabbing outside the buffer range! */
+	if (num >= fh->buffers.num_buffers || num < 0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - buffer %d out of range\n",
+			ZR_DEVNAME(zr), __func__, num);
+		return -EINVAL;
+	}
+
+	/* what is the codec mode right now? */
+	if (zr->codec_mode == BUZ_MODE_IDLE) {
+		zr->jpg_settings = fh->jpg_settings;
+	} else if (zr->codec_mode != mode) {
+		/* wrong codec mode active - invalid */
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - codec in wrong mode\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	if (fh->buffers.active == ZORAN_FREE) {
+		if (zr->jpg_buffers.active == ZORAN_FREE) {
+			zr->jpg_buffers = fh->buffers;
+			fh->buffers.active = ZORAN_ACTIVE;
+		} else {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - another session is already capturing\n",
+				ZR_DEVNAME(zr), __func__);
+			res = -EBUSY;
+		}
+	}
+
+	if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
+		/* Ok load up the jpeg codec */
+		zr36057_enable_jpg(zr, mode);
+	}
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+
+	if (!res) {
+		switch (zr->jpg_buffers.buffer[num].state) {
+		case BUZ_STATE_DONE:
+			dprintk(2,
+				KERN_WARNING
+				"%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
+				ZR_DEVNAME(zr), __func__);
+		case BUZ_STATE_USER:
+			/* since there is at least one unused buffer there's room for at
+			 *least one more pend[] entry */
+			zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
+			zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
+			fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
+			zoran_feed_stat_com(zr);
+			break;
+		default:
+		case BUZ_STATE_DMA:
+		case BUZ_STATE_PEND:
+			if (zr->jpg_buffers.active == ZORAN_FREE) {
+				fh->buffers.active = ZORAN_FREE;
+				zr->jpg_buffers.allocated = 0;
+			}
+			res = -EBUSY;	/* what are you doing? */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	if (!res && zr->jpg_buffers.active == ZORAN_FREE)
+		zr->jpg_buffers.active = fh->buffers.active;
+
+	return res;
+}
+
+static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
+{
+	struct zoran *zr = fh->zr;
+	int res = 0;
+
+	/* Does the user want to stop streaming? */
+	if (frame < 0) {
+		if (zr->codec_mode == mode) {
+			if (fh->buffers.active == ZORAN_FREE) {
+				dprintk(1,
+					KERN_ERR
+					"%s: %s(-1) - session not active\n",
+					ZR_DEVNAME(zr), __func__);
+				return -EINVAL;
+			}
+			fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
+			zr->jpg_buffers.allocated = 0;
+			zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+			return 0;
+		} else {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - stop streaming but not in streaming mode\n",
+				ZR_DEVNAME(zr), __func__);
+			return -EINVAL;
+		}
+	}
+
+	if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
+		return res;
+
+	/* Start the jpeg codec when the first frame is queued  */
+	if (!res && zr->jpg_que_head == 1)
+		jpeg_start(zr);
+
+	return res;
+}
+
+/*
+ *   Sync on a MJPEG buffer
+ */
+
+static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
+{
+	struct zoran *zr = fh->zr;
+	unsigned long flags;
+	int frame;
+
+	if (fh->buffers.active == ZORAN_FREE) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - capture is not currently active\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+	if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
+	    zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - codec not in streaming mode\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+	if (!wait_event_interruptible_timeout(zr->jpg_capq,
+			(zr->jpg_que_tail != zr->jpg_dma_tail ||
+			 zr->jpg_dma_tail == zr->jpg_dma_head),
+			10*HZ)) {
+		int isr;
+
+		btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+		udelay(1);
+		zr->codec->control(zr->codec, CODEC_G_STATUS,
+					   sizeof(isr), &isr);
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - timeout: codec isr=0x%02x\n",
+			ZR_DEVNAME(zr), __func__, isr);
+
+		return -ETIME;
+
+	}
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+
+	spin_lock_irqsave(&zr->spinlock, flags);
+
+	if (zr->jpg_dma_tail != zr->jpg_dma_head)
+		frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+	else
+		frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+	/* buffer should now be in BUZ_STATE_DONE */
+	if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
+		dprintk(2,
+			KERN_ERR "%s: %s - internal state error\n",
+			ZR_DEVNAME(zr), __func__);
+
+	*bs = zr->jpg_buffers.buffer[frame].bs;
+	bs->frame = frame;
+	zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
+	fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
+
+	spin_unlock_irqrestore(&zr->spinlock, flags);
+
+	return 0;
+}
+
+static void zoran_open_init_session(struct zoran_fh *fh)
+{
+	int i;
+	struct zoran *zr = fh->zr;
+
+	/* Per default, map the V4L Buffers */
+	map_mode_raw(fh);
+
+	/* take over the card's current settings */
+	fh->overlay_settings = zr->overlay_settings;
+	fh->overlay_settings.is_set = 0;
+	fh->overlay_settings.format = zr->overlay_settings.format;
+	fh->overlay_active = ZORAN_FREE;
+
+	/* v4l settings */
+	fh->v4l_settings = zr->v4l_settings;
+	/* jpg settings */
+	fh->jpg_settings = zr->jpg_settings;
+
+	/* buffers */
+	memset(&fh->buffers, 0, sizeof(fh->buffers));
+	for (i = 0; i < MAX_FRAME; i++) {
+		fh->buffers.buffer[i].state = BUZ_STATE_USER;	/* nothing going on */
+		fh->buffers.buffer[i].bs.frame = i;
+	}
+	fh->buffers.allocated = 0;
+	fh->buffers.active = ZORAN_FREE;
+}
+
+static void zoran_close_end_session(struct zoran_fh *fh)
+{
+	struct zoran *zr = fh->zr;
+
+	/* overlay */
+	if (fh->overlay_active != ZORAN_FREE) {
+		fh->overlay_active = zr->overlay_active = ZORAN_FREE;
+		zr->v4l_overlay_active = 0;
+		if (!zr->v4l_memgrab_active)
+			zr36057_overlay(zr, 0);
+		zr->overlay_mask = NULL;
+	}
+
+	if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+		/* v4l capture */
+		if (fh->buffers.active != ZORAN_FREE) {
+			unsigned long flags;
+
+			spin_lock_irqsave(&zr->spinlock, flags);
+			zr36057_set_memgrab(zr, 0);
+			zr->v4l_buffers.allocated = 0;
+			zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+			spin_unlock_irqrestore(&zr->spinlock, flags);
+		}
+
+		/* v4l buffers */
+		if (fh->buffers.allocated)
+			v4l_fbuffer_free(fh);
+	} else {
+		/* jpg capture */
+		if (fh->buffers.active != ZORAN_FREE) {
+			zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+			zr->jpg_buffers.allocated = 0;
+			zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+		}
+
+		/* jpg buffers */
+		if (fh->buffers.allocated)
+			jpg_fbuffer_free(fh);
+	}
+}
+
+/*
+ *   Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int zoran_open(struct file *file)
+{
+	struct zoran *zr = video_drvdata(file);
+	struct zoran_fh *fh;
+	int res, first_open = 0;
+
+	dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
+		ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
+
+	mutex_lock(&zr->other_lock);
+
+	if (zr->user >= 2048) {
+		dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
+			ZR_DEVNAME(zr), zr->user);
+		res = -EBUSY;
+		goto fail_unlock;
+	}
+
+	/* now, create the open()-specific file_ops struct */
+	fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
+	if (!fh) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - allocation of zoran_fh failed\n",
+			ZR_DEVNAME(zr), __func__);
+		res = -ENOMEM;
+		goto fail_unlock;
+	}
+	/* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
+	 * on norm-change! */
+	fh->overlay_mask =
+	    kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+	if (!fh->overlay_mask) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - allocation of overlay_mask failed\n",
+			ZR_DEVNAME(zr), __func__);
+		res = -ENOMEM;
+		goto fail_fh;
+	}
+
+	if (zr->user++ == 0)
+		first_open = 1;
+
+	/*mutex_unlock(&zr->resource_lock);*/
+
+	/* default setup - TODO: look at flags */
+	if (first_open) {	/* First device open */
+		zr36057_restart(zr);
+		zoran_open_init_params(zr);
+		zoran_init_hardware(zr);
+
+		btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+	}
+
+	/* set file_ops stuff */
+	file->private_data = fh;
+	fh->zr = zr;
+	zoran_open_init_session(fh);
+	mutex_unlock(&zr->other_lock);
+
+	return 0;
+
+fail_fh:
+	kfree(fh);
+fail_unlock:
+	mutex_unlock(&zr->other_lock);
+
+	dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n",
+		ZR_DEVNAME(zr), res, zr->user);
+
+	return res;
+}
+
+static int
+zoran_close(struct file  *file)
+{
+	struct zoran_fh *fh = file->private_data;
+	struct zoran *zr = fh->zr;
+
+	dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
+		ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
+
+	/* kernel locks (fs/device.c), so don't do that ourselves
+	 * (prevents deadlocks) */
+	mutex_lock(&zr->other_lock);
+
+	zoran_close_end_session(fh);
+
+	if (zr->user-- == 1) {	/* Last process */
+		/* Clean up JPEG process */
+		wake_up_interruptible(&zr->jpg_capq);
+		zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+		zr->jpg_buffers.allocated = 0;
+		zr->jpg_buffers.active = ZORAN_FREE;
+
+		/* disable interrupts */
+		btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+		if (zr36067_debug > 1)
+			print_interrupts(zr);
+
+		/* Overlay off */
+		zr->v4l_overlay_active = 0;
+		zr36057_overlay(zr, 0);
+		zr->overlay_mask = NULL;
+
+		/* capture off */
+		wake_up_interruptible(&zr->v4l_capq);
+		zr36057_set_memgrab(zr, 0);
+		zr->v4l_buffers.allocated = 0;
+		zr->v4l_buffers.active = ZORAN_FREE;
+		zoran_set_pci_master(zr, 0);
+
+		if (!pass_through) {	/* Switch to color bar */
+			decoder_call(zr, video, s_stream, 0);
+			encoder_call(zr, video, s_routing, 2, 0, 0);
+		}
+	}
+	mutex_unlock(&zr->other_lock);
+
+	file->private_data = NULL;
+	kfree(fh->overlay_mask);
+	kfree(fh);
+
+	dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
+
+	return 0;
+}
+
+
+static ssize_t
+zoran_read (struct file *file,
+	    char        __user *data,
+	    size_t       count,
+	    loff_t      *ppos)
+{
+	/* we simply don't support read() (yet)... */
+
+	return -EINVAL;
+}
+
+static ssize_t
+zoran_write (struct file *file,
+	     const char  __user *data,
+	     size_t       count,
+	     loff_t      *ppos)
+{
+	/* ...and the same goes for write() */
+
+	return -EINVAL;
+}
+
+static int setup_fbuffer(struct zoran_fh *fh,
+	       void                      *base,
+	       const struct zoran_format *fmt,
+	       int                        width,
+	       int                        height,
+	       int                        bytesperline)
+{
+	struct zoran *zr = fh->zr;
+
+	/* (Ronald) v4l/v4l2 guidelines */
+	if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	/* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
+	   ALi Magik (that needs very low latency while the card needs a
+	   higher value always) */
+
+	if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
+		return -ENXIO;
+
+	/* we need a bytesperline value, even if not given */
+	if (!bytesperline)
+		bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
+
+#if 0
+	if (zr->overlay_active) {
+		/* dzjee... stupid users... don't even bother to turn off
+		 * overlay before changing the memory location...
+		 * normally, we would return errors here. However, one of
+		 * the tools that does this is... xawtv! and since xawtv
+		 * is used by +/- 99% of the users, we'd rather be user-
+		 * friendly and silently do as if nothing went wrong */
+		dprintk(3,
+			KERN_ERR
+			"%s: %s - forced overlay turnoff because framebuffer changed\n",
+			ZR_DEVNAME(zr), __func__);
+		zr36057_overlay(zr, 0);
+	}
+#endif
+
+	if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - no valid overlay format given\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+	if (height <= 0 || width <= 0 || bytesperline <= 0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
+			ZR_DEVNAME(zr), __func__, width, height, bytesperline);
+		return -EINVAL;
+	}
+	if (bytesperline & 3) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - bytesperline (%d) must be 4-byte aligned\n",
+			ZR_DEVNAME(zr), __func__, bytesperline);
+		return -EINVAL;
+	}
+
+	zr->vbuf_base = (void *) ((unsigned long) base & ~3);
+	zr->vbuf_height = height;
+	zr->vbuf_width = width;
+	zr->vbuf_depth = fmt->depth;
+	zr->overlay_settings.format = fmt;
+	zr->vbuf_bytesperline = bytesperline;
+
+	/* The user should set new window parameters */
+	zr->overlay_settings.is_set = 0;
+
+	return 0;
+}
+
+
+static int setup_window(struct zoran_fh *fh,
+			int x,
+			int y,
+			int width,
+			int height,
+			struct v4l2_clip __user *clips,
+			unsigned int clipcount,
+			void __user *bitmap)
+{
+	struct zoran *zr = fh->zr;
+	struct v4l2_clip *vcp = NULL;
+	int on, end;
+
+
+	if (!zr->vbuf_base) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - frame buffer has to be set first\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	if (!fh->overlay_settings.format) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - no overlay format set\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	if (clipcount > 2048) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - invalid clipcount\n",
+			 ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * The video front end needs 4-byte alinged line sizes, we correct that
+	 * silently here if necessary
+	 */
+	if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
+		end = (x + width) & ~1;	/* round down */
+		x = (x + 1) & ~1;	/* round up */
+		width = end - x;
+	}
+
+	if (zr->vbuf_depth == 24) {
+		end = (x + width) & ~3;	/* round down */
+		x = (x + 3) & ~3;	/* round up */
+		width = end - x;
+	}
+
+	if (width > BUZ_MAX_WIDTH)
+		width = BUZ_MAX_WIDTH;
+	if (height > BUZ_MAX_HEIGHT)
+		height = BUZ_MAX_HEIGHT;
+
+	/* Check for invalid parameters */
+	if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
+	    width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - width = %d or height = %d invalid\n",
+			ZR_DEVNAME(zr), __func__, width, height);
+		return -EINVAL;
+	}
+
+	fh->overlay_settings.x = x;
+	fh->overlay_settings.y = y;
+	fh->overlay_settings.width = width;
+	fh->overlay_settings.height = height;
+	fh->overlay_settings.clipcount = clipcount;
+
+	/*
+	 * If an overlay is running, we have to switch it off
+	 * and switch it on again in order to get the new settings in effect.
+	 *
+	 * We also want to avoid that the overlay mask is written
+	 * when an overlay is running.
+	 */
+
+	on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
+	    zr->overlay_active != ZORAN_FREE &&
+	    fh->overlay_active != ZORAN_FREE;
+	if (on)
+		zr36057_overlay(zr, 0);
+
+	/*
+	 *   Write the overlay mask if clips are wanted.
+	 *   We prefer a bitmap.
+	 */
+	if (bitmap) {
+		/* fake value - it just means we want clips */
+		fh->overlay_settings.clipcount = 1;
+
+		if (copy_from_user(fh->overlay_mask, bitmap,
+				   (width * height + 7) / 8)) {
+			return -EFAULT;
+		}
+	} else if (clipcount) {
+		/* write our own bitmap from the clips */
+		vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
+		if (vcp == NULL) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - Alloc of clip mask failed\n",
+				ZR_DEVNAME(zr), __func__);
+			return -ENOMEM;
+		}
+		if (copy_from_user
+		    (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
+			vfree(vcp);
+			return -EFAULT;
+		}
+		write_overlay_mask(fh, vcp, clipcount);
+		vfree(vcp);
+	}
+
+	fh->overlay_settings.is_set = 1;
+	if (fh->overlay_active != ZORAN_FREE &&
+	    zr->overlay_active != ZORAN_FREE)
+		zr->overlay_settings = fh->overlay_settings;
+
+	if (on)
+		zr36057_overlay(zr, 1);
+
+	/* Make sure the changes come into effect */
+	return wait_grab_pending(zr);
+}
+
+static int setup_overlay(struct zoran_fh *fh, int on)
+{
+	struct zoran *zr = fh->zr;
+
+	/* If there is nothing to do, return immediately */
+	if ((on && fh->overlay_active != ZORAN_FREE) ||
+	    (!on && fh->overlay_active == ZORAN_FREE))
+		return 0;
+
+	/* check whether we're touching someone else's overlay */
+	if (on && zr->overlay_active != ZORAN_FREE &&
+	    fh->overlay_active == ZORAN_FREE) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - overlay is already active for another session\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EBUSY;
+	}
+	if (!on && zr->overlay_active != ZORAN_FREE &&
+	    fh->overlay_active == ZORAN_FREE) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - you cannot cancel someone else's session\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EPERM;
+	}
+
+	if (on == 0) {
+		zr->overlay_active = fh->overlay_active = ZORAN_FREE;
+		zr->v4l_overlay_active = 0;
+		/* When a grab is running, the video simply
+		 * won't be switched on any more */
+		if (!zr->v4l_memgrab_active)
+			zr36057_overlay(zr, 0);
+		zr->overlay_mask = NULL;
+	} else {
+		if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - buffer or window not set\n",
+				ZR_DEVNAME(zr), __func__);
+			return -EINVAL;
+		}
+		if (!fh->overlay_settings.format) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - no overlay format set\n",
+				ZR_DEVNAME(zr), __func__);
+			return -EINVAL;
+		}
+		zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
+		zr->v4l_overlay_active = 1;
+		zr->overlay_mask = fh->overlay_mask;
+		zr->overlay_settings = fh->overlay_settings;
+		if (!zr->v4l_memgrab_active)
+			zr36057_overlay(zr, 1);
+		/* When a grab is running, the video will be
+		 * switched on when grab is finished */
+	}
+
+	/* Make sure the changes come into effect */
+	return wait_grab_pending(zr);
+}
+
+/* get the status of a buffer in the clients buffer queue */
+static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
+				    struct v4l2_buffer *buf, int num)
+{
+	struct zoran *zr = fh->zr;
+	unsigned long flags;
+
+	buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:
+		/* check range */
+		if (num < 0 || num >= fh->buffers.num_buffers ||
+		    !fh->buffers.allocated) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - wrong number or buffers not allocated\n",
+				ZR_DEVNAME(zr), __func__);
+			return -EINVAL;
+		}
+
+		spin_lock_irqsave(&zr->spinlock, flags);
+		dprintk(3,
+			KERN_DEBUG
+			"%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
+			ZR_DEVNAME(zr), __func__,
+			"FAL"[fh->buffers.active], num,
+			"UPMD"[zr->v4l_buffers.buffer[num].state],
+			fh->buffers.buffer[num].map ? 'Y' : 'N');
+		spin_unlock_irqrestore(&zr->spinlock, flags);
+
+		buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf->length = fh->buffers.buffer_size;
+
+		/* get buffer */
+		buf->bytesused = fh->buffers.buffer[num].bs.length;
+		if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+		    fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+			buf->sequence = fh->buffers.buffer[num].bs.seq;
+			buf->flags |= V4L2_BUF_FLAG_DONE;
+			buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+		} else {
+			buf->flags |= V4L2_BUF_FLAG_QUEUED;
+		}
+
+		if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
+			buf->field = V4L2_FIELD_TOP;
+		else
+			buf->field = V4L2_FIELD_INTERLACED;
+
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+
+		/* check range */
+		if (num < 0 || num >= fh->buffers.num_buffers ||
+		    !fh->buffers.allocated) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - wrong number or buffers not allocated\n",
+				ZR_DEVNAME(zr), __func__);
+			return -EINVAL;
+		}
+
+		buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+			      V4L2_BUF_TYPE_VIDEO_CAPTURE :
+			      V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		buf->length = fh->buffers.buffer_size;
+
+		/* these variables are only written after frame has been captured */
+		if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+		    fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+			buf->sequence = fh->buffers.buffer[num].bs.seq;
+			buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+			buf->bytesused = fh->buffers.buffer[num].bs.length;
+			buf->flags |= V4L2_BUF_FLAG_DONE;
+		} else {
+			buf->flags |= V4L2_BUF_FLAG_QUEUED;
+		}
+
+		/* which fields are these? */
+		if (fh->jpg_settings.TmpDcm != 1)
+			buf->field = fh->jpg_settings.odd_even ?
+				V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+		else
+			buf->field = fh->jpg_settings.odd_even ?
+				V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
+
+		break;
+
+	default:
+
+		dprintk(5,
+			KERN_ERR
+			"%s: %s - invalid buffer type|map_mode (%d|%d)\n",
+			ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
+		return -EINVAL;
+	}
+
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->index = num;
+	buf->m.offset = buf->length * num;
+
+	return 0;
+}
+
+static int
+zoran_set_norm (struct zoran *zr,
+		v4l2_std_id norm)
+{
+	int on;
+
+	if (zr->v4l_buffers.active != ZORAN_FREE ||
+	    zr->jpg_buffers.active != ZORAN_FREE) {
+		dprintk(1,
+			KERN_WARNING
+			"%s: %s called while in playback/capture mode\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EBUSY;
+	}
+
+	if (!(norm & zr->card.norms)) {
+		dprintk(1,
+			KERN_ERR "%s: %s - unsupported norm %llx\n",
+			ZR_DEVNAME(zr), __func__, norm);
+		return -EINVAL;
+	}
+
+	if (norm == V4L2_STD_ALL) {
+		unsigned int status = 0;
+		v4l2_std_id std = 0;
+
+		decoder_call(zr, video, querystd, &std);
+		decoder_call(zr, core, s_std, std);
+
+		/* let changes come into effect */
+		ssleep(2);
+
+		decoder_call(zr, video, g_input_status, &status);
+		if (status & V4L2_IN_ST_NO_SIGNAL) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s - no norm detected\n",
+				ZR_DEVNAME(zr), __func__);
+			/* reset norm */
+			decoder_call(zr, core, s_std, zr->norm);
+			return -EIO;
+		}
+
+		norm = std;
+	}
+	if (norm & V4L2_STD_SECAM)
+		zr->timing = zr->card.tvn[2];
+	else if (norm & V4L2_STD_NTSC)
+		zr->timing = zr->card.tvn[1];
+	else
+		zr->timing = zr->card.tvn[0];
+
+	/* We switch overlay off and on since a change in the
+	 * norm needs different VFE settings */
+	on = zr->overlay_active && !zr->v4l_memgrab_active;
+	if (on)
+		zr36057_overlay(zr, 0);
+
+	decoder_call(zr, core, s_std, norm);
+	encoder_call(zr, video, s_std_output, norm);
+
+	if (on)
+		zr36057_overlay(zr, 1);
+
+	/* Make sure the changes come into effect */
+	zr->norm = norm;
+
+	return 0;
+}
+
+static int
+zoran_set_input (struct zoran *zr,
+		 int           input)
+{
+	if (input == zr->input) {
+		return 0;
+	}
+
+	if (zr->v4l_buffers.active != ZORAN_FREE ||
+	    zr->jpg_buffers.active != ZORAN_FREE) {
+		dprintk(1,
+			KERN_WARNING
+			"%s: %s called while in playback/capture mode\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EBUSY;
+	}
+
+	if (input < 0 || input >= zr->card.inputs) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - unnsupported input %d\n",
+			ZR_DEVNAME(zr), __func__, input);
+		return -EINVAL;
+	}
+
+	zr->input = input;
+
+	decoder_call(zr, video, s_routing,
+			zr->card.input[input].muxsel, 0, 0);
+
+	return 0;
+}
+
+/*
+ *   ioctl routine
+ */
+
+static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	memset(cap, 0, sizeof(*cap));
+	strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
+	strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+		 pci_name(zr->pci_dev));
+	cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+			    V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
+	return 0;
+}
+
+static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
+{
+	unsigned int num, i;
+
+	for (num = i = 0; i < NUM_FORMATS; i++) {
+		if (zoran_formats[i].flags & flag && num++ == fmt->index) {
+			strncpy(fmt->description, zoran_formats[i].name,
+				sizeof(fmt->description) - 1);
+			/* fmt struct pre-zeroed, so adding '\0' not needed */
+			fmt->pixelformat = zoran_formats[i].fourcc;
+			if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+				fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
+					    struct v4l2_fmtdesc *f)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
+}
+
+static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
+					    struct v4l2_fmtdesc *f)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
+}
+
+static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
+					    struct v4l2_fmtdesc *f)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
+}
+
+static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	mutex_lock(&zr->resource_lock);
+
+	fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
+	fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
+		(fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
+	fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+	fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+	if (fh->jpg_settings.TmpDcm == 1)
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+	else
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+	fmt->fmt.pix.bytesperline = 0;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	if (fh->map_mode != ZORAN_MAP_MODE_RAW)
+		return zoran_g_fmt_vid_out(file, fh, fmt);
+
+	mutex_lock(&zr->resource_lock);
+	fmt->fmt.pix.width = fh->v4l_settings.width;
+	fmt->fmt.pix.height = fh->v4l_settings.height;
+	fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
+					fh->v4l_settings.height;
+	fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
+	fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+	fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+	if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+		fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+	else
+		fmt->fmt.pix.field = V4L2_FIELD_TOP;
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	mutex_lock(&zr->resource_lock);
+
+	fmt->fmt.win.w.left = fh->overlay_settings.x;
+	fmt->fmt.win.w.top = fh->overlay_settings.y;
+	fmt->fmt.win.w.width = fh->overlay_settings.width;
+	fmt->fmt.win.w.height = fh->overlay_settings.height;
+	if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
+		fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
+	else
+		fmt->fmt.win.field = V4L2_FIELD_TOP;
+
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
+		fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
+	if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
+		fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
+	if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
+		fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
+	if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
+		fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
+
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	struct zoran_jpg_settings settings;
+	int res = 0;
+
+	if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+		return -EINVAL;
+
+	mutex_lock(&zr->resource_lock);
+	settings = fh->jpg_settings;
+
+	/* we actually need to set 'real' parameters now */
+	if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
+		settings.TmpDcm = 1;
+	else
+		settings.TmpDcm = 2;
+	settings.decimation = 0;
+	if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+		settings.VerDcm = 2;
+	else
+		settings.VerDcm = 1;
+	if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+		settings.HorDcm = 4;
+	else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+		settings.HorDcm = 2;
+	else
+		settings.HorDcm = 1;
+	if (settings.TmpDcm == 1)
+		settings.field_per_buff = 2;
+	else
+		settings.field_per_buff = 1;
+
+	if (settings.HorDcm > 1) {
+		settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+		settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+	} else {
+		settings.img_x = 0;
+		settings.img_width = BUZ_MAX_WIDTH;
+	}
+
+	/* check */
+	res = zoran_check_jpg_settings(zr, &settings, 1);
+	if (res)
+		goto tryfmt_unlock_and_return;
+
+	/* tell the user what we actually did */
+	fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+	fmt->fmt.pix.height = settings.img_height * 2 /
+		(settings.TmpDcm * settings.VerDcm);
+	if (settings.TmpDcm == 1)
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+	else
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+	fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
+	fmt->fmt.pix.bytesperline = 0;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+tryfmt_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int bpp;
+	int i;
+
+	if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+		return zoran_try_fmt_vid_out(file, fh, fmt);
+
+	mutex_lock(&zr->resource_lock);
+
+	for (i = 0; i < NUM_FORMATS; i++)
+		if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
+			break;
+
+	if (i == NUM_FORMATS) {
+		mutex_unlock(&zr->resource_lock);
+		return -EINVAL;
+	}
+
+	bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8);
+	v4l_bound_align_image(
+		&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2,
+		&fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0);
+	mutex_unlock(&zr->resource_lock);
+
+	return 0;
+}
+
+static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res;
+
+	dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
+			fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+			fmt->fmt.win.w.width,
+			fmt->fmt.win.w.height,
+			fmt->fmt.win.clipcount,
+			fmt->fmt.win.bitmap);
+	mutex_lock(&zr->resource_lock);
+	res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+			   fmt->fmt.win.w.width, fmt->fmt.win.w.height,
+			   (struct v4l2_clip __user *)fmt->fmt.win.clips,
+			   fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	__le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
+	struct zoran_jpg_settings settings;
+	int res = 0;
+
+	dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
+			fmt->fmt.pix.width, fmt->fmt.pix.height,
+			fmt->fmt.pix.pixelformat,
+			(char *) &printformat);
+	if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+		return -EINVAL;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (fh->buffers.allocated) {
+		dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+			ZR_DEVNAME(zr));
+		res = -EBUSY;
+		goto sfmtjpg_unlock_and_return;
+	}
+
+	settings = fh->jpg_settings;
+
+	/* we actually need to set 'real' parameters now */
+	if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
+		settings.TmpDcm = 1;
+	else
+		settings.TmpDcm = 2;
+	settings.decimation = 0;
+	if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+		settings.VerDcm = 2;
+	else
+		settings.VerDcm = 1;
+	if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+		settings.HorDcm = 4;
+	else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+		settings.HorDcm = 2;
+	else
+		settings.HorDcm = 1;
+	if (settings.TmpDcm == 1)
+		settings.field_per_buff = 2;
+	else
+		settings.field_per_buff = 1;
+
+	if (settings.HorDcm > 1) {
+		settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+		settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+	} else {
+		settings.img_x = 0;
+		settings.img_width = BUZ_MAX_WIDTH;
+	}
+
+	/* check */
+	res = zoran_check_jpg_settings(zr, &settings, 0);
+	if (res)
+		goto sfmtjpg_unlock_and_return;
+
+	/* it's ok, so set them */
+	fh->jpg_settings = settings;
+
+	map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+	/* tell the user what we actually did */
+	fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+	fmt->fmt.pix.height = settings.img_height * 2 /
+		(settings.TmpDcm * settings.VerDcm);
+	if (settings.TmpDcm == 1)
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+	else
+		fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+				V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+	fmt->fmt.pix.bytesperline = 0;
+	fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+sfmtjpg_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
+					struct v4l2_format *fmt)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int i;
+	int res = 0;
+
+	if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+		return zoran_s_fmt_vid_out(file, fh, fmt);
+
+	for (i = 0; i < NUM_FORMATS; i++)
+		if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
+			break;
+	if (i == NUM_FORMATS) {
+		dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
+			ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
+		return -EINVAL;
+	}
+
+	mutex_lock(&zr->resource_lock);
+
+	if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
+	    fh->buffers.active != ZORAN_FREE) {
+		dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+				ZR_DEVNAME(zr));
+		res = -EBUSY;
+		goto sfmtv4l_unlock_and_return;
+	}
+	if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+		fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
+	if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+		fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+
+	map_mode_raw(fh);
+
+	res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
+				   &zoran_formats[i]);
+	if (res)
+		goto sfmtv4l_unlock_and_return;
+
+	/* tell the user the results/missing stuff */
+	fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+	fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
+	fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+	if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+		fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+	else
+		fmt->fmt.pix.field = V4L2_FIELD_TOP;
+
+sfmtv4l_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_g_fbuf(struct file *file, void *__fh,
+		struct v4l2_framebuffer *fb)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	memset(fb, 0, sizeof(*fb));
+	mutex_lock(&zr->resource_lock);
+	fb->base = zr->vbuf_base;
+	fb->fmt.width = zr->vbuf_width;
+	fb->fmt.height = zr->vbuf_height;
+	if (zr->overlay_settings.format)
+		fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
+	fb->fmt.bytesperline = zr->vbuf_bytesperline;
+	mutex_unlock(&zr->resource_lock);
+	fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+	fb->fmt.field = V4L2_FIELD_INTERLACED;
+	fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+	return 0;
+}
+
+static int zoran_s_fbuf(struct file *file, void *__fh,
+		const struct v4l2_framebuffer *fb)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int i, res = 0;
+	__le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+
+	for (i = 0; i < NUM_FORMATS; i++)
+		if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+			break;
+	if (i == NUM_FORMATS) {
+		dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
+			ZR_DEVNAME(zr), fb->fmt.pixelformat,
+			(char *)&printformat);
+		return -EINVAL;
+	}
+
+	mutex_lock(&zr->resource_lock);
+	res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
+			    fb->fmt.height, fb->fmt.bytesperline);
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res;
+
+	mutex_lock(&zr->resource_lock);
+	res = setup_overlay(fh, on);
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
+
+static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0;
+
+	if (req->memory != V4L2_MEMORY_MMAP) {
+		dprintk(2,
+				KERN_ERR
+				"%s: only MEMORY_MMAP capture is supported, not %d\n",
+				ZR_DEVNAME(zr), req->memory);
+		return -EINVAL;
+	}
+
+	if (req->count == 0)
+		return zoran_streamoff(file, fh, req->type);
+
+	mutex_lock(&zr->resource_lock);
+	if (fh->buffers.allocated) {
+		dprintk(2,
+				KERN_ERR
+				"%s: VIDIOC_REQBUFS - buffers already allocated\n",
+				ZR_DEVNAME(zr));
+		res = -EBUSY;
+		goto v4l2reqbuf_unlock_and_return;
+	}
+
+	if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
+	    req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		/* control user input */
+		if (req->count < 2)
+			req->count = 2;
+		if (req->count > v4l_nbufs)
+			req->count = v4l_nbufs;
+
+		/* The next mmap will map the V4L buffers */
+		map_mode_raw(fh);
+		fh->buffers.num_buffers = req->count;
+
+		if (v4l_fbuffer_alloc(fh)) {
+			res = -ENOMEM;
+			goto v4l2reqbuf_unlock_and_return;
+		}
+	} else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
+		   fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+		/* we need to calculate size ourselves now */
+		if (req->count < 4)
+			req->count = 4;
+		if (req->count > jpg_nbufs)
+			req->count = jpg_nbufs;
+
+		/* The next mmap will map the MJPEG buffers */
+		map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		fh->buffers.num_buffers = req->count;
+		fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+		if (jpg_fbuffer_alloc(fh)) {
+			res = -ENOMEM;
+			goto v4l2reqbuf_unlock_and_return;
+		}
+	} else {
+		dprintk(1,
+				KERN_ERR
+				"%s: VIDIOC_REQBUFS - unknown type %d\n",
+				ZR_DEVNAME(zr), req->type);
+		res = -EINVAL;
+		goto v4l2reqbuf_unlock_and_return;
+	}
+v4l2reqbuf_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res;
+
+	mutex_lock(&zr->resource_lock);
+	res = zoran_v4l2_buffer_status(fh, buf, buf->index);
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0, codec_mode, buf_type;
+
+	mutex_lock(&zr->resource_lock);
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+			dprintk(1, KERN_ERR
+				"%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+				ZR_DEVNAME(zr), buf->type, fh->map_mode);
+			res = -EINVAL;
+			goto qbuf_unlock_and_return;
+		}
+
+		res = zoran_v4l_queue_frame(fh, buf->index);
+		if (res)
+			goto qbuf_unlock_and_return;
+		if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
+			zr36057_set_memgrab(zr, 1);
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+		if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+			buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+			codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
+		} else {
+			buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			codec_mode = BUZ_MODE_MOTION_COMPRESS;
+		}
+
+		if (buf->type != buf_type) {
+			dprintk(1, KERN_ERR
+				"%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+				ZR_DEVNAME(zr), buf->type, fh->map_mode);
+			res = -EINVAL;
+			goto qbuf_unlock_and_return;
+		}
+
+		res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
+		if (res != 0)
+			goto qbuf_unlock_and_return;
+		if (zr->codec_mode == BUZ_MODE_IDLE &&
+		    fh->buffers.active == ZORAN_LOCKED)
+			zr36057_enable_jpg(zr, codec_mode);
+
+		break;
+
+	default:
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_QBUF - unsupported type %d\n",
+			ZR_DEVNAME(zr), buf->type);
+		res = -EINVAL;
+		break;
+	}
+qbuf_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0, buf_type, num = -1;	/* compiler borks here (?) */
+
+	mutex_lock(&zr->resource_lock);
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+			dprintk(1, KERN_ERR
+				"%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+				ZR_DEVNAME(zr), buf->type, fh->map_mode);
+			res = -EINVAL;
+			goto dqbuf_unlock_and_return;
+		}
+
+		num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+		if (file->f_flags & O_NONBLOCK &&
+		    zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
+			res = -EAGAIN;
+			goto dqbuf_unlock_and_return;
+		}
+		res = v4l_sync(fh, num);
+		if (res)
+			goto dqbuf_unlock_and_return;
+		zr->v4l_sync_tail++;
+		res = zoran_v4l2_buffer_status(fh, buf, num);
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+	{
+		struct zoran_sync bs;
+
+		if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
+			buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		else
+			buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		if (buf->type != buf_type) {
+			dprintk(1, KERN_ERR
+				"%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+				ZR_DEVNAME(zr), buf->type, fh->map_mode);
+			res = -EINVAL;
+			goto dqbuf_unlock_and_return;
+		}
+
+		num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+		if (file->f_flags & O_NONBLOCK &&
+		    zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
+			res = -EAGAIN;
+			goto dqbuf_unlock_and_return;
+		}
+		bs.frame = 0; /* suppress compiler warning */
+		res = jpg_sync(fh, &bs);
+		if (res)
+			goto dqbuf_unlock_and_return;
+		res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
+		break;
+	}
+
+	default:
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_DQBUF - unsupported type %d\n",
+			ZR_DEVNAME(zr), buf->type);
+		res = -EINVAL;
+		break;
+	}
+dqbuf_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0;
+
+	mutex_lock(&zr->resource_lock);
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:	/* raw capture */
+		if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
+		    fh->buffers.active != ZORAN_ACTIVE) {
+			res = -EBUSY;
+			goto strmon_unlock_and_return;
+		}
+
+		zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+		zr->v4l_settings = fh->v4l_settings;
+
+		zr->v4l_sync_tail = zr->v4l_pend_tail;
+		if (!zr->v4l_memgrab_active &&
+		    zr->v4l_pend_head != zr->v4l_pend_tail) {
+			zr36057_set_memgrab(zr, 1);
+		}
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+		/* what is the codec mode right now? */
+		if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
+		    fh->buffers.active != ZORAN_ACTIVE) {
+			res = -EBUSY;
+			goto strmon_unlock_and_return;
+		}
+
+		zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+
+		if (zr->jpg_que_head != zr->jpg_que_tail) {
+			/* Start the jpeg codec when the first frame is queued  */
+			jpeg_start(zr);
+		}
+		break;
+
+	default:
+		dprintk(1,
+			KERN_ERR
+			"%s: VIDIOC_STREAMON - invalid map mode %d\n",
+			ZR_DEVNAME(zr), fh->map_mode);
+		res = -EINVAL;
+		break;
+	}
+strmon_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int i, res = 0;
+	unsigned long flags;
+
+	mutex_lock(&zr->resource_lock);
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:	/* raw capture */
+		if (fh->buffers.active == ZORAN_FREE &&
+		    zr->v4l_buffers.active != ZORAN_FREE) {
+			res = -EPERM;	/* stay off other's settings! */
+			goto strmoff_unlock_and_return;
+		}
+		if (zr->v4l_buffers.active == ZORAN_FREE)
+			goto strmoff_unlock_and_return;
+
+		spin_lock_irqsave(&zr->spinlock, flags);
+		/* unload capture */
+		if (zr->v4l_memgrab_active) {
+
+			zr36057_set_memgrab(zr, 0);
+		}
+
+		for (i = 0; i < fh->buffers.num_buffers; i++)
+			zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
+		fh->buffers = zr->v4l_buffers;
+
+		zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+
+		zr->v4l_grab_seq = 0;
+		zr->v4l_pend_head = zr->v4l_pend_tail = 0;
+		zr->v4l_sync_tail = 0;
+
+		spin_unlock_irqrestore(&zr->spinlock, flags);
+
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+		if (fh->buffers.active == ZORAN_FREE &&
+		    zr->jpg_buffers.active != ZORAN_FREE) {
+			res = -EPERM;	/* stay off other's settings! */
+			goto strmoff_unlock_and_return;
+		}
+		if (zr->jpg_buffers.active == ZORAN_FREE)
+			goto strmoff_unlock_and_return;
+
+		res = jpg_qbuf(fh, -1,
+			     (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+			     BUZ_MODE_MOTION_COMPRESS :
+			     BUZ_MODE_MOTION_DECOMPRESS);
+		if (res)
+			goto strmoff_unlock_and_return;
+		break;
+	default:
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
+			ZR_DEVNAME(zr), fh->map_mode);
+		res = -EINVAL;
+		break;
+	}
+strmoff_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_queryctrl(struct file *file, void *__fh,
+					struct v4l2_queryctrl *ctrl)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	/* we only support hue/saturation/contrast/brightness */
+	if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+	    ctrl->id > V4L2_CID_HUE)
+		return -EINVAL;
+
+	decoder_call(zr, core, queryctrl, ctrl);
+
+	return 0;
+}
+
+static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	/* we only support hue/saturation/contrast/brightness */
+	if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+	    ctrl->id > V4L2_CID_HUE)
+		return -EINVAL;
+
+	mutex_lock(&zr->resource_lock);
+	decoder_call(zr, core, g_ctrl, ctrl);
+	mutex_unlock(&zr->resource_lock);
+
+	return 0;
+}
+
+static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	/* we only support hue/saturation/contrast/brightness */
+	if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+	    ctrl->id > V4L2_CID_HUE)
+		return -EINVAL;
+
+	mutex_lock(&zr->resource_lock);
+	decoder_call(zr, core, s_ctrl, ctrl);
+	mutex_unlock(&zr->resource_lock);
+
+	return 0;
+}
+
+static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	mutex_lock(&zr->resource_lock);
+	*std = zr->norm;
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0;
+
+	mutex_lock(&zr->resource_lock);
+	res = zoran_set_norm(zr, *std);
+	if (res)
+		goto sstd_unlock_and_return;
+
+	res = wait_grab_pending(zr);
+sstd_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_enum_input(struct file *file, void *__fh,
+				 struct v4l2_input *inp)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	if (inp->index >= zr->card.inputs)
+		return -EINVAL;
+
+	strncpy(inp->name, zr->card.input[inp->index].name,
+		sizeof(inp->name) - 1);
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std = V4L2_STD_ALL;
+
+	/* Get status of video decoder */
+	mutex_lock(&zr->resource_lock);
+	decoder_call(zr, video, g_input_status, &inp->status);
+	mutex_unlock(&zr->resource_lock);
+	return 0;
+}
+
+static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+
+	mutex_lock(&zr->resource_lock);
+	*input = zr->input;
+	mutex_unlock(&zr->resource_lock);
+
+	return 0;
+}
+
+static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res;
+
+	mutex_lock(&zr->resource_lock);
+	res = zoran_set_input(zr, input);
+	if (res)
+		goto sinput_unlock_and_return;
+
+	/* Make sure the changes come into effect */
+	res = wait_grab_pending(zr);
+sinput_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_enum_output(struct file *file, void *__fh,
+				  struct v4l2_output *outp)
+{
+	if (outp->index != 0)
+		return -EINVAL;
+
+	outp->index = 0;
+	outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
+	strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
+
+	return 0;
+}
+
+static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
+{
+	*output = 0;
+
+	return 0;
+}
+
+static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
+{
+	if (output != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* cropping (sub-frame capture) */
+static int zoran_cropcap(struct file *file, void *__fh,
+					struct v4l2_cropcap *cropcap)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int type = cropcap->type, res = 0;
+
+	memset(cropcap, 0, sizeof(*cropcap));
+	cropcap->type = type;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	     fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+			ZR_DEVNAME(zr));
+		res = -EINVAL;
+		goto cropcap_unlock_and_return;
+	}
+
+	cropcap->bounds.top = cropcap->bounds.left = 0;
+	cropcap->bounds.width = BUZ_MAX_WIDTH;
+	cropcap->bounds.height = BUZ_MAX_HEIGHT;
+	cropcap->defrect.top = cropcap->defrect.left = 0;
+	cropcap->defrect.width = BUZ_MIN_WIDTH;
+	cropcap->defrect.height = BUZ_MIN_HEIGHT;
+cropcap_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int type = crop->type, res = 0;
+
+	memset(crop, 0, sizeof(*crop));
+	crop->type = type;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	     fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+		dprintk(1,
+			KERN_ERR
+			"%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+			ZR_DEVNAME(zr));
+		res = -EINVAL;
+		goto gcrop_unlock_and_return;
+	}
+
+	crop->c.top = fh->jpg_settings.img_y;
+	crop->c.left = fh->jpg_settings.img_x;
+	crop->c.width = fh->jpg_settings.img_width;
+	crop->c.height = fh->jpg_settings.img_height;
+
+gcrop_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0;
+	struct zoran_jpg_settings settings;
+
+	settings = fh->jpg_settings;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (fh->buffers.allocated) {
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_S_CROP - cannot change settings while active\n",
+			ZR_DEVNAME(zr));
+		res = -EBUSY;
+		goto scrop_unlock_and_return;
+	}
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	     fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+		dprintk(1, KERN_ERR
+			"%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+			ZR_DEVNAME(zr));
+		res = -EINVAL;
+		goto scrop_unlock_and_return;
+	}
+
+	/* move into a form that we understand */
+	settings.img_x = crop->c.left;
+	settings.img_y = crop->c.top;
+	settings.img_width = crop->c.width;
+	settings.img_height = crop->c.height;
+
+	/* check validity */
+	res = zoran_check_jpg_settings(zr, &settings, 0);
+	if (res)
+		goto scrop_unlock_and_return;
+
+	/* accept */
+	fh->jpg_settings = settings;
+
+scrop_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+	return res;
+}
+
+static int zoran_g_jpegcomp(struct file *file, void *__fh,
+					struct v4l2_jpegcompression *params)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	memset(params, 0, sizeof(*params));
+
+	mutex_lock(&zr->resource_lock);
+
+	params->quality = fh->jpg_settings.jpg_comp.quality;
+	params->APPn = fh->jpg_settings.jpg_comp.APPn;
+	memcpy(params->APP_data,
+	       fh->jpg_settings.jpg_comp.APP_data,
+	       fh->jpg_settings.jpg_comp.APP_len);
+	params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+	memcpy(params->COM_data,
+	       fh->jpg_settings.jpg_comp.COM_data,
+	       fh->jpg_settings.jpg_comp.COM_len);
+	params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
+	params->jpeg_markers =
+	    fh->jpg_settings.jpg_comp.jpeg_markers;
+
+	mutex_unlock(&zr->resource_lock);
+
+	return 0;
+}
+
+static int zoran_s_jpegcomp(struct file *file, void *__fh,
+					const struct v4l2_jpegcompression *params)
+{
+	struct zoran_fh *fh = __fh;
+	struct zoran *zr = fh->zr;
+	int res = 0;
+	struct zoran_jpg_settings settings;
+
+	settings = fh->jpg_settings;
+
+	settings.jpg_comp = *params;
+
+	mutex_lock(&zr->resource_lock);
+
+	if (fh->buffers.active != ZORAN_FREE) {
+		dprintk(1, KERN_WARNING
+			"%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
+			ZR_DEVNAME(zr));
+		res = -EBUSY;
+		goto sjpegc_unlock_and_return;
+	}
+
+	res = zoran_check_jpg_settings(zr, &settings, 0);
+	if (res)
+		goto sjpegc_unlock_and_return;
+	if (!fh->buffers.allocated)
+		fh->buffers.buffer_size =
+			zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+	fh->jpg_settings.jpg_comp = settings.jpg_comp;
+sjpegc_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static unsigned int
+zoran_poll (struct file *file,
+	    poll_table  *wait)
+{
+	struct zoran_fh *fh = file->private_data;
+	struct zoran *zr = fh->zr;
+	int res = 0, frame;
+	unsigned long flags;
+
+	/* we should check whether buffers are ready to be synced on
+	 * (w/o waits - O_NONBLOCK) here
+	 * if ready for read (sync), return POLLIN|POLLRDNORM,
+	 * if ready for write (sync), return POLLOUT|POLLWRNORM,
+	 * if error, return POLLERR,
+	 * if no buffers queued or so, return POLLNVAL
+	 */
+
+	mutex_lock(&zr->resource_lock);
+
+	switch (fh->map_mode) {
+	case ZORAN_MAP_MODE_RAW:
+		poll_wait(file, &zr->v4l_capq, wait);
+		frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+
+		spin_lock_irqsave(&zr->spinlock, flags);
+		dprintk(3,
+			KERN_DEBUG
+			"%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
+			ZR_DEVNAME(zr), __func__,
+			"FAL"[fh->buffers.active], zr->v4l_sync_tail,
+			"UPMD"[zr->v4l_buffers.buffer[frame].state],
+			zr->v4l_pend_tail, zr->v4l_pend_head);
+		/* Process is the one capturing? */
+		if (fh->buffers.active != ZORAN_FREE &&
+		    /* Buffer ready to DQBUF? */
+		    zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
+			res = POLLIN | POLLRDNORM;
+		spin_unlock_irqrestore(&zr->spinlock, flags);
+
+		break;
+
+	case ZORAN_MAP_MODE_JPG_REC:
+	case ZORAN_MAP_MODE_JPG_PLAY:
+		poll_wait(file, &zr->jpg_capq, wait);
+		frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+		spin_lock_irqsave(&zr->spinlock, flags);
+		dprintk(3,
+			KERN_DEBUG
+			"%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
+			ZR_DEVNAME(zr), __func__,
+			"FAL"[fh->buffers.active], zr->jpg_que_tail,
+			"UPMD"[zr->jpg_buffers.buffer[frame].state],
+			zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
+		if (fh->buffers.active != ZORAN_FREE &&
+		    zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
+			if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
+				res = POLLIN | POLLRDNORM;
+			else
+				res = POLLOUT | POLLWRNORM;
+		}
+		spin_unlock_irqrestore(&zr->spinlock, flags);
+
+		break;
+
+	default:
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - internal error, unknown map_mode=%d\n",
+			ZR_DEVNAME(zr), __func__, fh->map_mode);
+		res = POLLNVAL;
+	}
+
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+
+/*
+ * This maps the buffers to user space.
+ *
+ * Depending on the state of fh->map_mode
+ * the V4L or the MJPEG buffers are mapped
+ * per buffer or all together
+ *
+ * Note that we need to connect to some
+ * unmap signal event to unmap the de-allocate
+ * the buffer accordingly (zoran_vm_close())
+ */
+
+static void
+zoran_vm_open (struct vm_area_struct *vma)
+{
+	struct zoran_mapping *map = vma->vm_private_data;
+
+	map->count++;
+}
+
+static void
+zoran_vm_close (struct vm_area_struct *vma)
+{
+	struct zoran_mapping *map = vma->vm_private_data;
+	struct zoran_fh *fh = map->fh;
+	struct zoran *zr = fh->zr;
+	int i;
+
+	if (--map->count > 0)
+		return;
+
+	dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
+		__func__, mode_name(fh->map_mode));
+
+	for (i = 0; i < fh->buffers.num_buffers; i++) {
+		if (fh->buffers.buffer[i].map == map)
+			fh->buffers.buffer[i].map = NULL;
+	}
+	kfree(map);
+
+	/* Any buffers still mapped? */
+	for (i = 0; i < fh->buffers.num_buffers; i++)
+		if (fh->buffers.buffer[i].map)
+			return;
+
+	dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
+		__func__, mode_name(fh->map_mode));
+
+	mutex_lock(&zr->resource_lock);
+
+	if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+		if (fh->buffers.active != ZORAN_FREE) {
+			unsigned long flags;
+
+			spin_lock_irqsave(&zr->spinlock, flags);
+			zr36057_set_memgrab(zr, 0);
+			zr->v4l_buffers.allocated = 0;
+			zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+			spin_unlock_irqrestore(&zr->spinlock, flags);
+		}
+		v4l_fbuffer_free(fh);
+	} else {
+		if (fh->buffers.active != ZORAN_FREE) {
+			jpg_qbuf(fh, -1, zr->codec_mode);
+			zr->jpg_buffers.allocated = 0;
+			zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+		}
+		jpg_fbuffer_free(fh);
+	}
+
+	mutex_unlock(&zr->resource_lock);
+}
+
+static const struct vm_operations_struct zoran_vm_ops = {
+	.open = zoran_vm_open,
+	.close = zoran_vm_close,
+};
+
+static int
+zoran_mmap (struct file           *file,
+	    struct vm_area_struct *vma)
+{
+	struct zoran_fh *fh = file->private_data;
+	struct zoran *zr = fh->zr;
+	unsigned long size = (vma->vm_end - vma->vm_start);
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	int i, j;
+	unsigned long page, start = vma->vm_start, todo, pos, fraglen;
+	int first, last;
+	struct zoran_mapping *map;
+	int res = 0;
+
+	dprintk(3,
+		KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
+		ZR_DEVNAME(zr), __func__,
+		mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
+
+	if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
+	    !(vma->vm_flags & VM_WRITE)) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
+			ZR_DEVNAME(zr), __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&zr->resource_lock);
+
+	if (!fh->buffers.allocated) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s(%s) - buffers not yet allocated\n",
+			ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
+		res = -ENOMEM;
+		goto mmap_unlock_and_return;
+	}
+
+	first = offset / fh->buffers.buffer_size;
+	last = first - 1 + size / fh->buffers.buffer_size;
+	if (offset % fh->buffers.buffer_size != 0 ||
+	    size % fh->buffers.buffer_size != 0 || first < 0 ||
+	    last < 0 || first >= fh->buffers.num_buffers ||
+	    last >= fh->buffers.buffer_size) {
+		dprintk(1,
+			KERN_ERR
+			"%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+			ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
+			fh->buffers.buffer_size,
+			fh->buffers.num_buffers);
+		res = -EINVAL;
+		goto mmap_unlock_and_return;
+	}
+
+	/* Check if any buffers are already mapped */
+	for (i = first; i <= last; i++) {
+		if (fh->buffers.buffer[i].map) {
+			dprintk(1,
+				KERN_ERR
+				"%s: %s(%s) - buffer %d already mapped\n",
+				ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
+			res = -EBUSY;
+			goto mmap_unlock_and_return;
+		}
+	}
+
+	/* map these buffers */
+	map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+	if (!map) {
+		res = -ENOMEM;
+		goto mmap_unlock_and_return;
+	}
+	map->fh = fh;
+	map->count = 1;
+
+	vma->vm_ops = &zoran_vm_ops;
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_private_data = map;
+
+	if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+		for (i = first; i <= last; i++) {
+			todo = size;
+			if (todo > fh->buffers.buffer_size)
+				todo = fh->buffers.buffer_size;
+			page = fh->buffers.buffer[i].v4l.fbuffer_phys;
+			if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+							todo, PAGE_SHARED)) {
+				dprintk(1,
+					KERN_ERR
+					"%s: %s(V4L) - remap_pfn_range failed\n",
+					ZR_DEVNAME(zr), __func__);
+				res = -EAGAIN;
+				goto mmap_unlock_and_return;
+			}
+			size -= todo;
+			start += todo;
+			fh->buffers.buffer[i].map = map;
+			if (size == 0)
+				break;
+		}
+	} else {
+		for (i = first; i <= last; i++) {
+			for (j = 0;
+			     j < fh->buffers.buffer_size / PAGE_SIZE;
+			     j++) {
+				fraglen =
+				    (le32_to_cpu(fh->buffers.buffer[i].jpg.
+				     frag_tab[2 * j + 1]) & ~1) << 1;
+				todo = size;
+				if (todo > fraglen)
+					todo = fraglen;
+				pos =
+				    le32_to_cpu(fh->buffers.
+				    buffer[i].jpg.frag_tab[2 * j]);
+				/* should just be pos on i386 */
+				page = virt_to_phys(bus_to_virt(pos))
+								>> PAGE_SHIFT;
+				if (remap_pfn_range(vma, start, page,
+							todo, PAGE_SHARED)) {
+					dprintk(1,
+						KERN_ERR
+						"%s: %s(V4L) - remap_pfn_range failed\n",
+						ZR_DEVNAME(zr), __func__);
+					res = -EAGAIN;
+					goto mmap_unlock_and_return;
+				}
+				size -= todo;
+				start += todo;
+				if (size == 0)
+					break;
+				if (le32_to_cpu(fh->buffers.buffer[i].jpg.
+				    frag_tab[2 * j + 1]) & 1)
+					break;	/* was last fragment */
+			}
+			fh->buffers.buffer[i].map = map;
+			if (size == 0)
+				break;
+
+		}
+	}
+
+mmap_unlock_and_return:
+	mutex_unlock(&zr->resource_lock);
+
+	return res;
+}
+
+static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
+	.vidioc_querycap    		    = zoran_querycap,
+	.vidioc_cropcap       		    = zoran_cropcap,
+	.vidioc_s_crop       		    = zoran_s_crop,
+	.vidioc_g_crop       		    = zoran_g_crop,
+	.vidioc_enum_input     		    = zoran_enum_input,
+	.vidioc_g_input      		    = zoran_g_input,
+	.vidioc_s_input      		    = zoran_s_input,
+	.vidioc_enum_output    		    = zoran_enum_output,
+	.vidioc_g_output     		    = zoran_g_output,
+	.vidioc_s_output     		    = zoran_s_output,
+	.vidioc_g_fbuf			    = zoran_g_fbuf,
+	.vidioc_s_fbuf			    = zoran_s_fbuf,
+	.vidioc_g_std 			    = zoran_g_std,
+	.vidioc_s_std 			    = zoran_s_std,
+	.vidioc_g_jpegcomp 		    = zoran_g_jpegcomp,
+	.vidioc_s_jpegcomp 		    = zoran_s_jpegcomp,
+	.vidioc_overlay			    = zoran_overlay,
+	.vidioc_reqbufs			    = zoran_reqbufs,
+	.vidioc_querybuf		    = zoran_querybuf,
+	.vidioc_qbuf			    = zoran_qbuf,
+	.vidioc_dqbuf			    = zoran_dqbuf,
+	.vidioc_streamon		    = zoran_streamon,
+	.vidioc_streamoff		    = zoran_streamoff,
+	.vidioc_enum_fmt_vid_cap 	    = zoran_enum_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_out 	    = zoran_enum_fmt_vid_out,
+	.vidioc_enum_fmt_vid_overlay 	    = zoran_enum_fmt_vid_overlay,
+	.vidioc_g_fmt_vid_cap 		    = zoran_g_fmt_vid_cap,
+	.vidioc_g_fmt_vid_out               = zoran_g_fmt_vid_out,
+	.vidioc_g_fmt_vid_overlay           = zoran_g_fmt_vid_overlay,
+	.vidioc_s_fmt_vid_cap  		    = zoran_s_fmt_vid_cap,
+	.vidioc_s_fmt_vid_out               = zoran_s_fmt_vid_out,
+	.vidioc_s_fmt_vid_overlay           = zoran_s_fmt_vid_overlay,
+	.vidioc_try_fmt_vid_cap  	    = zoran_try_fmt_vid_cap,
+	.vidioc_try_fmt_vid_out 	    = zoran_try_fmt_vid_out,
+	.vidioc_try_fmt_vid_overlay 	    = zoran_try_fmt_vid_overlay,
+	.vidioc_queryctrl 		    = zoran_queryctrl,
+	.vidioc_s_ctrl       		    = zoran_s_ctrl,
+	.vidioc_g_ctrl       		    = zoran_g_ctrl,
+};
+
+/* please use zr->resource_lock consistently and kill this wrapper */
+static long zoran_ioctl(struct file *file, unsigned int cmd,
+			unsigned long arg)
+{
+	struct zoran_fh *fh = file->private_data;
+	struct zoran *zr = fh->zr;
+	int ret;
+
+	mutex_lock(&zr->other_lock);
+	ret = video_ioctl2(file, cmd, arg);
+	mutex_unlock(&zr->other_lock);
+
+	return ret;
+}
+
+static const struct v4l2_file_operations zoran_fops = {
+	.owner = THIS_MODULE,
+	.open = zoran_open,
+	.release = zoran_close,
+	.unlocked_ioctl = zoran_ioctl,
+	.read = zoran_read,
+	.write = zoran_write,
+	.mmap = zoran_mmap,
+	.poll = zoran_poll,
+};
+
+struct video_device zoran_template __devinitdata = {
+	.name = ZORAN_NAME,
+	.fops = &zoran_fops,
+	.ioctl_ops = &zoran_ioctl_ops,
+	.release = &zoran_vdev_release,
+	.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c
new file mode 100644
index 000000000000..f1423b777db1
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_procfs.c
@@ -0,0 +1,225 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles the procFS entries (/proc/ZORAN[%d])
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/seq_file.h>
+
+#include <linux/ctype.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_procfs.h"
+#include "zoran_card.h"
+
+#ifdef CONFIG_PROC_FS
+struct procfs_params_zr36067 {
+	char *name;
+	short reg;
+	u32 mask;
+	short bit;
+};
+
+static const struct procfs_params_zr36067 zr67[] = {
+	{"HSPol", 0x000, 1, 30},
+	{"HStart", 0x000, 0x3ff, 10},
+	{"HEnd", 0x000, 0x3ff, 0},
+
+	{"VSPol", 0x004, 1, 30},
+	{"VStart", 0x004, 0x3ff, 10},
+	{"VEnd", 0x004, 0x3ff, 0},
+
+	{"ExtFl", 0x008, 1, 26},
+	{"TopField", 0x008, 1, 25},
+	{"VCLKPol", 0x008, 1, 24},
+	{"DupFld", 0x008, 1, 20},
+	{"LittleEndian", 0x008, 1, 0},
+
+	{"HsyncStart", 0x10c, 0xffff, 16},
+	{"LineTot", 0x10c, 0xffff, 0},
+
+	{"NAX", 0x110, 0xffff, 16},
+	{"PAX", 0x110, 0xffff, 0},
+
+	{"NAY", 0x114, 0xffff, 16},
+	{"PAY", 0x114, 0xffff, 0},
+
+	/* {"",,,}, */
+
+	{NULL, 0, 0, 0},
+};
+
+static void
+setparam (struct zoran *zr,
+	  char         *name,
+	  char         *sval)
+{
+	int i = 0, reg0, reg, val;
+
+	while (zr67[i].name != NULL) {
+		if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
+			reg = reg0 = btread(zr67[i].reg);
+			reg &= ~(zr67[i].mask << zr67[i].bit);
+			if (!isdigit(sval[0]))
+				break;
+			val = simple_strtoul(sval, NULL, 0);
+			if ((val & ~zr67[i].mask))
+				break;
+			reg |= (val & zr67[i].mask) << zr67[i].bit;
+			dprintk(4,
+				KERN_INFO
+				"%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
+				ZR_DEVNAME(zr), zr67[i].reg, reg0, reg,
+				zr67[i].name, val);
+			btwrite(reg, zr67[i].reg);
+			break;
+		}
+		i++;
+	}
+}
+
+static int zoran_show(struct seq_file *p, void *v)
+{
+	struct zoran *zr = p->private;
+	int i;
+
+	seq_printf(p, "ZR36067 registers:\n");
+	for (i = 0; i < 0x130; i += 16)
+		seq_printf(p, "%03X %08X  %08X  %08X  %08X \n", i,
+			   btread(i), btread(i+4), btread(i+8), btread(i+12));
+	return 0;
+}
+
+static int zoran_open(struct inode *inode, struct file *file)
+{
+	struct zoran *data = PDE(inode)->data;
+	return single_open(file, zoran_show, data);
+}
+
+static ssize_t zoran_write(struct file *file, const char __user *buffer,
+			size_t count, loff_t *ppos)
+{
+	struct zoran *zr = PDE(file->f_path.dentry->d_inode)->data;
+	char *string, *sp;
+	char *line, *ldelim, *varname, *svar, *tdelim;
+
+	if (count > 32768)	/* Stupidity filter */
+		return -EINVAL;
+
+	string = sp = vmalloc(count + 1);
+	if (!string) {
+		dprintk(1,
+			KERN_ERR
+			"%s: write_proc: can not allocate memory\n",
+			ZR_DEVNAME(zr));
+		return -ENOMEM;
+	}
+	if (copy_from_user(string, buffer, count)) {
+		vfree (string);
+		return -EFAULT;
+	}
+	string[count] = 0;
+	dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n",
+		ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr);
+	ldelim = " \t\n";
+	tdelim = "=";
+	line = strpbrk(sp, ldelim);
+	while (line) {
+		*line = 0;
+		svar = strpbrk(sp, tdelim);
+		if (svar) {
+			*svar = 0;
+			varname = sp;
+			svar++;
+			setparam(zr, varname, svar);
+		}
+		sp = line + 1;
+		line = strpbrk(sp, ldelim);
+	}
+	vfree(string);
+
+	return count;
+}
+
+static const struct file_operations zoran_operations = {
+	.owner		= THIS_MODULE,
+	.open		= zoran_open,
+	.read		= seq_read,
+	.write		= zoran_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+int
+zoran_proc_init (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+	char name[8];
+
+	snprintf(name, 7, "zoran%d", zr->id);
+	zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr);
+	if (zr->zoran_proc != NULL) {
+		dprintk(2,
+			KERN_INFO
+			"%s: procfs entry /proc/%s allocated. data=%p\n",
+			ZR_DEVNAME(zr), name, zr->zoran_proc->data);
+	} else {
+		dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n",
+			ZR_DEVNAME(zr), name);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+void
+zoran_proc_cleanup (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+	char name[8];
+
+	snprintf(name, 7, "zoran%d", zr->id);
+	if (zr->zoran_proc)
+		remove_proc_entry(name, NULL);
+	zr->zoran_proc = NULL;
+#endif
+}
diff --git a/drivers/media/pci/zoran/zoran_procfs.h b/drivers/media/pci/zoran/zoran_procfs.h
new file mode 100644
index 000000000000..f2d5b1ba448f
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_procfs.h
@@ -0,0 +1,36 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ *   Ronald Bultje    <rbultje@ronald.bitfreak.net>
+ *   Laurent Pinchart <laurent.pinchart@skynet.be>
+ *   Mailinglist      <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_PROCFS_H__
+#define __ZORAN_PROCFS_H__
+
+extern int zoran_proc_init(struct zoran *zr);
+extern void zoran_proc_cleanup(struct zoran *zr);
+
+#endif				/* __ZORAN_PROCFS_H__ */
diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c
new file mode 100644
index 000000000000..b87ddba8608f
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36016.c
@@ -0,0 +1,524 @@
+/*
+ * Zoran ZR36016 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR016_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* v4l  API */
+
+/* headerfile of this module */
+#include "zr36016.h"
+
+/* codec io API */
+#include "videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+  just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36016_codecs;
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* =========================================================================
+   Local hardware I/O functions:
+
+   read/write via codec layer (registers are located in the master device)
+   ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36016_read (struct zr36016 *ptr,
+	      u16             reg)
+{
+	u8 value = 0;
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->readreg)
+		value =
+		    (ptr->codec->master_data->
+		     readreg(ptr->codec, reg)) & 0xFF;
+	else
+		dprintk(1,
+			KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+			ptr->name);
+
+	dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+		value);
+
+	return value;
+}
+
+static void
+zr36016_write (struct zr36016 *ptr,
+	       u16             reg,
+	       u8              value)
+{
+	dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+		reg);
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->writereg) {
+		ptr->codec->master_data->writereg(ptr->codec, reg, value);
+	} else
+		dprintk(1,
+			KERN_ERR
+			"%s: invalid I/O setup, nothing written!\n",
+			ptr->name);
+}
+
+/* indirect read and write functions */
+/* the 016 supports auto-addr-increment, but
+ * writing it all time cost not much and is safer... */
+static u8
+zr36016_readi (struct zr36016 *ptr,
+	       u16             reg)
+{
+	u8 value = 0;
+
+	// just in case something is wrong...
+	if ((ptr->codec->master_data->writereg) &&
+	    (ptr->codec->master_data->readreg)) {
+		ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);	// ADDR
+		value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF;	// DATA
+	} else
+		dprintk(1,
+			KERN_ERR
+			"%s: invalid I/O setup, nothing read (i)!\n",
+			ptr->name);
+
+	dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name,
+		reg, value);
+	return value;
+}
+
+static void
+zr36016_writei (struct zr36016 *ptr,
+		u16             reg,
+		u8              value)
+{
+	dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
+		value, reg);
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->writereg) {
+		ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);	// ADDR
+		ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF);	// DATA
+	} else
+		dprintk(1,
+			KERN_ERR
+			"%s: invalid I/O setup, nothing written (i)!\n",
+			ptr->name);
+}
+
+/* =========================================================================
+   Local helper function:
+
+   version read
+   ========================================================================= */
+
+/* version kept in datastructure */
+static u8
+zr36016_read_version (struct zr36016 *ptr)
+{
+	ptr->version = zr36016_read(ptr, 0) >> 4;
+	return ptr->version;
+}
+
+/* =========================================================================
+   Local helper function:
+
+   basic test of "connectivity", writes/reads to/from PAX-Lo register
+   ========================================================================= */
+
+static int
+zr36016_basic_test (struct zr36016 *ptr)
+{
+	if (debug) {
+		int i;
+		zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
+		dprintk(1, KERN_INFO "%s: registers: ", ptr->name);
+		for (i = 0; i <= 0x0b; i++)
+			dprintk(1, "%02x ", zr36016_readi(ptr, i));
+		dprintk(1, "\n");
+	}
+	// for testing just write 0, then the default value to a register and read
+	// it back in both cases
+	zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
+	if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, can't connect to vfe processor!\n",
+			ptr->name);
+		return -ENXIO;
+	}
+	zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
+	if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, can't connect to vfe processor!\n",
+			ptr->name);
+		return -ENXIO;
+	}
+	// we allow version numbers from 0-3, should be enough, though
+	zr36016_read_version(ptr);
+	if (ptr->version & 0x0c) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, suspicious version %d found...\n",
+			ptr->name, ptr->version);
+		return -ENXIO;
+	}
+
+	return 0;		/* looks good! */
+}
+
+/* =========================================================================
+   Local helper function:
+
+   simple loop for pushing the init datasets - NO USE --
+   ========================================================================= */
+
+#if 0
+static int zr36016_pushit (struct zr36016 *ptr,
+			   u16             startreg,
+			   u16             len,
+			   const char     *data)
+{
+	int i=0;
+
+	dprintk(4, "%s: write data block to 0x%04x (len=%d)\n",
+		ptr->name, startreg,len);
+	while (i<len) {
+		zr36016_writei(ptr, startreg++,  data[i++]);
+	}
+
+	return i;
+}
+#endif
+
+/* =========================================================================
+   Basic datasets & init:
+
+   //TODO//
+   ========================================================================= */
+
+// needed offset values          PAL NTSC SECAM
+static const int zr016_xoff[] = { 20, 20, 20 };
+static const int zr016_yoff[] = { 8, 9, 7 };
+
+static void
+zr36016_init (struct zr36016 *ptr)
+{
+	// stop any processing
+	zr36016_write(ptr, ZR016_GOSTOP, 0);
+
+	// mode setup (yuv422 in and out, compression/expansuon due to mode)
+	zr36016_write(ptr, ZR016_MODE,
+		      ZR016_YUV422 | ZR016_YUV422_YUV422 |
+		      (ptr->mode == CODEC_DO_COMPRESSION ?
+		       ZR016_COMPRESSION : ZR016_EXPANSION));
+
+	// misc setup
+	zr36016_writei(ptr, ZR016I_SETUP1,
+		       (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
+		       (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
+	zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
+
+	// Window setup
+	// (no extra offset for now, norm defines offset, default width height)
+	zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
+	zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
+	zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
+	zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
+	zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
+	zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
+	zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
+	zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
+
+	/* shall we continue now, please? */
+	zr36016_write(ptr, ZR016_GOSTOP, 1);
+}
+
+/* =========================================================================
+   CODEC API FUNCTIONS
+
+   this functions are accessed by the master via the API structure
+   ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+   this should be the last call from the master before starting processing */
+static int
+zr36016_set_mode (struct videocodec *codec,
+		  int                mode)
+{
+	struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+	dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+	if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+		return -EINVAL;
+
+	ptr->mode = mode;
+	zr36016_init(ptr);
+
+	return 0;
+}
+
+/* set picture size */
+static int
+zr36016_set_video (struct videocodec   *codec,
+		   struct tvnorm       *norm,
+		   struct vfe_settings *cap,
+		   struct vfe_polarity *pol)
+{
+	struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+	dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
+		ptr->name, norm->HStart, norm->VStart,
+		cap->x, cap->y, cap->width, cap->height,
+		cap->decimation);
+
+	/* if () return -EINVAL;
+	 * trust the master driver that it knows what it does - so
+	 * we allow invalid startx/y for now ... */
+	ptr->width = cap->width;
+	ptr->height = cap->height;
+	/* (Ronald) This is ugly. zoran_device.c, line 387
+	 * already mentions what happens if HStart is even
+	 * (blue faces, etc., cr/cb inversed). There's probably
+	 * some good reason why HStart is 0 instead of 1, so I'm
+	 * leaving it to this for now, but really... This can be
+	 * done a lot simpler */
+	ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x;
+	/* Something to note here (I don't understand it), setting
+	 * VStart too high will cause the codec to 'not work'. I
+	 * really don't get it. values of 16 (VStart) already break
+	 * it here. Just '0' seems to work. More testing needed! */
+	ptr->yoff = norm->VStart + cap->y;
+	/* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
+	ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
+	ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
+
+	return 0;
+}
+
+/* additional control functions */
+static int
+zr36016_control (struct videocodec *codec,
+		 int                type,
+		 int                size,
+		 void              *data)
+{
+	struct zr36016 *ptr = (struct zr36016 *) codec->data;
+	int *ival = (int *) data;
+
+	dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+		size);
+
+	switch (type) {
+	case CODEC_G_STATUS:	/* get last status - we don't know it ... */
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = 0;
+		break;
+
+	case CODEC_G_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = 0;
+		break;
+
+	case CODEC_S_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		if (*ival != 0)
+			return -EINVAL;
+		/* not needed, do nothing */
+		return 0;
+
+	case CODEC_G_VFE:
+	case CODEC_S_VFE:
+		return 0;
+
+	case CODEC_S_MMAP:
+		/* not available, give an error */
+		return -ENXIO;
+
+	default:
+		return -EINVAL;
+	}
+
+	return size;
+}
+
+/* =========================================================================
+   Exit and unregister function:
+
+   Deinitializes Zoran's JPEG processor
+   ========================================================================= */
+
+static int
+zr36016_unset (struct videocodec *codec)
+{
+	struct zr36016 *ptr = codec->data;
+
+	if (ptr) {
+		/* do wee need some codec deinit here, too ???? */
+
+		dprintk(1, "%s: finished codec #%d\n", ptr->name,
+			ptr->num);
+		kfree(ptr);
+		codec->data = NULL;
+
+		zr36016_codecs--;
+		return 0;
+	}
+
+	return -EFAULT;
+}
+
+/* =========================================================================
+   Setup and registry function:
+
+   Initializes Zoran's JPEG processor
+
+   Also sets pixel size, average code size, mode (compr./decompr.)
+   (the given size is determined by the processor with the video interface)
+   ========================================================================= */
+
+static int
+zr36016_setup (struct videocodec *codec)
+{
+	struct zr36016 *ptr;
+	int res;
+
+	dprintk(2, "zr36016: initializing VFE subsystem #%d.\n",
+		zr36016_codecs);
+
+	if (zr36016_codecs == MAX_CODECS) {
+		dprintk(1,
+			KERN_ERR "zr36016: Can't attach more codecs!\n");
+		return -ENOSPC;
+	}
+	//mem structure init
+	codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL);
+	if (NULL == ptr) {
+		dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n");
+		return -ENOMEM;
+	}
+
+	snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]",
+		 zr36016_codecs);
+	ptr->num = zr36016_codecs++;
+	ptr->codec = codec;
+
+	//testing
+	res = zr36016_basic_test(ptr);
+	if (res < 0) {
+		zr36016_unset(codec);
+		return res;
+	}
+	//final setup
+	ptr->mode = CODEC_DO_COMPRESSION;
+	ptr->width = 768;
+	ptr->height = 288;
+	ptr->xdec = 1;
+	ptr->ydec = 0;
+	zr36016_init(ptr);
+
+	dprintk(1, KERN_INFO "%s: codec v%d attached and running\n",
+		ptr->name, ptr->version);
+
+	return 0;
+}
+
+static const struct videocodec zr36016_codec = {
+	.owner = THIS_MODULE,
+	.name = "zr36016",
+	.magic = 0L,		// magic not used
+	.flags =
+	    CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
+	    CODEC_FLAG_DECODER,
+	.type = CODEC_TYPE_ZR36016,
+	.setup = zr36016_setup,	// functionality
+	.unset = zr36016_unset,
+	.set_mode = zr36016_set_mode,
+	.set_video = zr36016_set_video,
+	.control = zr36016_control,
+	// others are not used
+};
+
+/* =========================================================================
+   HOOK IN DRIVER AS KERNEL MODULE
+   ========================================================================= */
+
+static int __init
+zr36016_init_module (void)
+{
+	//dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION);
+	zr36016_codecs = 0;
+	return videocodec_register(&zr36016_codec);
+}
+
+static void __exit
+zr36016_cleanup_module (void)
+{
+	if (zr36016_codecs) {
+		dprintk(1,
+			"zr36016: something's wrong - %d codecs left somehow.\n",
+			zr36016_codecs);
+	}
+	videocodec_unregister(&zr36016_codec);
+}
+
+module_init(zr36016_init_module);
+module_exit(zr36016_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36016 video frontends "
+		   ZR016_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h
new file mode 100644
index 000000000000..8c79229f69d1
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36016.h
@@ -0,0 +1,111 @@
+/*
+ * Zoran ZR36016 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * 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 ZR36016_H
+#define ZR36016_H
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36016 {
+	char name[32];
+	int num;
+	/* io datastructure */
+	struct videocodec *codec;
+	// coder status
+	__u8 version;
+	// actual coder setup
+	int mode;
+
+	__u16 xoff;
+	__u16 yoff;
+	__u16 width;
+	__u16 height;
+	__u16 xdec;
+	__u16 ydec;
+};
+
+/* direct  register addresses */
+#define ZR016_GOSTOP      0x00
+#define ZR016_MODE        0x01
+#define ZR016_IADDR       0x02
+#define ZR016_IDATA       0x03
+
+/* indirect  register addresses */
+#define ZR016I_SETUP1     0x00
+#define ZR016I_SETUP2     0x01
+#define ZR016I_NAX_LO     0x02
+#define ZR016I_NAX_HI     0x03
+#define ZR016I_PAX_LO     0x04
+#define ZR016I_PAX_HI     0x05
+#define ZR016I_NAY_LO     0x06
+#define ZR016I_NAY_HI     0x07
+#define ZR016I_PAY_LO     0x08
+#define ZR016I_PAY_HI     0x09
+#define ZR016I_NOL_LO     0x0a
+#define ZR016I_NOL_HI     0x0b
+
+/* possible values for mode register */
+#define ZR016_RGB444_YUV444  0x00
+#define ZR016_RGB444_YUV422  0x01
+#define ZR016_RGB444_YUV411  0x02
+#define ZR016_RGB444_Y400    0x03
+#define ZR016_RGB444_RGB444  0x04
+#define ZR016_YUV444_YUV444  0x08
+#define ZR016_YUV444_YUV422  0x09
+#define ZR016_YUV444_YUV411  0x0a
+#define ZR016_YUV444_Y400    0x0b
+#define ZR016_YUV444_RGB444  0x0c
+#define ZR016_YUV422_YUV422  0x11
+#define ZR016_YUV422_YUV411  0x12
+#define ZR016_YUV422_Y400    0x13
+#define ZR016_YUV411_YUV411  0x16
+#define ZR016_YUV411_Y400    0x17
+#define ZR016_4444_4444      0x19
+#define ZR016_100_100        0x1b
+
+#define ZR016_RGB444         0x00
+#define ZR016_YUV444         0x20
+#define ZR016_YUV422         0x40
+
+#define ZR016_COMPRESSION    0x80
+#define ZR016_EXPANSION      0x80
+
+/* possible values for setup 1 register */
+#define ZR016_CKRT           0x80
+#define ZR016_VERT           0x40
+#define ZR016_HORZ           0x20
+#define ZR016_HRFL           0x10
+#define ZR016_DSFL           0x08
+#define ZR016_SBFL           0x04
+#define ZR016_RSTR           0x02
+#define ZR016_CNTI           0x01
+
+/* possible values for setup 2 register */
+#define ZR016_SYEN           0x40
+#define ZR016_CCIR           0x04
+#define ZR016_SIGN           0x02
+#define ZR016_YMCS           0x01
+
+#endif				/*fndef ZR36016_H */
diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c
new file mode 100644
index 000000000000..e1985609af4b
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36050.c
@@ -0,0 +1,900 @@
+/*
+ * Zoran ZR36050 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR050_VERSION "v0.7.1"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* headerfile of this module */
+#include "zr36050.h"
+
+/* codec io API */
+#include "videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+  just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36050_codecs;
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* =========================================================================
+   Local hardware I/O functions:
+
+   read/write via codec layer (registers are located in the master device)
+   ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36050_read (struct zr36050 *ptr,
+	      u16             reg)
+{
+	u8 value = 0;
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->readreg)
+		value = (ptr->codec->master_data->readreg(ptr->codec,
+							  reg)) & 0xFF;
+	else
+		dprintk(1,
+			KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+			ptr->name);
+
+	dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+		value);
+
+	return value;
+}
+
+static void
+zr36050_write (struct zr36050 *ptr,
+	       u16             reg,
+	       u8              value)
+{
+	dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+		reg);
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->writereg)
+		ptr->codec->master_data->writereg(ptr->codec, reg, value);
+	else
+		dprintk(1,
+			KERN_ERR
+			"%s: invalid I/O setup, nothing written!\n",
+			ptr->name);
+}
+
+/* =========================================================================
+   Local helper function:
+
+   status read
+   ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36050_read_status1 (struct zr36050 *ptr)
+{
+	ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1);
+
+	zr36050_read(ptr, 0);
+	return ptr->status1;
+}
+
+/* =========================================================================
+   Local helper function:
+
+   scale factor read
+   ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36050_read_scalefactor (struct zr36050 *ptr)
+{
+	ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) |
+			 (zr36050_read(ptr, ZR050_SF_LO) & 0xFF);
+
+	/* leave 0 selected for an eventually GO from master */
+	zr36050_read(ptr, 0);
+	return ptr->scalefact;
+}
+
+/* =========================================================================
+   Local helper function:
+
+   wait if codec is ready to proceed (end of processing) or time is over
+   ========================================================================= */
+
+static void
+zr36050_wait_end (struct zr36050 *ptr)
+{
+	int i = 0;
+
+	while (!(zr36050_read_status1(ptr) & 0x4)) {
+		udelay(1);
+		if (i++ > 200000) {	// 200ms, there is for sure something wrong!!!
+			dprintk(1,
+				"%s: timeout at wait_end (last status: 0x%02x)\n",
+				ptr->name, ptr->status1);
+			break;
+		}
+	}
+}
+
+/* =========================================================================
+   Local helper function:
+
+   basic test of "connectivity", writes/reads to/from memory the SOF marker
+   ========================================================================= */
+
+static int
+zr36050_basic_test (struct zr36050 *ptr)
+{
+	zr36050_write(ptr, ZR050_SOF_IDX, 0x00);
+	zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00);
+	if ((zr36050_read(ptr, ZR050_SOF_IDX) |
+	     zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, can't connect to jpeg processor!\n",
+			ptr->name);
+		return -ENXIO;
+	}
+	zr36050_write(ptr, ZR050_SOF_IDX, 0xff);
+	zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0);
+	if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) |
+	     zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, can't connect to jpeg processor!\n",
+			ptr->name);
+		return -ENXIO;
+	}
+
+	zr36050_wait_end(ptr);
+	if ((ptr->status1 & 0x4) == 0) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, jpeg processor failed (end flag)!\n",
+			ptr->name);
+		return -EBUSY;
+	}
+
+	return 0;		/* looks good! */
+}
+
+/* =========================================================================
+   Local helper function:
+
+   simple loop for pushing the init datasets
+   ========================================================================= */
+
+static int
+zr36050_pushit (struct zr36050 *ptr,
+		u16             startreg,
+		u16             len,
+		const char     *data)
+{
+	int i = 0;
+
+	dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+		startreg, len);
+	while (i < len) {
+		zr36050_write(ptr, startreg++, data[i++]);
+	}
+
+	return i;
+}
+
+/* =========================================================================
+   Basic datasets:
+
+   jpeg baseline setup data (you find it on lots places in internet, or just
+   extract it from any regular .jpg image...)
+
+   Could be variable, but until it's not needed it they are just fixed to save
+   memory. Otherwise expand zr36050 structure with arrays, push the values to
+   it and initialize from there, as e.g. the linux zr36057/60 driver does it.
+   ========================================================================= */
+
+static const char zr36050_dqt[0x86] = {
+	0xff, 0xdb,		//Marker: DQT
+	0x00, 0x84,		//Length: 2*65+2
+	0x00,			//Pq,Tq first table
+	0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+	0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+	0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+	0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+	0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+	0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+	0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+	0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+	0x01,			//Pq,Tq second table
+	0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+	0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36050_dht[0x1a4] = {
+	0xff, 0xc4,		//Marker: DHT
+	0x01, 0xa2,		//Length: 2*AC, 2*DC
+	0x00,			//DC first table
+	0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+	0x01,			//DC second table
+	0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+	0x10,			//AC first table
+	0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+	0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+	0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+	0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+	0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+	0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+	0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+	0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+	0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+	0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+	0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+	0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+	0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+	0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+	0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+	0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+	0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA,
+	0x11,			//AC second table
+	0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+	0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+	0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+	0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+	0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+	0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+	0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+	0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+	0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+	0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+	0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+	0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+	0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+	0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+	0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+	0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+	0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+	0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS          0x3	//Y,U,V
+#define BASELINE_PRECISION        0x8	//MCU size (?)
+static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's QT
+static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's DC
+static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+   Local helper functions:
+
+   calculation and setup of parameter-dependent JPEG baseline segments
+   (needed for compression only)
+   ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+			 of each color component */
+
+static int
+zr36050_set_sof (struct zr36050 *ptr)
+{
+	char sof_data[34];	// max. size of register set
+	int i;
+
+	dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+		ptr->width, ptr->height, NO_OF_COMPONENTS);
+	sof_data[0] = 0xff;
+	sof_data[1] = 0xc0;
+	sof_data[2] = 0x00;
+	sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+	sof_data[4] = BASELINE_PRECISION;	// only '8' possible with zr36050
+	sof_data[5] = (ptr->height) >> 8;
+	sof_data[6] = (ptr->height) & 0xff;
+	sof_data[7] = (ptr->width) >> 8;
+	sof_data[8] = (ptr->width) & 0xff;
+	sof_data[9] = NO_OF_COMPONENTS;
+	for (i = 0; i < NO_OF_COMPONENTS; i++) {
+		sof_data[10 + (i * 3)] = i;	// index identifier
+		sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]);	// sampling ratios
+		sof_data[12 + (i * 3)] = zr36050_tq[i];	// Q table selection
+	}
+	return zr36050_pushit(ptr, ZR050_SOF_IDX,
+			      (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+			of each color component */
+
+static int
+zr36050_set_sos (struct zr36050 *ptr)
+{
+	char sos_data[16];	// max. size of register set
+	int i;
+
+	dprintk(3, "%s: write SOS\n", ptr->name);
+	sos_data[0] = 0xff;
+	sos_data[1] = 0xda;
+	sos_data[2] = 0x00;
+	sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+	sos_data[4] = NO_OF_COMPONENTS;
+	for (i = 0; i < NO_OF_COMPONENTS; i++) {
+		sos_data[5 + (i * 2)] = i;	// index
+		sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i];	// AC/DC tbl.sel.
+	}
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00;	// scan start
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F;
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+	return zr36050_pushit(ptr, ZR050_SOS1_IDX,
+			      4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+			      sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36050_set_dri (struct zr36050 *ptr)
+{
+	char dri_data[6];	// max. size of register set
+
+	dprintk(3, "%s: write DRI\n", ptr->name);
+	dri_data[0] = 0xff;
+	dri_data[1] = 0xdd;
+	dri_data[2] = 0x00;
+	dri_data[3] = 0x04;
+	dri_data[4] = ptr->dri >> 8;
+	dri_data[5] = ptr->dri & 0xff;
+	return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+   Setup function:
+
+   Setup compression/decompression of Zoran's JPEG processor
+   ( see also zoran 36050 manual )
+
+   ... sorry for the spaghetti code ...
+   ========================================================================= */
+static void
+zr36050_init (struct zr36050 *ptr)
+{
+	int sum = 0;
+	long bitcnt, tmp;
+
+	if (ptr->mode == CODEC_DO_COMPRESSION) {
+		dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+		/* 050 communicates with 057 in master mode */
+		zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR);
+
+		/* encoding table preload for compression */
+		zr36050_write(ptr, ZR050_MODE,
+			      ZR050_MO_COMP | ZR050_MO_TLM);
+		zr36050_write(ptr, ZR050_OPTIONS, 0);
+
+		/* disable all IRQs */
+		zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+		zr36050_write(ptr, ZR050_INT_REQ_1, 3);	// low 2 bits always 1
+
+		/* volume control settings */
+		/*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/
+		zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8);
+		zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff);
+
+		zr36050_write(ptr, ZR050_AF_HI, 0xff);
+		zr36050_write(ptr, ZR050_AF_M, 0xff);
+		zr36050_write(ptr, ZR050_AF_LO, 0xff);
+
+		/* setup the variable jpeg tables */
+		sum += zr36050_set_sof(ptr);
+		sum += zr36050_set_sos(ptr);
+		sum += zr36050_set_dri(ptr);
+
+		/* setup the fixed jpeg tables - maybe variable, though -
+		 * (see table init section above) */
+		dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name);
+		sum += zr36050_pushit(ptr, ZR050_DQT_IDX,
+				      sizeof(zr36050_dqt), zr36050_dqt);
+		sum += zr36050_pushit(ptr, ZR050_DHT_IDX,
+				      sizeof(zr36050_dht), zr36050_dht);
+		zr36050_write(ptr, ZR050_APP_IDX, 0xff);
+		zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn);
+		zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00);
+		zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2);
+		sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60,
+				      ptr->app.data) + 4;
+		zr36050_write(ptr, ZR050_COM_IDX, 0xff);
+		zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe);
+		zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00);
+		zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2);
+		sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60,
+				      ptr->com.data) + 4;
+
+		/* do the internal huffman table preload */
+		zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+		zr36050_write(ptr, ZR050_GO, 1);	// launch codec
+		zr36050_wait_end(ptr);
+		dprintk(2, "%s: Status after table preload: 0x%02x\n",
+			ptr->name, ptr->status1);
+
+		if ((ptr->status1 & 0x4) == 0) {
+			dprintk(1, KERN_ERR "%s: init aborted!\n",
+				ptr->name);
+			return;	// something is wrong, its timed out!!!!
+		}
+
+		/* setup misc. data for compression (target code sizes) */
+
+		/* size of compressed code to reach without header data */
+		sum = ptr->real_code_vol - sum;
+		bitcnt = sum << 3;	/* need the size in bits */
+
+		tmp = bitcnt >> 16;
+		dprintk(3,
+			"%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+			ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+		zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8);
+		zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff);
+		tmp = bitcnt & 0xffff;
+		zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8);
+		zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff);
+
+		bitcnt -= bitcnt >> 7;	// bits without stuffing
+		bitcnt -= ((bitcnt * 5) >> 6);	// bits without eob
+
+		tmp = bitcnt >> 16;
+		dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+			ptr->name, bitcnt, tmp);
+		zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8);
+		zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff);
+		tmp = bitcnt & 0xffff;
+		zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8);
+		zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff);
+
+		/* compression setup with or without bitrate control */
+		zr36050_write(ptr, ZR050_MODE,
+			      ZR050_MO_COMP | ZR050_MO_PASS2 |
+			      (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0));
+
+		/* this headers seem to deliver "valid AVI" jpeg frames */
+		zr36050_write(ptr, ZR050_MARKERS_EN,
+			      ZR050_ME_DQT | ZR050_ME_DHT |
+			      ((ptr->app.len > 0) ? ZR050_ME_APP : 0) |
+			      ((ptr->com.len > 0) ? ZR050_ME_COM : 0));
+	} else {
+		dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+		/* 050 communicates with 055 in master mode */
+		zr36050_write(ptr, ZR050_HARDWARE,
+			      ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK);
+
+		/* encoding table preload */
+		zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM);
+
+		/* disable all IRQs */
+		zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+		zr36050_write(ptr, ZR050_INT_REQ_1, 3);	// low 2 bits always 1
+
+		dprintk(3, "%s: write DHT\n", ptr->name);
+		zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht),
+			       zr36050_dht);
+
+		/* do the internal huffman table preload */
+		zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+		zr36050_write(ptr, ZR050_GO, 1);	// launch codec
+		zr36050_wait_end(ptr);
+		dprintk(2, "%s: Status after table preload: 0x%02x\n",
+			ptr->name, ptr->status1);
+
+		if ((ptr->status1 & 0x4) == 0) {
+			dprintk(1, KERN_ERR "%s: init aborted!\n",
+				ptr->name);
+			return;	// something is wrong, its timed out!!!!
+		}
+
+		/* setup misc. data for expansion */
+		zr36050_write(ptr, ZR050_MODE, 0);
+		zr36050_write(ptr, ZR050_MARKERS_EN, 0);
+	}
+
+	/* adr on selected, to allow GO from master */
+	zr36050_read(ptr, 0);
+}
+
+/* =========================================================================
+   CODEC API FUNCTIONS
+
+   this functions are accessed by the master via the API structure
+   ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+   this should be the last call from the master before starting processing */
+static int
+zr36050_set_mode (struct videocodec *codec,
+		  int                mode)
+{
+	struct zr36050 *ptr = (struct zr36050 *) codec->data;
+
+	dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+	if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+		return -EINVAL;
+
+	ptr->mode = mode;
+	zr36050_init(ptr);
+
+	return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36050_set_video (struct videocodec   *codec,
+		   struct tvnorm       *norm,
+		   struct vfe_settings *cap,
+		   struct vfe_polarity *pol)
+{
+	struct zr36050 *ptr = (struct zr36050 *) codec->data;
+	int size;
+
+	dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n",
+		ptr->name, norm->HStart, norm->VStart,
+		cap->x, cap->y, cap->width, cap->height,
+		cap->decimation, cap->quality);
+	/* if () return -EINVAL;
+	 * trust the master driver that it knows what it does - so
+	 * we allow invalid startx/y and norm for now ... */
+	ptr->width = cap->width / (cap->decimation & 0xff);
+	ptr->height = cap->height / ((cap->decimation >> 8) & 0xff);
+
+	/* (KM) JPEG quality */
+	size = ptr->width * ptr->height;
+	size *= 16; /* size in bits */
+	/* apply quality setting */
+	size = size * cap->quality / 200;
+
+	/* Minimum: 1kb */
+	if (size < 8192)
+		size = 8192;
+	/* Maximum: 7/8 of code buffer */
+	if (size > ptr->total_code_vol * 7)
+		size = ptr->total_code_vol * 7;
+
+	ptr->real_code_vol = size >> 3; /* in bytes */
+
+	/* Set max_block_vol here (previously in zr36050_init, moved
+	 * here for consistency with zr36060 code */
+	zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);
+
+	return 0;
+}
+
+/* additional control functions */
+static int
+zr36050_control (struct videocodec *codec,
+		 int                type,
+		 int                size,
+		 void              *data)
+{
+	struct zr36050 *ptr = (struct zr36050 *) codec->data;
+	int *ival = (int *) data;
+
+	dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+		size);
+
+	switch (type) {
+	case CODEC_G_STATUS:	/* get last status */
+		if (size != sizeof(int))
+			return -EFAULT;
+		zr36050_read_status1(ptr);
+		*ival = ptr->status1;
+		break;
+
+	case CODEC_G_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = CODEC_MODE_BJPG;
+		break;
+
+	case CODEC_S_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		if (*ival != CODEC_MODE_BJPG)
+			return -EINVAL;
+		/* not needed, do nothing */
+		return 0;
+
+	case CODEC_G_VFE:
+	case CODEC_S_VFE:
+		/* not needed, do nothing */
+		return 0;
+
+	case CODEC_S_MMAP:
+		/* not available, give an error */
+		return -ENXIO;
+
+	case CODEC_G_JPEG_TDS_BYTE:	/* get target volume in byte */
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = ptr->total_code_vol;
+		break;
+
+	case CODEC_S_JPEG_TDS_BYTE:	/* get target volume in byte */
+		if (size != sizeof(int))
+			return -EFAULT;
+		ptr->total_code_vol = *ival;
+		/* (Kieran Morrissey)
+		 * code copied from zr36060.c to ensure proper bitrate */
+		ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+		break;
+
+	case CODEC_G_JPEG_SCALE:	/* get scaling factor */
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = zr36050_read_scalefactor(ptr);
+		break;
+
+	case CODEC_S_JPEG_SCALE:	/* set scaling factor */
+		if (size != sizeof(int))
+			return -EFAULT;
+		ptr->scalefact = *ival;
+		break;
+
+	case CODEC_G_JPEG_APP_DATA: {	/* get appn marker data */
+		struct jpeg_app_marker *app = data;
+
+		if (size != sizeof(struct jpeg_app_marker))
+			return -EFAULT;
+
+		*app = ptr->app;
+		break;
+	}
+
+	case CODEC_S_JPEG_APP_DATA: {	 /* set appn marker data */
+		struct jpeg_app_marker *app = data;
+
+		if (size != sizeof(struct jpeg_app_marker))
+			return -EFAULT;
+
+		ptr->app = *app;
+		break;
+	}
+
+	case CODEC_G_JPEG_COM_DATA: {	/* get comment marker data */
+		struct jpeg_com_marker *com = data;
+
+		if (size != sizeof(struct jpeg_com_marker))
+			return -EFAULT;
+
+		*com = ptr->com;
+		break;
+	}
+
+	case CODEC_S_JPEG_COM_DATA: {	/* set comment marker data */
+		struct jpeg_com_marker *com = data;
+
+		if (size != sizeof(struct jpeg_com_marker))
+			return -EFAULT;
+
+		ptr->com = *com;
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+
+	return size;
+}
+
+/* =========================================================================
+   Exit and unregister function:
+
+   Deinitializes Zoran's JPEG processor
+   ========================================================================= */
+
+static int
+zr36050_unset (struct videocodec *codec)
+{
+	struct zr36050 *ptr = codec->data;
+
+	if (ptr) {
+		/* do wee need some codec deinit here, too ???? */
+
+		dprintk(1, "%s: finished codec #%d\n", ptr->name,
+			ptr->num);
+		kfree(ptr);
+		codec->data = NULL;
+
+		zr36050_codecs--;
+		return 0;
+	}
+
+	return -EFAULT;
+}
+
+/* =========================================================================
+   Setup and registry function:
+
+   Initializes Zoran's JPEG processor
+
+   Also sets pixel size, average code size, mode (compr./decompr.)
+   (the given size is determined by the processor with the video interface)
+   ========================================================================= */
+
+static int
+zr36050_setup (struct videocodec *codec)
+{
+	struct zr36050 *ptr;
+	int res;
+
+	dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n",
+		zr36050_codecs);
+
+	if (zr36050_codecs == MAX_CODECS) {
+		dprintk(1,
+			KERN_ERR "zr36050: Can't attach more codecs!\n");
+		return -ENOSPC;
+	}
+	//mem structure init
+	codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL);
+	if (NULL == ptr) {
+		dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n");
+		return -ENOMEM;
+	}
+
+	snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]",
+		 zr36050_codecs);
+	ptr->num = zr36050_codecs++;
+	ptr->codec = codec;
+
+	//testing
+	res = zr36050_basic_test(ptr);
+	if (res < 0) {
+		zr36050_unset(codec);
+		return res;
+	}
+	//final setup
+	memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8);
+	memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8);
+
+	ptr->bitrate_ctrl = 0;	/* 0 or 1 - fixed file size flag
+				 * (what is the difference?) */
+	ptr->mode = CODEC_DO_COMPRESSION;
+	ptr->width = 384;
+	ptr->height = 288;
+	ptr->total_code_vol = 16000;
+	ptr->max_block_vol = 240;
+	ptr->scalefact = 0x100;
+	ptr->dri = 1;
+
+	/* no app/com marker by default */
+	ptr->app.appn = 0;
+	ptr->app.len = 0;
+	ptr->com.len = 0;
+
+	zr36050_init(ptr);
+
+	dprintk(1, KERN_INFO "%s: codec attached and running\n",
+		ptr->name);
+
+	return 0;
+}
+
+static const struct videocodec zr36050_codec = {
+	.owner = THIS_MODULE,
+	.name = "zr36050",
+	.magic = 0L,		// magic not used
+	.flags =
+	    CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+	    CODEC_FLAG_DECODER,
+	.type = CODEC_TYPE_ZR36050,
+	.setup = zr36050_setup,	// functionality
+	.unset = zr36050_unset,
+	.set_mode = zr36050_set_mode,
+	.set_video = zr36050_set_video,
+	.control = zr36050_control,
+	// others are not used
+};
+
+/* =========================================================================
+   HOOK IN DRIVER AS KERNEL MODULE
+   ========================================================================= */
+
+static int __init
+zr36050_init_module (void)
+{
+	//dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION);
+	zr36050_codecs = 0;
+	return videocodec_register(&zr36050_codec);
+}
+
+static void __exit
+zr36050_cleanup_module (void)
+{
+	if (zr36050_codecs) {
+		dprintk(1,
+			"zr36050: something's wrong - %d codecs left somehow.\n",
+			zr36050_codecs);
+	}
+	videocodec_unregister(&zr36050_codec);
+}
+
+module_init(zr36050_init_module);
+module_exit(zr36050_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors "
+		   ZR050_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h
new file mode 100644
index 000000000000..9f52f0cdde50
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36050.h
@@ -0,0 +1,184 @@
+/*
+ * Zoran ZR36050 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * 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 ZR36050_H
+#define ZR36050_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36050 {
+	char name[32];
+	int num;
+	/* io datastructure */
+	struct videocodec *codec;
+	// last coder status
+	__u8 status1;
+	// actual coder setup
+	int mode;
+
+	__u16 width;
+	__u16 height;
+
+	__u16 bitrate_ctrl;
+
+	__u32 total_code_vol;
+	__u32 real_code_vol;
+	__u16 max_block_vol;
+
+	__u8 h_samp_ratio[8];
+	__u8 v_samp_ratio[8];
+	__u16 scalefact;
+	__u16 dri;
+
+	/* com/app marker */
+	struct jpeg_com_marker com;
+	struct jpeg_app_marker app;
+};
+
+/* zr36050 register addresses */
+#define ZR050_GO                  0x000
+#define ZR050_HARDWARE            0x002
+#define ZR050_MODE                0x003
+#define ZR050_OPTIONS             0x004
+#define ZR050_MBCV                0x005
+#define ZR050_MARKERS_EN          0x006
+#define ZR050_INT_REQ_0           0x007
+#define ZR050_INT_REQ_1           0x008
+#define ZR050_TCV_NET_HI          0x009
+#define ZR050_TCV_NET_MH          0x00a
+#define ZR050_TCV_NET_ML          0x00b
+#define ZR050_TCV_NET_LO          0x00c
+#define ZR050_TCV_DATA_HI         0x00d
+#define ZR050_TCV_DATA_MH         0x00e
+#define ZR050_TCV_DATA_ML         0x00f
+#define ZR050_TCV_DATA_LO         0x010
+#define ZR050_SF_HI               0x011
+#define ZR050_SF_LO               0x012
+#define ZR050_AF_HI               0x013
+#define ZR050_AF_M                0x014
+#define ZR050_AF_LO               0x015
+#define ZR050_ACV_HI              0x016
+#define ZR050_ACV_MH              0x017
+#define ZR050_ACV_ML              0x018
+#define ZR050_ACV_LO              0x019
+#define ZR050_ACT_HI              0x01a
+#define ZR050_ACT_MH              0x01b
+#define ZR050_ACT_ML              0x01c
+#define ZR050_ACT_LO              0x01d
+#define ZR050_ACV_TRUN_HI         0x01e
+#define ZR050_ACV_TRUN_MH         0x01f
+#define ZR050_ACV_TRUN_ML         0x020
+#define ZR050_ACV_TRUN_LO         0x021
+#define ZR050_STATUS_0            0x02e
+#define ZR050_STATUS_1            0x02f
+
+#define ZR050_SOF_IDX             0x040
+#define ZR050_SOS1_IDX            0x07a
+#define ZR050_SOS2_IDX            0x08a
+#define ZR050_SOS3_IDX            0x09a
+#define ZR050_SOS4_IDX            0x0aa
+#define ZR050_DRI_IDX             0x0c0
+#define ZR050_DNL_IDX             0x0c6
+#define ZR050_DQT_IDX             0x0cc
+#define ZR050_DHT_IDX             0x1d4
+#define ZR050_APP_IDX             0x380
+#define ZR050_COM_IDX             0x3c0
+
+/* zr36050 hardware register bits */
+
+#define ZR050_HW_BSWD                0x80
+#define ZR050_HW_MSTR                0x40
+#define ZR050_HW_DMA                 0x20
+#define ZR050_HW_CFIS_1_CLK          0x00
+#define ZR050_HW_CFIS_2_CLK          0x04
+#define ZR050_HW_CFIS_3_CLK          0x08
+#define ZR050_HW_CFIS_4_CLK          0x0C
+#define ZR050_HW_CFIS_5_CLK          0x10
+#define ZR050_HW_CFIS_6_CLK          0x14
+#define ZR050_HW_CFIS_7_CLK          0x18
+#define ZR050_HW_CFIS_8_CLK          0x1C
+#define ZR050_HW_BELE                0x01
+
+/* zr36050 mode register bits */
+
+#define ZR050_MO_COMP                0x80
+#define ZR050_MO_COMP                0x80
+#define ZR050_MO_ATP                 0x40
+#define ZR050_MO_PASS2               0x20
+#define ZR050_MO_TLM                 0x10
+#define ZR050_MO_DCONLY              0x08
+#define ZR050_MO_BRC                 0x04
+
+#define ZR050_MO_ATP                 0x40
+#define ZR050_MO_PASS2               0x20
+#define ZR050_MO_TLM                 0x10
+#define ZR050_MO_DCONLY              0x08
+
+/* zr36050 option register bits */
+
+#define ZR050_OP_NSCN_1              0x00
+#define ZR050_OP_NSCN_2              0x20
+#define ZR050_OP_NSCN_3              0x40
+#define ZR050_OP_NSCN_4              0x60
+#define ZR050_OP_NSCN_5              0x80
+#define ZR050_OP_NSCN_6              0xA0
+#define ZR050_OP_NSCN_7              0xC0
+#define ZR050_OP_NSCN_8              0xE0
+#define ZR050_OP_OVF                 0x10
+
+
+/* zr36050 markers-enable register bits */
+
+#define ZR050_ME_APP                 0x80
+#define ZR050_ME_COM                 0x40
+#define ZR050_ME_DRI                 0x20
+#define ZR050_ME_DQT                 0x10
+#define ZR050_ME_DHT                 0x08
+#define ZR050_ME_DNL                 0x04
+#define ZR050_ME_DQTI                0x02
+#define ZR050_ME_DHTI                0x01
+
+/* zr36050 status0/1 register bit masks */
+
+#define ZR050_ST_RST_MASK            0x20
+#define ZR050_ST_SOF_MASK            0x02
+#define ZR050_ST_SOS_MASK            0x02
+#define ZR050_ST_DATRDY_MASK         0x80
+#define ZR050_ST_MRKDET_MASK         0x40
+#define ZR050_ST_RFM_MASK            0x10
+#define ZR050_ST_RFD_MASK            0x08
+#define ZR050_ST_END_MASK            0x04
+#define ZR050_ST_TCVOVF_MASK         0x02
+#define ZR050_ST_DATOVF_MASK         0x01
+
+/* pixel component idx */
+
+#define ZR050_Y_COMPONENT         0
+#define ZR050_U_COMPONENT         1
+#define ZR050_V_COMPONENT         2
+
+#endif				/*fndef ZR36050_H */
diff --git a/drivers/media/pci/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h
new file mode 100644
index 000000000000..54c9362aa980
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36057.h
@@ -0,0 +1,168 @@
+/*
+ * zr36057.h - zr36057 register offsets
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZR36057_H_
+#define _ZR36057_H_
+
+
+/* Zoran ZR36057 registers */
+
+#define ZR36057_VFEHCR          0x000	/* Video Front End, Horizontal Configuration Register */
+#define ZR36057_VFEHCR_HSPol            (1<<30)
+#define ZR36057_VFEHCR_HStart           10
+#define ZR36057_VFEHCR_HEnd		0
+#define ZR36057_VFEHCR_Hmask		0x3ff
+
+#define ZR36057_VFEVCR          0x004	/* Video Front End, Vertical Configuration Register */
+#define ZR36057_VFEVCR_VSPol            (1<<30)
+#define ZR36057_VFEVCR_VStart           10
+#define ZR36057_VFEVCR_VEnd		0
+#define ZR36057_VFEVCR_Vmask		0x3ff
+
+#define ZR36057_VFESPFR         0x008	/* Video Front End, Scaler and Pixel Format Register */
+#define ZR36057_VFESPFR_ExtFl           (1<<26)
+#define ZR36057_VFESPFR_TopField        (1<<25)
+#define ZR36057_VFESPFR_VCLKPol         (1<<24)
+#define ZR36057_VFESPFR_HFilter         21
+#define ZR36057_VFESPFR_HorDcm          14
+#define ZR36057_VFESPFR_VerDcm          8
+#define ZR36057_VFESPFR_DispMode        6
+#define ZR36057_VFESPFR_YUV422          (0<<3)
+#define ZR36057_VFESPFR_RGB888          (1<<3)
+#define ZR36057_VFESPFR_RGB565          (2<<3)
+#define ZR36057_VFESPFR_RGB555          (3<<3)
+#define ZR36057_VFESPFR_ErrDif          (1<<2)
+#define ZR36057_VFESPFR_Pack24          (1<<1)
+#define ZR36057_VFESPFR_LittleEndian    (1<<0)
+
+#define ZR36057_VDTR            0x00c	/* Video Display "Top" Register */
+
+#define ZR36057_VDBR            0x010	/* Video Display "Bottom" Register */
+
+#define ZR36057_VSSFGR          0x014	/* Video Stride, Status, and Frame Grab Register */
+#define ZR36057_VSSFGR_DispStride       16
+#define ZR36057_VSSFGR_VidOvf           (1<<8)
+#define ZR36057_VSSFGR_SnapShot         (1<<1)
+#define ZR36057_VSSFGR_FrameGrab        (1<<0)
+
+#define ZR36057_VDCR            0x018	/* Video Display Configuration Register */
+#define ZR36057_VDCR_VidEn              (1<<31)
+#define ZR36057_VDCR_MinPix             24
+#define ZR36057_VDCR_Triton             (1<<24)
+#define ZR36057_VDCR_VidWinHt           12
+#define ZR36057_VDCR_VidWinWid          0
+
+#define ZR36057_MMTR            0x01c	/* Masking Map "Top" Register */
+
+#define ZR36057_MMBR            0x020	/* Masking Map "Bottom" Register */
+
+#define ZR36057_OCR             0x024	/* Overlay Control Register */
+#define ZR36057_OCR_OvlEnable           (1 << 15)
+#define ZR36057_OCR_MaskStride          0
+
+#define ZR36057_SPGPPCR         0x028	/* System, PCI, and General Purpose Pins Control Register */
+#define ZR36057_SPGPPCR_SoftReset	(1<<24)
+
+#define ZR36057_GPPGCR1         0x02c	/* General Purpose Pins and GuestBus Control Register (1) */
+
+#define ZR36057_MCSAR           0x030	/* MPEG Code Source Address Register */
+
+#define ZR36057_MCTCR           0x034	/* MPEG Code Transfer Control Register */
+#define ZR36057_MCTCR_CodTime           (1 << 30)
+#define ZR36057_MCTCR_CEmpty            (1 << 29)
+#define ZR36057_MCTCR_CFlush            (1 << 28)
+#define ZR36057_MCTCR_CodGuestID	20
+#define ZR36057_MCTCR_CodGuestReg	16
+
+#define ZR36057_MCMPR           0x038	/* MPEG Code Memory Pointer Register */
+
+#define ZR36057_ISR             0x03c	/* Interrupt Status Register */
+#define ZR36057_ISR_GIRQ1               (1<<30)
+#define ZR36057_ISR_GIRQ0               (1<<29)
+#define ZR36057_ISR_CodRepIRQ           (1<<28)
+#define ZR36057_ISR_JPEGRepIRQ          (1<<27)
+
+#define ZR36057_ICR             0x040	/* Interrupt Control Register */
+#define ZR36057_ICR_GIRQ1               (1<<30)
+#define ZR36057_ICR_GIRQ0               (1<<29)
+#define ZR36057_ICR_CodRepIRQ           (1<<28)
+#define ZR36057_ICR_JPEGRepIRQ          (1<<27)
+#define ZR36057_ICR_IntPinEn            (1<<24)
+
+#define ZR36057_I2CBR           0x044	/* I2C Bus Register */
+#define ZR36057_I2CBR_SDA       	(1<<1)
+#define ZR36057_I2CBR_SCL       	(1<<0)
+
+#define ZR36057_JMC             0x100	/* JPEG Mode and Control */
+#define ZR36057_JMC_JPG                 (1 << 31)
+#define ZR36057_JMC_JPGExpMode          (0 << 29)
+#define ZR36057_JMC_JPGCmpMode          (1 << 29)
+#define ZR36057_JMC_MJPGExpMode         (2 << 29)
+#define ZR36057_JMC_MJPGCmpMode         (3 << 29)
+#define ZR36057_JMC_RTBUSY_FB           (1 << 6)
+#define ZR36057_JMC_Go_en               (1 << 5)
+#define ZR36057_JMC_SyncMstr            (1 << 4)
+#define ZR36057_JMC_Fld_per_buff        (1 << 3)
+#define ZR36057_JMC_VFIFO_FB            (1 << 2)
+#define ZR36057_JMC_CFIFO_FB            (1 << 1)
+#define ZR36057_JMC_Stll_LitEndian      (1 << 0)
+
+#define ZR36057_JPC             0x104	/* JPEG Process Control */
+#define ZR36057_JPC_P_Reset             (1 << 7)
+#define ZR36057_JPC_CodTrnsEn           (1 << 5)
+#define ZR36057_JPC_Active              (1 << 0)
+
+#define ZR36057_VSP             0x108	/* Vertical Sync Parameters */
+#define ZR36057_VSP_VsyncSize           16
+#define ZR36057_VSP_FrmTot              0
+
+#define ZR36057_HSP             0x10c	/* Horizontal Sync Parameters */
+#define ZR36057_HSP_HsyncStart          16
+#define ZR36057_HSP_LineTot             0
+
+#define ZR36057_FHAP            0x110	/* Field Horizontal Active Portion */
+#define ZR36057_FHAP_NAX                16
+#define ZR36057_FHAP_PAX                0
+
+#define ZR36057_FVAP            0x114	/* Field Vertical Active Portion */
+#define ZR36057_FVAP_NAY                16
+#define ZR36057_FVAP_PAY                0
+
+#define ZR36057_FPP             0x118	/* Field Process Parameters */
+#define ZR36057_FPP_Odd_Even            (1 << 0)
+
+#define ZR36057_JCBA            0x11c	/* JPEG Code Base Address */
+
+#define ZR36057_JCFT            0x120	/* JPEG Code FIFO Threshold */
+
+#define ZR36057_JCGI            0x124	/* JPEG Codec Guest ID */
+#define ZR36057_JCGI_JPEGuestID         4
+#define ZR36057_JCGI_JPEGuestReg        0
+
+#define ZR36057_GCR2            0x12c	/* GuestBus Control Register (2) */
+
+#define ZR36057_POR             0x200	/* Post Office Register */
+#define ZR36057_POR_POPen               (1<<25)
+#define ZR36057_POR_POTime              (1<<24)
+#define ZR36057_POR_PODir               (1<<23)
+
+#define ZR36057_STR             0x300	/* "Still" Transfer Register */
+
+#endif
diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c
new file mode 100644
index 000000000000..f08546fe2234
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36060.c
@@ -0,0 +1,1010 @@
+/*
+ * Zoran ZR36060 basic configuration functions
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR060_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* headerfile of this module */
+#include "zr36060.h"
+
+/* codec io API */
+#include "videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+  just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36060_codecs;
+
+static bool low_bitrate;
+module_param(low_bitrate, bool, 0);
+MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate");
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* =========================================================================
+   Local hardware I/O functions:
+
+   read/write via codec layer (registers are located in the master device)
+   ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36060_read (struct zr36060 *ptr,
+	      u16             reg)
+{
+	u8 value = 0;
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->readreg)
+		value = (ptr->codec->master_data->readreg(ptr->codec,
+							  reg)) & 0xff;
+	else
+		dprintk(1,
+			KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+			ptr->name);
+
+	//dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value);
+
+	return value;
+}
+
+static void
+zr36060_write(struct zr36060 *ptr,
+	      u16             reg,
+	      u8              value)
+{
+	//dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg);
+	dprintk(4, "0x%02x @0x%04x\n", value, reg);
+
+	// just in case something is wrong...
+	if (ptr->codec->master_data->writereg)
+		ptr->codec->master_data->writereg(ptr->codec, reg, value);
+	else
+		dprintk(1,
+			KERN_ERR
+			"%s: invalid I/O setup, nothing written!\n",
+			ptr->name);
+}
+
+/* =========================================================================
+   Local helper function:
+
+   status read
+   ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36060_read_status (struct zr36060 *ptr)
+{
+	ptr->status = zr36060_read(ptr, ZR060_CFSR);
+
+	zr36060_read(ptr, 0);
+	return ptr->status;
+}
+
+/* =========================================================================
+   Local helper function:
+
+   scale factor read
+   ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36060_read_scalefactor (struct zr36060 *ptr)
+{
+	ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) |
+			 (zr36060_read(ptr, ZR060_SF_LO) & 0xFF);
+
+	/* leave 0 selected for an eventually GO from master */
+	zr36060_read(ptr, 0);
+	return ptr->scalefact;
+}
+
+/* =========================================================================
+   Local helper function:
+
+   wait if codec is ready to proceed (end of processing) or time is over
+   ========================================================================= */
+
+static void
+zr36060_wait_end (struct zr36060 *ptr)
+{
+	int i = 0;
+
+	while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) {
+		udelay(1);
+		if (i++ > 200000) {	// 200ms, there is for sure something wrong!!!
+			dprintk(1,
+				"%s: timeout at wait_end (last status: 0x%02x)\n",
+				ptr->name, ptr->status);
+			break;
+		}
+	}
+}
+
+/* =========================================================================
+   Local helper function:
+
+   basic test of "connectivity", writes/reads to/from memory the SOF marker
+   ========================================================================= */
+
+static int
+zr36060_basic_test (struct zr36060 *ptr)
+{
+	if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) &&
+	    (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, can't connect to jpeg processor!\n",
+			ptr->name);
+		return -ENXIO;
+	}
+
+	zr36060_wait_end(ptr);
+	if (ptr->status & ZR060_CFSR_Busy) {
+		dprintk(1,
+			KERN_ERR
+			"%s: attach failed, jpeg processor failed (end flag)!\n",
+			ptr->name);
+		return -EBUSY;
+	}
+
+	return 0;		/* looks good! */
+}
+
+/* =========================================================================
+   Local helper function:
+
+   simple loop for pushing the init datasets
+   ========================================================================= */
+
+static int
+zr36060_pushit (struct zr36060 *ptr,
+		u16             startreg,
+		u16             len,
+		const char     *data)
+{
+	int i = 0;
+
+	dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+		startreg, len);
+	while (i < len) {
+		zr36060_write(ptr, startreg++, data[i++]);
+	}
+
+	return i;
+}
+
+/* =========================================================================
+   Basic datasets:
+
+   jpeg baseline setup data (you find it on lots places in internet, or just
+   extract it from any regular .jpg image...)
+
+   Could be variable, but until it's not needed it they are just fixed to save
+   memory. Otherwise expand zr36060 structure with arrays, push the values to
+   it and initialize from there, as e.g. the linux zr36057/60 driver does it.
+   ========================================================================= */
+
+static const char zr36060_dqt[0x86] = {
+	0xff, 0xdb,		//Marker: DQT
+	0x00, 0x84,		//Length: 2*65+2
+	0x00,			//Pq,Tq first table
+	0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+	0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+	0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+	0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+	0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+	0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+	0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+	0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+	0x01,			//Pq,Tq second table
+	0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+	0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+	0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36060_dht[0x1a4] = {
+	0xff, 0xc4,		//Marker: DHT
+	0x01, 0xa2,		//Length: 2*AC, 2*DC
+	0x00,			//DC first table
+	0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+	0x01,			//DC second table
+	0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+	0x10,			//AC first table
+	0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+	0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+	0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+	0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+	0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+	0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+	0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+	0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+	0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+	0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+	0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+	0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+	0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+	0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+	0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+	0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+	0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA,
+	0x11,			//AC second table
+	0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+	0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+	0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+	0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+	0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+	0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+	0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+	0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+	0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+	0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+	0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+	0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+	0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+	0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+	0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+	0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+	0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+	0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS          0x3	//Y,U,V
+#define BASELINE_PRECISION        0x8	//MCU size (?)
+static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's QT
+static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's DC
+static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };	//table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+   Local helper functions:
+
+   calculation and setup of parameter-dependent JPEG baseline segments
+   (needed for compression only)
+   ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+			 of each color component */
+
+static int
+zr36060_set_sof (struct zr36060 *ptr)
+{
+	char sof_data[34];	// max. size of register set
+	int i;
+
+	dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+		ptr->width, ptr->height, NO_OF_COMPONENTS);
+	sof_data[0] = 0xff;
+	sof_data[1] = 0xc0;
+	sof_data[2] = 0x00;
+	sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+	sof_data[4] = BASELINE_PRECISION;	// only '8' possible with zr36060
+	sof_data[5] = (ptr->height) >> 8;
+	sof_data[6] = (ptr->height) & 0xff;
+	sof_data[7] = (ptr->width) >> 8;
+	sof_data[8] = (ptr->width) & 0xff;
+	sof_data[9] = NO_OF_COMPONENTS;
+	for (i = 0; i < NO_OF_COMPONENTS; i++) {
+		sof_data[10 + (i * 3)] = i;	// index identifier
+		sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) |
+					 (ptr->v_samp_ratio[i]); // sampling ratios
+		sof_data[12 + (i * 3)] = zr36060_tq[i];	// Q table selection
+	}
+	return zr36060_pushit(ptr, ZR060_SOF_IDX,
+			      (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+			of each color component */
+
+static int
+zr36060_set_sos (struct zr36060 *ptr)
+{
+	char sos_data[16];	// max. size of register set
+	int i;
+
+	dprintk(3, "%s: write SOS\n", ptr->name);
+	sos_data[0] = 0xff;
+	sos_data[1] = 0xda;
+	sos_data[2] = 0x00;
+	sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+	sos_data[4] = NO_OF_COMPONENTS;
+	for (i = 0; i < NO_OF_COMPONENTS; i++) {
+		sos_data[5 + (i * 2)] = i;	// index
+		sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) |
+					zr36060_ta[i]; // AC/DC tbl.sel.
+	}
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00;	// scan start
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f;
+	sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+	return zr36060_pushit(ptr, ZR060_SOS_IDX,
+			      4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+			      sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36060_set_dri (struct zr36060 *ptr)
+{
+	char dri_data[6];	// max. size of register set
+
+	dprintk(3, "%s: write DRI\n", ptr->name);
+	dri_data[0] = 0xff;
+	dri_data[1] = 0xdd;
+	dri_data[2] = 0x00;
+	dri_data[3] = 0x04;
+	dri_data[4] = (ptr->dri) >> 8;
+	dri_data[5] = (ptr->dri) & 0xff;
+	return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+   Setup function:
+
+   Setup compression/decompression of Zoran's JPEG processor
+   ( see also zoran 36060 manual )
+
+   ... sorry for the spaghetti code ...
+   ========================================================================= */
+static void
+zr36060_init (struct zr36060 *ptr)
+{
+	int sum = 0;
+	long bitcnt, tmp;
+
+	if (ptr->mode == CODEC_DO_COMPRESSION) {
+		dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+		zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+		/* 060 communicates with 067 in master mode */
+		zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+		/* Compression with or without variable scale factor */
+		/*FIXME: What about ptr->bitrate_ctrl? */
+		zr36060_write(ptr, ZR060_CMR,
+			      ZR060_CMR_Comp | ZR060_CMR_Pass2 |
+			      ZR060_CMR_BRB);
+
+		/* Must be zero */
+		zr36060_write(ptr, ZR060_MBZ, 0x00);
+		zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+		zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+		/* Disable all IRQs - no DataErr means autoreset */
+		zr36060_write(ptr, ZR060_IMR, 0);
+
+		/* volume control settings */
+		zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8);
+		zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff);
+
+		zr36060_write(ptr, ZR060_AF_HI, 0xff);
+		zr36060_write(ptr, ZR060_AF_M, 0xff);
+		zr36060_write(ptr, ZR060_AF_LO, 0xff);
+
+		/* setup the variable jpeg tables */
+		sum += zr36060_set_sof(ptr);
+		sum += zr36060_set_sos(ptr);
+		sum += zr36060_set_dri(ptr);
+
+		/* setup the fixed jpeg tables - maybe variable, though -
+		 * (see table init section above) */
+		sum +=
+		    zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt),
+				   zr36060_dqt);
+		sum +=
+		    zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+				   zr36060_dht);
+		zr36060_write(ptr, ZR060_APP_IDX, 0xff);
+		zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn);
+		zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00);
+		zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2);
+		sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60,
+				      ptr->app.data) + 4;
+		zr36060_write(ptr, ZR060_COM_IDX, 0xff);
+		zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe);
+		zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00);
+		zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2);
+		sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60,
+				      ptr->com.data) + 4;
+
+		/* setup misc. data for compression (target code sizes) */
+
+		/* size of compressed code to reach without header data */
+		sum = ptr->real_code_vol - sum;
+		bitcnt = sum << 3;	/* need the size in bits */
+
+		tmp = bitcnt >> 16;
+		dprintk(3,
+			"%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+			ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+		zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8);
+		zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff);
+		tmp = bitcnt & 0xffff;
+		zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8);
+		zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff);
+
+		bitcnt -= bitcnt >> 7;	// bits without stuffing
+		bitcnt -= ((bitcnt * 5) >> 6);	// bits without eob
+
+		tmp = bitcnt >> 16;
+		dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+			ptr->name, bitcnt, tmp);
+		zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8);
+		zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff);
+		tmp = bitcnt & 0xffff;
+		zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8);
+		zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff);
+
+		/* JPEG markers to be included in the compressed stream */
+		zr36060_write(ptr, ZR060_MER,
+			      ZR060_MER_DQT | ZR060_MER_DHT |
+			      ((ptr->com.len > 0) ? ZR060_MER_Com : 0) |
+			      ((ptr->app.len > 0) ? ZR060_MER_App : 0));
+
+		/* Setup the Video Frontend */
+		/* Limit pixel range to 16..235 as per CCIR-601 */
+		zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+
+	} else {
+		dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+		zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+		/* 060 communicates with 067 in master mode */
+		zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+		/* Decompression */
+		zr36060_write(ptr, ZR060_CMR, 0);
+
+		/* Must be zero */
+		zr36060_write(ptr, ZR060_MBZ, 0x00);
+		zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+		zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+		/* Disable all IRQs - no DataErr means autoreset */
+		zr36060_write(ptr, ZR060_IMR, 0);
+
+		/* setup misc. data for expansion */
+		zr36060_write(ptr, ZR060_MER, 0);
+
+		/* setup the fixed jpeg tables - maybe variable, though -
+		 * (see table init section above) */
+		zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+			       zr36060_dht);
+
+		/* Setup the Video Frontend */
+		//zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt);
+		//this doesn't seem right and doesn't work...
+		zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+	}
+
+	/* Load the tables */
+	zr36060_write(ptr, ZR060_LOAD,
+		      ZR060_LOAD_SyncRst | ZR060_LOAD_Load);
+	zr36060_wait_end(ptr);
+	dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name,
+		ptr->status);
+
+	if (ptr->status & ZR060_CFSR_Busy) {
+		dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name);
+		return;		// something is wrong, its timed out!!!!
+	}
+}
+
+/* =========================================================================
+   CODEC API FUNCTIONS
+
+   this functions are accessed by the master via the API structure
+   ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+   this should be the last call from the master before starting processing */
+static int
+zr36060_set_mode (struct videocodec *codec,
+		  int                mode)
+{
+	struct zr36060 *ptr = (struct zr36060 *) codec->data;
+
+	dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+	if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+		return -EINVAL;
+
+	ptr->mode = mode;
+	zr36060_init(ptr);
+
+	return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36060_set_video (struct videocodec   *codec,
+		   struct tvnorm       *norm,
+		   struct vfe_settings *cap,
+		   struct vfe_polarity *pol)
+{
+	struct zr36060 *ptr = (struct zr36060 *) codec->data;
+	u32 reg;
+	int size;
+
+	dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name,
+		cap->x, cap->y, cap->width, cap->height, cap->decimation);
+
+	/* if () return -EINVAL;
+	 * trust the master driver that it knows what it does - so
+	 * we allow invalid startx/y and norm for now ... */
+	ptr->width = cap->width / (cap->decimation & 0xff);
+	ptr->height = cap->height / (cap->decimation >> 8);
+
+	zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+	/* Note that VSPol/HSPol bits in zr36060 have the opposite
+	 * meaning of their zr360x7 counterparts with the same names
+	 * N.b. for VSPol this is only true if FIVEdge = 0 (default,
+	 * left unchanged here - in accordance with datasheet).
+	*/
+	reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0)
+	    | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0)
+	    | (pol->field_pol ? ZR060_VPR_FIPol : 0)
+	    | (pol->blank_pol ? ZR060_VPR_BLPol : 0)
+	    | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0)
+	    | (pol->poe_pol ? ZR060_VPR_PoePol : 0)
+	    | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0)
+	    | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0);
+	zr36060_write(ptr, ZR060_VPR, reg);
+
+	reg = 0;
+	switch (cap->decimation & 0xff) {
+	default:
+	case 1:
+		break;
+
+	case 2:
+		reg |= ZR060_SR_HScale2;
+		break;
+
+	case 4:
+		reg |= ZR060_SR_HScale4;
+		break;
+	}
+
+	switch (cap->decimation >> 8) {
+	default:
+	case 1:
+		break;
+
+	case 2:
+		reg |= ZR060_SR_VScale;
+		break;
+	}
+	zr36060_write(ptr, ZR060_SR, reg);
+
+	zr36060_write(ptr, ZR060_BCR_Y, 0x00);
+	zr36060_write(ptr, ZR060_BCR_U, 0x80);
+	zr36060_write(ptr, ZR060_BCR_V, 0x80);
+
+	/* sync generator */
+
+	reg = norm->Ht - 1;	/* Vtotal */
+	zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff);
+
+	reg = norm->Wt - 1;	/* Htotal */
+	zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff);
+
+	reg = 6 - 1;		/* VsyncSize */
+	zr36060_write(ptr, ZR060_SGR_VSYNC, reg);
+
+	//reg   = 30 - 1;               /* HsyncSize */
+///*CP*/        reg = (zr->params.norm == 1 ? 57 : 68);
+	reg = 68;
+	zr36060_write(ptr, ZR060_SGR_HSYNC, reg);
+
+	reg = norm->VStart - 1;	/* BVstart */
+	zr36060_write(ptr, ZR060_SGR_BVSTART, reg);
+
+	reg += norm->Ha / 2;	/* BVend */
+	zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff);
+
+	reg = norm->HStart - 1;	/* BHstart */
+	zr36060_write(ptr, ZR060_SGR_BHSTART, reg);
+
+	reg += norm->Wa;	/* BHend */
+	zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff);
+
+	/* active area */
+	reg = cap->y + norm->VStart;	/* Vstart */
+	zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff);
+
+	reg += cap->height;	/* Vend */
+	zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff);
+
+	reg = cap->x + norm->HStart;	/* Hstart */
+	zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff);
+
+	reg += cap->width;	/* Hend */
+	zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff);
+
+	/* subimage area */
+	reg = norm->VStart - 4;	/* SVstart */
+	zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff);
+
+	reg += norm->Ha / 2 + 8;	/* SVend */
+	zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff);
+
+	reg = norm->HStart /*+ 64 */  - 4;	/* SHstart */
+	zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff);
+
+	reg += norm->Wa + 8;	/* SHend */
+	zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff);
+	zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff);
+
+	size = ptr->width * ptr->height;
+	/* Target compressed field size in bits: */
+	size = size * 16;	/* uncompressed size in bits */
+	/* (Ronald) by default, quality = 100 is a compression
+	 * ratio 1:2. Setting low_bitrate (insmod option) sets
+	 * it to 1:4 (instead of 1:2, zr36060 max) as limit because the
+	 * buz can't handle more at decimation=1... Use low_bitrate if
+	 * you have a Buz, unless you know what you're doing */
+	size = size * cap->quality / (low_bitrate ? 400 : 200);
+	/* Lower limit (arbitrary, 1 KB) */
+	if (size < 8192)
+		size = 8192;
+	/* Upper limit: 7/8 of the code buffers */
+	if (size > ptr->total_code_vol * 7)
+		size = ptr->total_code_vol * 7;
+
+	ptr->real_code_vol = size >> 3;	/* in bytes */
+
+	/* the MBCVR is the *maximum* block volume, according to the
+	 * JPEG ISO specs, this shouldn't be used, since that allows
+	 * for the best encoding quality. So set it to it's max value */
+	reg = ptr->max_block_vol;
+	zr36060_write(ptr, ZR060_MBCVR, reg);
+
+	return 0;
+}
+
+/* additional control functions */
+static int
+zr36060_control (struct videocodec *codec,
+		 int                type,
+		 int                size,
+		 void              *data)
+{
+	struct zr36060 *ptr = (struct zr36060 *) codec->data;
+	int *ival = (int *) data;
+
+	dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+		size);
+
+	switch (type) {
+	case CODEC_G_STATUS:	/* get last status */
+		if (size != sizeof(int))
+			return -EFAULT;
+		zr36060_read_status(ptr);
+		*ival = ptr->status;
+		break;
+
+	case CODEC_G_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = CODEC_MODE_BJPG;
+		break;
+
+	case CODEC_S_CODEC_MODE:
+		if (size != sizeof(int))
+			return -EFAULT;
+		if (*ival != CODEC_MODE_BJPG)
+			return -EINVAL;
+		/* not needed, do nothing */
+		return 0;
+
+	case CODEC_G_VFE:
+	case CODEC_S_VFE:
+		/* not needed, do nothing */
+		return 0;
+
+	case CODEC_S_MMAP:
+		/* not available, give an error */
+		return -ENXIO;
+
+	case CODEC_G_JPEG_TDS_BYTE:	/* get target volume in byte */
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = ptr->total_code_vol;
+		break;
+
+	case CODEC_S_JPEG_TDS_BYTE:	/* get target volume in byte */
+		if (size != sizeof(int))
+			return -EFAULT;
+		ptr->total_code_vol = *ival;
+		ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+		break;
+
+	case CODEC_G_JPEG_SCALE:	/* get scaling factor */
+		if (size != sizeof(int))
+			return -EFAULT;
+		*ival = zr36060_read_scalefactor(ptr);
+		break;
+
+	case CODEC_S_JPEG_SCALE:	/* set scaling factor */
+		if (size != sizeof(int))
+			return -EFAULT;
+		ptr->scalefact = *ival;
+		break;
+
+	case CODEC_G_JPEG_APP_DATA: {	/* get appn marker data */
+		struct jpeg_app_marker *app = data;
+
+		if (size != sizeof(struct jpeg_app_marker))
+			return -EFAULT;
+
+		*app = ptr->app;
+		break;
+	}
+
+	case CODEC_S_JPEG_APP_DATA: {	/* set appn marker data */
+		struct jpeg_app_marker *app = data;
+
+		if (size != sizeof(struct jpeg_app_marker))
+			return -EFAULT;
+
+		ptr->app = *app;
+		break;
+	}
+
+	case CODEC_G_JPEG_COM_DATA: {	/* get comment marker data */
+		struct jpeg_com_marker *com = data;
+
+		if (size != sizeof(struct jpeg_com_marker))
+			return -EFAULT;
+
+		*com = ptr->com;
+		break;
+	}
+
+	case CODEC_S_JPEG_COM_DATA: {	/* set comment marker data */
+		struct jpeg_com_marker *com = data;
+
+		if (size != sizeof(struct jpeg_com_marker))
+			return -EFAULT;
+
+		ptr->com = *com;
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+
+	return size;
+}
+
+/* =========================================================================
+   Exit and unregister function:
+
+   Deinitializes Zoran's JPEG processor
+   ========================================================================= */
+
+static int
+zr36060_unset (struct videocodec *codec)
+{
+	struct zr36060 *ptr = codec->data;
+
+	if (ptr) {
+		/* do wee need some codec deinit here, too ???? */
+
+		dprintk(1, "%s: finished codec #%d\n", ptr->name,
+			ptr->num);
+		kfree(ptr);
+		codec->data = NULL;
+
+		zr36060_codecs--;
+		return 0;
+	}
+
+	return -EFAULT;
+}
+
+/* =========================================================================
+   Setup and registry function:
+
+   Initializes Zoran's JPEG processor
+
+   Also sets pixel size, average code size, mode (compr./decompr.)
+   (the given size is determined by the processor with the video interface)
+   ========================================================================= */
+
+static int
+zr36060_setup (struct videocodec *codec)
+{
+	struct zr36060 *ptr;
+	int res;
+
+	dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n",
+		zr36060_codecs);
+
+	if (zr36060_codecs == MAX_CODECS) {
+		dprintk(1,
+			KERN_ERR "zr36060: Can't attach more codecs!\n");
+		return -ENOSPC;
+	}
+	//mem structure init
+	codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL);
+	if (NULL == ptr) {
+		dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n");
+		return -ENOMEM;
+	}
+
+	snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]",
+		 zr36060_codecs);
+	ptr->num = zr36060_codecs++;
+	ptr->codec = codec;
+
+	//testing
+	res = zr36060_basic_test(ptr);
+	if (res < 0) {
+		zr36060_unset(codec);
+		return res;
+	}
+	//final setup
+	memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8);
+	memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8);
+
+	ptr->bitrate_ctrl = 0;	/* 0 or 1 - fixed file size flag
+				 * (what is the difference?) */
+	ptr->mode = CODEC_DO_COMPRESSION;
+	ptr->width = 384;
+	ptr->height = 288;
+	ptr->total_code_vol = 16000;	/* CHECKME */
+	ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+	ptr->max_block_vol = 240;	/* CHECKME, was 120 is 240 */
+	ptr->scalefact = 0x100;
+	ptr->dri = 1;		/* CHECKME, was 8 is 1 */
+
+	/* by default, no COM or APP markers - app should set those */
+	ptr->com.len = 0;
+	ptr->app.appn = 0;
+	ptr->app.len = 0;
+
+	zr36060_init(ptr);
+
+	dprintk(1, KERN_INFO "%s: codec attached and running\n",
+		ptr->name);
+
+	return 0;
+}
+
+static const struct videocodec zr36060_codec = {
+	.owner = THIS_MODULE,
+	.name = "zr36060",
+	.magic = 0L,		// magic not used
+	.flags =
+	    CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+	    CODEC_FLAG_DECODER | CODEC_FLAG_VFE,
+	.type = CODEC_TYPE_ZR36060,
+	.setup = zr36060_setup,	// functionality
+	.unset = zr36060_unset,
+	.set_mode = zr36060_set_mode,
+	.set_video = zr36060_set_video,
+	.control = zr36060_control,
+	// others are not used
+};
+
+/* =========================================================================
+   HOOK IN DRIVER AS KERNEL MODULE
+   ========================================================================= */
+
+static int __init
+zr36060_init_module (void)
+{
+	//dprintk(1, "zr36060 driver %s\n",ZR060_VERSION);
+	zr36060_codecs = 0;
+	return videocodec_register(&zr36060_codec);
+}
+
+static void __exit
+zr36060_cleanup_module (void)
+{
+	if (zr36060_codecs) {
+		dprintk(1,
+			"zr36060: something's wrong - %d codecs left somehow.\n",
+			zr36060_codecs);
+	}
+
+	/* however, we can't just stay alive */
+	videocodec_unregister(&zr36060_codec);
+}
+
+module_init(zr36060_init_module);
+module_exit(zr36060_cleanup_module);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>");
+MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors "
+		   ZR060_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h
new file mode 100644
index 000000000000..914ffa4ad8d3
--- /dev/null
+++ b/drivers/media/pci/zoran/zr36060.h
@@ -0,0 +1,220 @@
+/*
+ * Zoran ZR36060 basic configuration functions - header file
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * 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 ZR36060_H
+#define ZR36060_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36060 {
+	char name[32];
+	int num;
+	/* io datastructure */
+	struct videocodec *codec;
+	// last coder status
+	__u8 status;
+	// actual coder setup
+	int mode;
+
+	__u16 width;
+	__u16 height;
+
+	__u16 bitrate_ctrl;
+
+	__u32 total_code_vol;
+	__u32 real_code_vol;
+	__u16 max_block_vol;
+
+	__u8 h_samp_ratio[8];
+	__u8 v_samp_ratio[8];
+	__u16 scalefact;
+	__u16 dri;
+
+	/* app/com marker data */
+	struct jpeg_app_marker app;
+	struct jpeg_com_marker com;
+};
+
+/* ZR36060 register addresses */
+#define ZR060_LOAD			0x000
+#define ZR060_CFSR			0x001
+#define ZR060_CIR			0x002
+#define ZR060_CMR			0x003
+#define ZR060_MBZ			0x004
+#define ZR060_MBCVR			0x005
+#define ZR060_MER			0x006
+#define ZR060_IMR			0x007
+#define ZR060_ISR			0x008
+#define ZR060_TCV_NET_HI		0x009
+#define ZR060_TCV_NET_MH		0x00a
+#define ZR060_TCV_NET_ML		0x00b
+#define ZR060_TCV_NET_LO		0x00c
+#define ZR060_TCV_DATA_HI		0x00d
+#define ZR060_TCV_DATA_MH		0x00e
+#define ZR060_TCV_DATA_ML		0x00f
+#define ZR060_TCV_DATA_LO		0x010
+#define ZR060_SF_HI			0x011
+#define ZR060_SF_LO			0x012
+#define ZR060_AF_HI			0x013
+#define ZR060_AF_M			0x014
+#define ZR060_AF_LO			0x015
+#define ZR060_ACV_HI			0x016
+#define ZR060_ACV_MH			0x017
+#define ZR060_ACV_ML			0x018
+#define ZR060_ACV_LO			0x019
+#define ZR060_ACT_HI			0x01a
+#define ZR060_ACT_MH			0x01b
+#define ZR060_ACT_ML			0x01c
+#define ZR060_ACT_LO			0x01d
+#define ZR060_ACV_TRUN_HI		0x01e
+#define ZR060_ACV_TRUN_MH		0x01f
+#define ZR060_ACV_TRUN_ML		0x020
+#define ZR060_ACV_TRUN_LO		0x021
+#define ZR060_IDR_DEV			0x022
+#define ZR060_IDR_REV			0x023
+#define ZR060_TCR_HI			0x024
+#define ZR060_TCR_LO			0x025
+#define ZR060_VCR			0x030
+#define ZR060_VPR			0x031
+#define ZR060_SR			0x032
+#define ZR060_BCR_Y			0x033
+#define ZR060_BCR_U			0x034
+#define ZR060_BCR_V			0x035
+#define ZR060_SGR_VTOTAL_HI		0x036
+#define ZR060_SGR_VTOTAL_LO		0x037
+#define ZR060_SGR_HTOTAL_HI		0x038
+#define ZR060_SGR_HTOTAL_LO		0x039
+#define ZR060_SGR_VSYNC			0x03a
+#define ZR060_SGR_HSYNC			0x03b
+#define ZR060_SGR_BVSTART		0x03c
+#define ZR060_SGR_BHSTART		0x03d
+#define ZR060_SGR_BVEND_HI		0x03e
+#define ZR060_SGR_BVEND_LO		0x03f
+#define ZR060_SGR_BHEND_HI		0x040
+#define ZR060_SGR_BHEND_LO		0x041
+#define ZR060_AAR_VSTART_HI		0x042
+#define ZR060_AAR_VSTART_LO		0x043
+#define ZR060_AAR_VEND_HI		0x044
+#define ZR060_AAR_VEND_LO		0x045
+#define ZR060_AAR_HSTART_HI		0x046
+#define ZR060_AAR_HSTART_LO		0x047
+#define ZR060_AAR_HEND_HI		0x048
+#define ZR060_AAR_HEND_LO		0x049
+#define ZR060_SWR_VSTART_HI		0x04a
+#define ZR060_SWR_VSTART_LO		0x04b
+#define ZR060_SWR_VEND_HI		0x04c
+#define ZR060_SWR_VEND_LO		0x04d
+#define ZR060_SWR_HSTART_HI		0x04e
+#define ZR060_SWR_HSTART_LO		0x04f
+#define ZR060_SWR_HEND_HI		0x050
+#define ZR060_SWR_HEND_LO		0x051
+
+#define ZR060_SOF_IDX			0x060
+#define ZR060_SOS_IDX			0x07a
+#define ZR060_DRI_IDX			0x0c0
+#define ZR060_DQT_IDX			0x0cc
+#define ZR060_DHT_IDX			0x1d4
+#define ZR060_APP_IDX			0x380
+#define ZR060_COM_IDX			0x3c0
+
+/* ZR36060 LOAD register bits */
+
+#define ZR060_LOAD_Load			(1 << 7)
+#define ZR060_LOAD_SyncRst		(1 << 0)
+
+/* ZR36060 Code FIFO Status register bits */
+
+#define ZR060_CFSR_Busy			(1 << 7)
+#define ZR060_CFSR_CBusy		(1 << 2)
+#define ZR060_CFSR_CFIFO		(3 << 0)
+
+/* ZR36060 Code Interface register */
+
+#define ZR060_CIR_Code16		(1 << 7)
+#define ZR060_CIR_Endian		(1 << 6)
+#define ZR060_CIR_CFIS			(1 << 2)
+#define ZR060_CIR_CodeMstr		(1 << 0)
+
+/* ZR36060 Codec Mode register */
+
+#define ZR060_CMR_Comp			(1 << 7)
+#define ZR060_CMR_ATP			(1 << 6)
+#define ZR060_CMR_Pass2			(1 << 5)
+#define ZR060_CMR_TLM			(1 << 4)
+#define ZR060_CMR_BRB			(1 << 2)
+#define ZR060_CMR_FSF			(1 << 1)
+
+/* ZR36060 Markers Enable register */
+
+#define ZR060_MER_App			(1 << 7)
+#define ZR060_MER_Com			(1 << 6)
+#define ZR060_MER_DRI			(1 << 5)
+#define ZR060_MER_DQT			(1 << 4)
+#define ZR060_MER_DHT			(1 << 3)
+
+/* ZR36060 Interrupt Mask register */
+
+#define ZR060_IMR_EOAV			(1 << 3)
+#define ZR060_IMR_EOI			(1 << 2)
+#define ZR060_IMR_End			(1 << 1)
+#define ZR060_IMR_DataErr		(1 << 0)
+
+/* ZR36060 Interrupt Status register */
+
+#define ZR060_ISR_ProCnt		(3 << 6)
+#define ZR060_ISR_EOAV			(1 << 3)
+#define ZR060_ISR_EOI			(1 << 2)
+#define ZR060_ISR_End			(1 << 1)
+#define ZR060_ISR_DataErr		(1 << 0)
+
+/* ZR36060 Video Control register */
+
+#define ZR060_VCR_Video8		(1 << 7)
+#define ZR060_VCR_Range			(1 << 6)
+#define ZR060_VCR_FIDet			(1 << 3)
+#define ZR060_VCR_FIVedge		(1 << 2)
+#define ZR060_VCR_FIExt			(1 << 1)
+#define ZR060_VCR_SyncMstr		(1 << 0)
+
+/* ZR36060 Video Polarity register */
+
+#define ZR060_VPR_VCLKPol		(1 << 7)
+#define ZR060_VPR_PValPol		(1 << 6)
+#define ZR060_VPR_PoePol		(1 << 5)
+#define ZR060_VPR_SImgPol		(1 << 4)
+#define ZR060_VPR_BLPol			(1 << 3)
+#define ZR060_VPR_FIPol			(1 << 2)
+#define ZR060_VPR_HSPol			(1 << 1)
+#define ZR060_VPR_VSPol			(1 << 0)
+
+/* ZR36060 Scaling register */
+
+#define ZR060_SR_VScale			(1 << 2)
+#define ZR060_SR_HScale2		(1 << 0)
+#define ZR060_SR_HScale4		(2 << 0)
+
+#endif				/*fndef ZR36060_H */