aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/Kconfig54
-rw-r--r--drivers/media/Makefile5
-rw-r--r--drivers/media/common/Kconfig12
-rw-r--r--drivers/media/common/Makefile6
-rw-r--r--drivers/media/common/ir-common.c381
-rw-r--r--drivers/media/common/saa7146_core.c547
-rw-r--r--drivers/media/common/saa7146_fops.c564
-rw-r--r--drivers/media/common/saa7146_hlp.c1036
-rw-r--r--drivers/media/common/saa7146_i2c.c421
-rw-r--r--drivers/media/common/saa7146_vbi.c508
-rw-r--r--drivers/media/common/saa7146_video.c1509
-rw-r--r--drivers/media/common/saa7146_vv_ksyms.c12
-rw-r--r--drivers/media/dvb/Kconfig47
-rw-r--r--drivers/media/dvb/Makefile5
-rw-r--r--drivers/media/dvb/b2c2/Kconfig26
-rw-r--r--drivers/media/dvb/b2c2/Makefile6
-rw-r--r--drivers/media/dvb/b2c2/b2c2-common.c214
-rw-r--r--drivers/media/dvb/b2c2/b2c2-usb-core.c549
-rw-r--r--drivers/media/dvb/b2c2/skystar2.c2644
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig19
-rw-r--r--drivers/media/dvb/bt8xx/Makefile5
-rw-r--r--drivers/media/dvb/bt8xx/bt878.c588
-rw-r--r--drivers/media/dvb/bt8xx/bt878.h147
-rw-r--r--drivers/media/dvb/bt8xx/dst.c1089
-rw-r--r--drivers/media/dvb/bt8xx/dst.h40
-rw-r--r--drivers/media/dvb/bt8xx/dst_priv.h36
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c797
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.h59
-rw-r--r--drivers/media/dvb/cinergyT2/Kconfig85
-rw-r--r--drivers/media/dvb/cinergyT2/Makefile3
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c965
-rw-r--r--drivers/media/dvb/dibusb/Kconfig62
-rw-r--r--drivers/media/dvb/dibusb/Makefile11
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-core.c558
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-dvb.c185
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c582
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-firmware.c87
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-remote.c316
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-usb.c303
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb.h327
-rw-r--r--drivers/media/dvb/dibusb/dvb-fe-dtt200u.c263
-rw-r--r--drivers/media/dvb/dvb-core/Kconfig11
-rw-r--r--drivers/media/dvb/dvb-core/Makefile9
-rw-r--r--drivers/media/dvb/dvb-core/demux.h301
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c1137
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.h128
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.c1778
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.h134
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.c1294
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.h146
-rw-r--r--drivers/media/dvb/dvb-core/dvb_filter.c603
-rw-r--r--drivers/media/dvb/dvb-core/dvb_filter.h246
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c915
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h126
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c1381
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.h46
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c270
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.h173
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c449
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h104
-rw-r--r--drivers/media/dvb/frontends/Kconfig172
-rw-r--r--drivers/media/dvb/frontends/Makefile30
-rw-r--r--drivers/media/dvb/frontends/at76c651.c450
-rw-r--r--drivers/media/dvb/frontends/at76c651.h47
-rw-r--r--drivers/media/dvb/frontends/cx22700.c435
-rw-r--r--drivers/media/dvb/frontends/cx22700.h41
-rw-r--r--drivers/media/dvb/frontends/cx22702.c519
-rw-r--r--drivers/media/dvb/frontends/cx22702.h46
-rw-r--r--drivers/media/dvb/frontends/cx24110.c657
-rw-r--r--drivers/media/dvb/frontends/cx24110.h45
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.c83
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.h137
-rw-r--r--drivers/media/dvb/frontends/dib3000.h54
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c784
-rw-r--r--drivers/media/dvb/frontends/dib3000mb_priv.h467
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c931
-rw-r--r--drivers/media/dvb/frontends/dib3000mc_priv.h428
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c168
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h34
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.c279
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.h32
-rw-r--r--drivers/media/dvb/frontends/l64781.c602
-rw-r--r--drivers/media/dvb/frontends/l64781.h42
-rw-r--r--drivers/media/dvb/frontends/mt312.c729
-rw-r--r--drivers/media/dvb/frontends/mt312.h47
-rw-r--r--drivers/media/dvb/frontends/mt312_priv.h162
-rw-r--r--drivers/media/dvb/frontends/mt352.c610
-rw-r--r--drivers/media/dvb/frontends/mt352.h72
-rw-r--r--drivers/media/dvb/frontends/mt352_priv.h127
-rw-r--r--drivers/media/dvb/frontends/nxt2002.c705
-rw-r--r--drivers/media/dvb/frontends/nxt2002.h23
-rw-r--r--drivers/media/dvb/frontends/nxt6000.c554
-rw-r--r--drivers/media/dvb/frontends/nxt6000.h43
-rw-r--r--drivers/media/dvb/frontends/nxt6000_priv.h265
-rw-r--r--drivers/media/dvb/frontends/or51132.c628
-rw-r--r--drivers/media/dvb/frontends/or51132.h48
-rw-r--r--drivers/media/dvb/frontends/or51211.c631
-rw-r--r--drivers/media/dvb/frontends/or51211.h44
-rw-r--r--drivers/media/dvb/frontends/sp8870.c614
-rw-r--r--drivers/media/dvb/frontends/sp8870.h45
-rw-r--r--drivers/media/dvb/frontends/sp887x.c606
-rw-r--r--drivers/media/dvb/frontends/sp887x.h29
-rw-r--r--drivers/media/dvb/frontends/stv0297.c798
-rw-r--r--drivers/media/dvb/frontends/stv0297.h44
-rw-r--r--drivers/media/dvb/frontends/stv0299.c731
-rw-r--r--drivers/media/dvb/frontends/stv0299.h104
-rw-r--r--drivers/media/dvb/frontends/tda10021.c469
-rw-r--r--drivers/media/dvb/frontends/tda10021.h42
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c1206
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h56
-rw-r--r--drivers/media/dvb/frontends/tda8083.c456
-rw-r--r--drivers/media/dvb/frontends/tda8083.h45
-rw-r--r--drivers/media/dvb/frontends/tda80xx.c734
-rw-r--r--drivers/media/dvb/frontends/tda80xx.h51
-rw-r--r--drivers/media/dvb/frontends/ves1820.c450
-rw-r--r--drivers/media/dvb/frontends/ves1820.h51
-rw-r--r--drivers/media/dvb/frontends/ves1x93.c545
-rw-r--r--drivers/media/dvb/frontends/ves1x93.h50
-rw-r--r--drivers/media/dvb/ttpci/Kconfig134
-rw-r--r--drivers/media/dvb/ttpci/Makefile23
-rw-r--r--drivers/media/dvb/ttpci/av7110.c2739
-rw-r--r--drivers/media/dvb/ttpci/av7110.h284
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c1459
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.h29
-rw-r--r--drivers/media/dvb/ttpci/av7110_ca.c390
-rw-r--r--drivers/media/dvb/ttpci/av7110_ca.h14
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.c1170
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.h500
-rw-r--r--drivers/media/dvb/ttpci/av7110_ipack.c403
-rw-r--r--drivers/media/dvb/ttpci/av7110_ipack.h12
-rw-r--r--drivers/media/dvb/ttpci/av7110_ir.c212
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c771
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c1014
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c995
-rw-r--r--drivers/media/dvb/ttpci/budget-core.c480
-rw-r--r--drivers/media/dvb/ttpci/budget-patch.c754
-rw-r--r--drivers/media/dvb/ttpci/budget.c573
-rw-r--r--drivers/media/dvb/ttpci/budget.h110
-rw-r--r--drivers/media/dvb/ttpci/fdump.c44
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.c146
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.h33
-rw-r--r--drivers/media/dvb/ttusb-budget/Kconfig15
-rw-r--r--drivers/media/dvb/ttusb-budget/Makefile3
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c1610
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h1644
-rw-r--r--drivers/media/dvb/ttusb-dec/Kconfig21
-rw-r--r--drivers/media/dvb/ttusb-dec/Makefile3
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c1744
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusbdecfe.c255
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusbdecfe.h38
-rw-r--r--drivers/media/radio/Kconfig354
-rw-r--r--drivers/media/radio/Makefile22
-rw-r--r--drivers/media/radio/miropcm20-radio.c264
-rw-r--r--drivers/media/radio/miropcm20-rds-core.c210
-rw-r--r--drivers/media/radio/miropcm20-rds-core.h19
-rw-r--r--drivers/media/radio/miropcm20-rds.c133
-rw-r--r--drivers/media/radio/radio-aimslab.c368
-rw-r--r--drivers/media/radio/radio-aztech.c315
-rw-r--r--drivers/media/radio/radio-cadet.c620
-rw-r--r--drivers/media/radio/radio-gemtek-pci.c416
-rw-r--r--drivers/media/radio/radio-gemtek.c304
-rw-r--r--drivers/media/radio/radio-maestro.c332
-rw-r--r--drivers/media/radio/radio-maxiradio.c349
-rw-r--r--drivers/media/radio/radio-rtrack2.c266
-rw-r--r--drivers/media/radio/radio-sf16fmi.c328
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c434
-rw-r--r--drivers/media/radio/radio-terratec.c341
-rw-r--r--drivers/media/radio/radio-trust.c320
-rw-r--r--drivers/media/radio/radio-typhoon.c383
-rw-r--r--drivers/media/radio/radio-zoltrix.c385
-rw-r--r--drivers/media/video/Kconfig360
-rw-r--r--drivers/media/video/Makefile56
-rw-r--r--drivers/media/video/adv7170.c535
-rw-r--r--drivers/media/video/adv7175.c585
-rw-r--r--drivers/media/video/arv.c916
-rw-r--r--drivers/media/video/bt819.c660
-rw-r--r--drivers/media/video/bt832.c271
-rw-r--r--drivers/media/video/bt832.h305
-rw-r--r--drivers/media/video/bt848.h366
-rw-r--r--drivers/media/video/bt856.c442
-rw-r--r--drivers/media/video/btcx-risc.c262
-rw-r--r--drivers/media/video/btcx-risc.h35
-rw-r--r--drivers/media/video/bttv-cards.c4350
-rw-r--r--drivers/media/video/bttv-driver.c4116
-rw-r--r--drivers/media/video/bttv-gpio.c190
-rw-r--r--drivers/media/video/bttv-i2c.c461
-rw-r--r--drivers/media/video/bttv-if.c160
-rw-r--r--drivers/media/video/bttv-risc.c802
-rw-r--r--drivers/media/video/bttv-vbi.c235
-rw-r--r--drivers/media/video/bttv.h338
-rw-r--r--drivers/media/video/bttvp.h399
-rw-r--r--drivers/media/video/bw-qcam.c1027
-rw-r--r--drivers/media/video/bw-qcam.h68
-rw-r--r--drivers/media/video/c-qcam.c855
-rw-r--r--drivers/media/video/cpia.c4073
-rw-r--r--drivers/media/video/cpia.h430
-rw-r--r--drivers/media/video/cpia_pp.c886
-rw-r--r--drivers/media/video/cpia_usb.c662
-rw-r--r--drivers/media/video/cs8420.h50
-rw-r--r--drivers/media/video/cx88/Makefile11
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c911
-rw-r--r--drivers/media/video/cx88/cx88-cards.c938
-rw-r--r--drivers/media/video/cx88/cx88-core.c1239
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c381
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c213
-rw-r--r--drivers/media/video/cx88/cx88-input.c396
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c466
-rw-r--r--drivers/media/video/cx88/cx88-reg.h787
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c1032
-rw-r--r--drivers/media/video/cx88/cx88-vbi.c248
-rw-r--r--drivers/media/video/cx88/cx88-video.c2277
-rw-r--r--drivers/media/video/cx88/cx88.h551
-rw-r--r--drivers/media/video/dpc7146.c401
-rw-r--r--drivers/media/video/hexium_gemini.c556
-rw-r--r--drivers/media/video/hexium_orion.c522
-rw-r--r--drivers/media/video/ibmmpeg2.h94
-rw-r--r--drivers/media/video/ir-kbd-gpio.c444
-rw-r--r--drivers/media/video/ir-kbd-i2c.c492
-rw-r--r--drivers/media/video/meye.c2041
-rw-r--r--drivers/media/video/meye.h318
-rw-r--r--drivers/media/video/msp3400.c1876
-rw-r--r--drivers/media/video/msp3400.h36
-rw-r--r--drivers/media/video/mt20xx.c558
-rw-r--r--drivers/media/video/mxb.c1035
-rw-r--r--drivers/media/video/mxb.h42
-rw-r--r--drivers/media/video/ovcamchip/Makefile4
-rw-r--r--drivers/media/video/ovcamchip/ov6x20.c415
-rw-r--r--drivers/media/video/ovcamchip/ov6x30.c374
-rw-r--r--drivers/media/video/ovcamchip/ov76be.c303
-rw-r--r--drivers/media/video/ovcamchip/ov7x10.c335
-rw-r--r--drivers/media/video/ovcamchip/ov7x20.c455
-rw-r--r--drivers/media/video/ovcamchip/ovcamchip_core.c444
-rw-r--r--drivers/media/video/ovcamchip/ovcamchip_priv.h87
-rw-r--r--drivers/media/video/planb.c2303
-rw-r--r--drivers/media/video/planb.h231
-rw-r--r--drivers/media/video/pms.c1060
-rw-r--r--drivers/media/video/saa5246a.c843
-rw-r--r--drivers/media/video/saa5246a.h364
-rw-r--r--drivers/media/video/saa5249.c725
-rw-r--r--drivers/media/video/saa7110.c623
-rw-r--r--drivers/media/video/saa7111.c627
-rw-r--r--drivers/media/video/saa7114.c1241
-rw-r--r--drivers/media/video/saa7121.h132
-rw-r--r--drivers/media/video/saa7134/Makefile11
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c543
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c2018
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c1237
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c266
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c436
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c453
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c491
-rw-r--r--drivers/media/video/saa7134/saa7134-oss.c857
-rw-r--r--drivers/media/video/saa7134/saa7134-reg.h366
-rw-r--r--drivers/media/video/saa7134/saa7134-ts.c243
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c1031
-rw-r--r--drivers/media/video/saa7134/saa7134-vbi.c270
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2406
-rw-r--r--drivers/media/video/saa7134/saa7134.h618
-rw-r--r--drivers/media/video/saa7146.h115
-rw-r--r--drivers/media/video/saa7146reg.h283
-rw-r--r--drivers/media/video/saa7185.c524
-rw-r--r--drivers/media/video/saa7196.h117
-rw-r--r--drivers/media/video/stradis.c2258
-rw-r--r--drivers/media/video/tda7432.c556
-rw-r--r--drivers/media/video/tda8290.c224
-rw-r--r--drivers/media/video/tda9840.c254
-rw-r--r--drivers/media/video/tda9840.h35
-rw-r--r--drivers/media/video/tda9875.c423
-rw-r--r--drivers/media/video/tda9887.c801
-rw-r--r--drivers/media/video/tea6415c.c223
-rw-r--r--drivers/media/video/tea6415c.h39
-rw-r--r--drivers/media/video/tea6420.c200
-rw-r--r--drivers/media/video/tea6420.h17
-rw-r--r--drivers/media/video/tuner-3036.c220
-rw-r--r--drivers/media/video/tuner-core.c443
-rw-r--r--drivers/media/video/tuner-simple.c474
-rw-r--r--drivers/media/video/tvaudio.c1740
-rw-r--r--drivers/media/video/tvaudio.h14
-rw-r--r--drivers/media/video/tveeprom.c587
-rw-r--r--drivers/media/video/tvmixer.c367
-rw-r--r--drivers/media/video/v4l1-compat.c1036
-rw-r--r--drivers/media/video/v4l2-common.c282
-rw-r--r--drivers/media/video/video-buf-dvb.c251
-rw-r--r--drivers/media/video/video-buf.c1290
-rw-r--r--drivers/media/video/videocodec.c490
-rw-r--r--drivers/media/video/videocodec.h358
-rw-r--r--drivers/media/video/videodev.c436
-rw-r--r--drivers/media/video/vino.c347
-rw-r--r--drivers/media/video/vino.h131
-rw-r--r--drivers/media/video/vpx3220.c754
-rw-r--r--drivers/media/video/w9966.c984
-rw-r--r--drivers/media/video/zoran.h515
-rw-r--r--drivers/media/video/zoran_card.c1583
-rw-r--r--drivers/media/video/zoran_card.h45
-rw-r--r--drivers/media/video/zoran_device.c1785
-rw-r--r--drivers/media/video/zoran_device.h91
-rw-r--r--drivers/media/video/zoran_driver.c4699
-rw-r--r--drivers/media/video/zoran_procfs.c233
-rw-r--r--drivers/media/video/zoran_procfs.h36
-rw-r--r--drivers/media/video/zr36016.c532
-rw-r--r--drivers/media/video/zr36016.h111
-rw-r--r--drivers/media/video/zr36050.c907
-rw-r--r--drivers/media/video/zr36050.h184
-rw-r--r--drivers/media/video/zr36057.h168
-rw-r--r--drivers/media/video/zr36060.c1016
-rw-r--r--drivers/media/video/zr36060.h220
-rw-r--r--drivers/media/video/zr36120.c2073
-rw-r--r--drivers/media/video/zr36120.h279
-rw-r--r--drivers/media/video/zr36120_i2c.c132
-rw-r--r--drivers/media/video/zr36120_mem.c79
-rw-r--r--drivers/media/video/zr36120_mem.h3
311 files changed, 160942 insertions, 0 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
new file mode 100644
index 000000000000..c2602b340491
--- /dev/null
+++ b/drivers/media/Kconfig
@@ -0,0 +1,54 @@
+#
+# Multimedia device configuration
+#
+
+menu "Multimedia devices"
+
+config VIDEO_DEV
+ tristate "Video For Linux"
+ ---help---
+ Support for audio/video capture and overlay devices and FM radio
+ cards. The exact capabilities of each device vary. User tools for
+ this are available from
+ <ftp://ftp.uk.linux.org/pub/linux/video4linux/>.
+
+ This kernel includes support for the new Video for Linux Two API,
+ (V4L2) as well as the original system. Drivers and applications
+ need to be rewritten to use V4L2, but drivers for popular cards
+ and applications for most video capture functions already exist.
+
+ Documentation for the original API is included in the file
+ <file:Documentation/video4linux/API.html>. Documentation for V4L2 is
+ available on the web at <http://bytesex.org/v4l/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called videodev.
+
+source "drivers/media/video/Kconfig"
+
+source "drivers/media/radio/Kconfig"
+
+source "drivers/media/dvb/Kconfig"
+
+source "drivers/media/common/Kconfig"
+
+config VIDEO_TUNER
+ tristate
+
+config VIDEO_BUF
+ tristate
+
+config VIDEO_BUF_DVB
+ tristate
+
+config VIDEO_BTCX
+ tristate
+
+config VIDEO_IR
+ tristate
+
+config VIDEO_TVEEPROM
+ tristate
+
+endmenu
+
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
new file mode 100644
index 000000000000..772d6112fb3b
--- /dev/null
+++ b/drivers/media/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel multimedia device drivers.
+#
+
+obj-y := video/ radio/ dvb/ common/
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
new file mode 100644
index 000000000000..caebd0a1c021
--- /dev/null
+++ b/drivers/media/common/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_SAA7146
+ tristate
+ select I2C
+
+config VIDEO_SAA7146_VV
+ tristate
+ select VIDEO_BUF
+ select VIDEO_VIDEOBUF
+ select VIDEO_SAA7146
+
+config VIDEO_VIDEOBUF
+ tristate
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
new file mode 100644
index 000000000000..97b4341255ea
--- /dev/null
+++ b/drivers/media/common/Makefile
@@ -0,0 +1,6 @@
+saa7146-objs := saa7146_i2c.o saa7146_core.o
+saa7146_vv-objs := saa7146_vv_ksyms.o saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
+obj-$(CONFIG_VIDEO_IR) += ir-common.o
diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c
new file mode 100644
index 000000000000..8c842e2f59a2
--- /dev/null
+++ b/drivers/media/common/ir-common.c
@@ -0,0 +1,381 @@
+/*
+ * $Id: ir-common.c,v 1.8 2005/02/22 12:28:40 kraxel Exp $
+ *
+ * some common structs and functions to handle infrared remotes via
+ * input layer ...
+ *
+ * (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <media/ir-common.h>
+
+/* -------------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static int repeat = 1;
+module_param(repeat, int, 0444);
+MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)");
+
+static int debug = 0; /* debug level (0,1,2) */
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG fmt , ## arg)
+
+/* -------------------------------------------------------------------------- */
+
+/* generic RC5 keytable */
+/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */
+/* used by old (black) Hauppauge remotes */
+IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = {
+ [ 0x00 ] = KEY_KP0, // 0
+ [ 0x01 ] = KEY_KP1, // 1
+ [ 0x02 ] = KEY_KP2, // 2
+ [ 0x03 ] = KEY_KP3, // 3
+ [ 0x04 ] = KEY_KP4, // 4
+ [ 0x05 ] = KEY_KP5, // 5
+ [ 0x06 ] = KEY_KP6, // 6
+ [ 0x07 ] = KEY_KP7, // 7
+ [ 0x08 ] = KEY_KP8, // 8
+ [ 0x09 ] = KEY_KP9, // 9
+
+ [ 0x0b ] = KEY_CHANNEL, // channel / program (japan: 11)
+ [ 0x0c ] = KEY_POWER, // standby
+ [ 0x0d ] = KEY_MUTE, // mute / demute
+ [ 0x0f ] = KEY_TV, // display
+ [ 0x10 ] = KEY_VOLUMEUP, // volume +
+ [ 0x11 ] = KEY_VOLUMEDOWN, // volume -
+ [ 0x12 ] = KEY_BRIGHTNESSUP, // brightness +
+ [ 0x13 ] = KEY_BRIGHTNESSDOWN, // brightness -
+ [ 0x1e ] = KEY_SEARCH, // search +
+ [ 0x20 ] = KEY_CHANNELUP, // channel / program +
+ [ 0x21 ] = KEY_CHANNELDOWN, // channel / program -
+ [ 0x22 ] = KEY_CHANNEL, // alt / channel
+ [ 0x23 ] = KEY_LANGUAGE, // 1st / 2nd language
+ [ 0x26 ] = KEY_SLEEP, // sleeptimer
+ [ 0x2e ] = KEY_MENU, // 2nd controls (USA: menu)
+ [ 0x30 ] = KEY_PAUSE, // pause
+ [ 0x32 ] = KEY_REWIND, // rewind
+ [ 0x33 ] = KEY_GOTO, // go to
+ [ 0x35 ] = KEY_PLAY, // play
+ [ 0x36 ] = KEY_STOP, // stop
+ [ 0x37 ] = KEY_RECORD, // recording
+ [ 0x3c ] = KEY_TEXT, // teletext submode (Japan: 12)
+ [ 0x3d ] = KEY_SUSPEND, // system standby
+
+#if 0 /* FIXME */
+ [ 0x0a ] = KEY_RESERVED, // 1/2/3 digits (japan: 10)
+ [ 0x0e ] = KEY_RESERVED, // P.P. (personal preference)
+ [ 0x14 ] = KEY_RESERVED, // colour saturation +
+ [ 0x15 ] = KEY_RESERVED, // colour saturation -
+ [ 0x16 ] = KEY_RESERVED, // bass +
+ [ 0x17 ] = KEY_RESERVED, // bass -
+ [ 0x18 ] = KEY_RESERVED, // treble +
+ [ 0x19 ] = KEY_RESERVED, // treble -
+ [ 0x1a ] = KEY_RESERVED, // balance right
+ [ 0x1b ] = KEY_RESERVED, // balance left
+ [ 0x1c ] = KEY_RESERVED, // contrast +
+ [ 0x1d ] = KEY_RESERVED, // contrast -
+ [ 0x1f ] = KEY_RESERVED, // tint/hue +
+ [ 0x24 ] = KEY_RESERVED, // spacial stereo on/off
+ [ 0x25 ] = KEY_RESERVED, // mono / stereo (USA)
+ [ 0x27 ] = KEY_RESERVED, // tint / hue -
+ [ 0x28 ] = KEY_RESERVED, // RF switch/PIP select
+ [ 0x29 ] = KEY_RESERVED, // vote
+ [ 0x2a ] = KEY_RESERVED, // timed page/channel clck
+ [ 0x2b ] = KEY_RESERVED, // increment (USA)
+ [ 0x2c ] = KEY_RESERVED, // decrement (USA)
+ [ 0x2d ] = KEY_RESERVED, //
+ [ 0x2f ] = KEY_RESERVED, // PIP shift
+ [ 0x31 ] = KEY_RESERVED, // erase
+ [ 0x34 ] = KEY_RESERVED, // wind
+ [ 0x38 ] = KEY_RESERVED, // external 1
+ [ 0x39 ] = KEY_RESERVED, // external 2
+ [ 0x3a ] = KEY_RESERVED, // PIP display mode
+ [ 0x3b ] = KEY_RESERVED, // view data mode / advance
+ [ 0x3e ] = KEY_RESERVED, // crispener on/off
+ [ 0x3f ] = KEY_RESERVED, // system select
+#endif
+};
+EXPORT_SYMBOL_GPL(ir_codes_rc5_tv);
+
+/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
+IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
+ [ 5 ] = KEY_KP1,
+ [ 6 ] = KEY_KP2,
+ [ 7 ] = KEY_KP3,
+ [ 9 ] = KEY_KP4,
+ [ 10 ] = KEY_KP5,
+ [ 11 ] = KEY_KP6,
+ [ 13 ] = KEY_KP7,
+ [ 14 ] = KEY_KP8,
+ [ 15 ] = KEY_KP9,
+ [ 18 ] = KEY_KP0,
+
+ [ 0 ] = KEY_POWER,
+// [ 27 ] = MTS button
+ [ 2 ] = KEY_TUNER, // TV/FM
+ [ 30 ] = KEY_VIDEO,
+// [ 22 ] = display button
+ [ 4 ] = KEY_VOLUMEUP,
+ [ 8 ] = KEY_VOLUMEDOWN,
+ [ 12 ] = KEY_CHANNELUP,
+ [ 16 ] = KEY_CHANNELDOWN,
+ [ 3 ] = KEY_ZOOM, // fullscreen
+ [ 31 ] = KEY_SUBTITLE, // closed caption/teletext
+ [ 32 ] = KEY_SLEEP,
+// [ 41 ] = boss key
+ [ 20 ] = KEY_MUTE,
+ [ 43 ] = KEY_RED,
+ [ 44 ] = KEY_GREEN,
+ [ 45 ] = KEY_YELLOW,
+ [ 46 ] = KEY_BLUE,
+ [ 24 ] = KEY_KPPLUS, //fine tune +
+ [ 25 ] = KEY_KPMINUS, //fine tune -
+// [ 42 ] = picture in picture
+ [ 33 ] = KEY_KPDOT,
+ [ 19 ] = KEY_KPENTER,
+// [ 17 ] = recall
+ [ 34 ] = KEY_BACK,
+ [ 35 ] = KEY_PLAYPAUSE,
+ [ 36 ] = KEY_NEXT,
+// [ 37 ] = time shifting
+ [ 38 ] = KEY_STOP,
+ [ 39 ] = KEY_RECORD
+// [ 40 ] = snapshot
+};
+EXPORT_SYMBOL_GPL(ir_codes_winfast);
+
+/* empty keytable, can be used as placeholder for not-yet created keytables */
+IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = {
+ [ 42 ] = KEY_COFFEE,
+};
+EXPORT_SYMBOL_GPL(ir_codes_empty);
+
+/* Hauppauge: the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ * almost rc5 coding, but some non-standard keys */
+IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = {
+ [ 0x00 ] = KEY_KP0, // 0
+ [ 0x01 ] = KEY_KP1, // 1
+ [ 0x02 ] = KEY_KP2, // 2
+ [ 0x03 ] = KEY_KP3, // 3
+ [ 0x04 ] = KEY_KP4, // 4
+ [ 0x05 ] = KEY_KP5, // 5
+ [ 0x06 ] = KEY_KP6, // 6
+ [ 0x07 ] = KEY_KP7, // 7
+ [ 0x08 ] = KEY_KP8, // 8
+ [ 0x09 ] = KEY_KP9, // 9
+ [ 0x0b ] = KEY_RED, // red button
+ [ 0x0c ] = KEY_OPTION, // black key without text
+ [ 0x0d ] = KEY_MENU, // menu
+ [ 0x0f ] = KEY_MUTE, // mute
+ [ 0x10 ] = KEY_VOLUMEUP, // volume +
+ [ 0x11 ] = KEY_VOLUMEDOWN, // volume -
+ [ 0x1e ] = KEY_NEXT, // skip >|
+ [ 0x1f ] = KEY_EXIT, // back/exit
+ [ 0x20 ] = KEY_CHANNELUP, // channel / program +
+ [ 0x21 ] = KEY_CHANNELDOWN, // channel / program -
+ [ 0x22 ] = KEY_CHANNEL, // source (old black remote)
+ [ 0x24 ] = KEY_PREVIOUS, // replay |<
+ [ 0x25 ] = KEY_ENTER, // OK
+ [ 0x26 ] = KEY_SLEEP, // minimize (old black remote)
+ [ 0x29 ] = KEY_BLUE, // blue key
+ [ 0x2e ] = KEY_GREEN, // green button
+ [ 0x30 ] = KEY_PAUSE, // pause
+ [ 0x32 ] = KEY_REWIND, // backward <<
+ [ 0x34 ] = KEY_FASTFORWARD, // forward >>
+ [ 0x35 ] = KEY_PLAY, // play
+ [ 0x36 ] = KEY_STOP, // stop
+ [ 0x37 ] = KEY_RECORD, // recording
+ [ 0x38 ] = KEY_YELLOW, // yellow key
+ [ 0x3b ] = KEY_SELECT, // top right button
+ [ 0x3c ] = KEY_ZOOM, // full
+ [ 0x3d ] = KEY_POWER, // system power (green button)
+};
+EXPORT_SYMBOL(ir_codes_hauppauge_new);
+
+/* -------------------------------------------------------------------------- */
+
+static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
+{
+ if (KEY_RESERVED == ir->keycode) {
+ printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n",
+ dev->name,ir->ir_key,ir->ir_raw,ir->keypressed);
+ return;
+ }
+ dprintk(1,"%s: key event code=%d down=%d\n",
+ dev->name,ir->keycode,ir->keypressed);
+ input_report_key(dev,ir->keycode,ir->keypressed);
+ input_sync(dev);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
+ int ir_type, IR_KEYTAB_TYPE *ir_codes)
+{
+ int i;
+
+ ir->ir_type = ir_type;
+ if (ir_codes)
+ memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes));
+
+ init_input_dev(dev);
+ dev->keycode = ir->ir_codes;
+ dev->keycodesize = sizeof(IR_KEYTAB_TYPE);
+ dev->keycodemax = IR_KEYTAB_SIZE;
+ for (i = 0; i < IR_KEYTAB_SIZE; i++)
+ set_bit(ir->ir_codes[i], dev->keybit);
+ clear_bit(0, dev->keybit);
+
+ set_bit(EV_KEY, dev->evbit);
+ if (repeat)
+ set_bit(EV_REP, dev->evbit);
+}
+
+void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
+{
+ if (ir->keypressed) {
+ ir->keypressed = 0;
+ ir_input_key_event(dev,ir);
+ }
+}
+
+void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
+ u32 ir_key, u32 ir_raw)
+{
+ u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key);
+
+ if (ir->keypressed && ir->keycode != keycode) {
+ ir->keypressed = 0;
+ ir_input_key_event(dev,ir);
+ }
+ if (!ir->keypressed) {
+ ir->ir_key = ir_key;
+ ir->ir_raw = ir_raw;
+ ir->keycode = keycode;
+ ir->keypressed = 1;
+ ir_input_key_event(dev,ir);
+ }
+#if 0
+ /* maybe do something like this ??? */
+ input_event(a, EV_IR, ir->ir_type, ir->ir_raw);
+#endif
+}
+
+/* -------------------------------------------------------------------------- */
+
+u32 ir_extract_bits(u32 data, u32 mask)
+{
+ int mbit, vbit;
+ u32 value;
+
+ value = 0;
+ vbit = 0;
+ for (mbit = 0; mbit < 32; mbit++) {
+ if (!(mask & ((u32)1 << mbit)))
+ continue;
+ if (data & ((u32)1 << mbit))
+ value |= (1 << vbit);
+ vbit++;
+ }
+ return value;
+}
+
+static int inline getbit(u32 *samples, int bit)
+{
+ return (samples[bit/32] & (1 << (31-(bit%32)))) ? 1 : 0;
+}
+
+/* sump raw samples for visual debugging ;) */
+int ir_dump_samples(u32 *samples, int count)
+{
+ int i, bit, start;
+
+ printk(KERN_DEBUG "ir samples: ");
+ start = 0;
+ for (i = 0; i < count * 32; i++) {
+ bit = getbit(samples,i);
+ if (bit)
+ start = 1;
+ if (0 == start)
+ continue;
+ printk("%s", bit ? "#" : "_");
+ }
+ printk("\n");
+ return 0;
+}
+
+/* decode raw samples, biphase coding, used by rc5 for example */
+int ir_decode_biphase(u32 *samples, int count, int low, int high)
+{
+ int i,last,bit,len,flips;
+ u32 value;
+
+ /* find start bit (1) */
+ for (i = 0; i < 32; i++) {
+ bit = getbit(samples,i);
+ if (bit)
+ break;
+ }
+
+ /* go decoding */
+ len = 0;
+ flips = 0;
+ value = 1;
+ for (; i < count * 32; i++) {
+ if (len > high)
+ break;
+ if (flips > 1)
+ break;
+ last = bit;
+ bit = getbit(samples,i);
+ if (last == bit) {
+ len++;
+ continue;
+ }
+ if (len < low) {
+ len++;
+ flips++;
+ continue;
+ }
+ value <<= 1;
+ value |= bit;
+ flips = 0;
+ len = 1;
+ }
+ return value;
+}
+
+EXPORT_SYMBOL_GPL(ir_input_init);
+EXPORT_SYMBOL_GPL(ir_input_nokey);
+EXPORT_SYMBOL_GPL(ir_input_keydown);
+
+EXPORT_SYMBOL_GPL(ir_extract_bits);
+EXPORT_SYMBOL_GPL(ir_dump_samples);
+EXPORT_SYMBOL_GPL(ir_decode_biphase);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
new file mode 100644
index 000000000000..9f6c19ac1285
--- /dev/null
+++ b/drivers/media/common/saa7146_core.c
@@ -0,0 +1,547 @@
+/*
+ saa7146.o - driver for generic saa7146-based hardware
+
+ Copyright (C) 1998-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.
+*/
+
+#include <media/saa7146.h>
+
+LIST_HEAD(saa7146_devices);
+DECLARE_MUTEX(saa7146_devices_lock);
+
+static int saa7146_num = 0;
+
+unsigned int saa7146_debug = 0;
+
+module_param(saa7146_debug, int, 0644);
+MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
+
+#if 0
+static void dump_registers(struct saa7146_dev* dev)
+{
+ int i = 0;
+
+ INFO((" @ %li jiffies:\n",jiffies));
+ for(i = 0; i <= 0x148; i+=4) {
+ printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
+ }
+}
+#endif
+
+/****************************************************************************
+ * gpio and debi helper functions
+ ****************************************************************************/
+
+void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
+{
+ u32 value = 0;
+
+ BUG_ON(port > 3);
+
+ value = saa7146_read(dev, GPIO_CTRL);
+ value &= ~(0xff << (8*port));
+ value |= (data << (8*port));
+ saa7146_write(dev, GPIO_CTRL, value);
+}
+
+/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
+int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
+{
+ unsigned long start;
+
+ /* wait for registers to be programmed */
+ start = jiffies;
+ while (1) {
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (time_after(jiffies, start + HZ/20)) {
+ DEB_S(("timed out while waiting for registers getting programmed\n"));
+ return -ETIMEDOUT;
+ }
+ if (nobusyloop)
+ msleep(1);
+ }
+
+ /* wait for transfer to complete */
+ start = jiffies;
+ while (1) {
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (time_after(jiffies, start + HZ/4)) {
+ DEB_S(("timed out while waiting for transfer completion\n"));
+ return -ETIMEDOUT;
+ }
+ if (nobusyloop)
+ msleep(1);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * general helper functions
+ ****************************************************************************/
+
+/* this is videobuf_vmalloc_to_sg() from video-buf.c
+ make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
+ may be triggered on highmem machines */
+static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
+{
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+
+ sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL);
+ if (NULL == sglist)
+ return NULL;
+ memset(sglist,0,sizeof(struct scatterlist)*nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+ pg = vmalloc_to_page(virt);
+ if (NULL == pg)
+ goto err;
+ if (PageHighMem(pg))
+ BUG();
+ sglist[i].page = pg;
+ sglist[i].length = PAGE_SIZE;
+ }
+ return sglist;
+
+ err:
+ kfree(sglist);
+ return NULL;
+}
+
+/********************************************************************************/
+/* common page table functions */
+
+char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
+{
+ int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
+ char *mem = vmalloc_32(length);
+ int slen = 0;
+
+ if (NULL == mem) {
+ return NULL;
+ }
+
+ if (!(pt->slist = vmalloc_to_sg(mem, pages))) {
+ vfree(mem);
+ return NULL;
+ }
+
+ if (saa7146_pgtable_alloc(pci, pt)) {
+ kfree(pt->slist);
+ pt->slist = NULL;
+ vfree(mem);
+ return NULL;
+ }
+
+ slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE);
+ if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) {
+ return NULL;
+ }
+
+ return mem;
+}
+
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+ if (NULL != pt->slist) {
+ kfree(pt->slist);
+ pt->slist = NULL;
+ }
+}
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
+ if (NULL == cpu) {
+ return -ENOMEM;
+ }
+ pt->size = PAGE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+
+ return 0;
+}
+
+int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
+ struct scatterlist *list, int sglen )
+{
+ u32 *ptr, fill;
+ int nr_pages = 0;
+ int i,p;
+
+ BUG_ON(0 == sglen);
+ BUG_ON(list->offset > PAGE_SIZE);
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt->offset = list->offset;
+
+ ptr = pt->cpu;
+ for (i = 0; i < sglen; i++, list++) {
+/*
+ printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
+*/
+ for (p = 0; p * 4096 < list->length; p++, ptr++) {
+ *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
+ nr_pages++;
+ }
+ }
+
+
+ /* safety; fill the page table up with the last valid page */
+ fill = *(ptr-1);
+ for(i=nr_pages;i<1024;i++) {
+ *ptr++ = fill;
+ }
+
+/*
+ ptr = pt->cpu;
+ printk("offset: %d\n",pt->offset);
+ for(i=0;i<5;i++) {
+ printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
+ }
+*/
+ return 0;
+}
+
+/********************************************************************************/
+/* interrupt handler */
+static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct saa7146_dev *dev = dev_id;
+ u32 isr = 0;
+
+ /* read out the interrupt status register */
+ isr = saa7146_read(dev, ISR);
+
+ /* is this our interrupt? */
+ if ( 0 == isr ) {
+ /* nope, some other device */
+ return IRQ_NONE;
+ }
+
+ saa7146_write(dev, ISR, isr);
+
+ if( 0 != (dev->ext)) {
+ if( 0 != (dev->ext->irq_mask & isr )) {
+ if( 0 != dev->ext->irq_func ) {
+ dev->ext->irq_func(dev, &isr);
+ }
+ isr &= ~dev->ext->irq_mask;
+ }
+ }
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ if( 0 != dev->vv_data && 0 != dev->vv_callback) {
+ dev->vv_callback(dev,isr);
+ }
+ isr &= ~MASK_27;
+ }
+ if (0 != (isr & (MASK_28))) {
+ if( 0 != dev->vv_data && 0 != dev->vv_callback) {
+ dev->vv_callback(dev,isr);
+ }
+ isr &= ~MASK_28;
+ }
+ if (0 != (isr & (MASK_16|MASK_17))) {
+ u32 status = saa7146_read(dev, I2C_STATUS);
+ if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ /* only wake up if we expect something */
+ if( 0 != dev->i2c_op ) {
+ u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2;
+ u32 ssr = (saa7146_read(dev, SSR) >> 17) & 0x1f;
+ DEB_I2C(("irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).\n",status,psr,ssr));
+ dev->i2c_op = 0;
+ wake_up(&dev->i2c_wq);
+ } else {
+ DEB_I2C(("unexpected irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
+ }
+ } else {
+ DEB_I2C(("unhandled irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
+ }
+ isr &= ~(MASK_16|MASK_17);
+ }
+ if( 0 != isr ) {
+ ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
+ ERR(("disabling interrupt source(s)!\n"));
+ SAA7146_IER_DISABLE(dev,isr);
+ }
+ return IRQ_HANDLED;
+}
+
+/*********************************************************************************/
+/* configuration-functions */
+
+static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
+{
+ struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
+ struct saa7146_extension *ext = pci_ext->ext;
+ struct saa7146_dev *dev;
+ int err = -ENOMEM;
+
+ dev = kmalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
+ if (!dev) {
+ ERR(("out of memory.\n"));
+ goto out;
+ }
+
+ /* clear out mem for sure */
+ memset(dev, 0x0, sizeof(struct saa7146_dev));
+
+ DEB_EE(("pci:%p\n",pci));
+
+ err = pci_enable_device(pci);
+ if (err < 0) {
+ ERR(("pci_enable_device() failed.\n"));
+ goto err_free;
+ }
+
+ /* enable bus-mastering */
+ pci_set_master(pci);
+
+ dev->pci = pci;
+
+ /* get chip-revision; this is needed to enable bug-fixes */
+ err = pci_read_config_dword(pci, PCI_CLASS_REVISION, &dev->revision);
+ if (err < 0) {
+ ERR(("pci_read_config_dword() failed.\n"));
+ goto err_disable;
+ }
+ dev->revision &= 0xf;
+
+ /* remap the memory from virtual to physical adress */
+
+ err = pci_request_region(pci, 0, "saa7146");
+ if (err < 0)
+ goto err_disable;
+
+ dev->mem = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!dev->mem) {
+ ERR(("ioremap() failed.\n"));
+ err = -ENODEV;
+ goto err_release;
+ }
+
+ /* we don't do a master reset here anymore, it screws up
+ some boards that don't have an i2c-eeprom for configuration
+ values */
+/*
+ saa7146_write(dev, MC1, MASK_31);
+*/
+
+ /* disable all irqs */
+ saa7146_write(dev, IER, 0);
+
+ /* shut down all dma transfers and rps tasks */
+ saa7146_write(dev, MC1, 0x30ff0000);
+
+ /* clear out any rps-signals pending */
+ saa7146_write(dev, MC2, 0xf8000000);
+
+ /* request an interrupt for the saa7146 */
+ err = request_irq(pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT,
+ dev->name, dev);
+ if (err < 0) {
+ ERR(("request_irq() failed.\n"));
+ goto err_unmap;
+ }
+
+ err = -ENOMEM;
+
+ /* get memory for various stuff */
+ dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps0.dma_handle);
+ if (!dev->d_rps0.cpu_addr)
+ goto err_free_irq;
+ memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps1.dma_handle);
+ if (!dev->d_rps1.cpu_addr)
+ goto err_free_rps0;
+ memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_i2c.dma_handle);
+ if (!dev->d_i2c.cpu_addr)
+ goto err_free_rps1;
+ memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ /* the rest + print status message */
+
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
+ INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device));
+ dev->ext = ext;
+
+ pci_set_drvdata(pci, dev);
+
+ init_MUTEX(&dev->lock);
+ spin_lock_init(&dev->int_slock);
+ spin_lock_init(&dev->slock);
+
+ init_MUTEX(&dev->i2c_lock);
+
+ dev->module = THIS_MODULE;
+ init_waitqueue_head(&dev->i2c_wq);
+
+ /* set some sane pci arbitrition values */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ /* TODO: use the status code of the callback */
+
+ err = -ENODEV;
+
+ if (ext->probe && ext->probe(dev)) {
+ DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
+ goto err_free_i2c;
+ }
+
+ if (ext->attach(dev, pci_ext)) {
+ DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
+ goto err_unprobe;
+ }
+
+ INIT_LIST_HEAD(&dev->item);
+ list_add_tail(&dev->item,&saa7146_devices);
+ saa7146_num++;
+
+ err = 0;
+out:
+ return err;
+
+err_unprobe:
+ pci_set_drvdata(pci, NULL);
+err_free_i2c:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
+err_free_rps1:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
+ dev->d_rps1.dma_handle);
+err_free_rps0:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
+ dev->d_rps0.dma_handle);
+err_free_irq:
+ free_irq(pci->irq, (void *)dev);
+err_unmap:
+ iounmap(dev->mem);
+err_release:
+ pci_release_region(pci, 0);
+err_disable:
+ pci_disable_device(pci);
+err_free:
+ kfree(dev);
+ goto out;
+}
+
+static void saa7146_remove_one(struct pci_dev *pdev)
+{
+ struct saa7146_dev* dev = pci_get_drvdata(pdev);
+ struct {
+ void *addr;
+ dma_addr_t dma;
+ } dev_map[] = {
+ { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
+ { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
+ { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
+ { NULL, 0 }
+ }, *p;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ dev->ext->detach(dev);
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* disable all irqs, release irq-routine */
+ saa7146_write(dev, IER, 0);
+
+ free_irq(pdev->irq, dev);
+
+ for (p = dev_map; p->addr; p++)
+ pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
+
+ iounmap(dev->mem);
+ pci_release_region(pdev, 0);
+ list_del(&dev->item);
+ pci_disable_device(pdev);
+ kfree(dev);
+
+ saa7146_num--;
+}
+
+/*********************************************************************************/
+/* extension handling functions */
+
+int saa7146_register_extension(struct saa7146_extension* ext)
+{
+ DEB_EE(("ext:%p\n",ext));
+
+ ext->driver.name = ext->name;
+ ext->driver.id_table = ext->pci_tbl;
+ ext->driver.probe = saa7146_init_one;
+ ext->driver.remove = saa7146_remove_one;
+
+ printk("saa7146: register extension '%s'.\n",ext->name);
+ return pci_module_init(&ext->driver);
+}
+
+int saa7146_unregister_extension(struct saa7146_extension* ext)
+{
+ DEB_EE(("ext:%p\n",ext));
+ printk("saa7146: unregister extension '%s'.\n",ext->name);
+ pci_unregister_driver(&ext->driver);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(saa7146_register_extension);
+EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
+
+/* misc functions used by extension modules */
+EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
+EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
+
+EXPORT_SYMBOL_GPL(saa7146_setgpio);
+
+EXPORT_SYMBOL_GPL(saa7146_i2c_transfer);
+EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
+
+EXPORT_SYMBOL_GPL(saa7146_debug);
+EXPORT_SYMBOL_GPL(saa7146_devices);
+EXPORT_SYMBOL_GPL(saa7146_devices_lock);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
new file mode 100644
index 000000000000..cb826c9adfe7
--- /dev/null
+++ b/drivers/media/common/saa7146_fops.c
@@ -0,0 +1,564 @@
+#include <media/saa7146_vv.h>
+#include <linux/version.h>
+
+#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1)
+
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (fh->resources & bit) {
+ DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ /* have it already allocated */
+ return 1;
+ }
+
+ /* is it free? */
+ down(&dev->lock);
+ if (vv->resources & bit) {
+ DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
+ /* no, someone else uses it */
+ up(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ vv->resources |= bit;
+ DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
+ up(&dev->lock);
+ return 1;
+}
+
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if ((fh->resources & bits) != bits)
+ BUG();
+
+ down(&dev->lock);
+ fh->resources &= ~bits;
+ vv->resources &= ~bits;
+ DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
+ up(&dev->lock);
+}
+
+
+/********************************************************************************/
+/* common dma functions */
+
+void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf)
+{
+ DEB_EE(("dev:%p, buf:%p\n",dev,buf));
+
+ if (in_interrupt())
+ BUG();
+
+ videobuf_waiton(&buf->vb,0,0);
+ videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma);
+ videobuf_dma_free(&buf->vb.dma);
+ buf->vb.state = STATE_NEEDS_INIT;
+}
+
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
+
+ BUG_ON(!q);
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D(("immediately activating buffer %p\n", buf));
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = STATE_QUEUED;
+ DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
+ DEB_EE(("q->curr:%p\n",q->curr));
+
+ BUG_ON(!q->curr);
+
+ /* finish current buffer */
+ if (NULL == q->curr) {
+ DEB_D(("aiii. no current buffer\n"));
+ return;
+ }
+
+ q->curr->vb.state = state;
+ do_gettimeofday(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+
+ q->curr = NULL;
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ BUG_ON(!q);
+
+ DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
+
+ assert_spin_locked(&dev->slock);
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
+ q->curr = buf;
+ DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_INT(("no next buffer. stopping.\n"));
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ saa7146_write(dev, MC2, (MASK_02|MASK_18));
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+*/
+ }
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(unsigned long data)
+{
+ struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D(("timeout on %p\n", q->curr));
+ saa7146_buffer_finish(dev,q,STATE_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+/*
+ saa7146_buffer_next(dev,q);
+*/
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/********************************************************************************/
+/* file operations */
+
+static int fops_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = iminor(inode);
+ struct saa7146_dev *h = NULL, *dev = NULL;
+ struct list_head *list;
+ struct saa7146_fh *fh = NULL;
+ int result = 0;
+
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ DEB_EE(("inode:%p, file:%p, minor:%d\n",inode,file,minor));
+
+ if (down_interruptible(&saa7146_devices_lock))
+ return -ERESTARTSYS;
+
+ list_for_each(list,&saa7146_devices) {
+ h = list_entry(list, struct saa7146_dev, item);
+ if( NULL == h->vv_data ) {
+ DEB_D(("device %p has not registered video devices.\n",h));
+ continue;
+ }
+ DEB_D(("trying: %p @ major %d,%d\n",h,h->vv_data->video_minor,h->vv_data->vbi_minor));
+
+ if (h->vv_data->video_minor == minor) {
+ dev = h;
+ }
+ if (h->vv_data->vbi_minor == minor) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ dev = h;
+ }
+ }
+ if (NULL == dev) {
+ DEB_S(("no such video device.\n"));
+ result = -ENODEV;
+ goto out;
+ }
+
+ DEB_D(("using: %p\n",dev));
+
+ /* check if an extension is registered */
+ if( NULL == dev->ext ) {
+ DEB_S(("no extension registered for this device.\n"));
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* allocate per open data */
+ fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ DEB_S(("cannot allocate memory for per open data.\n"));
+ result = -ENOMEM;
+ goto out;
+ }
+ memset(fh,0,sizeof(*fh));
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+
+ if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ DEB_S(("initializing vbi...\n"));
+ result = saa7146_vbi_uops.open(dev,file);
+ } else {
+ DEB_S(("initializing video...\n"));
+ result = saa7146_video_uops.open(dev,file);
+ }
+
+ if (0 != result) {
+ goto out;
+ }
+
+ if( 0 == try_module_get(dev->ext->module)) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ result = 0;
+out:
+ if( fh != 0 && result != 0 ) {
+ kfree(fh);
+ file->private_data = NULL;
+ }
+ up(&saa7146_devices_lock);
+ return result;
+}
+
+static int fops_release(struct inode *inode, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_EE(("inode:%p, file:%p\n",inode,file));
+
+ if (down_interruptible(&saa7146_devices_lock))
+ return -ERESTARTSYS;
+
+ if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ saa7146_vbi_uops.release(dev,file);
+ } else {
+ saa7146_video_uops.release(dev,file);
+ }
+
+ module_put(dev->ext->module);
+ file->private_data = NULL;
+ kfree(fh);
+
+ up(&saa7146_devices_lock);
+
+ return 0;
+}
+
+int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg);
+static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+/*
+ DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
+*/
+ return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl);
+}
+
+static int fops_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_queue *q;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
+ q = &fh->video_q;
+ break;
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ return 0;
+ }
+ return videobuf_mmap_mapper(q,vma);
+}
+
+static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ struct videobuf_queue *q;
+
+ DEB_EE(("file:%p, poll:%p\n",file, wait));
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if( 0 == fh->vbi_q.streaming )
+ return videobuf_poll_stream(file, &fh->vbi_q, wait);
+ q = &fh->vbi_q;
+ } else {
+ DEB_D(("using video queue.\n"));
+ q = &fh->video_q;
+ }
+
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+
+ if (!buf) {
+ DEB_D(("buf == NULL!\n"));
+ return POLLERR;
+ }
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
+ DEB_D(("poll succeeded!\n"));
+ return POLLIN|POLLRDNORM;
+ }
+
+ DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
+ return 0;
+}
+
+static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
+ return saa7146_video_uops.read(file,data,count,ppos);
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
+ return saa7146_vbi_uops.read(file,data,count,ppos);
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static struct file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .poll = fops_poll,
+ .mmap = fops_mmap,
+ .ioctl = fops_ioctl,
+ .llseek = no_llseek,
+};
+
+void vv_callback(struct saa7146_dev *dev, unsigned long status)
+{
+ u32 isr = status;
+
+ DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
+ saa7146_video_uops.irq_done(dev,isr);
+ }
+
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
+ wake_up(&dev->vv_data->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+}
+
+static struct video_device device_template =
+{
+ .hardware = VID_HARDWARE_SAA7146,
+ .fops = &video_fops,
+ .minor = -1,
+};
+
+int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+{
+ struct saa7146_vv *vv = kmalloc (sizeof(struct saa7146_vv),GFP_KERNEL);
+ if( NULL == vv ) {
+ ERR(("out of memory. aborting.\n"));
+ return -1;
+ }
+ memset(vv, 0x0, sizeof(*vv));
+
+ DEB_EE(("dev:%p\n",dev));
+
+ /* set default values for video parts of the saa7146 */
+ saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+ /* enable video-port pins */
+ saa7146_write(dev, MC1, (MASK_10 | MASK_26));
+
+ /* save per-device extension data (one extension can
+ handle different devices that might need different
+ configuration data) */
+ dev->ext_vv_data = ext_vv;
+
+ vv->video_minor = -1;
+ vv->vbi_minor = -1;
+
+ vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
+ if( NULL == vv->d_clipping.cpu_addr ) {
+ ERR(("out of memory. aborting.\n"));
+ kfree(vv);
+ return -1;
+ }
+ memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
+
+ saa7146_video_uops.init(dev,vv);
+ saa7146_vbi_uops.init(dev,vv);
+
+ dev->vv_data = vv;
+ dev->vv_callback = &vv_callback;
+
+ return 0;
+}
+
+int saa7146_vv_release(struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ pci_free_consistent(dev->pci, SAA7146_RPS_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ kfree(vv);
+ dev->vv_data = NULL;
+ dev->vv_callback = NULL;
+
+ return 0;
+}
+
+int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
+ char *name, int type)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct video_device *vfd;
+
+ DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
+
+ // released by vfd->release
+ vfd = video_device_alloc();
+ if (vfd == NULL)
+ return -ENOMEM;
+
+ memcpy(vfd, &device_template, sizeof(struct video_device));
+ strlcpy(vfd->name, name, sizeof(vfd->name));
+ vfd->release = video_device_release;
+ vfd->priv = dev;
+
+ // fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
+ if (video_register_device(vfd, type, -1) < 0) {
+ ERR(("cannot register v4l2 device. skipping.\n"));
+ return -1;
+ }
+
+ if( VFL_TYPE_GRABBER == type ) {
+ vv->video_minor = vfd->minor;
+ INFO(("%s: registered device video%d [v4l2]\n",
+ dev->name, vfd->minor & 0x1f));
+ } else {
+ vv->vbi_minor = vfd->minor;
+ INFO(("%s: registered device vbi%d [v4l2]\n",
+ dev->name, vfd->minor & 0x1f));
+ }
+
+ *vid = vfd;
+ return 0;
+}
+
+int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ if( VFL_TYPE_GRABBER == (*vid)->type ) {
+ vv->video_minor = -1;
+ } else {
+ vv->vbi_minor = -1;
+ }
+
+ video_unregister_device(*vid);
+ *vid = NULL;
+
+ return 0;
+}
+
+static int __init saa7146_vv_init_module(void)
+{
+ return 0;
+}
+
+
+static void __exit saa7146_vv_cleanup_module(void)
+{
+}
+
+module_init(saa7146_vv_init_module);
+module_exit(saa7146_vv_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
new file mode 100644
index 000000000000..ec52dff8cb69
--- /dev/null
+++ b/drivers/media/common/saa7146_hlp.c
@@ -0,0 +1,1036 @@
+#include <linux/kernel.h>
+#include <media/saa7146_vv.h>
+
+static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
+{
+ /* clear out the necessary bits */
+ *clip_format &= 0x0000ffff;
+ /* set these bits new */
+ *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
+}
+
+static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
+{
+ *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
+ *hps_ctrl |= (source << 30) | (sync << 28);
+}
+
+static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
+{
+ int hyo = 0, hxo = 0;
+
+ hyo = vv->standard->v_offset;
+ hxo = vv->standard->h_offset;
+
+ *hps_h_scale &= ~(MASK_B0 | 0xf00);
+ *hps_h_scale |= (hxo << 0);
+
+ *hps_ctrl &= ~(MASK_W0 | MASK_B2);
+ *hps_ctrl |= (hyo << 12);
+}
+
+/* helper functions for the calculation of the horizontal- and vertical
+ scaling registers, clip-format-register etc ...
+ these functions take pointers to the (most-likely read-out
+ original-values) and manipulate them according to the requested
+ changes.
+*/
+
+/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_h_coeff_tab [] = {
+ {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
+ {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
+ {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
+ {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
+ {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
+ {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
+ {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
+ {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
+ {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
+ {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
+};
+
+/* table of attenuation values for horizontal scaling */
+static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
+
+/* calculate horizontal scale registers */
+static int calculate_h_scale_registers(struct saa7146_dev *dev,
+ int in_x, int out_x, int flip_lr,
+ u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
+{
+ /* horizontal prescaler */
+ u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
+ /* horizontal scaler */
+ u32 xim = 0, xp = 0, xsci =0;
+ /* vertical scale & gain */
+ u32 pfuv = 0;
+
+ /* helper variables */
+ u32 h_atten = 0, i = 0;
+
+ if ( 0 == out_x ) {
+ return -EINVAL;
+ }
+
+ /* mask out vanity-bit */
+ *hps_ctrl &= ~MASK_29;
+
+ /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
+ [1/2 .. 1/3) : 2
+ [1/3 .. 1/4) : 3
+ ... */
+ if (in_x > out_x) {
+ xpsc = in_x / out_x;
+ }
+ else {
+ /* zooming */
+ xpsc = 1;
+ }
+
+ /* if flip_lr-bit is set, number of pixels after
+ horizontal prescaling must be < 384 */
+ if ( 0 != flip_lr ) {
+
+ /* set vanity bit */
+ *hps_ctrl |= MASK_29;
+
+ while (in_x / xpsc >= 384 )
+ xpsc++;
+ }
+ /* if zooming is wanted, number of pixels after
+ horizontal prescaling must be < 768 */
+ else {
+ while ( in_x / xpsc >= 768 )
+ xpsc++;
+ }
+
+ /* maximum prescale is 64 (p.69) */
+ if ( xpsc > 64 )
+ xpsc = 64;
+
+ /* keep xacm clear*/
+ xacm = 0;
+
+ /* set horizontal filter parameters (CXY = CXUV) */
+ cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
+ cxuv = cxy;
+
+ /* calculate and set horizontal fine scale (xsci) */
+
+ /* bypass the horizontal scaler ? */
+ if ( (in_x == out_x) && ( 1 == xpsc ) )
+ xsci = 0x400;
+ else
+ xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
+
+ /* set start phase for horizontal fine scale (xp) to 0 */
+ xp = 0;
+
+ /* set xim, if we bypass the horizontal scaler */
+ if ( 0x400 == xsci )
+ xim = 1;
+ else
+ xim = 0;
+
+ /* if the prescaler is bypassed, enable horizontal
+ accumulation mode (xacm) and clear dcgx */
+ if( 1 == xpsc ) {
+ xacm = 1;
+ dcgx = 0;
+ } else {
+ xacm = 0;
+ /* get best match in the table of attenuations
+ for horizontal scaling */
+ h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
+
+ for (i = 0; h_attenuation[i] != 0; i++) {
+ if (h_attenuation[i] >= h_atten)
+ break;
+ }
+
+ dcgx = i;
+ }
+
+ /* the horizontal scaling increment controls the UV filter
+ to reduce the bandwith to improve the display quality,
+ so set it ... */
+ if ( xsci == 0x400)
+ pfuv = 0x00;
+ else if ( xsci < 0x600)
+ pfuv = 0x01;
+ else if ( xsci < 0x680)
+ pfuv = 0x11;
+ else if ( xsci < 0x700)
+ pfuv = 0x22;
+ else
+ pfuv = 0x33;
+
+
+ *hps_v_gain &= MASK_W0|MASK_B2;
+ *hps_v_gain |= (pfuv << 24);
+
+ *hps_h_scale &= ~(MASK_W1 | 0xf000);
+ *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
+
+ *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
+
+ return 0;
+}
+
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_v_coeff_tab [] = {
+ {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
+ {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
+ {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
+ {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
+ {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
+ {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
+ {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
+ {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
+ {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
+ {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
+};
+
+/* table of attenuation values for vertical scaling */
+static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
+
+/* calculate vertical scale registers */
+static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
+ int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
+{
+ int lpi = 0;
+
+ /* vertical scaling */
+ u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
+ /* vertical scale & gain */
+ u32 dcgy = 0, cya_cyb = 0;
+
+ /* helper variables */
+ u32 v_atten = 0, i = 0;
+
+ /* error, if vertical zooming */
+ if ( in_y < out_y ) {
+ return -EINVAL;
+ }
+
+ /* linear phase interpolation may be used
+ if scaling is between 1 and 1/2 (both fields used)
+ or scaling is between 1/2 and 1/4 (if only one field is used) */
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ if( 2*out_y >= in_y) {
+ lpi = 1;
+ }
+ } else if (field == V4L2_FIELD_TOP
+ || field == V4L2_FIELD_ALTERNATE
+ || field == V4L2_FIELD_BOTTOM) {
+ if( 4*out_y >= in_y ) {
+ lpi = 1;
+ }
+ out_y *= 2;
+ }
+ if( 0 != lpi ) {
+
+ yacm = 0;
+ yacl = 0;
+ cya_cyb = 0x00ff;
+
+ /* calculate scaling increment */
+ if ( in_y > out_y )
+ ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
+ else
+ ysci = 0;
+
+ dcgy = 0;
+
+ /* calculate ype and ypo */
+ ype = ysci / 16;
+ ypo = ype + (ysci / 64);
+
+ } else {
+ yacm = 1;
+
+ /* calculate scaling increment */
+ ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
+
+ /* calculate ype and ypo */
+ ypo = ype = ((ysci + 15) / 16);
+
+ /* the sequence length interval (yacl) has to be set according
+ to the prescale value, e.g. [n .. 1/2) : 0
+ [1/2 .. 1/3) : 1
+ [1/3 .. 1/4) : 2
+ ... */
+ if ( ysci < 512) {
+ yacl = 0;
+ } else {
+ yacl = ( ysci / (1024 - ysci) );
+ }
+
+ /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
+ cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
+
+ /* get best match in the table of attenuations for vertical scaling */
+ v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
+
+ for (i = 0; v_attenuation[i] != 0; i++) {
+ if (v_attenuation[i] >= v_atten)
+ break;
+ }
+
+ dcgy = i;
+ }
+
+ /* ypo and ype swapped in spec ? */
+ *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
+
+ *hps_v_gain &= ~(MASK_W0|MASK_B2);
+ *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
+
+ return 0;
+}
+
+/* simple bubble-sort algorithm with duplicate elimination */
+static int sort_and_eliminate(u32* values, int* count)
+{
+ int low = 0, high = 0, top = 0, temp = 0;
+ int cur = 0, next = 0;
+
+ /* sanity checks */
+ if( (0 > *count) || (NULL == values) ) {
+ return -EINVAL;
+ }
+
+ /* bubble sort the first ´count´ items of the array ´values´ */
+ for( top = *count; top > 0; top--) {
+ for( low = 0, high = 1; high < top; low++, high++) {
+ if( values[low] > values[high] ) {
+ temp = values[low];
+ values[low] = values[high];
+ values[high] = temp;
+ }
+ }
+ }
+
+ /* remove duplicate items */
+ for( cur = 0, next = 1; next < *count; next++) {
+ if( values[cur] != values[next])
+ values[++cur] = values[next];
+ }
+
+ *count = cur + 1;
+
+ return 0;
+}
+
+static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
+ struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 *clipping = vv->d_clipping.cpu_addr;
+
+ int width = fh->ov.win.w.width;
+ int height = fh->ov.win.w.height;
+ int clipcount = fh->ov.nclips;
+
+ u32 line_list[32];
+ u32 pixel_list[32];
+ int numdwords = 0;
+
+ int i = 0, j = 0;
+ int cnt_line = 0, cnt_pixel = 0;
+
+ int x[32], y[32], w[32], h[32];
+
+ /* clear out memory */
+ memset(&line_list[0], 0x00, sizeof(u32)*32);
+ memset(&pixel_list[0], 0x00, sizeof(u32)*32);
+ memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
+
+ /* fill the line and pixel-lists */
+ for(i = 0; i < clipcount; i++) {
+ int l = 0, r = 0, t = 0, b = 0;
+
+ x[i] = fh->ov.clips[i].c.left;
+ y[i] = fh->ov.clips[i].c.top;
+ w[i] = fh->ov.clips[i].c.width;
+ h[i] = fh->ov.clips[i].c.height;
+
+ if( w[i] < 0) {
+ x[i] += w[i]; w[i] = -w[i];
+ }
+ if( h[i] < 0) {
+ y[i] += h[i]; h[i] = -h[i];
+ }
+ if( x[i] < 0) {
+ w[i] += x[i]; x[i] = 0;
+ }
+ if( y[i] < 0) {
+ h[i] += y[i]; y[i] = 0;
+ }
+ if( 0 != vv->vflip ) {
+ y[i] = height - y[i] - h[i];
+ }
+
+ l = x[i];
+ r = x[i]+w[i];
+ t = y[i];
+ b = y[i]+h[i];
+
+ /* insert left/right coordinates */
+ pixel_list[ 2*i ] = min_t(int, l, width);
+ pixel_list[(2*i)+1] = min_t(int, r, width);
+ /* insert top/bottom coordinates */
+ line_list[ 2*i ] = min_t(int, t, height);
+ line_list[(2*i)+1] = min_t(int, b, height);
+ }
+
+ /* sort and eliminate lists */
+ cnt_line = cnt_pixel = 2*clipcount;
+ sort_and_eliminate( &pixel_list[0], &cnt_pixel );
+ sort_and_eliminate( &line_list[0], &cnt_line );
+
+ /* calculate the number of used u32s */
+ numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2;
+ numdwords = max_t(int, 4, numdwords);
+ numdwords = min_t(int, 64, numdwords);
+
+ /* fill up cliptable */
+ for(i = 0; i < cnt_pixel; i++) {
+ clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16);
+ }
+ for(i = 0; i < cnt_line; i++) {
+ clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16);
+ }
+
+ /* fill up cliptable with the display infos */
+ for(j = 0; j < clipcount; j++) {
+
+ for(i = 0; i < cnt_pixel; i++) {
+
+ if( x[j] < 0)
+ x[j] = 0;
+
+ if( pixel_list[i] < (x[j] + w[j])) {
+
+ if ( pixel_list[i] >= x[j] ) {
+ clipping[2*i] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ for(i = 0; i < cnt_line; i++) {
+
+ if( y[j] < 0)
+ y[j] = 0;
+
+ if( line_list[i] < (y[j] + h[j]) ) {
+
+ if( line_list[i] >= y[j] ) {
+ clipping[(2*i)+1] |= cpu_to_le32(1 << j);
+ }
+ }
+ }
+ }
+
+ /* adjust arbitration control register */
+ *arbtr_ctrl &= 0xffff00ff;
+ *arbtr_ctrl |= 0x00001c00;
+
+ vdma2->base_even = vv->d_clipping.dma_handle;
+ vdma2->base_odd = vv->d_clipping.dma_handle;
+ vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords));
+ vdma2->base_page = 0x04;
+ vdma2->pitch = 0x00;
+ vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
+
+ /* set clipping-mode. this depends on the field(s) used */
+ *clip_format &= 0xfffffff7;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ *clip_format |= 0x00000008;
+ } else {
+ *clip_format |= 0x00000000;
+ }
+}
+
+/* disable clipping */
+static void saa7146_disable_clipping(struct saa7146_dev *dev)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* mask out relevant bits (=lower word)*/
+ clip_format &= MASK_W1;
+
+ /* upload clipping-registers*/
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ /* disable video dma2 */
+ saa7146_write(dev, MC1, MASK_21);
+}
+
+static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ enum v4l2_field field = fh->ov.win.field;
+ struct saa7146_video_dma vdma2;
+ u32 clip_format;
+ u32 arbtr_ctrl;
+
+ /* check clipcount, disable clipping if clipcount == 0*/
+ if( fh->ov.nclips == 0 ) {
+ saa7146_disable_clipping(dev);
+ return;
+ }
+
+ clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+ arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+
+ calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
+
+ /* set clipping format */
+ clip_format &= 0xffff0008;
+ clip_format |= (SAA7146_CLIPPING_RECT << 4);
+
+ /* prepare video dma2 */
+ saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
+ saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
+ saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
+ saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
+ saa7146_write(dev, PITCH2, vdma2.pitch);
+ saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
+
+ /* prepare the rest */
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+
+ /* upload clip_control-register, clipping-registers, enable video dma2 */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
+ saa7146_write(dev, MC1, (MASK_05 | MASK_21));
+}
+
+static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int source = vv->current_hps_source;
+ int sync = vv->current_hps_sync;
+
+ u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
+
+ /* set vertical scale */
+ hps_v_scale = 0; /* all bits get set by the function-call */
+ hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
+ calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
+
+ /* set horizontal scale */
+ hps_ctrl = 0;
+ hps_h_prescale = 0; /* all bits get set in the function */
+ hps_h_scale = 0;
+ calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+
+ /* set hyo and hxo */
+ calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
+ calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
+
+ /* write out new register contents */
+ saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
+ saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
+ saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
+
+ /* upload shadow-ram registers */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
+}
+
+/* calculate the new memory offsets for a desired position */
+static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *sfmt = format_by_fourcc(dev, pixelformat);
+
+ int b_depth = vv->ov_fmt->depth;
+ int b_bpl = vv->ov_fb.fmt.bytesperline;
+ u32 base = (u32)vv->ov_fb.base;
+
+ struct saa7146_video_dma vdma1;
+
+ /* calculate memory offsets for picture, look if we shall top-down-flip */
+ vdma1.pitch = 2*b_bpl;
+ if ( 0 == vv->vflip ) {
+ vdma1.base_even = (u32)base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2));
+ }
+ else {
+ vdma1.base_even = (u32)base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if ( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ vdma1.base_page = sfmt->swap;
+ vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels;
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+}
+
+static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* call helper function */
+ calculate_output_format_register(dev,palette,&clip_format);
+
+ /* update the hps registers */
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+}
+
+/* select input-source */
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 hps_ctrl = 0;
+
+ /* read old state */
+ hps_ctrl = saa7146_read(dev, HPS_CTRL);
+
+ hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
+ hps_ctrl |= (source << 30) | (sync << 28);
+
+ /* write back & upload register */
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ vv->current_hps_source = source;
+ vv->current_hps_sync = sync;
+}
+
+int saa7146_enable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field);
+ saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat);
+ saa7146_set_output_format(dev, vv->ov_fmt->trans);
+ saa7146_set_clipping_rect(fh);
+
+ /* enable video dma1 */
+ saa7146_write(dev, MC1, (MASK_06 | MASK_22));
+ return 0;
+}
+
+void saa7146_disable_overlay(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+
+ /* disable clipping + video dma1 */
+ saa7146_disable_clipping(dev);
+ saa7146_write(dev, MC1, MASK_22);
+}
+
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
+{
+ int where = 0;
+
+ if( which < 1 || which > 3) {
+ return;
+ }
+
+ /* calculate starting address */
+ where = (which-1)*0x18;
+
+ saa7146_write(dev, where, vdma->base_odd);
+ saa7146_write(dev, where+0x04, vdma->base_even);
+ saa7146_write(dev, where+0x08, vdma->prot_addr);
+ saa7146_write(dev, where+0x0c, vdma->pitch);
+ saa7146_write(dev, where+0x10, vdma->base_page);
+ saa7146_write(dev, where+0x14, vdma->num_line_byte);
+
+ /* upload */
+ saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
+/*
+ printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
+ printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
+ printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
+ printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
+ printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
+ printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
+*/
+}
+
+static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ int bytesperline = buf->fmt->bytesperline;
+ enum v4l2_field field = buf->fmt->field;
+
+ int depth = sfmt->depth;
+
+ DEB_CAP(("[size=%dx%d,fields=%s]\n",
+ width,height,v4l2_field_names[field]));
+
+ if( bytesperline != 0) {
+ vdma1.pitch = bytesperline*2;
+ } else {
+ vdma1.pitch = (width*depth*2)/8;
+ }
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ return 0;
+}
+
+static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width;
+ vdma3->pitch = width;
+
+ /* fixme: look at bytesperline! */
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[1].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[2].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+
+ return 0;
+}
+
+static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ int height = buf->fmt->height;
+ int width = buf->fmt->width;
+
+ vdma2->pitch = width/2;
+ vdma3->pitch = width/2;
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[2].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[1].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+ return 0;
+}
+
+static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_video_dma vdma1;
+ struct saa7146_video_dma vdma2;
+ struct saa7146_video_dma vdma3;
+
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ enum v4l2_field field = buf->fmt->field;
+
+ BUG_ON(0 == buf->pt[0].dma);
+ BUG_ON(0 == buf->pt[1].dma);
+ BUG_ON(0 == buf->pt[2].dma);
+
+ DEB_CAP(("[size=%dx%d,fields=%s]\n",
+ width,height,v4l2_field_names[field]));
+
+ /* fixme: look at bytesperline! */
+
+ /* fixme: what happens for user space buffers here?. The offsets are
+ most likely wrong, this version here only works for page-aligned
+ buffers, modifications to the pagetable-functions are necessary...*/
+
+ vdma1.pitch = width*2;
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
+ }
+
+ vdma2.num_line_byte = 0; /* unused */
+ vdma2.base_page = buf->pt[1].dma | ME1;
+
+ vdma3.num_line_byte = 0; /* unused */
+ vdma3.base_page = buf->pt[2].dma | ME1;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ calc_planar_420(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ case 16: {
+ calc_planar_422(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.base_even;
+ vdma2.base_even = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.base_even;
+ vdma3.base_even = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ vdma2.pitch *= -1;
+ vdma3.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
+ saa7146_write_out_dma(dev, 3, &vdma2);
+ saa7146_write_out_dma(dev, 2, &vdma3);
+ } else {
+ saa7146_write_out_dma(dev, 2, &vdma2);
+ saa7146_write_out_dma(dev, 3, &vdma3);
+ }
+ return 0;
+}
+
+static void program_capture_engine(struct saa7146_dev *dev, int planar)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ int count = 0;
+
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
+
+ /* set rps register 0 */
+ WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS0(MASK_27 | MASK_11);
+
+ /* turn on video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
+ WRITE_RPS0(MASK_06 | MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn on video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_05 | MASK_21); /* => values */
+
+ /* turn on video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_04 | MASK_20); /* => values */
+ }
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ }
+
+ /* turn off video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
+ WRITE_RPS0(MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn off video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_21); /* => values */
+
+ /* turn off video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_20); /* => values */
+ }
+
+ /* generate interrupt */
+ WRITE_RPS0(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS0(CMD_STOP);
+}
+
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 vdma1_prot_addr;
+
+ DEB_CAP(("buf:%p, next:%p\n",buf,next));
+
+ vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
+ if( 0 == vdma1_prot_addr ) {
+ /* clear out beginning of streaming bit (rps register 0)*/
+ DEB_CAP(("forcing sync to new frame\n"));
+ saa7146_write(dev, MC2, MASK_27 );
+ }
+
+ saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
+ saa7146_set_output_format(dev, sfmt->trans);
+ saa7146_disable_clipping(dev);
+
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vv->last_field = V4L2_FIELD_BOTTOM;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vv->last_field = V4L2_FIELD_TOP;
+ }
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ calculate_video_dma_grab_planar(dev, buf);
+ program_capture_engine(dev,1);
+ } else {
+ calculate_video_dma_grab_packed(dev, buf);
+ program_capture_engine(dev,0);
+ }
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+ printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
+*/
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+}
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
new file mode 100644
index 000000000000..781f23f0cbcc
--- /dev/null
+++ b/drivers/media/common/saa7146_i2c.c
@@ -0,0 +1,421 @@
+#include <linux/version.h>
+#include <media/saa7146_vv.h>
+
+static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
+{
+//fm DEB_I2C(("'%s'.\n", adapter->name));
+
+ return I2C_FUNC_I2C
+ | I2C_FUNC_SMBUS_QUICK
+ | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+/* this function returns the status-register of our i2c-device */
+static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
+{
+ u32 iicsta = saa7146_read(dev, I2C_STATUS);
+/*
+ DEB_I2C(("status: 0x%08x\n",iicsta));
+*/
+ return iicsta;
+}
+
+/* this function runs through the i2c-messages and prepares the data to be
+ sent through the saa7146. have a look at the specifications p. 122 ff
+ to understand this. it returns the number of u32s to send, or -1
+ in case of an error. */
+static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
+{
+ int h1, h2;
+ int i, j, addr;
+ int mem = 0, op_count = 0;
+
+ /* first determine size of needed memory */
+ for(i = 0; i < num; i++) {
+ mem += m[i].len + 1;
+ }
+
+ /* worst case: we need one u32 for three bytes to be send
+ plus one extra byte to address the device */
+ mem = 1 + ((mem-1) / 3);
+
+ /* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
+ size. if we exceed this limit... */
+ if ( (4*mem) > SAA7146_I2C_MEM ) {
+//fm DEB_I2C(("cannot prepare i2c-message.\n"));
+ return -ENOMEM;
+ }
+
+ /* be careful: clear out the i2c-mem first */
+ memset(op,0,sizeof(u32)*mem);
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ /* insert the address of the i2c-slave.
+ note: we get 7 bit i2c-addresses,
+ so we have to perform a translation */
+ addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= ( (u8)addr << ((3-h2)*8));
+ op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* insert the data bytes */
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
+ op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2));
+ op_count++;
+ }
+
+ }
+
+ /* have a look at the last byte inserted:
+ if it was: ...CONT change it to ...STOP */
+ h1 = (op_count-1)/3; h2 = (op_count-1)%3;
+ if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
+ op[h1] &= ~(0x2 << ((3-h2)*2));
+ op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
+ }
+
+ /* return the number of u32s to send */
+ return mem;
+}
+
+/* this functions loops through all i2c-messages. normally, it should determine
+ which bytes were read through the adapter and write them back to the corresponding
+ i2c-message. but instead, we simply write back all bytes.
+ fixme: this could be improved. */
+static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op)
+{
+ int i, j;
+ int op_count = 0;
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ op_count++;
+
+ /* loop throgh all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* write back all bytes that could have been read */
+ m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
+ op_count++;
+ }
+ }
+
+ return 0;
+}
+
+/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
+static int saa7146_i2c_reset(struct saa7146_dev *dev)
+{
+ /* get current status */
+ u32 status = saa7146_i2c_status(dev);
+
+ /* clear registers for sure */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, 0);
+
+ /* check if any operation is still in progress */
+ if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
+
+ /* yes, kill ongoing operation */
+ DEB_I2C(("busy_state detected.\n"));
+
+ /* set "ABORT-OPERATION"-bit (bit 7)*/
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending; this is needed because p.123, note 1 */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* check if any error is (still) present. (this can be necessary because p.123, note 1) */
+ status = saa7146_i2c_status(dev);
+
+ if ( dev->i2c_bitrate != status ) {
+
+ DEB_I2C(("error_state detected. status:0x%08x\n",status));
+
+ /* Repeat the abort operation. This seems to be necessary
+ after serious protocol errors caused by e.g. the SAA7740 */
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+
+ /* the data sheet says it might be necessary to clear the status
+ twice after an abort */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ msleep(SAA7146_I2C_DELAY);
+ }
+
+ /* if any error is still present, a fatal error has occured ... */
+ status = saa7146_i2c_status(dev);
+ if ( dev->i2c_bitrate != status ) {
+ DEB_I2C(("fatal error. status:0x%08x\n",status));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* this functions writes out the data-byte 'dword' to the i2c-device.
+ it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
+ failed badly (e.g. address error) */
+static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay)
+{
+ u32 status = 0, mc2 = 0;
+ int trial = 0;
+ unsigned long timeout;
+
+ /* write out i2c-command */
+ DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
+
+ if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
+
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, *dword);
+
+ dev->i2c_op = 1;
+ SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
+ if (signal_pending (current)) {
+ /* a signal arrived */
+ return -ERESTARTSYS;
+ }
+ status = saa7146_read(dev, I2C_STATUS);
+ } else {
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, *dword);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ /* do not poll for i2c-status before upload is complete */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ while(1) {
+ mc2 = (saa7146_read(dev, MC2) & 0x1);
+ if( 0 != mc2 ) {
+ break;
+ }
+ if (time_after(jiffies,timeout)) {
+ printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for MC2\n");
+ return -EIO;
+ }
+ }
+ /* wait until we get a transfer done or error */
+ timeout = jiffies + HZ/100 + 1; /* 10ms */
+ while(1) {
+ /**
+ * first read usually delivers bogus results...
+ */
+ saa7146_i2c_status(dev);
+ status = saa7146_i2c_status(dev);
+ if ((status & 0x3) != 1)
+ break;
+ if (time_after(jiffies,timeout)) {
+ /* this is normal when probing the bus
+ * (no answer from nonexisistant device...)
+ */
+ DEB_I2C(("saa7146_i2c_writeout: timed out waiting for end of xfer\n"));
+ return -EIO;
+ }
+ if ((++trial < 20) && short_delay)
+ udelay(10);
+ else
+ msleep(1);
+ }
+ }
+
+ /* give a detailed status report */
+ if ( 0 != (status & SAA7146_I2C_ERR)) {
+
+ if( 0 != (status & SAA7146_I2C_SPERR) ) {
+ DEB_I2C(("error due to invalid start/stop condition.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_DTERR) ) {
+ DEB_I2C(("error in data transmission.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_DRERR) ) {
+ DEB_I2C(("error when receiving data.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_AL) ) {
+ DEB_I2C(("error because arbitration lost.\n"));
+ }
+
+ /* we handle address-errors here */
+ if( 0 != (status & SAA7146_I2C_APERR) ) {
+ DEB_I2C(("error in address phase.\n"));
+ return -EREMOTEIO;
+ }
+
+ return -EIO;
+ }
+
+ /* read back data, just in case we were reading ... */
+ *dword = saa7146_read(dev, I2C_TRANSFER);
+
+ DEB_I2C(("after: 0x%08x\n",*dword));
+ return 0;
+}
+
+int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
+{
+ int i = 0, count = 0;
+ u32* buffer = dev->d_i2c.cpu_addr;
+ int err = 0;
+ int address_err = 0;
+ int short_delay = 0;
+
+ if (down_interruptible (&dev->i2c_lock))
+ return -ERESTARTSYS;
+
+ for(i=0;i<num;i++) {
+ DEB_I2C(("msg:%d/%d\n",i+1,num));
+ }
+
+ /* prepare the message(s), get number of u32s to transfer */
+ count = saa7146_i2c_msg_prepare(msgs, num, buffer);
+ if ( 0 > count ) {
+ err = -1;
+ goto out;
+ }
+
+ if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) )
+ short_delay = 1;
+
+ do {
+ /* reset the i2c-device if necessary */
+ err = saa7146_i2c_reset(dev);
+ if ( 0 > err ) {
+ DEB_I2C(("could not reset i2c-device.\n"));
+ goto out;
+ }
+
+ /* write out the u32s one after another */
+ for(i = 0; i < count; i++) {
+ err = saa7146_i2c_writeout(dev, &buffer[i], short_delay);
+ if ( 0 != err) {
+ /* this one is unsatisfying: some i2c slaves on some
+ dvb cards don't acknowledge correctly, so the saa7146
+ thinks that an address error occured. in that case, the
+ transaction should be retrying, even if an address error
+ occured. analog saa7146 based cards extensively rely on
+ i2c address probing, however, and address errors indicate that a
+ device is really *not* there. retrying in that case
+ increases the time the device needs to probe greatly, so
+ it should be avoided. because of the fact, that only
+ analog based cards use irq based i2c transactions (for dvb
+ cards, this screwes up other interrupt sources), we bail out
+ completely for analog cards after an address error and trust
+ the saa7146 address error detection. */
+ if ( -EREMOTEIO == err ) {
+ if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
+ goto out;
+ }
+ address_err++;
+ }
+ DEB_I2C(("error while sending message(s). starting again.\n"));
+ break;
+ }
+ }
+ if( 0 == err ) {
+ err = num;
+ break;
+ }
+
+ /* delay a bit before retrying */
+ msleep(10);
+
+ } while (err != num && retries--);
+
+ /* if every retry had an address error, exit right away */
+ if (address_err == retries) {
+ goto out;
+ }
+
+ /* if any things had to be read, get the results */
+ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
+ DEB_I2C(("could not cleanup i2c-message.\n"));
+ err = -1;
+ goto out;
+ }
+
+ /* return the number of delivered messages */
+ DEB_I2C(("transmission successful. (msg:%d).\n",err));
+out:
+ /* another bug in revision 0: the i2c-registers get uploaded randomly by other
+ uploads, so we better clear them out before continueing */
+ if( 0 == dev->revision ) {
+ u32 zero = 0;
+ saa7146_i2c_reset(dev);
+ if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
+ INFO(("revision 0 error. this should never happen.\n"));
+ }
+ }
+
+ up(&dev->i2c_lock);
+ return err;
+}
+
+/* utility functions */
+static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
+{
+ struct saa7146_dev* dev = i2c_get_adapdata(adapter);
+
+ /* use helper function to transfer data */
+ return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
+}
+
+
+/*****************************************************************************/
+/* i2c-adapter helper functions */
+#include <linux/i2c-id.h>
+
+/* exported algorithm data */
+static struct i2c_algorithm saa7146_algo = {
+ .name = "saa7146 i2c algorithm",
+ .id = I2C_ALGO_SAA7146,
+ .master_xfer = saa7146_i2c_xfer,
+ .functionality = saa7146_i2c_func,
+};
+
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
+{
+ DEB_EE(("bitrate: 0x%08x\n",bitrate));
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24));
+
+ dev->i2c_bitrate = bitrate;
+ saa7146_i2c_reset(dev);
+
+ if( NULL != i2c_adapter ) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+ i2c_adapter->data = dev;
+#else
+ BUG_ON(!i2c_adapter->class);
+ i2c_set_adapdata(i2c_adapter,dev);
+#endif
+ i2c_adapter->algo = &saa7146_algo;
+ i2c_adapter->algo_data = NULL;
+ i2c_adapter->id = I2C_ALGO_SAA7146;
+ i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
+ i2c_adapter->retries = SAA7146_I2C_RETRIES;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
new file mode 100644
index 000000000000..cb86a97fda1f
--- /dev/null
+++ b/drivers/media/common/saa7146_vbi.c
@@ -0,0 +1,508 @@
+#include <media/saa7146_vv.h>
+
+static int vbi_pixel_to_capture = 720 * 2;
+
+static int vbi_workaround(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ int count = 0;
+ int i;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ DEB_VBI(("dev:%p\n",dev));
+
+ /* once again, a bug in the saa7146: the brs acquisition
+ is buggy and especially the BXO-counter does not work
+ as specified. there is this workaround, but please
+ don't let me explain it. ;-) */
+
+ cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
+ if (NULL == cpu)
+ return -ENOMEM;
+
+ /* setup some basic programming, just for the workaround */
+ saa7146_write(dev, BASE_EVEN3, dma_addr);
+ saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
+ saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
+ saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
+ saa7146_write(dev, BASE_PAGE3, 0x0);
+ saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BXO = 1h, BRS to outbound */
+ WRITE_RPS1(0xc000008c);
+ /* wait for vbi_a or vbi_b*/
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ DEB_D(("...using port b\n"));
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
+/*
+ WRITE_RPS1(CMD_PAUSE | MASK_09);
+*/
+ } else {
+ DEB_D(("...using port a\n"));
+ WRITE_RPS1(CMD_PAUSE | MASK_10);
+ }
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
+ WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load video-dma3 NumLines3 and NumBytes3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
+ /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
+ WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
+ WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs and video-dma3*/
+ WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
+ /* load mc2 register: enable dma3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
+ WRITE_RPS1(MASK_20 | MASK_04);
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+ /* stop rps1 */
+ WRITE_RPS1(CMD_STOP);
+
+ /* we have to do the workaround twice to be sure that
+ everything is ok */
+ for(i = 0; i < 2; i++) {
+
+ /* indicate to the irq handler that we do the workaround */
+ saa7146_write(dev, MC2, MASK_31|MASK_15);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev,MASK_28);
+
+ /* prepare to wait to be woken up by the irq-handler */
+ add_wait_queue(&vv->vbi_wq, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ /* start rps1 to enable workaround */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ schedule();
+
+ DEB_VBI(("brs bug workaround %d/1.\n",i));
+
+ remove_wait_queue(&vv->vbi_wq, &wait);
+ current->state = TASK_RUNNING;
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev,MASK_28);
+
+ /* stop video-dma3 */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if(signal_pending(current)) {
+
+ DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1)));
+
+ /* stop rps1 for sure */
+ saa7146_write(dev, MC1, MASK_29);
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return -EINTR;
+ }
+ }
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return 0;
+}
+
+static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ struct saa7146_video_dma vdma3;
+
+ int count = 0;
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+/*
+ vdma3.base_even = 0xc8000000+2560*70;
+ vdma3.base_odd = 0xc8000000;
+ vdma3.prot_addr = 0xc8000000+2560*164;
+ vdma3.pitch = 2560;
+ vdma3.base_page = 0;
+ vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
+*/
+ vdma3.base_even = buf->pt[2].offset;
+ vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
+ vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
+ vdma3.pitch = vbi_pixel_to_capture;
+ vdma3.base_page = buf->pt[2].dma | ME1;
+ vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
+
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
+
+ /* we don't wait here for the first field anymore. this is different from the video
+ capture and might cause that the first buffer is only half filled (with only
+ one field). but since this is some sort of streaming data, this is not that negative.
+ but by doing this, we can use the whole engine from video-buf.c... */
+
+/*
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
+*/
+ /* set bit 1 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS1(MASK_28 | MASK_12);
+
+ /* turn on video-dma3 */
+ WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS1(MASK_04 | MASK_20); /* => values */
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ WRITE_RPS1(CMD_PAUSE | o_wait);
+ WRITE_RPS1(CMD_PAUSE | e_wait);
+
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS1(CMD_STOP);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_28);
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+}
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ buf->vb.state = STATE_ACTIVE;
+
+ DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
+ saa7146_set_vbi_capture(dev,buf,next);
+
+ mod_timer(&vv->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 file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ int err = 0;
+ int lines, llength, size;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+ size = lines * llength;
+
+ DEB_VBI(("vb:%p\n",vb));
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_VBI(("size mismatch.\n"));
+ return -EINVAL;
+ }
+
+ if (buf->vb.size != size)
+ saa7146_dma_free(dev,buf);
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->vb.field = field; // FIXME: check this
+
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+
+ err = videobuf_iolock(dev->pci,&buf->vb, NULL);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen);
+ if (0 != err)
+ return err;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_VBI(("error out.\n"));
+ saa7146_dma_free(dev,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ int llength,lines;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+
+ *size = lines * llength;
+ *count = 2;
+
+ DEB_VBI(("count:%d, size:%d\n",*count,*size));
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI(("vb:%p\n",vb));
+ saa7146_buffer_queue(dev,&vv->vbi_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI(("vb:%p\n",vb));
+ saa7146_dma_free(dev,buf);
+}
+
+static struct videobuf_queue_ops vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void vbi_stop(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ unsigned long flags;
+ DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps1 */
+ saa7146_write(dev, MC1, MASK_29);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_28);
+
+ /* shut down dma 3 transfers */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if (vv->vbi_q.curr) {
+ saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ }
+
+ videobuf_queue_cancel(&fh->vbi_q);
+
+ vv->vbi_streaming = NULL;
+
+ del_timer(&vv->vbi_q.timeout);
+ del_timer(&fh->vbi_read_timeout);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static void vbi_read_timeout(unsigned long data)
+{
+ struct file *file = (struct file*)data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+
+ vbi_stop(fh, file);
+}
+
+static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ DEB_VBI(("dev:%p\n",dev));
+
+ INIT_LIST_HEAD(&vv->vbi_q.queue);
+
+ init_timer(&vv->vbi_q.timeout);
+ vv->vbi_q.timeout.function = saa7146_buffer_timeout;
+ vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q);
+ vv->vbi_q.dev = dev;
+
+ init_waitqueue_head(&vv->vbi_wq);
+}
+
+static int vbi_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
+
+ u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+ int ret = 0;
+
+ DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+
+ ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
+ if (0 == ret) {
+ DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
+ return -EBUSY;
+ }
+
+ /* adjust arbitrition control for video dma 3 */
+ arbtr_ctrl &= ~0x1f0000;
+ arbtr_ctrl |= 0x1d0000;
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+ saa7146_write(dev, MC2, (MASK_04|MASK_20));
+
+ memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt));
+
+ fh->vbi_fmt.sampling_rate = 27000000;
+ fh->vbi_fmt.offset = 248; /* todo */
+ fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture;
+ fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ fh->vbi_fmt.start[0] = 5;
+ fh->vbi_fmt.count[0] = 16;
+ fh->vbi_fmt.start[1] = 312;
+ fh->vbi_fmt.count[1] = 16;
+
+ videobuf_queue_init(&fh->vbi_q, &vbi_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
+ sizeof(struct saa7146_buf),
+ file);
+ init_MUTEX(&fh->vbi_q.lock);
+
+ init_timer(&fh->vbi_read_timeout);
+ fh->vbi_read_timeout.function = vbi_read_timeout;
+ fh->vbi_read_timeout.data = (unsigned long)file;
+
+ /* initialize the brs */
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
+ } else {
+ saa7146_write(dev, BRS_CTRL, 0x00000001);
+
+ if (0 != (ret = vbi_workaround(dev))) {
+ DEB_VBI(("vbi workaround failed!\n"));
+ /* return ret;*/
+ }
+ }
+
+ /* upload brs register */
+ saa7146_write(dev, MC2, (MASK_08|MASK_24));
+ return 0;
+}
+
+static void vbi_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+
+ if( fh == vv->vbi_streaming ) {
+ vbi_stop(fh, file);
+ }
+ saa7146_res_free(fh, RESOURCE_DMA3_BRS);
+}
+
+static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ spin_lock(&dev->slock);
+
+ if (vv->vbi_q.curr) {
+ DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr));
+ /* this must be += 2, one count for each field */
+ vv->vbi_fieldcount+=2;
+ vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
+ saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ } else {
+ DEB_VBI(("dev:%p\n",dev));
+ }
+ saa7146_buffer_next(dev,&vv->vbi_q,1);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+
+ if( NULL == vv->vbi_streaming ) {
+ // fixme: check if dma3 is available
+ // fixme: activate vbi engine here if necessary. (really?)
+ vv->vbi_streaming = fh;
+ }
+
+ if( fh != vv->vbi_streaming ) {
+ DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming));
+ return -EBUSY;
+ }
+
+ mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
+ ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+/*
+ printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
+ printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
+ printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
+ printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
+ printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
+ printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
+ printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
+*/
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_vbi_uops = {
+ .init = vbi_init,
+ .open = vbi_open,
+ .release = vbi_close,
+ .irq_done = vbi_irq_done,
+ .read = vbi_read,
+};
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
new file mode 100644
index 000000000000..8dd4d15ca36d
--- /dev/null
+++ b/drivers/media/common/saa7146_video.c
@@ -0,0 +1,1509 @@
+#include <media/saa7146_vv.h>
+
+static int max_memory = 32;
+
+module_param(max_memory, int, 0644);
+MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
+
+#define IS_CAPTURE_ACTIVE(fh) \
+ (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
+
+#define IS_OVERLAY_ACTIVE(fh) \
+ (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
+
+/* format descriptions for capture and preview */
+static struct saa7146_format formats[] = {
+ {
+ .name = "RGB-8 (3-3-2)",
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .trans = RGB08_COMPOSED,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "RGB-16 (5/B-6/G-5/R)",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .trans = RGB16_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }, {
+ .name = "RGB-24 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .trans = RGB24_COMPOSED,
+ .depth = 24,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (R-G-B)",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ .swap = 0x2,
+ }, {
+ .name = "Greyscale-8",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .trans = Y8,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "YUV 4:2:2 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .trans = YUV422_DECOMPOSED,
+ .depth = 16,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YVU 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:2 (U-Y-V-Y)",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .trans = YUV422_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }
+};
+
+/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
+ due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
+ (like V4L2_PIX_FMT_YUYV) ... 8-( */
+
+static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
+
+struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i, j = NUM_FORMATS;
+
+ for (i = 0; i < j; i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc));
+ return NULL;
+}
+
+static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ f->fmt.pix = fh->video_fmt;
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ f->fmt.win = fh->ov.win;
+ return 0;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ {
+ f->fmt.vbi = fh->vbi_fmt;
+ return 0;
+ }
+ default:
+ DEB_D(("invalid format type '%d'.\n",f->type));
+ return -EINVAL;
+ }
+}
+
+static int try_win(struct saa7146_dev *dev, struct v4l2_window *win)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ if (NULL == vv->ov_fb.base) {
+ DEB_D(("no fb base set.\n"));
+ return -EINVAL;
+ }
+ if (NULL == vv->ov_fmt) {
+ DEB_D(("no fb fmt set.\n"));
+ return -EINVAL;
+ }
+ if (win->w.width < 48 || win->w.height < 32) {
+ DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height));
+ return -EINVAL;
+ }
+ if (win->clipcount > 16) {
+ DEB_D(("clipcount too big.\n"));
+ return -EINVAL;
+ }
+
+ field = win->field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ 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:
+ case V4L2_FIELD_ALTERNATE:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default: {
+ DEB_D(("no known field mode '%d'.\n",field));
+ 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 try_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+ int calc_bpl;
+
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+
+ fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat);
+ if (NULL == fmt) {
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_ALTERNATE: {
+ vv->last_field = V4L2_FIELD_TOP;
+ maxh = maxh / 2;
+ break;
+ }
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ break;
+ default: {
+ DEB_D(("no known field mode '%d'.\n",field));
+ return -EINVAL;
+ }
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+
+ calc_bpl = (f->fmt.pix.width * fmt->depth)/8;
+
+ if (f->fmt.pix.bytesperline < calc_bpl)
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage));
+
+ return 0;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+ err = try_win(dev,&f->fmt.win);
+ if (0 != err) {
+ return err;
+ }
+ return 0;
+ default:
+ DEB_EE(("unknown format type '%d'\n",f->type));
+ return -EINVAL;
+ }
+}
+
+int saa7146_start_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int ret = 0, err = 0;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ /* check if we have overlay informations */
+ if( NULL == fh->ov.fh ) {
+ DEB_D(("no overlay data available. try S_FMT first.\n"));
+ return -EAGAIN;
+ }
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("streaming capture is active.\n"));
+ return -EBUSY;
+ }
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_D(("overlay is already active.\n"));
+ return 0;
+ }
+ DEB_D(("overlay is already active in another open.\n"));
+ return -EBUSY;
+ }
+
+ if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+ DEB_D(("cannot get necessary overlay resources\n"));
+ return -EBUSY;
+ }
+
+ err = try_win(dev,&fh->ov.win);
+ if (0 != err) {
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
+ }
+
+ vv->ov_data = &fh->ov;
+
+ DEB_D(("%dx%d+%d+%d %s field=%s\n",
+ fh->ov.win.w.width,fh->ov.win.w.height,
+ fh->ov.win.w.left,fh->ov.win.w.top,
+ vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
+
+ if (0 != (ret = saa7146_enable_overlay(fh))) {
+ DEB_D(("enabling overlay failed: %d\n",ret));
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return ret;
+ }
+
+ vv->video_status = STATUS_OVERLAY;
+ vv->video_fh = fh;
+
+ return 0;
+}
+
+int saa7146_stop_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("streaming capture is active.\n"));
+ return -EBUSY;
+ }
+
+ /* check if overlay is running at all */
+ if ((vv->video_status & STATUS_OVERLAY) == 0) {
+ DEB_D(("no active overlay.\n"));
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_D(("overlay is active, but in another open.\n"));
+ return -EBUSY;
+ }
+
+ vv->video_status = 0;
+ vv->video_fh = NULL;
+
+ saa7146_disable_overlay(fh);
+
+ saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+
+ return 0;
+}
+
+static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_EE(("streaming capture is active\n"));
+ return -EBUSY;
+ }
+ err = try_fmt(fh,f);
+ if (0 != err)
+ return err;
+ fh->video_fmt = f->fmt.pix;
+ DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat));
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+ err = try_win(dev,&f->fmt.win);
+ if (0 != err)
+ return err;
+ down(&dev->lock);
+ fh->ov.win = f->fmt.win;
+ fh->ov.nclips = f->fmt.win.clipcount;
+ if (fh->ov.nclips > 16)
+ fh->ov.nclips = 16;
+ if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) {
+ up(&dev->lock);
+ return -EFAULT;
+ }
+
+ /* fh->ov.fh is used to indicate that we have valid overlay informations, too */
+ fh->ov.fh = fh;
+
+ up(&dev->lock);
+
+ /* check if our current overlay is active */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+ default:
+ DEB_D(("unknown format type '%d'\n",f->type));
+ return -EINVAL;
+ }
+}
+
+/********************************************************************************/
+/* device controls */
+
+static struct v4l2_queryctrl controls[] = {
+ {
+ .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 = 64,
+ .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_VFLIP,
+ .name = "Vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_HFLIP,
+ .name = "Horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);
+
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
+
+static struct v4l2_queryctrl* ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < NUM_CONTROLS; i++)
+ if (controls[i].id == id)
+ return controls+i;
+ return NULL;
+}
+
+static int get_control(struct saa7146_fh *fh, struct v4l2_control *c)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ const struct v4l2_queryctrl* ctrl;
+ u32 value = 0;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0xff & (value >> 24);
+ DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value));
+ break;
+ case V4L2_CID_CONTRAST:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0x7f & (value >> 16);
+ DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value));
+ break;
+ case V4L2_CID_SATURATION:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0x7f & (value >> 0);
+ DEB_D(("V4L2_CID_SATURATION: %d\n",c->value));
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = vv->vflip;
+ DEB_D(("V4L2_CID_VFLIP: %d\n",c->value));
+ break;
+ case V4L2_CID_HFLIP:
+ c->value = vv->hflip;
+ DEB_D(("V4L2_CID_HFLIP: %d\n",c->value));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ const struct v4l2_queryctrl* ctrl;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl) {
+ DEB_D(("unknown control %d\n",c->id));
+ return -EINVAL;
+ }
+
+ down(&dev->lock);
+
+ 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: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0x00ffffff;
+ value |= (c->value << 24);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0xff00ffff;
+ value |= (c->value << 16);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0xffffff00;
+ value |= (c->value << 0);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_HFLIP:
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+ up(&dev->lock);
+ return -EINVAL;
+ }
+ vv->hflip = c->value;
+ break;
+ case V4L2_CID_VFLIP:
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+ up(&dev->lock);
+ return -EINVAL;
+ }
+ vv->vflip = c->value;
+ break;
+ default: {
+ return -EINVAL;
+ }
+ }
+ up(&dev->lock);
+
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+/********************************************************************************/
+/* common pagetable functions */
+
+static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct pci_dev *pci = dev->pci;
+ struct scatterlist *list = buf->vb.dma.sglist;
+ int length = buf->vb.dma.sglen;
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length));
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ u32 *ptr1, *ptr2, *ptr3;
+ u32 fill;
+
+ int size = buf->fmt->width*buf->fmt->height;
+ int i,p,m1,m2,m3,o1,o2;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/4))%PAGE_SIZE;
+ DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ break;
+ }
+ case 16: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/2))%PAGE_SIZE;
+ DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2));
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ /* walk all pages, copy all page addresses to ptr1 */
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < list->length; p++, ptr1++) {
+ *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
+ }
+ }
+/*
+ ptr1 = pt1->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+*/
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = buf->vb.dma.sglist->offset;
+ pt2->offset = pt1->offset+o1;
+ pt3->offset = pt1->offset+o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for(i = m1; i <= m2 ; i++, ptr2++) {
+ *ptr2 = ptr1[i];
+ }
+ fill = *(ptr2-1);
+ for(;i<1024;i++,ptr2++) {
+ *ptr2 = fill;
+ }
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for(i = m2; i <= m3; i++,ptr3++) {
+ *ptr3 = ptr1[i];
+ }
+ fill = *(ptr3-1);
+ for(;i<1024;i++,ptr3++) {
+ *ptr3 = fill;
+ }
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu+m1;
+ fill = pt1->cpu[m1];
+ for(i=m1;i<1024;i++,ptr1++) {
+ *ptr1 = fill;
+ }
+/*
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
+ }
+*/
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+ return saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+static int video_begin(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned int resource;
+ int ret = 0, err = 0;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_S(("already capturing.\n"));
+ return 0;
+ }
+ DEB_S(("already capturing in another open.\n"));
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ DEB_S(("warning: suspending overlay video for streaming capture.\n"));
+ 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 (0 != err) {
+ DEB_D(("suspending video failed. aborting\n"));
+ return err;
+ }
+ }
+
+ fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ }
+
+ ret = saa7146_res_get(fh, resource);
+ if (0 == ret) {
+ DEB_S(("cannot get capture resource %d\n",resource));
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+ return -EBUSY;
+ }
+
+ /* clear out beginning of streaming bit (rps register 0)*/
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_27);
+
+ vv->video_fh = fh;
+ vv->video_status = STATUS_CAPTURE;
+
+ return 0;
+}
+
+static int video_end(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned long flags;
+ unsigned int resource;
+ u32 dmas = 0;
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S(("not capturing.\n"));
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S(("capturing, but in another open.\n"));
+ return -EBUSY;
+ }
+
+ fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ dmas = MASK_22 | MASK_21 | MASK_20;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ dmas = MASK_22;
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_27);
+
+ /* shut down all used video dma transfers */
+ saa7146_write(dev, MC1, dmas);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ vv->video_fh = NULL;
+ vv->video_status = 0;
+
+ saa7146_res_free(fh, resource);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others). userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+
+int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int err = 0, result = 0, ee = 0;
+
+ struct saa7146_use_ops *ops;
+ struct videobuf_queue *q;
+
+ /* check if extension handles the command */
+ for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) {
+ if( cmd == dev->ext_vv_data->ioctls[ee].cmd )
+ break;
+ }
+
+ if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
+ DEB_D(("extension handles ioctl exclusive.\n"));
+ result = dev->ext_vv_data->ioctl(fh, cmd, arg);
+ return result;
+ }
+ if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) {
+ DEB_D(("extension handles ioctl before.\n"));
+ result = dev->ext_vv_data->ioctl(fh, cmd, arg);
+ if( -EAGAIN != result ) {
+ return result;
+ }
+ }
+
+ /* fixme: add handle "after" case (is it still needed?) */
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ ops = &saa7146_video_uops;
+ q = &fh->video_q;
+ break;
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ ops = &saa7146_vbi_uops;
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ return 0;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ memset(cap,0,sizeof(*cap));
+
+ DEB_EE(("VIDIOC_QUERYCAP\n"));
+
+ strcpy(cap->driver, "saa7146 v4l2");
+ strlcpy(cap->card, dev->ext->name, sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7146_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->capabilities |= dev->ext_vv_data->capabilities;
+ return 0;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+
+ DEB_EE(("VIDIOC_G_FBUF\n"));
+
+ *fb = vv->ov_fb;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ return 0;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ struct saa7146_format *fmt;
+
+ DEB_EE(("VIDIOC_S_FBUF\n"));
+
+ if(!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
+ if (NULL == fmt) {
+ return -EINVAL;
+ }
+
+ /* planar formats are not allowed for overlay video, clipping and video dma would clash */
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
+ }
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh != fh) {
+ DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+ return -EBUSY;
+ }
+ }
+
+ down(&dev->lock);
+
+ /* ok, accept it */
+ vv->ov_fb = *fb;
+ vv->ov_fmt = fmt;
+ if (0 == vv->ov_fb.fmt.bytesperline)
+ vv->ov_fb.fmt.bytesperline =
+ vv->ov_fb.fmt.width*fmt->depth/8;
+
+ up(&dev->lock);
+
+ return 0;
+ }
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+ int index;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY: {
+ index = f->index;
+ if (index < 0 || index >= NUM_FORMATS) {
+ return -EINVAL;
+ }
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ strlcpy(f->description,formats[index].name,sizeof(f->description));
+ f->pixelformat = formats[index].pixelformat;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index));
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ const struct v4l2_queryctrl *ctrl;
+ struct v4l2_queryctrl *c = arg;
+
+ 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);
+ if( NULL == ctrl ) {
+ return -EINVAL;
+/*
+ c->flags = V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+*/
+ }
+
+ DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
+ *c = *ctrl;
+ return 0;
+ }
+ case VIDIOC_G_CTRL: {
+ DEB_EE(("VIDIOC_G_CTRL\n"));
+ return get_control(fh,arg);
+ }
+ case VIDIOC_S_CTRL:
+ {
+ DEB_EE(("VIDIOC_S_CTRL\n"));
+ err = set_control(fh,arg);
+ return err;
+ }
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_streamparm *parm = arg;
+ if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
+ return -EINVAL;
+ }
+ memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
+ parm->parm.capture.readbuffers = 1;
+ // fixme: only for PAL!
+ parm->parm.capture.timeperframe.numerator = 1;
+ parm->parm.capture.timeperframe.denominator = 25;
+ return 0;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_G_FMT\n"));
+ return g_fmt(fh,f);
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_S_FMT\n"));
+ return s_fmt(fh,f);
+ }
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_TRY_FMT\n"));
+ return try_fmt(fh,f);
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+ DEB_EE(("VIDIOC_G_STD\n"));
+ *id = vv->standard->id;
+ return 0;
+ }
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ if (e->index < 0 )
+ return -EINVAL;
+ if( e->index < dev->ext_vv_data->num_stds ) {
+ DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
+ v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
+ return 0;
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *id = arg;
+ int found = 0;
+ int i, err;
+
+ DEB_EE(("VIDIOC_S_STD\n"));
+
+ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+ DEB_D(("cannot change video standard while streaming capture is active\n"));
+ return -EBUSY;
+ }
+
+ 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 (0 != err) {
+ DEB_D(("suspending video failed. aborting\n"));
+ return err;
+ }
+ }
+
+ down(&dev->lock);
+
+ for(i = 0; i < dev->ext_vv_data->num_stds; i++)
+ if (*id & dev->ext_vv_data->stds[i].id)
+ break;
+ if (i != dev->ext_vv_data->num_stds) {
+ vv->standard = &dev->ext_vv_data->stds[i];
+ if( NULL != dev->ext_vv_data->std_callback )
+ dev->ext_vv_data->std_callback(dev, vv->standard);
+ found = 1;
+ }
+
+ up(&dev->lock);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ if( 0 == found ) {
+ DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name));
+ return 0;
+ }
+ case VIDIOC_OVERLAY:
+
+
+
+
+ {
+ int on = *(int *)arg;
+ int err = 0;
+
+ DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
+ if (on != 0) {
+ err = saa7146_start_preview(fh);
+ } else {
+ err = saa7146_stop_preview(fh);
+ }
+ return err;
+ }
+ case VIDIOC_REQBUFS: {
+ struct v4l2_requestbuffers *req = arg;
+ DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
+ return videobuf_reqbufs(q,req);
+ }
+ case VIDIOC_QUERYBUF: {
+ struct v4l2_buffer *buf = arg;
+ DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
+ return videobuf_querybuf(q,buf);
+ }
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer *buf = arg;
+ int ret = 0;
+ ret = videobuf_qbuf(q,buf);
+ DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
+ return ret;
+ }
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = arg;
+ int ret = 0;
+ ret = videobuf_dqbuf(q,buf,file->f_flags & O_NONBLOCK);
+ DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
+ return ret;
+ }
+ case VIDIOC_STREAMON: {
+ int *type = arg;
+ DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
+
+ err = video_begin(fh);
+ if( 0 != err) {
+ return err;
+ }
+ err = videobuf_streamon(q);
+ return err;
+ }
+ case VIDIOC_STREAMOFF: {
+ int *type = arg;
+
+ DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
+
+ /* ugly: we need to copy some checks from video_end(),
+ because videobuf_streamoff() relies on the capture running.
+ check and fix this */
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S(("not capturing.\n"));
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S(("capturing, but in another open.\n"));
+ return -EBUSY;
+ }
+
+ err = videobuf_streamoff(q);
+ if (0 != err) {
+ DEB_D(("warning: videobuf_streamoff() failed.\n"));
+ video_end(fh, file);
+ } else {
+ err = video_end(fh, file);
+ }
+ return err;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *mbuf = arg;
+ struct videobuf_queue *q;
+ int i;
+
+ /* fixme: number of capture buffers and sizes for v4l apps */
+ int gbuffers = 2;
+ int gbufsize = 768*576*4;
+
+ DEB_D(("VIDIOCGMBUF \n"));
+
+ q = &fh->video_q;
+ down(&q->lock);
+ err = videobuf_mmap_setup(q,gbuffers,gbufsize,
+ V4L2_MEMORY_MMAP);
+ if (err < 0) {
+ up(&q->lock);
+ return err;
+ }
+ memset(mbuf,0,sizeof(*mbuf));
+ mbuf->frames = gbuffers;
+ mbuf->size = gbuffers * gbufsize;
+ for (i = 0; i < gbuffers; i++)
+ mbuf->offsets[i] = i * gbufsize;
+ up(&q->lock);
+ return 0;
+ }
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ saa7146_video_do_ioctl);
+ }
+ return 0;
+}
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate (struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ buf->vb.state = STATE_ACTIVE;
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&vv->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 file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+ int size,err = 0;
+
+ DEB_CAP(("vbuf:%p\n",vb));
+
+ /* sanity checks */
+ if (fh->video_fmt.width < 48 ||
+ fh->video_fmt.height < 32 ||
+ fh->video_fmt.width > vv->standard->h_max_out ||
+ fh->video_fmt.height > vv->standard->v_max_out) {
+ DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height));
+ return -EINVAL;
+ }
+
+ size = fh->video_fmt.sizeimage;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_D(("size mismatch.\n"));
+ return -EINVAL;
+ }
+
+ DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
+ if (buf->vb.width != fh->video_fmt.width ||
+ buf->vb.bytesperline != fh->video_fmt.bytesperline ||
+ buf->vb.height != fh->video_fmt.height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->vb.field != fh->video_fmt.field ||
+ buf->fmt != &fh->video_fmt) {
+ saa7146_dma_free(dev,buf);
+ }
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ struct saa7146_format *sfmt;
+
+ buf->vb.bytesperline = fh->video_fmt.bytesperline;
+ buf->vb.width = fh->video_fmt.width;
+ buf->vb.height = fh->video_fmt.height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = &fh->video_fmt;
+ buf->vb.field = fh->video_fmt.field;
+
+ sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ err = videobuf_iolock(dev->pci,&buf->vb, &vv->ov_fb);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build(dev,buf);
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_D(("error out.\n"));
+ saa7146_dma_free(dev,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+
+ if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
+ *count = MAX_SAA7146_CAPTURE_BUFFERS;
+
+ *size = fh->video_fmt.sizeimage;
+
+ /* check if we exceed the "max_memory" parameter */
+ if( (*count * *size) > (max_memory*1048576) ) {
+ *count = (max_memory*1048576) / *size;
+ }
+
+ DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size));
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP(("vbuf:%p\n",vb));
+ saa7146_buffer_queue(fh->dev,&vv->video_q,buf);
+}
+
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP(("vbuf:%p\n",vb));
+ saa7146_dma_free(dev,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************************************************************************/
+/* file operations */
+
+static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ INIT_LIST_HEAD(&vv->video_q.queue);
+
+ init_timer(&vv->video_q.timeout);
+ vv->video_q.timeout.function = saa7146_buffer_timeout;
+ vv->video_q.timeout.data = (unsigned long)(&vv->video_q);
+ vv->video_q.dev = dev;
+
+ /* set some default values */
+ vv->standard = &dev->ext_vv_data->stds[0];
+
+ /* FIXME: what's this? */
+ vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+}
+
+
+static int video_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
+ struct saa7146_format *sfmt;
+
+ fh->video_fmt.width = 384;
+ fh->video_fmt.height = 288;
+ fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24;
+ fh->video_fmt.bytesperline = 0;
+ fh->video_fmt.field = V4L2_FIELD_ANY;
+ sfmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8;
+
+ videobuf_queue_init(&fh->video_q, &video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7146_buf),
+ file);
+
+ init_MUTEX(&fh->video_q.lock);
+
+ return 0;
+}
+
+
+static void video_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ err = video_end(fh, file);
+ } else if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ err = saa7146_stop_preview(fh);
+ }
+
+ /* hmm, why is this function declared void? */
+ /* return err */
+}
+
+
+static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_q;
+
+ spin_lock(&dev->slock);
+ DEB_CAP(("called.\n"));
+
+ /* only finish the buffer if we have one... */
+ if( NULL != q->curr ) {
+ saa7146_buffer_finish(dev,q,STATE_DONE);
+ }
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_EE(("called.\n"));
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ /* fixme: should we allow read() captures while streaming capture? */
+ if (vv->video_fh == fh) {
+ DEB_S(("already capturing.\n"));
+ return -EBUSY;
+ }
+ DEB_S(("already capturing in another open.\n"));
+ return -EBUSY;
+ }
+
+ ret = video_begin(fh);
+ if( 0 != ret) {
+ goto out;
+ }
+
+ ret = videobuf_read_one(&fh->video_q , data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (ret != 0) {
+ video_end(fh, file);
+ } else {
+ ret = video_end(fh, file);
+ }
+out:
+ /* restart overlay if it was active before */
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .open = video_open,
+ .release = video_close,
+ .irq_done = video_irq_done,
+ .read = video_read,
+};
diff --git a/drivers/media/common/saa7146_vv_ksyms.c b/drivers/media/common/saa7146_vv_ksyms.c
new file mode 100644
index 000000000000..62226eb4753b
--- /dev/null
+++ b/drivers/media/common/saa7146_vv_ksyms.c
@@ -0,0 +1,12 @@
+#include <linux/module.h>
+#include <media/saa7146_vv.h>
+
+EXPORT_SYMBOL_GPL(saa7146_start_preview);
+EXPORT_SYMBOL_GPL(saa7146_stop_preview);
+
+EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
+EXPORT_SYMBOL_GPL(saa7146_register_device);
+EXPORT_SYMBOL_GPL(saa7146_unregister_device);
+
+EXPORT_SYMBOL_GPL(saa7146_vv_init);
+EXPORT_SYMBOL_GPL(saa7146_vv_release);
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
new file mode 100644
index 000000000000..883ec08490f4
--- /dev/null
+++ b/drivers/media/dvb/Kconfig
@@ -0,0 +1,47 @@
+#
+# Multimedia device configuration
+#
+
+menu "Digital Video Broadcasting Devices"
+
+config DVB
+ bool "DVB For Linux"
+ depends on NET && INET
+ ---help---
+ Support Digital Video Broadcasting hardware. Enable this if you
+ own a DVB adapter and want to use it or if you compile Linux for
+ a digital SetTopBox.
+
+ API specs and user tools are available from <http://www.linuxtv.org/>.
+
+ Please report problems regarding this driver to the LinuxDVB
+ mailing list.
+
+ If unsure say N.
+
+source "drivers/media/dvb/dvb-core/Kconfig"
+
+comment "Supported SAA7146 based PCI Adapters"
+ depends on DVB_CORE && PCI
+source "drivers/media/dvb/ttpci/Kconfig"
+
+comment "Supported USB Adapters"
+ depends on DVB_CORE && USB
+source "drivers/media/dvb/ttusb-budget/Kconfig"
+source "drivers/media/dvb/ttusb-dec/Kconfig"
+source "drivers/media/dvb/dibusb/Kconfig"
+source "drivers/media/dvb/cinergyT2/Kconfig"
+
+comment "Supported FlexCopII (B2C2) Adapters"
+ depends on DVB_CORE && PCI
+source "drivers/media/dvb/b2c2/Kconfig"
+
+comment "Supported BT878 Adapters"
+ depends on DVB_CORE && PCI
+source "drivers/media/dvb/bt8xx/Kconfig"
+
+comment "Supported DVB Frontends"
+ depends on DVB_CORE
+source "drivers/media/dvb/frontends/Kconfig"
+
+endmenu
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
new file mode 100644
index 000000000000..520fc3902819
--- /dev/null
+++ b/drivers/media/dvb/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel multimedia device drivers.
+#
+
+obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ cinergyT2/
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
new file mode 100644
index 000000000000..52596907a0be
--- /dev/null
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -0,0 +1,26 @@
+config DVB_B2C2_SKYSTAR
+ tristate "B2C2/Technisat Air/Sky/CableStar 2 PCI"
+ depends on DVB_CORE && PCI
+ select DVB_STV0299
+ select DVB_MT352
+ select DVB_MT312
+ select DVB_NXT2002
+ help
+ Support for the Skystar2 PCI DVB card by Technisat, which
+ is equipped with the FlexCopII chipset by B2C2, and
+ for the B2C2/BBTI Air2PC-ATSC card.
+
+ Say Y if you own such a device and want to use it.
+
+config DVB_B2C2_USB
+ tristate "B2C2/Technisat Air/Sky/Cable2PC USB"
+ depends on DVB_CORE && USB && EXPERIMENTAL
+ select DVB_STV0299
+ select DVB_MT352
+ help
+ Support for the Air/Sky/Cable2PC USB DVB device by B2C2. Currently
+ the does nothing, but providing basic function for the used usb
+ protocol.
+
+ Say Y if you own such a device and want to use it.
+
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
new file mode 100644
index 000000000000..9fb1247bfab8
--- /dev/null
+++ b/drivers/media/dvb/b2c2/Makefile
@@ -0,0 +1,6 @@
+obj-b2c2-usb = b2c2-usb-core.o b2c2-common.o
+
+obj-$(CONFIG_DVB_B2C2_SKYSTAR) += skystar2.o
+obj-$(CONFIG_DVB_B2C2_USB) + = b2c2-usb.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/b2c2/b2c2-common.c b/drivers/media/dvb/b2c2/b2c2-common.c
new file mode 100644
index 000000000000..000d60c405e3
--- /dev/null
+++ b/drivers/media/dvb/b2c2/b2c2-common.c
@@ -0,0 +1,214 @@
+/*
+ * b2c2-common.c - common methods for the B2C2/Technisat SkyStar2 PCI DVB card and
+ * for the B2C2/Technisat Sky/Cable/AirStar USB devices
+ * based on the FlexCopII/FlexCopIII by B2C2, Inc.
+ *
+ * Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
+ * FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
+ * Vincenzo Di Massa, hawk.it at tiscalinet.it
+ *
+ * Converted to Linux coding style
+ * Misc reorganization, polishing, restyling
+ * Roberto Ragusa, r.ragusa at libero.it
+ *
+ * Added hardware filtering support,
+ * Niklas Peinecke, peinecke at gdv.uni-hannover.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "stv0299.h"
+#include "mt352.h"
+#include "mt312.h"
+
+static int samsung_tbmu24112_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 int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+// struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ div = params->frequency / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x84; // 0xC4
+ buf[3] = 0x08;
+
+ if (params->frequency < 1500000) buf[3] |= 0x10;
+
+// if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x35,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0xC3,
+ 0x0C, 0x00,
+ 0x0D, 0x81,
+ 0x0E, 0x23,
+ 0x0F, 0x12,
+ 0x10, 0x7E,
+ 0x11, 0x84,
+ 0x12, 0xB9,
+ 0x13, 0x88,
+ 0x14, 0x89,
+ 0x15, 0xC9,
+ 0x16, 0x00,
+ 0x17, 0x5C,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1A, 0x00,
+ 0x1C, 0x00,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x3A,
+ 0x20, 0x2E,
+ 0x21, 0x80,
+ 0x22, 0xFF,
+ 0x23, 0xC1,
+ 0x28, 0x00,
+ 0x29, 0x1E,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFE,
+ 0x34, 0x93,
+ 0xff, 0xff,
+};
+
+static struct stv0299_config samsung_tbmu24112_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_tbmu24112_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .enhanced_tuning = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0229_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+ .pll_set = samsung_tbmu24112_pll_set,
+};
+
+
+
+
+
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+ static u8 mt352_clock_config [] = { 0x89, 0x10, 0x2d };
+ static u8 mt352_reset [] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
+ 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 samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+ u32 div;
+ unsigned char bs = 0;
+
+ #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+ div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
+ if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
+ if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
+
+ pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = 0xcc;
+ pllbuf[4] = bs;
+
+ return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+
+ .demod_address = 0x0f,
+ .demod_init = samsung_tdtc9251dh0_demod_init,
+ .pll_set = samsung_tdtc9251dh0_pll_set,
+};
+
+static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+// struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ div = (params->frequency + (125/2)) / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = (div >> 0) & 0xff;
+ buf[2] = 0x84 | ((div >> 10) & 0x60);
+ buf[3] = 0x80;
+
+ if (params->frequency < 1550000)
+ buf[3] |= 0x02;
+
+ //if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+
+ .demod_address = 0x0e,
+ .pll_set = skystar23_samsung_tbdu18132_pll_set,
+};
diff --git a/drivers/media/dvb/b2c2/b2c2-usb-core.c b/drivers/media/dvb/b2c2/b2c2-usb-core.c
new file mode 100644
index 000000000000..9306da046c91
--- /dev/null
+++ b/drivers/media/dvb/b2c2/b2c2-usb-core.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2004 Patrick Boettcher <patrick.boettcher@desy.de>,
+ * Luca Bertagnolio <>,
+ *
+ * based on information provided by John Jurrius from BBTI, Inc.
+ *
+ * This program is free software; 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+/* debug */
+#define dprintk(level,args...) \
+ do { if ((debug & level)) { printk(args); } } while (0)
+#define debug_dump(b,l) if (debug) {\
+ int i; deb_xfer("%s: %d > ",__FUNCTION__,l); \
+ for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
+ deb_xfer("\n");\
+}
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4 (or-able)).");
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_ts(args...) dprintk(0x02,args)
+#define deb_ctrl(args...) dprintk(0x04,args)
+
+/* Version information */
+#define DRIVER_VERSION "0.0"
+#define DRIVER_DESC "Driver for B2C2/Technisat Air/Cable/Sky-2-PC USB devices"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+/* transfer parameters */
+#define B2C2_USB_FRAMES_PER_ISO 4
+#define B2C2_USB_NUM_ISO_URB 4 /* TODO check out a good value */
+
+#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(b2c2->udev,0)
+#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(b2c2->udev,0)
+#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(b2c2->udev,0x81)
+
+struct usb_b2c2_usb {
+ struct usb_device *udev;
+ struct usb_interface *uintf;
+
+ u8 *iso_buffer;
+ int buffer_size;
+ dma_addr_t iso_dma_handle;
+ struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
+};
+
+
+/*
+ * USB
+ * 10 90 34 12 78 56 04 00
+ * usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+ * 0x90,
+ * 0x10,
+ * 0x1234,
+ * 0x5678,
+ * buf,
+ * 4,
+ * 5*HZ);
+ *
+ * extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
+ * __u8 request,
+ * __u8 requesttype,
+ * __u16 value,
+ * __u16 index,
+ * void *data,
+ * __u16 size,
+ * int timeout);
+ *
+ */
+
+/* request types */
+typedef enum {
+
+/* something is wrong with this part
+ RTYPE_READ_DW = (1 << 6),
+ RTYPE_WRITE_DW_1 = (3 << 6),
+ RTYPE_READ_V8_MEMORY = (6 << 6),
+ RTYPE_WRITE_V8_MEMORY = (7 << 6),
+ RTYPE_WRITE_V8_FLASH = (8 << 6),
+ RTYPE_GENERIC = (9 << 6),
+*/
+ RTYPE_READ_DW = (3 << 6),
+ RTYPE_WRITE_DW_1 = (1 << 6),
+
+ RTYPE_READ_V8_MEMORY = (6 << 6),
+ RTYPE_WRITE_V8_MEMORY = (7 << 6),
+ RTYPE_WRITE_V8_FLASH = (8 << 6),
+ RTYPE_GENERIC = (9 << 6),
+} b2c2_usb_request_type_t;
+
+/* request */
+typedef enum {
+ B2C2_USB_WRITE_V8_MEM = 0x04,
+ B2C2_USB_READ_V8_MEM = 0x05,
+ B2C2_USB_READ_REG = 0x08,
+ B2C2_USB_WRITE_REG = 0x0A,
+/* B2C2_USB_WRITEREGLO = 0x0A, */
+ B2C2_USB_WRITEREGHI = 0x0B,
+ B2C2_USB_FLASH_BLOCK = 0x10,
+ B2C2_USB_I2C_REQUEST = 0x11,
+ B2C2_USB_UTILITY = 0x12,
+} b2c2_usb_request_t;
+
+/* function definition for I2C_REQUEST */
+typedef enum {
+ USB_FUNC_I2C_WRITE = 0x01,
+ USB_FUNC_I2C_MULTIWRITE = 0x02,
+ USB_FUNC_I2C_READ = 0x03,
+ USB_FUNC_I2C_REPEATWRITE = 0x04,
+ USB_FUNC_GET_DESCRIPTOR = 0x05,
+ USB_FUNC_I2C_REPEATREAD = 0x06,
+/* DKT 020208 - add this to support special case of DiSEqC */
+ USB_FUNC_I2C_CHECKWRITE = 0x07,
+ USB_FUNC_I2C_CHECKRESULT = 0x08,
+} b2c2_usb_i2c_function_t;
+
+/*
+ * function definition for UTILITY request 0x12
+ * DKT 020304 - new utility function
+ */
+typedef enum {
+ UTILITY_SET_FILTER = 0x01,
+ UTILITY_DATA_ENABLE = 0x02,
+ UTILITY_FLEX_MULTIWRITE = 0x03,
+ UTILITY_SET_BUFFER_SIZE = 0x04,
+ UTILITY_FLEX_OPERATOR = 0x05,
+ UTILITY_FLEX_RESET300_START = 0x06,
+ UTILITY_FLEX_RESET300_STOP = 0x07,
+ UTILITY_FLEX_RESET300 = 0x08,
+ UTILITY_SET_ISO_SIZE = 0x09,
+ UTILITY_DATA_RESET = 0x0A,
+ UTILITY_GET_DATA_STATUS = 0x10,
+ UTILITY_GET_V8_REG = 0x11,
+/* DKT 020326 - add function for v1.14 */
+ UTILITY_SRAM_WRITE = 0x12,
+ UTILITY_SRAM_READ = 0x13,
+ UTILITY_SRAM_TESTFILL = 0x14,
+ UTILITY_SRAM_TESTSET = 0x15,
+ UTILITY_SRAM_TESTVERIFY = 0x16,
+} b2c2_usb_utility_function_t;
+
+#define B2C2_WAIT_FOR_OPERATION_RW 1 // 1 s
+#define B2C2_WAIT_FOR_OPERATION_RDW 3 // 3 s
+#define B2C2_WAIT_FOR_OPERATION_WDW 1 // 1 s
+
+#define B2C2_WAIT_FOR_OPERATION_V8READ 3 // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8WRITE 3 // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8FLASH 3 // 3 s
+
+/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
+ * in the IBI address, to make the V8 code simpler.
+ * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
+ * in general: 0000 0HHH 000L LL00
+ * IBI ADDRESS FORMAT: RHHH BLLL
+ *
+ * where R is the read(1)/write(0) bit, B is the busy bit
+ * and HHH and LLL are the two sets of three bits from the PCI address.
+ */
+#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
+#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
+
+/*
+ * DKT 020228 - forget about this VENDOR_BUFFER_SIZE, read and write register
+ * deal with DWORD or 4 bytes, that should be should from now on
+ */
+static u32 b2c2_usb_read_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI)
+{
+ u32 val;
+ u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | 0x0080;
+ int len = usb_control_msg(b2c2->udev,
+ B2C2_USB_CTRL_PIPE_IN,
+ B2C2_USB_READ_REG,
+ RTYPE_READ_DW,
+ wAddress,
+ 0,
+ &val,
+ sizeof(u32),
+ B2C2_WAIT_FOR_OPERATION_RDW * 1000);
+
+ if (len != sizeof(u32)) {
+ err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+ return -EIO;
+ } else
+ return val;
+}
+
+/*
+ * DKT 020228 - from now on, we don't support anything older than firm 1.00
+ * I eliminated the write register as a 2 trip of writing hi word and lo word
+ * and force this to write only 4 bytes at a time.
+ * NOTE: this should work with all the firmware from 1.00 and newer
+ */
+static int b2c2_usb_write_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI, u32 val)
+{
+ u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI);
+ int len = usb_control_msg(b2c2->udev,
+ B2C2_USB_CTRL_PIPE_OUT,
+ B2C2_USB_WRITE_REG,
+ RTYPE_WRITE_DW_1,
+ wAddress,
+ 0,
+ &val,
+ sizeof(u32),
+ B2C2_WAIT_FOR_OPERATION_RDW * 1000);
+
+ if (len != sizeof(u32)) {
+ err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+ return -EIO;
+ } else
+ return 0;
+}
+
+/*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+static int b2c2_usb_v8_memory_req(struct usb_b2c2_usb *b2c2,
+ b2c2_usb_request_t req, u8 page, u16 wAddress,
+ u16 buflen, u8 *pbBuffer)
+{
+ u8 dwRequestType;
+ u16 wIndex;
+ int nWaitTime,pipe,len;
+
+ wIndex = page << 8;
+
+ switch (req) {
+ case B2C2_USB_READ_V8_MEM:
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+ dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
+ pipe = B2C2_USB_CTRL_PIPE_IN;
+ break;
+ case B2C2_USB_WRITE_V8_MEM:
+ wIndex |= pbBuffer[0];
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+ dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+ case B2C2_USB_FLASH_BLOCK:
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
+ dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+ default:
+ deb_info("unsupported request for v8_mem_req %x.\n",req);
+ return -EINVAL;
+ }
+ len = usb_control_msg(b2c2->udev,pipe,
+ req,
+ dwRequestType,
+ wAddress,
+ wIndex,
+ pbBuffer,
+ buflen,
+ nWaitTime * 1000);
+ return len == buflen ? 0 : -EIO;
+}
+
+static int b2c2_usb_i2c_req(struct usb_b2c2_usb *b2c2,
+ b2c2_usb_request_t req, b2c2_usb_i2c_function_t func,
+ u8 port, u8 chipaddr, u8 addr, u8 buflen, u8 *buf)
+{
+ u16 wValue, wIndex;
+ int nWaitTime,pipe,len;
+ u8 dwRequestType;
+
+ switch (func) {
+ case USB_FUNC_I2C_WRITE:
+ case USB_FUNC_I2C_MULTIWRITE:
+ case USB_FUNC_I2C_REPEATWRITE:
+ /* DKT 020208 - add this to support special case of DiSEqC */
+ case USB_FUNC_I2C_CHECKWRITE:
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ nWaitTime = 2;
+ dwRequestType = (u8) RTYPE_GENERIC;
+ break;
+ case USB_FUNC_I2C_READ:
+ case USB_FUNC_I2C_REPEATREAD:
+ pipe = B2C2_USB_CTRL_PIPE_IN;
+ nWaitTime = 2;
+ dwRequestType = (u8) RTYPE_GENERIC;
+ break;
+ default:
+ deb_info("unsupported function for i2c_req %x\n",func);
+ return -EINVAL;
+ }
+ wValue = (func << 8 ) | port;
+ wIndex = (chipaddr << 8 ) | addr;
+
+ len = usb_control_msg(b2c2->udev,pipe,
+ req,
+ dwRequestType,
+ addr,
+ wIndex,
+ buf,
+ buflen,
+ nWaitTime * 1000);
+ return len == buflen ? 0 : -EIO;
+}
+
+int static b2c2_usb_utility_req(struct usb_b2c2_usb *b2c2, int set,
+ b2c2_usb_utility_function_t func, u8 extra, u16 wIndex,
+ u16 buflen, u8 *pvBuffer)
+{
+ u16 wValue;
+ int nWaitTime = 2,
+ pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
+ len;
+
+ wValue = (func << 8) | extra;
+
+ len = usb_control_msg(b2c2->udev,pipe,
+ B2C2_USB_UTILITY,
+ (u8) RTYPE_GENERIC,
+ wValue,
+ wIndex,
+ pvBuffer,
+ buflen,
+ nWaitTime * 1000);
+ return len == buflen ? 0 : -EIO;
+}
+
+
+
+static void b2c2_dumpfourreg(struct usb_b2c2_usb *b2c2, u16 offs)
+{
+ u32 r0,r1,r2,r3;
+ r0 = r1 = r2 = r3 = 0;
+ r0 = b2c2_usb_read_dw(b2c2,offs);
+ r1 = b2c2_usb_read_dw(b2c2,offs + 0x04);
+ r2 = b2c2_usb_read_dw(b2c2,offs + 0x08);
+ r3 = b2c2_usb_read_dw(b2c2,offs + 0x0c);
+ deb_ctrl("dump: offset: %03x, %08x, %08x, %08x, %08x\n",offs,r0,r1,r2,r3);
+}
+
+static void b2c2_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+ struct usb_b2c2_usb *b2c2 = urb->context;
+ deb_ts("urb completed, bufsize: %d\n",urb->transfer_buffer_length);
+
+// urb_submit_urb(urb,GFP_ATOMIC); enable for real action
+}
+
+static void b2c2_exit_usb(struct usb_b2c2_usb *b2c2)
+{
+ int i;
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+ if (b2c2->iso_urb[i] != NULL) { /* not sure about unlink_urb and iso-urbs TODO */
+ deb_info("unlinking/killing urb no. %d\n",i);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7)
+ usb_unlink_urb(b2c2->iso_urb[i]);
+#else
+ usb_kill_urb(b2c2->iso_urb[i]);
+#endif
+ usb_free_urb(b2c2->iso_urb[i]);
+ }
+
+ if (b2c2->iso_buffer != NULL)
+ pci_free_consistent(NULL,b2c2->buffer_size, b2c2->iso_buffer, b2c2->iso_dma_handle);
+
+}
+
+static int b2c2_init_usb(struct usb_b2c2_usb *b2c2)
+{
+ u16 frame_size = le16_to_cpu(b2c2->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
+ int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
+ int buffer_offset = 0;
+
+ deb_info("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
+ B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
+
+ b2c2->iso_buffer = pci_alloc_consistent(NULL,bufsize,&b2c2->iso_dma_handle);
+ if (b2c2->iso_buffer == NULL)
+ return -ENOMEM;
+ memset(b2c2->iso_buffer, 0, bufsize);
+ b2c2->buffer_size = bufsize;
+
+ /* creating iso urbs */
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+ if (!(b2c2->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
+ ret = -ENOMEM;
+ goto urb_error;
+ }
+ /* initialising and submitting iso urbs */
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+ int frame_offset = 0;
+ struct urb *urb = b2c2->iso_urb[i];
+ deb_info("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
+
+ urb->dev = b2c2->udev;
+ urb->context = b2c2;
+ urb->complete = b2c2_urb_complete;
+ urb->pipe = B2C2_USB_DATA_PIPE;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->interval = 1;
+ urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
+ urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
+ urb->transfer_buffer = b2c2->iso_buffer + buffer_offset;
+
+ buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+ for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+ deb_info("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
+ urb->iso_frame_desc[j].offset = frame_offset;
+ urb->iso_frame_desc[j].length = frame_size;
+ frame_offset += frame_size;
+ }
+
+ if ((ret = usb_submit_urb(b2c2->iso_urb[i],GFP_ATOMIC))) {
+ err("submitting urb %d failed with %d.",i,ret);
+ goto urb_error;
+ }
+ deb_info("submitted urb no. %d.\n",i);
+ }
+
+ ret = 0;
+ goto success;
+urb_error:
+ b2c2_exit_usb(b2c2);
+success:
+ return ret;
+}
+
+static int b2c2_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_b2c2_usb *b2c2 = NULL;
+ int ret;
+
+ b2c2 = kmalloc(sizeof(struct usb_b2c2_usb),GFP_KERNEL);
+ if (b2c2 == NULL) {
+ err("no memory");
+ return -ENOMEM;
+ }
+ b2c2->udev = udev;
+ b2c2->uintf = intf;
+
+ /* use the alternate setting with the larges buffer */
+ usb_set_interface(udev,0,1);
+
+ if ((ret = b2c2_init_usb(b2c2)))
+ goto usb_init_error;
+
+ usb_set_intfdata(intf,b2c2);
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ err("cannot handle USB speed because it is to sLOW.");
+ break;
+ case USB_SPEED_FULL:
+ info("running at FULL speed.");
+ break;
+ case USB_SPEED_HIGH:
+ info("running at HIGH speed.");
+ break;
+ case USB_SPEED_UNKNOWN: /* fall through */
+ default:
+ err("cannot handle USB speed because it is unkown.");
+ break;
+ }
+
+ b2c2_dumpfourreg(b2c2,0x200);
+ b2c2_dumpfourreg(b2c2,0x300);
+ b2c2_dumpfourreg(b2c2,0x400);
+ b2c2_dumpfourreg(b2c2,0x700);
+
+
+ if (ret == 0)
+ info("%s successfully initialized and connected.",DRIVER_DESC);
+ else
+ info("%s error while loading driver (%d)",DRIVER_DESC,ret);
+
+ ret = 0;
+ goto success;
+
+usb_init_error:
+ kfree(b2c2);
+success:
+ return ret;
+}
+
+static void b2c2_usb_disconnect(struct usb_interface *intf)
+{
+ struct usb_b2c2_usb *b2c2 = usb_get_intfdata(intf);
+ usb_set_intfdata(intf,NULL);
+ if (b2c2 != NULL) {
+ b2c2_exit_usb(b2c2);
+ kfree(b2c2);
+ }
+ info("%s successfully deinitialized and disconnected.",DRIVER_DESC);
+
+}
+
+static struct usb_device_id b2c2_usb_table [] = {
+ { USB_DEVICE(0x0af7, 0x0101) }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver b2c2_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = "dvb_b2c2_usb",
+ .probe = b2c2_usb_probe,
+ .disconnect = b2c2_usb_disconnect,
+ .id_table = b2c2_usb_table,
+};
+
+/* module stuff */
+static int __init b2c2_usb_init(void)
+{
+ int result;
+ if ((result = usb_register(&b2c2_usb_driver))) {
+ err("usb_register failed. Error number %d",result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit b2c2_usb_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&b2c2_usb_driver);
+}
+
+module_init (b2c2_usb_init);
+module_exit (b2c2_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, b2c2_usb_table);
diff --git a/drivers/media/dvb/b2c2/skystar2.c b/drivers/media/dvb/b2c2/skystar2.c
new file mode 100644
index 000000000000..336c178fcd5f
--- /dev/null
+++ b/drivers/media/dvb/b2c2/skystar2.c
@@ -0,0 +1,2644 @@
+/*
+ * skystar2.c - driver for the Technisat SkyStar2 PCI DVB card
+ * based on the FlexCopII by B2C2,Inc.
+ *
+ * Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
+ * FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
+ * Vincenzo Di Massa, hawk.it at tiscalinet.it
+ *
+ * Converted to Linux coding style
+ * Misc reorganization, polishing, restyling
+ * Roberto Ragusa, skystar2-c5b8 at robertoragusa dot it
+ *
+ * Added hardware filtering support,
+ * Niklas Peinecke, peinecke at gdv.uni-hannover.de
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#include "dvb_frontend.h"
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_net.h"
+#include "stv0299.h"
+#include "mt352.h"
+#include "mt312.h"
+#include "nxt2002.h"
+
+static int debug;
+static int enable_hw_filters = 2;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Set debugging level (0 = default, 1 = most messages, 2 = all messages).");
+module_param(enable_hw_filters, int, 0444);
+MODULE_PARM_DESC(enable_hw_filters, "enable hardware filters: supported values: 0 (none), 1, 2");
+
+#define dprintk(x...) do { if (debug>=1) printk(x); } while (0)
+#define ddprintk(x...) do { if (debug>=2) printk(x); } while (0)
+
+#define SIZE_OF_BUF_DMA1 0x3ac00
+#define SIZE_OF_BUF_DMA2 0x758
+
+#define MAX_N_HW_FILTERS (6+32)
+#define N_PID_SLOTS 256
+
+struct dmaq {
+ u32 bus_addr;
+ u32 head;
+ u32 tail;
+ u32 buffer_size;
+ u8 *buffer;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
+#define __iomem
+#endif
+
+struct adapter {
+ struct pci_dev *pdev;
+
+ u8 card_revision;
+ u32 b2c2_revision;
+ u32 pid_filter_max;
+ u32 mac_filter_max;
+ u32 irq;
+ void __iomem *io_mem;
+ unsigned long io_port;
+ u8 mac_addr[8];
+ u32 dw_sram_type;
+
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct i2c_adapter i2c_adap;
+ struct dvb_net dvbnet;
+
+ struct semaphore i2c_sem;
+
+ struct dmaq dmaq1;
+ struct dmaq dmaq2;
+
+ u32 dma_ctrl;
+ u32 dma_status;
+
+ int capturing;
+
+ spinlock_t lock;
+
+ int useable_hw_filters;
+ u16 hw_pids[MAX_N_HW_FILTERS];
+ u16 pid_list[N_PID_SLOTS];
+ int pid_rc[N_PID_SLOTS]; // ref counters for the pids
+ int pid_count;
+ int whole_bandwidth_count;
+ u32 mac_filter;
+
+ struct dvb_frontend* fe;
+ int (*fe_sleep)(struct dvb_frontend* fe);
+};
+
+#define write_reg_dw(adapter,reg,value) writel(value, adapter->io_mem + reg)
+#define read_reg_dw(adapter,reg) readl(adapter->io_mem + reg)
+
+static void write_reg_bitfield(struct adapter *adapter, u32 reg, u32 zeromask, u32 orvalue)
+{
+ u32 tmp;
+
+ tmp = read_reg_dw(adapter, reg);
+ tmp = (tmp & ~zeromask) | orvalue;
+ write_reg_dw(adapter, reg, tmp);
+}
+
+/* i2c functions */
+static int i2c_main_write_for_flex2(struct adapter *adapter, u32 command, u8 *buf, int retries)
+{
+ int i;
+ u32 value;
+
+ write_reg_dw(adapter, 0x100, 0);
+ write_reg_dw(adapter, 0x100, command);
+
+ for (i = 0; i < retries; i++) {
+ value = read_reg_dw(adapter, 0x100);
+
+ if ((value & 0x40000000) == 0) {
+ if ((value & 0x81000000) == 0x80000000) {
+ if (buf != 0)
+ *buf = (value >> 0x10) & 0xff;
+
+ return 1;
+ }
+ } else {
+ write_reg_dw(adapter, 0x100, 0);
+ write_reg_dw(adapter, 0x100, command);
+ }
+ }
+
+ return 0;
+}
+
+/* device = 0x10000000 for tuner, 0x20000000 for eeprom */
+static void i2c_main_setup(u32 device, u32 chip_addr, u8 op, u8 addr, u32 value, u32 len, u32 *command)
+{
+ *command = device | ((len - 1) << 26) | (value << 16) | (addr << 8) | chip_addr;
+
+ if (op != 0)
+ *command = *command | 0x03000000;
+ else
+ *command = *command | 0x01000000;
+}
+
+static int flex_i2c_read4(struct adapter *adapter, u32 device, u32 chip_addr, u16 addr, u8 *buf, u8 len)
+{
+ u32 command;
+ u32 value;
+
+ int result, i;
+
+ i2c_main_setup(device, chip_addr, 1, addr, 0, len, &command);
+
+ result = i2c_main_write_for_flex2(adapter, command, buf, 100000);
+
+ if ((result & 0xff) != 0) {
+ if (len > 1) {
+ value = read_reg_dw(adapter, 0x104);
+
+ for (i = 1; i < len; i++) {
+ buf[i] = value & 0xff;
+ value = value >> 8;
+ }
+ }
+ }
+
+ return result;
+}
+
+static int flex_i2c_write4(struct adapter *adapter, u32 device, u32 chip_addr, u32 addr, u8 *buf, u8 len)
+{
+ u32 command;
+ u32 value;
+ int i;
+
+ if (len > 1) {
+ value = 0;
+
+ for (i = len; i > 1; i--) {
+ value = value << 8;
+ value = value | buf[i - 1];
+ }
+
+ write_reg_dw(adapter, 0x104, value);
+ }
+
+ i2c_main_setup(device, chip_addr, 0, addr, buf[0], len, &command);
+
+ return i2c_main_write_for_flex2(adapter, command, NULL, 100000);
+}
+
+static void fixchipaddr(u32 device, u32 bus, u32 addr, u32 *ret)
+{
+ if (device == 0x20000000)
+ *ret = bus | ((addr >> 8) & 3);
+ else
+ *ret = bus;
+}
+
+static u32 flex_i2c_read(struct adapter *adapter, u32 device, u32 bus, u32 addr, u8 *buf, u32 len)
+{
+ u32 chipaddr;
+ u32 bytes_to_transfer;
+ u8 *start;
+
+ ddprintk("%s:\n", __FUNCTION__);
+
+ start = buf;
+
+ while (len != 0) {
+ bytes_to_transfer = len;
+
+ if (bytes_to_transfer > 4)
+ bytes_to_transfer = 4;
+
+ fixchipaddr(device, bus, addr, &chipaddr);
+
+ if (flex_i2c_read4(adapter, device, chipaddr, addr, buf, bytes_to_transfer) == 0)
+ return buf - start;
+
+ buf = buf + bytes_to_transfer;
+ addr = addr + bytes_to_transfer;
+ len = len - bytes_to_transfer;
+ };
+
+ return buf - start;
+}
+
+static u32 flex_i2c_write(struct adapter *adapter, u32 device, u32 bus, u32 addr, u8 *buf, u32 len)
+{
+ u32 chipaddr;
+ u32 bytes_to_transfer;
+ u8 *start;
+
+ ddprintk("%s:\n", __FUNCTION__);
+
+ start = buf;
+
+ while (len != 0) {
+ bytes_to_transfer = len;
+
+ if (bytes_to_transfer > 4)
+ bytes_to_transfer = 4;
+
+ fixchipaddr(device, bus, addr, &chipaddr);
+
+ if (flex_i2c_write4(adapter, device, chipaddr, addr, buf, bytes_to_transfer) == 0)
+ return buf - start;
+
+ buf = buf + bytes_to_transfer;
+ addr = addr + bytes_to_transfer;
+ len = len - bytes_to_transfer;
+ }
+
+ return buf - start;
+}
+
+static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msgs, int num)
+{
+ struct adapter *tmp = i2c_get_adapdata(adapter);
+ int i, ret = 0;
+
+ if (down_interruptible(&tmp->i2c_sem))
+ return -ERESTARTSYS;
+
+ ddprintk("%s: %d messages to transfer\n", __FUNCTION__, num);
+
+ for (i = 0; i < num; i++) {
+ ddprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i,
+ msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+ }
+
+ // read command
+ if ((num == 2) && (msgs[0].flags == 0) && (msgs[1].flags == I2C_M_RD) && (msgs[0].buf != NULL) && (msgs[1].buf != NULL)) {
+
+ ret = flex_i2c_read(tmp, 0x10000000, msgs[0].addr, msgs[0].buf[0], msgs[1].buf, msgs[1].len);
+
+ up(&tmp->i2c_sem);
+
+ if (ret != msgs[1].len) {
+ dprintk("%s: read error !\n", __FUNCTION__);
+
+ for (i = 0; i < 2; i++) {
+ dprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i,
+ msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+ }
+
+ return -EREMOTEIO;
+ }
+
+ return num;
+ }
+ // write command
+ for (i = 0; i < num; i++) {
+
+ if ((msgs[i].flags != 0) || (msgs[i].buf == NULL) || (msgs[i].len < 2))
+ return -EINVAL;
+
+ ret = flex_i2c_write(tmp, 0x10000000, msgs[i].addr, msgs[i].buf[0], &msgs[i].buf[1], msgs[i].len - 1);
+
+ up(&tmp->i2c_sem);
+
+ if (ret != msgs[0].len - 1) {
+ dprintk("%s: write error %i !\n", __FUNCTION__, ret);
+
+ dprintk("message %d: flags=0x%x, addr=0x%x, buf[0]=0x%x, len=%d \n", i,
+ msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+
+ return -EREMOTEIO;
+ }
+
+ return num;
+ }
+
+ printk("%s: unknown command format !\n", __FUNCTION__);
+
+ return -EINVAL;
+}
+
+/* SRAM (Skystar2 rev2.3 has one "ISSI IS61LV256" chip on board,
+ but it seems that FlexCopII can work with more than one chip) */
+static void sram_set_net_dest(struct adapter *adapter, u8 dest)
+{
+ u32 tmp;
+
+ udelay(1000);
+
+ tmp = (read_reg_dw(adapter, 0x714) & 0xfffffffc) | (dest & 3);
+
+ udelay(1000);
+
+ write_reg_dw(adapter, 0x714, tmp);
+ write_reg_dw(adapter, 0x714, tmp);
+
+ udelay(1000);
+
+ /* return value is never used? */
+/* return tmp; */
+}
+
+static void sram_set_cai_dest(struct adapter *adapter, u8 dest)
+{
+ u32 tmp;
+
+ udelay(1000);
+
+ tmp = (read_reg_dw(adapter, 0x714) & 0xfffffff3) | ((dest & 3) << 2);
+
+ udelay(1000);
+ udelay(1000);
+
+ write_reg_dw(adapter, 0x714, tmp);
+ write_reg_dw(adapter, 0x714, tmp);
+
+ udelay(1000);
+
+ /* return value is never used? */
+/* return tmp; */
+}
+
+static void sram_set_cao_dest(struct adapter *adapter, u8 dest)
+{
+ u32 tmp;
+
+ udelay(1000);
+
+ tmp = (read_reg_dw(adapter, 0x714) & 0xffffffcf) | ((dest & 3) << 4);
+
+ udelay(1000);
+ udelay(1000);
+
+ write_reg_dw(adapter, 0x714, tmp);
+ write_reg_dw(adapter, 0x714, tmp);
+
+ udelay(1000);
+
+ /* return value is never used? */
+/* return tmp; */
+}
+
+static void sram_set_media_dest(struct adapter *adapter, u8 dest)
+{
+ u32 tmp;
+
+ udelay(1000);
+
+ tmp = (read_reg_dw(adapter, 0x714) & 0xffffff3f) | ((dest & 3) << 6);
+
+ udelay(1000);
+ udelay(1000);
+
+ write_reg_dw(adapter, 0x714, tmp);
+ write_reg_dw(adapter, 0x714, tmp);
+
+ udelay(1000);
+
+ /* return value is never used? */
+/* return tmp; */
+}
+
+/* SRAM memory is accessed through a buffer register in the FlexCop
+ chip (0x700). This register has the following structure:
+ bits 0-14 : address
+ bit 15 : read/write flag
+ bits 16-23 : 8-bit word to write
+ bits 24-27 : = 4
+ bits 28-29 : memory bank selector
+ bit 31 : busy flag
+*/
+static void flex_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+ int i, retries;
+ u32 command;
+
+ for (i = 0; i < len; i++) {
+ command = bank | addr | 0x04000000 | (*buf << 0x10);
+
+ retries = 2;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ };
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __FUNCTION__);
+
+ write_reg_dw(adapter, 0x700, command);
+
+ buf++;
+ addr++;
+ }
+}
+
+static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+ int i, retries;
+ u32 command, value;
+
+ for (i = 0; i < len; i++) {
+ command = bank | addr | 0x04008000;
+
+ retries = 10000;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ };
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __FUNCTION__);
+
+ write_reg_dw(adapter, 0x700, command);
+
+ retries = 10000;
+
+ while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+ mdelay(1);
+ retries--;
+ };
+
+ if (retries == 0)
+ printk("%s: SRAM timeout\n", __FUNCTION__);
+
+ value = read_reg_dw(adapter, 0x700) >> 0x10;
+
+ *buf = (value & 0xff);
+
+ addr++;
+ buf++;
+ }
+}
+
+static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+ u32 bank;
+
+ bank = 0;
+
+ if (adapter->dw_sram_type == 0x20000) {
+ bank = (addr & 0x18000) << 0x0d;
+ }
+
+ if (adapter->dw_sram_type == 0x00000) {
+ if ((addr >> 0x0f) == 0)
+ bank = 0x20000000;
+ else
+ bank = 0x10000000;
+ }
+
+ flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+ u32 bank;
+
+ bank = 0;
+
+ if (adapter->dw_sram_type == 0x20000) {
+ bank = (addr & 0x18000) << 0x0d;
+ }
+
+ if (adapter->dw_sram_type == 0x00000) {
+ if ((addr >> 0x0f) == 0)
+ bank = 0x20000000;
+ else
+ bank = 0x10000000;
+ }
+
+ flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+ u32 length;
+
+ while (len != 0) {
+ length = len;
+
+ // check if the address range belongs to the same
+ // 32K memory chip. If not, the data is read from
+ // one chip at a time.
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_read_chunk(adapter, addr, buf, length);
+
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+ }
+}
+
+static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+ u32 length;
+
+ while (len != 0) {
+ length = len;
+
+ // check if the address range belongs to the same
+ // 32K memory chip. If not, the data is written to
+ // one chip at a time.
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_write_chunk(adapter, addr, buf, length);
+
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+ }
+}
+
+static void sram_set_size(struct adapter *adapter, u32 mask)
+{
+ write_reg_dw(adapter, 0x71c, (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
+}
+
+static void sram_init(struct adapter *adapter)
+{
+ u32 tmp;
+
+ tmp = read_reg_dw(adapter, 0x71c);
+
+ write_reg_dw(adapter, 0x71c, 1);
+
+ if (read_reg_dw(adapter, 0x71c) != 0) {
+ write_reg_dw(adapter, 0x71c, tmp);
+
+ adapter->dw_sram_type = tmp & 0x30000;
+
+ ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
+
+ } else {
+
+ adapter->dw_sram_type = 0x10000;
+
+ ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
+ }
+
+ /* return value is never used? */
+/* return adapter->dw_sram_type; */
+}
+
+static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+{
+ u8 tmp1, tmp2;
+
+ dprintk("%s: mask = %x, addr = %x\n", __FUNCTION__, mask, addr);
+
+ sram_set_size(adapter, mask);
+ sram_init(adapter);
+
+ tmp2 = 0xa5;
+ tmp1 = 0x4f;
+
+ sram_write(adapter, addr, &tmp2, 1);
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+ sram_read(adapter, addr, &tmp2, 1);
+
+ dprintk("%s: wrote 0xa5, read 0x%2x\n", __FUNCTION__, tmp2);
+
+ if (tmp2 != 0xa5)
+ return 0;
+
+ tmp2 = 0x5a;
+ tmp1 = 0xf4;
+
+ sram_write(adapter, addr, &tmp2, 1);
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+ sram_read(adapter, addr, &tmp2, 1);
+
+ dprintk("%s: wrote 0x5a, read 0x%2x\n", __FUNCTION__, tmp2);
+
+ if (tmp2 != 0x5a)
+ return 0;
+
+ return 1;
+}
+
+static u32 sram_length(struct adapter *adapter)
+{
+ if (adapter->dw_sram_type == 0x10000)
+ return 32768; // 32K
+ if (adapter->dw_sram_type == 0x00000)
+ return 65536; // 64K
+ if (adapter->dw_sram_type == 0x20000)
+ return 131072; // 128K
+
+ return 32768; // 32K
+}
+
+/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
+ - for 128K there are 4x32K chips at bank 0,1,2,3.
+ - for 64K there are 2x32K chips at bank 1,2.
+ - for 32K there is one 32K chip at bank 0.
+
+ FlexCop works only with one bank at a time. The bank is selected
+ by bits 28-29 of the 0x700 register.
+
+ bank 0 covers addresses 0x00000-0x07fff
+ bank 1 covers addresses 0x08000-0x0ffff
+ bank 2 covers addresses 0x10000-0x17fff
+ bank 3 covers addresses 0x18000-0x1ffff
+*/
+static int sram_detect_for_flex2(struct adapter *adapter)
+{
+ u32 tmp, tmp2, tmp3;
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ tmp = read_reg_dw(adapter, 0x208);
+ write_reg_dw(adapter, 0x208, 0);
+
+ tmp2 = read_reg_dw(adapter, 0x71c);
+
+ dprintk("%s: tmp2 = %x\n", __FUNCTION__, tmp2);
+
+ write_reg_dw(adapter, 0x71c, 1);
+
+ tmp3 = read_reg_dw(adapter, 0x71c);
+
+ dprintk("%s: tmp3 = %x\n", __FUNCTION__, tmp3);
+
+ write_reg_dw(adapter, 0x71c, tmp2);
+
+ // check for internal SRAM ???
+ tmp3--;
+ if (tmp3 != 0) {
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+
+ dprintk("%s: sram size = 32K\n", __FUNCTION__);
+
+ return 32;
+ }
+
+ if (sram_test_location(adapter, 0x20000, 0x18000) != 0) {
+ sram_set_size(adapter, 0x20000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+
+ dprintk("%s: sram size = 128K\n", __FUNCTION__);
+
+ return 128;
+ }
+
+ if (sram_test_location(adapter, 0x00000, 0x10000) != 0) {
+ sram_set_size(adapter, 0x00000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+
+ dprintk("%s: sram size = 64K\n", __FUNCTION__);
+
+ return 64;
+ }
+
+ if (sram_test_location(adapter, 0x10000, 0x00000) != 0) {
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+
+ dprintk("%s: sram size = 32K\n", __FUNCTION__);
+
+ return 32;
+ }
+
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+
+ dprintk("%s: SRAM detection failed. Set to 32K \n", __FUNCTION__);
+
+ return 0;
+}
+
+static void sll_detect_sram_size(struct adapter *adapter)
+{
+ sram_detect_for_flex2(adapter);
+}
+
+/* EEPROM (Skystar2 has one "24LC08B" chip on board) */
+/*
+static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+{
+ return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
+}
+*/
+
+static int eeprom_read(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+{
+ return flex_i2c_read(adapter, 0x20000000, 0x50, addr, buf, len);
+}
+
+static u8 calc_lrc(u8 *buf, int len)
+{
+ int i;
+ u8 sum;
+
+ sum = 0;
+
+ for (i = 0; i < len; i++)
+ sum = sum ^ buf[i];
+
+ return sum;
+}
+
+static int eeprom_lrc_read(struct adapter *adapter, u32 addr, u32 len, u8 *buf, int retries)
+{
+ int i;
+
+ for (i = 0; i < retries; i++) {
+ if (eeprom_read(adapter, addr, buf, len) == len) {
+ if (calc_lrc(buf, len - 1) == buf[len - 1])
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+static int eeprom_lrc_write(struct adapter *adapter, u32 addr, u32 len, u8 *wbuf, u8 *rbuf, int retries)
+{
+ int i;
+
+ for (i = 0; i < retries; i++) {
+ if (eeprom_write(adapter, addr, wbuf, len) == len) {
+ if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+*/
+
+
+/* These functions could be used to unlock SkyStar2 cards. */
+
+/*
+static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
+{
+ u8 rbuf[20];
+ u8 wbuf[20];
+
+ if (len != 16)
+ return 0;
+
+ memcpy(wbuf, key, len);
+
+ wbuf[16] = 0;
+ wbuf[17] = 0;
+ wbuf[18] = 0;
+ wbuf[19] = calc_lrc(wbuf, 19);
+
+ return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
+}
+
+static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
+{
+ u8 buf[20];
+
+ if (len != 16)
+ return 0;
+
+ if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0)
+ return 0;
+
+ memcpy(key, buf, len);
+
+ return 1;
+}
+*/
+
+static int eeprom_get_mac_addr(struct adapter *adapter, char type, u8 *mac)
+{
+ u8 tmp[8];
+
+ if (eeprom_lrc_read(adapter, 0x3f8, 8, tmp, 4) != 0) {
+ if (type != 0) {
+ mac[0] = tmp[0];
+ mac[1] = tmp[1];
+ mac[2] = tmp[2];
+ mac[3] = 0xfe;
+ mac[4] = 0xff;
+ mac[5] = tmp[3];
+ mac[6] = tmp[4];
+ mac[7] = tmp[5];
+
+ } else {
+
+ mac[0] = tmp[0];
+ mac[1] = tmp[1];
+ mac[2] = tmp[2];
+ mac[3] = tmp[3];
+ mac[4] = tmp[4];
+ mac[5] = tmp[5];
+ }
+
+ return 1;
+
+ } else {
+
+ if (type == 0) {
+ memset(mac, 0, 6);
+
+ } else {
+
+ memset(mac, 0, 8);
+ }
+
+ return 0;
+ }
+}
+
+/*
+static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
+{
+ u8 tmp[8];
+
+ if (type != 0) {
+ tmp[0] = mac[0];
+ tmp[1] = mac[1];
+ tmp[2] = mac[2];
+ tmp[3] = mac[5];
+ tmp[4] = mac[6];
+ tmp[5] = mac[7];
+
+ } else {
+
+ tmp[0] = mac[0];
+ tmp[1] = mac[1];
+ tmp[2] = mac[2];
+ tmp[3] = mac[3];
+ tmp[4] = mac[4];
+ tmp[5] = mac[5];
+ }
+
+ tmp[6] = 0;
+ tmp[7] = calc_lrc(tmp, 7);
+
+ if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
+ return 1;
+
+ return 0;
+}
+*/
+
+/* PID filter */
+
+/* every flexcop has 6 "lower" hw PID filters */
+/* these are enabled by setting bits 0-5 of 0x208 */
+/* for the 32 additional filters we have to select one */
+/* of them through 0x310 and modify through 0x314 */
+/* op: 0=disable, 1=enable */
+static void filter_enable_hw_filter(struct adapter *adapter, int id, u8 op)
+{
+ dprintk("%s: id=%d op=%d\n", __FUNCTION__, id, op);
+ if (id <= 5) {
+ u32 mask = (0x00000001 << id);
+ write_reg_bitfield(adapter, 0x208, mask, op ? mask : 0);
+ } else {
+ /* select */
+ write_reg_bitfield(adapter, 0x310, 0x1f, (id - 6) & 0x1f);
+ /* modify */
+ write_reg_bitfield(adapter, 0x314, 0x00006000, op ? 0x00004000 : 0);
+ }
+}
+
+/* this sets the PID that should pass the specified filter */
+static void pid_set_hw_pid(struct adapter *adapter, int id, u16 pid)
+{
+ dprintk("%s: id=%d pid=%d\n", __FUNCTION__, id, pid);
+ if (id <= 5) {
+ u32 adr = 0x300 + ((id & 6) << 1);
+ int shift = (id & 1) ? 16 : 0;
+ dprintk("%s: id=%d addr=%x %c pid=%d\n", __FUNCTION__, id, adr, (id & 1) ? 'h' : 'l', pid);
+ write_reg_bitfield(adapter, adr, (0x7fff) << shift, (pid & 0x1fff) << shift);
+ } else {
+ /* select */
+ write_reg_bitfield(adapter, 0x310, 0x1f, (id - 6) & 0x1f);
+ /* modify */
+ write_reg_bitfield(adapter, 0x314, 0x1fff, pid & 0x1fff);
+ }
+}
+
+
+/*
+static void filter_enable_null_filter(struct adapter *adapter, u32 op)
+{
+ dprintk("%s: op=%x\n", __FUNCTION__, op);
+
+ write_reg_bitfield(adapter, 0x208, 0x00000040, op?0x00000040:0);
+}
+*/
+
+static void filter_enable_mask_filter(struct adapter *adapter, u32 op)
+{
+ dprintk("%s: op=%x\n", __FUNCTION__, op);
+
+ write_reg_bitfield(adapter, 0x208, 0x00000080, op ? 0x00000080 : 0);
+}
+
+
+static void ctrl_enable_mac(struct adapter *adapter, u32 op)
+{
+ write_reg_bitfield(adapter, 0x208, 0x00004000, op ? 0x00004000 : 0);
+}
+
+static int ca_set_mac_dst_addr_filter(struct adapter *adapter, u8 *mac)
+{
+ u32 tmp1, tmp2;
+
+ tmp1 = (mac[3] << 0x18) | (mac[2] << 0x10) | (mac[1] << 0x08) | mac[0];
+ tmp2 = (mac[5] << 0x08) | mac[4];
+
+ write_reg_dw(adapter, 0x418, tmp1);
+ write_reg_dw(adapter, 0x41c, tmp2);
+
+ return 0;
+}
+
+/*
+static void set_ignore_mac_filter(struct adapter *adapter, u8 op)
+{
+ if (op != 0) {
+ write_reg_bitfield(adapter, 0x208, 0x00004000, 0);
+ adapter->mac_filter = 1;
+ } else {
+ if (adapter->mac_filter != 0) {
+ adapter->mac_filter = 0;
+ write_reg_bitfield(adapter, 0x208, 0x00004000, 0x00004000);
+ }
+ }
+}
+*/
+
+/*
+static void check_null_filter_enable(struct adapter *adapter)
+{
+ filter_enable_null_filter(adapter, 1);
+ filter_enable_mask_filter(adapter, 1);
+}
+*/
+
+static void pid_set_group_pid(struct adapter *adapter, u16 pid)
+{
+ u32 value;
+
+ dprintk("%s: pid=%x\n", __FUNCTION__, pid);
+ value = (pid & 0x3fff) | (read_reg_dw(adapter, 0x30c) & 0xffff0000);
+ write_reg_dw(adapter, 0x30c, value);
+}
+
+static void pid_set_group_mask(struct adapter *adapter, u16 pid)
+{
+ u32 value;
+
+ dprintk("%s: pid=%x\n", __FUNCTION__, pid);
+ value = ((pid & 0x3fff) << 0x10) | (read_reg_dw(adapter, 0x30c) & 0xffff);
+ write_reg_dw(adapter, 0x30c, value);
+}
+
+/*
+static int pid_get_group_pid(struct adapter *adapter)
+{
+ return read_reg_dw(adapter, 0x30c) & 0x00001fff;
+}
+
+static int pid_get_group_mask(struct adapter *adapter)
+{
+ return (read_reg_dw(adapter, 0x30c) >> 0x10)& 0x00001fff;
+}
+*/
+
+/*
+static void reset_hardware_pid_filter(struct adapter *adapter)
+{
+ pid_set_stream1_pid(adapter, 0x1fff);
+
+ pid_set_stream2_pid(adapter, 0x1fff);
+ filter_enable_stream2_filter(adapter, 0);
+
+ pid_set_pcr_pid(adapter, 0x1fff);
+ filter_enable_pcr_filter(adapter, 0);
+
+ pid_set_pmt_pid(adapter, 0x1fff);
+ filter_enable_pmt_filter(adapter, 0);
+
+ pid_set_ecm_pid(adapter, 0x1fff);
+ filter_enable_ecm_filter(adapter, 0);
+
+ pid_set_emm_pid(adapter, 0x1fff);
+ filter_enable_emm_filter(adapter, 0);
+}
+*/
+
+static void init_pids(struct adapter *adapter)
+{
+ int i;
+
+ adapter->pid_count = 0;
+ adapter->whole_bandwidth_count = 0;
+ for (i = 0; i < adapter->useable_hw_filters; i++) {
+ dprintk("%s: setting filter %d to 0x1fff\n", __FUNCTION__, i);
+ adapter->hw_pids[i] = 0x1fff;
+ pid_set_hw_pid(adapter, i, 0x1fff);
+}
+
+ pid_set_group_pid(adapter, 0);
+ pid_set_group_mask(adapter, 0x1fe0);
+}
+
+static void open_whole_bandwidth(struct adapter *adapter)
+{
+ dprintk("%s:\n", __FUNCTION__);
+ pid_set_group_pid(adapter, 0);
+ pid_set_group_mask(adapter, 0);
+/*
+ filter_enable_mask_filter(adapter, 1);
+*/
+}
+
+static void close_whole_bandwidth(struct adapter *adapter)
+{
+ dprintk("%s:\n", __FUNCTION__);
+ pid_set_group_pid(adapter, 0);
+ pid_set_group_mask(adapter, 0x1fe0);
+/*
+ filter_enable_mask_filter(adapter, 1);
+*/
+}
+
+static void whole_bandwidth_inc(struct adapter *adapter)
+{
+ if (adapter->whole_bandwidth_count++ == 0)
+ open_whole_bandwidth(adapter);
+}
+
+static void whole_bandwidth_dec(struct adapter *adapter)
+{
+ if (--adapter->whole_bandwidth_count <= 0)
+ close_whole_bandwidth(adapter);
+}
+
+/* The specified PID has to be let through the
+ hw filters.
+ We try to allocate an hardware filter and open whole
+ bandwidth when allocation is impossible.
+ All pids<=0x1f pass through the group filter.
+ Returns 1 on success, -1 on error */
+static int add_hw_pid(struct adapter *adapter, u16 pid)
+{
+ int i;
+
+ dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+ if (pid <= 0x1f)
+ return 1;
+
+ /* we can't use a filter for 0x2000, so no search */
+ if (pid != 0x2000) {
+ /* find an unused hardware filter */
+ for (i = 0; i < adapter->useable_hw_filters; i++) {
+ dprintk("%s: pid=%d searching slot=%d\n", __FUNCTION__, pid, i);
+ if (adapter->hw_pids[i] == 0x1fff) {
+ dprintk("%s: pid=%d slot=%d\n", __FUNCTION__, pid, i);
+ adapter->hw_pids[i] = pid;
+ pid_set_hw_pid(adapter, i, pid);
+ filter_enable_hw_filter(adapter, i, 1);
+ return 1;
+ }
+ }
+ }
+ /* if we have not used a filter, this pid depends on whole bandwidth */
+ dprintk("%s: pid=%d whole_bandwidth\n", __FUNCTION__, pid);
+ whole_bandwidth_inc(adapter);
+ return 1;
+ }
+
+/* returns -1 if the pid was not present in the filters */
+static int remove_hw_pid(struct adapter *adapter, u16 pid)
+{
+ int i;
+
+ dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+ if (pid <= 0x1f)
+ return 1;
+
+ /* we can't use a filter for 0x2000, so no search */
+ if (pid != 0x2000) {
+ for (i = 0; i < adapter->useable_hw_filters; i++) {
+ dprintk("%s: pid=%d searching slot=%d\n", __FUNCTION__, pid, i);
+ if (adapter->hw_pids[i] == pid) { // find the pid slot
+ dprintk("%s: pid=%d slot=%d\n", __FUNCTION__, pid, i);
+ adapter->hw_pids[i] = 0x1fff;
+ pid_set_hw_pid(adapter, i, 0x1fff);
+ filter_enable_hw_filter(adapter, i, 0);
+ return 1;
+ }
+ }
+ }
+ /* if we have not used a filter, this pid depended on whole bandwith */
+ dprintk("%s: pid=%d whole_bandwidth\n", __FUNCTION__, pid);
+ whole_bandwidth_dec(adapter);
+ return 1;
+ }
+
+/* Adds a PID to the filters.
+ Adding a pid more than once is possible, we keep reference counts.
+ Whole stream available through pid==0x2000.
+ Returns 1 on success, -1 on error */
+static int add_pid(struct adapter *adapter, u16 pid)
+{
+ int i;
+
+ dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+ if (pid > 0x1ffe && pid != 0x2000)
+ return -1;
+
+ // check if the pid is already present
+ for (i = 0; i < adapter->pid_count; i++)
+ if (adapter->pid_list[i] == pid) {
+ adapter->pid_rc[i]++; // increment ref counter
+ return 1;
+ }
+
+ if (adapter->pid_count == N_PID_SLOTS)
+ return -1; // no more pids can be added
+ adapter->pid_list[adapter->pid_count] = pid; // register pid
+ adapter->pid_rc[adapter->pid_count] = 1;
+ adapter->pid_count++;
+ // hardware setting
+ add_hw_pid(adapter, pid);
+
+ return 1;
+ }
+
+/* Removes a PID from the filters. */
+static int remove_pid(struct adapter *adapter, u16 pid)
+{
+ int i;
+
+ dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+ if (pid > 0x1ffe && pid != 0x2000)
+ return -1;
+
+ // check if the pid is present (it must be!)
+ for (i = 0; i < adapter->pid_count; i++) {
+ if (adapter->pid_list[i] == pid) {
+ adapter->pid_rc[i]--;
+ if (adapter->pid_rc[i] <= 0) {
+ // remove from the list
+ adapter->pid_count--;
+ adapter->pid_list[i]=adapter->pid_list[adapter->pid_count];
+ adapter->pid_rc[i] = adapter->pid_rc[adapter->pid_count];
+ // hardware setting
+ remove_hw_pid(adapter, pid);
+ }
+ return 1;
+ }
+ }
+
+ return -1;
+}
+
+
+/* dma & irq */
+static void ctrl_enable_smc(struct adapter *adapter, u32 op)
+{
+ write_reg_bitfield(adapter, 0x208, 0x00000800, op ? 0x00000800 : 0);
+}
+
+static void dma_enable_disable_irq(struct adapter *adapter, u32 flag1, u32 flag2, u32 flag3)
+{
+ adapter->dma_ctrl = adapter->dma_ctrl & 0x000f0000;
+
+ if (flag1 == 0) {
+ if (flag2 == 0)
+ adapter->dma_ctrl = adapter->dma_ctrl & ~0x00010000;
+ else
+ adapter->dma_ctrl = adapter->dma_ctrl | 0x00010000;
+
+ if (flag3 == 0)
+ adapter->dma_ctrl = adapter->dma_ctrl & ~0x00020000;
+ else
+ adapter->dma_ctrl = adapter->dma_ctrl | 0x00020000;
+
+ } else {
+
+ if (flag2 == 0)
+ adapter->dma_ctrl = adapter->dma_ctrl & ~0x00040000;
+ else
+ adapter->dma_ctrl = adapter->dma_ctrl | 0x00040000;
+
+ if (flag3 == 0)
+ adapter->dma_ctrl = adapter->dma_ctrl & ~0x00080000;
+ else
+ adapter->dma_ctrl = adapter->dma_ctrl | 0x00080000;
+ }
+}
+
+static void irq_dma_enable_disable_irq(struct adapter *adapter, u32 op)
+{
+ u32 value;
+
+ value = read_reg_dw(adapter, 0x208) & 0xfff0ffff;
+
+ if (op != 0)
+ value = value | (adapter->dma_ctrl & 0x000f0000);
+
+ write_reg_dw(adapter, 0x208, value);
+}
+
+/* FlexCopII has 2 dma channels. DMA1 is used to transfer TS data to
+ system memory.
+
+ The DMA1 buffer is divided in 2 subbuffers of equal size.
+ FlexCopII will transfer TS data to one subbuffer, signal an interrupt
+ when the subbuffer is full and continue fillig the second subbuffer.
+
+ For DMA1:
+ subbuffer size in 32-bit words is stored in the first 24 bits of
+ register 0x004. The last 8 bits of register 0x004 contain the number
+ of subbuffers.
+
+ the first 30 bits of register 0x000 contain the address of the first
+ subbuffer. The last 2 bits contain 0, when dma1 is disabled and 1,
+ when dma1 is enabled.
+
+ the first 30 bits of register 0x00c contain the address of the second
+ subbuffer. the last 2 bits contain 1.
+
+ register 0x008 will contain the address of the subbuffer that was filled
+ with TS data, when FlexCopII will generate an interrupt.
+
+ For DMA2:
+ subbuffer size in 32-bit words is stored in the first 24 bits of
+ register 0x014. The last 8 bits of register 0x014 contain the number
+ of subbuffers.
+
+ the first 30 bits of register 0x010 contain the address of the first
+ subbuffer. The last 2 bits contain 0, when dma1 is disabled and 1,
+ when dma1 is enabled.
+
+ the first 30 bits of register 0x01c contain the address of the second
+ subbuffer. the last 2 bits contain 1.
+
+ register 0x018 contains the address of the subbuffer that was filled
+ with TS data, when FlexCopII generates an interrupt.
+*/
+static int dma_init_dma(struct adapter *adapter, u32 dma_channel)
+{
+ u32 subbuffers, subbufsize, subbuf0, subbuf1;
+
+ if (dma_channel == 0) {
+ dprintk("%s: Initializing DMA1 channel\n", __FUNCTION__);
+
+ subbuffers = 2;
+
+ subbufsize = (((adapter->dmaq1.buffer_size / 2) / 4) << 8) | subbuffers;
+
+ subbuf0 = adapter->dmaq1.bus_addr & 0xfffffffc;
+
+ subbuf1 = ((adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) & 0xfffffffc) | 1;
+
+ dprintk("%s: first subbuffer address = 0x%x\n", __FUNCTION__, subbuf0);
+ udelay(1000);
+ write_reg_dw(adapter, 0x000, subbuf0);
+
+ dprintk("%s: subbuffer size = 0x%x\n", __FUNCTION__, (subbufsize >> 8) * 4);
+ udelay(1000);
+ write_reg_dw(adapter, 0x004, subbufsize);
+
+ dprintk("%s: second subbuffer address = 0x%x\n", __FUNCTION__, subbuf1);
+ udelay(1000);
+ write_reg_dw(adapter, 0x00c, subbuf1);
+
+ dprintk("%s: counter = 0x%x\n", __FUNCTION__, adapter->dmaq1.bus_addr & 0xfffffffc);
+ write_reg_dw(adapter, 0x008, adapter->dmaq1.bus_addr & 0xfffffffc);
+ udelay(1000);
+
+ dma_enable_disable_irq(adapter, 0, 1, subbuffers ? 1 : 0);
+
+ irq_dma_enable_disable_irq(adapter, 1);
+
+ sram_set_media_dest(adapter, 1);
+ sram_set_net_dest(adapter, 1);
+ sram_set_cai_dest(adapter, 2);
+ sram_set_cao_dest(adapter, 2);
+ }
+
+ if (dma_channel == 1) {
+ dprintk("%s: Initializing DMA2 channel\n", __FUNCTION__);
+
+ subbuffers = 2;
+
+ subbufsize = (((adapter->dmaq2.buffer_size / 2) / 4) << 8) | subbuffers;
+
+ subbuf0 = adapter->dmaq2.bus_addr & 0xfffffffc;
+
+ subbuf1 = ((adapter->dmaq2.bus_addr + adapter->dmaq2.buffer_size / 2) & 0xfffffffc) | 1;
+
+ dprintk("%s: first subbuffer address = 0x%x\n", __FUNCTION__, subbuf0);
+ udelay(1000);
+ write_reg_dw(adapter, 0x010, subbuf0);
+
+ dprintk("%s: subbuffer size = 0x%x\n", __FUNCTION__, (subbufsize >> 8) * 4);
+ udelay(1000);
+ write_reg_dw(adapter, 0x014, subbufsize);
+
+ dprintk("%s: second buffer address = 0x%x\n", __FUNCTION__, subbuf1);
+ udelay(1000);
+ write_reg_dw(adapter, 0x01c, subbuf1);
+
+ sram_set_cai_dest(adapter, 2);
+ }
+
+ return 0;
+}
+
+static void ctrl_enable_receive_data(struct adapter *adapter, u32 op)
+{
+ if (op == 0) {
+ write_reg_bitfield(adapter, 0x208, 0x00008000, 0);
+ adapter->dma_status = adapter->dma_status & ~0x00000004;
+ } else {
+ write_reg_bitfield(adapter, 0x208, 0x00008000, 0x00008000);
+ adapter->dma_status = adapter->dma_status | 0x00000004;
+ }
+}
+
+/* bit 0 of dma_mask is set to 1 if dma1 channel has to be enabled/disabled
+ bit 1 of dma_mask is set to 1 if dma2 channel has to be enabled/disabled
+*/
+static void dma_start_stop(struct adapter *adapter, u32 dma_mask, int start_stop)
+{
+ u32 dma_enable, dma1_enable, dma2_enable;
+
+ dprintk("%s: dma_mask=%x\n", __FUNCTION__, dma_mask);
+
+ if (start_stop == 1) {
+ dprintk("%s: starting dma\n", __FUNCTION__);
+
+ dma1_enable = 0;
+ dma2_enable = 0;
+
+ if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) == 0) && (adapter->dmaq1.bus_addr != 0)) {
+ adapter->dma_status = adapter->dma_status | 1;
+ dma1_enable = 1;
+ }
+
+ if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) == 0) && (adapter->dmaq2.bus_addr != 0)) {
+ adapter->dma_status = adapter->dma_status | 2;
+ dma2_enable = 1;
+ }
+ // enable dma1 and dma2
+ if ((dma1_enable == 1) && (dma2_enable == 1)) {
+ write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr | 1);
+ write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+ write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr | 1);
+
+ ctrl_enable_receive_data(adapter, 1);
+
+ return;
+ }
+ // enable dma1
+ if ((dma1_enable == 1) && (dma2_enable == 0)) {
+ write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr | 1);
+ write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+
+ ctrl_enable_receive_data(adapter, 1);
+
+ return;
+ }
+ // enable dma2
+ if ((dma1_enable == 0) && (dma2_enable == 1)) {
+ write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr | 1);
+
+ ctrl_enable_receive_data(adapter, 1);
+
+ return;
+ }
+ // start dma
+ if ((dma1_enable == 0) && (dma2_enable == 0)) {
+ ctrl_enable_receive_data(adapter, 1);
+
+ return;
+ }
+
+ } else {
+
+ dprintk("%s: stopping dma\n", __FUNCTION__);
+
+ dma_enable = adapter->dma_status & 0x00000003;
+
+ if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) != 0)) {
+ dma_enable = dma_enable & 0xfffffffe;
+ }
+
+ if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) != 0)) {
+ dma_enable = dma_enable & 0xfffffffd;
+ }
+ //stop dma
+ if ((dma_enable == 0) && ((adapter->dma_status & 4) != 0)) {
+ ctrl_enable_receive_data(adapter, 0);
+
+ udelay(3000);
+ }
+ //disable dma1
+ if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) != 0) && (adapter->dmaq1.bus_addr != 0)) {
+ write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr);
+ write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+
+ adapter->dma_status = adapter->dma_status & ~0x00000001;
+ }
+ //disable dma2
+ if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) != 0) && (adapter->dmaq2.bus_addr != 0)) {
+ write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr);
+
+ adapter->dma_status = adapter->dma_status & ~0x00000002;
+ }
+ }
+}
+
+static void open_stream(struct adapter *adapter, u16 pid)
+{
+ u32 dma_mask;
+
+ ++adapter->capturing;
+
+ filter_enable_mask_filter(adapter, 1);
+
+ add_pid(adapter, pid);
+
+ dprintk("%s: adapter->dma_status=%x\n", __FUNCTION__, adapter->dma_status);
+
+ if ((adapter->dma_status & 7) != 7) {
+ dma_mask = 0;
+
+ if (((adapter->dma_status & 0x10000000) != 0) && ((adapter->dma_status & 1) == 0)) {
+ dma_mask = dma_mask | 1;
+
+ adapter->dmaq1.head = 0;
+ adapter->dmaq1.tail = 0;
+
+ memset(adapter->dmaq1.buffer, 0, adapter->dmaq1.buffer_size);
+ }
+
+ if (((adapter->dma_status & 0x20000000) != 0) && ((adapter->dma_status & 2) == 0)) {
+ dma_mask = dma_mask | 2;
+
+ adapter->dmaq2.head = 0;
+ adapter->dmaq2.tail = 0;
+ }
+
+ if (dma_mask != 0) {
+ irq_dma_enable_disable_irq(adapter, 1);
+
+ dma_start_stop(adapter, dma_mask, 1);
+ }
+ }
+}
+
+static void close_stream(struct adapter *adapter, u16 pid)
+{
+ if (adapter->capturing > 0)
+ --adapter->capturing;
+
+ dprintk("%s: dma_status=%x\n", __FUNCTION__, adapter->dma_status);
+
+ if (adapter->capturing == 0) {
+ u32 dma_mask = 0;
+
+ if ((adapter->dma_status & 1) != 0)
+ dma_mask = dma_mask | 0x00000001;
+ if ((adapter->dma_status & 2) != 0)
+ dma_mask = dma_mask | 0x00000002;
+
+ if (dma_mask != 0) {
+ dma_start_stop(adapter, dma_mask, 0);
+ }
+ }
+ remove_pid(adapter, pid);
+}
+
+static void interrupt_service_dma1(struct adapter *adapter)
+{
+ struct dvb_demux *dvbdmx = &adapter->demux;
+
+ int n_cur_dma_counter;
+ u32 n_num_bytes_parsed;
+ u32 n_num_new_bytes_transferred;
+ u32 dw_default_packet_size = 188;
+ u8 gb_tmp_buffer[188];
+ u8 *pb_dma_buf_cur_pos;
+
+ n_cur_dma_counter = readl(adapter->io_mem + 0x008) - adapter->dmaq1.bus_addr;
+ n_cur_dma_counter = (n_cur_dma_counter / dw_default_packet_size) * dw_default_packet_size;
+
+ if ((n_cur_dma_counter < 0) || (n_cur_dma_counter > adapter->dmaq1.buffer_size)) {
+ dprintk("%s: dma counter outside dma buffer\n", __FUNCTION__);
+ return;
+ }
+
+ adapter->dmaq1.head = n_cur_dma_counter;
+
+ if (adapter->dmaq1.tail <= n_cur_dma_counter) {
+ n_num_new_bytes_transferred = n_cur_dma_counter - adapter->dmaq1.tail;
+
+ } else {
+
+ n_num_new_bytes_transferred = (adapter->dmaq1.buffer_size - adapter->dmaq1.tail) + n_cur_dma_counter;
+ }
+
+ ddprintk("%s: n_cur_dma_counter = %d\n", __FUNCTION__, n_cur_dma_counter);
+ ddprintk("%s: dmaq1.tail = %d\n", __FUNCTION__, adapter->dmaq1.tail);
+ ddprintk("%s: bytes_transferred = %d\n", __FUNCTION__, n_num_new_bytes_transferred);
+
+ if (n_num_new_bytes_transferred < dw_default_packet_size)
+ return;
+
+ n_num_bytes_parsed = 0;
+
+ while (n_num_bytes_parsed < n_num_new_bytes_transferred) {
+ pb_dma_buf_cur_pos = adapter->dmaq1.buffer + adapter->dmaq1.tail;
+
+ if (adapter->dmaq1.buffer + adapter->dmaq1.buffer_size < adapter->dmaq1.buffer + adapter->dmaq1.tail + 188) {
+ memcpy(gb_tmp_buffer, adapter->dmaq1.buffer + adapter->dmaq1.tail,
+ adapter->dmaq1.buffer_size - adapter->dmaq1.tail);
+ memcpy(gb_tmp_buffer + (adapter->dmaq1.buffer_size - adapter->dmaq1.tail), adapter->dmaq1.buffer,
+ (188 - (adapter->dmaq1.buffer_size - adapter->dmaq1.tail)));
+
+ pb_dma_buf_cur_pos = gb_tmp_buffer;
+ }
+
+ if (adapter->capturing != 0) {
+ dvb_dmx_swfilter_packets(dvbdmx, pb_dma_buf_cur_pos, dw_default_packet_size / 188);
+ }
+
+ n_num_bytes_parsed = n_num_bytes_parsed + dw_default_packet_size;
+
+ adapter->dmaq1.tail = adapter->dmaq1.tail + dw_default_packet_size;
+
+ if (adapter->dmaq1.tail >= adapter->dmaq1.buffer_size)
+ adapter->dmaq1.tail = adapter->dmaq1.tail - adapter->dmaq1.buffer_size;
+ };
+}
+
+static void interrupt_service_dma2(struct adapter *adapter)
+{
+ printk("%s:\n", __FUNCTION__);
+}
+
+static irqreturn_t isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct adapter *tmp = dev_id;
+
+ u32 value;
+
+ ddprintk("%s:\n", __FUNCTION__);
+
+ spin_lock_irq(&tmp->lock);
+
+ if (0 == ((value = read_reg_dw(tmp, 0x20c)) & 0x0f)) {
+ spin_unlock_irq(&tmp->lock);
+ return IRQ_NONE;
+ }
+
+ while (value != 0) {
+ if ((value & 0x03) != 0)
+ interrupt_service_dma1(tmp);
+ if ((value & 0x0c) != 0)
+ interrupt_service_dma2(tmp);
+ value = read_reg_dw(tmp, 0x20c) & 0x0f;
+ }
+
+ spin_unlock_irq(&tmp->lock);
+ return IRQ_HANDLED;
+}
+
+static int init_dma_queue_one(struct adapter *adapter, struct dmaq *dmaq,
+ int size, int dmaq_offset)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ dma_addr_t dma_addr;
+
+ dmaq->head = 0;
+ dmaq->tail = 0;
+
+ dmaq->buffer = pci_alloc_consistent(pdev, size + 0x80, &dma_addr);
+ if (!dmaq->buffer)
+ return -ENOMEM;
+
+ dmaq->bus_addr = dma_addr;
+ dmaq->buffer_size = size;
+
+ dma_init_dma(adapter, dmaq_offset);
+
+ ddprintk("%s: allocated dma buffer at 0x%p, length=%d\n",
+ __FUNCTION__, dmaq->buffer, size);
+
+ return 0;
+ }
+
+static int init_dma_queue(struct adapter *adapter)
+{
+ struct {
+ struct dmaq *dmaq;
+ u32 dma_status;
+ int size;
+ } dmaq_desc[] = {
+ { &adapter->dmaq1, 0x10000000, SIZE_OF_BUF_DMA1 },
+ { &adapter->dmaq2, 0x20000000, SIZE_OF_BUF_DMA2 }
+ }, *p = dmaq_desc;
+ int i;
+
+ for (i = 0; i < 2; i++, p++) {
+ if (init_dma_queue_one(adapter, p->dmaq, p->size, i) < 0)
+ adapter->dma_status &= ~p->dma_status;
+ else
+ adapter->dma_status |= p->dma_status;
+ }
+ return (adapter->dma_status & 0x30000000) ? 0 : -ENOMEM;
+}
+
+static void free_dma_queue_one(struct adapter *adapter, struct dmaq *dmaq)
+{
+ if (dmaq->buffer) {
+ pci_free_consistent(adapter->pdev, dmaq->buffer_size + 0x80,
+ dmaq->buffer, dmaq->bus_addr);
+ memset(dmaq, 0, sizeof(*dmaq));
+ }
+}
+
+static void free_dma_queue(struct adapter *adapter)
+{
+ struct dmaq *dmaq[] = {
+ &adapter->dmaq1,
+ &adapter->dmaq2,
+ NULL
+ }, **p;
+
+ for (p = dmaq; *p; p++)
+ free_dma_queue_one(adapter, *p);
+ }
+
+static void release_adapter(struct adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ iounmap(adapter->io_mem);
+ pci_disable_device(pdev);
+ pci_release_region(pdev, 0);
+ pci_release_region(pdev, 1);
+}
+
+static void free_adapter_object(struct adapter *adapter)
+{
+ dprintk("%s:\n", __FUNCTION__);
+
+ close_stream(adapter, 0);
+ free_irq(adapter->irq, adapter);
+ free_dma_queue(adapter);
+ release_adapter(adapter);
+ kfree(adapter);
+}
+
+static struct pci_driver skystar2_pci_driver;
+
+static int claim_adapter(struct adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ u16 var;
+ int ret;
+
+ ret = pci_request_region(pdev, 1, skystar2_pci_driver.name);
+ if (ret < 0)
+ goto out;
+
+ ret = pci_request_region(pdev, 0, skystar2_pci_driver.name);
+ if (ret < 0)
+ goto err_pci_release_1;
+
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &adapter->card_revision);
+
+ dprintk("%s: card revision %x \n", __FUNCTION__, adapter->card_revision);
+
+ ret = pci_enable_device(pdev);
+ if (ret < 0)
+ goto err_pci_release_0;
+
+ pci_read_config_word(pdev, 4, &var);
+
+ if ((var & 4) == 0)
+ pci_set_master(pdev);
+
+ adapter->io_port = pdev->resource[1].start;
+
+ adapter->io_mem = ioremap(pdev->resource[0].start, 0x800);
+
+ if (!adapter->io_mem) {
+ dprintk("%s: can not map io memory\n", __FUNCTION__);
+ ret = -EIO;
+ goto err_pci_disable;
+ }
+
+ dprintk("%s: io memory maped at %p\n", __FUNCTION__, adapter->io_mem);
+
+ ret = 1;
+out:
+ return ret;
+
+err_pci_disable:
+ pci_disable_device(pdev);
+err_pci_release_0:
+ pci_release_region(pdev, 0);
+err_pci_release_1:
+ pci_release_region(pdev, 1);
+ goto out;
+}
+
+/*
+static int sll_reset_flexcop(struct adapter *adapter)
+{
+ write_reg_dw(adapter, 0x208, 0);
+ write_reg_dw(adapter, 0x210, 0xb2ff);
+
+ return 0;
+}
+*/
+
+static void decide_how_many_hw_filters(struct adapter *adapter)
+{
+ int hw_filters;
+ int mod_option_hw_filters;
+
+ // FlexCop IIb & III have 6+32 hw filters
+ // FlexCop II has 6 hw filters, every other should have at least 6
+ switch (adapter->b2c2_revision) {
+ case 0x82: /* II */
+ hw_filters = 6;
+ break;
+ case 0xc3: /* IIB */
+ hw_filters = 6 + 32;
+ break;
+ case 0xc0: /* III */
+ hw_filters = 6 + 32;
+ break;
+ default:
+ hw_filters = 6;
+ break;
+ }
+ printk("%s: the chip has %i hardware filters", __FILE__, hw_filters);
+
+ mod_option_hw_filters = 0;
+ if (enable_hw_filters >= 1)
+ mod_option_hw_filters += 6;
+ if (enable_hw_filters >= 2)
+ mod_option_hw_filters += 32;
+
+ if (mod_option_hw_filters >= hw_filters) {
+ adapter->useable_hw_filters = hw_filters;
+ } else {
+ adapter->useable_hw_filters = mod_option_hw_filters;
+ printk(", but only %d will be used because of module option", mod_option_hw_filters);
+ }
+ printk("\n");
+ dprintk("%s: useable_hardware_filters set to %i\n", __FILE__, adapter->useable_hw_filters);
+}
+
+static int driver_initialize(struct pci_dev *pdev)
+{
+ struct adapter *adapter;
+ u32 tmp;
+ int ret = -ENOMEM;
+
+ adapter = kmalloc(sizeof(struct adapter), GFP_KERNEL);
+ if (!adapter) {
+ dprintk("%s: out of memory!\n", __FUNCTION__);
+ goto out;
+ }
+
+ memset(adapter, 0, sizeof(struct adapter));
+
+ pci_set_drvdata(pdev,adapter);
+
+ adapter->pdev = pdev;
+ adapter->irq = pdev->irq;
+
+ ret = claim_adapter(adapter);
+ if (ret < 0)
+ goto err_kfree;
+
+ irq_dma_enable_disable_irq(adapter, 0);
+
+ ret = request_irq(pdev->irq, isr, 0x4000000, "Skystar2", adapter);
+ if (ret < 0) {
+ dprintk("%s: unable to allocate irq=%d !\n", __FUNCTION__, pdev->irq);
+ goto err_release_adapter;
+ }
+
+ read_reg_dw(adapter, 0x208);
+ write_reg_dw(adapter, 0x208, 0);
+ write_reg_dw(adapter, 0x210, 0xb2ff);
+ write_reg_dw(adapter, 0x208, 0x40);
+
+ ret = init_dma_queue(adapter);
+ if (ret < 0)
+ goto err_free_irq;
+
+ adapter->b2c2_revision = (read_reg_dw(adapter, 0x204) >> 0x18);
+
+ switch (adapter->b2c2_revision) {
+ case 0x82:
+ printk("%s: FlexCopII(rev.130) chip found\n", __FILE__);
+ break;
+ case 0xc3:
+ printk("%s: FlexCopIIB(rev.195) chip found\n", __FILE__);
+ break;
+ case 0xc0:
+ printk("%s: FlexCopIII(rev.192) chip found\n", __FILE__);
+ break;
+ default:
+ printk("%s: The revision of the FlexCop chip on your card is %d\n", __FILE__, adapter->b2c2_revision);
+ printk("%s: This driver works only with FlexCopII(rev.130), FlexCopIIB(rev.195) and FlexCopIII(rev.192).\n", __FILE__);
+ ret = -ENODEV;
+ goto err_free_dma_queue;
+ }
+
+ decide_how_many_hw_filters(adapter);
+
+ init_pids(adapter);
+
+ tmp = read_reg_dw(adapter, 0x204);
+
+ write_reg_dw(adapter, 0x204, 0);
+ mdelay(20);
+
+ write_reg_dw(adapter, 0x204, tmp);
+ mdelay(10);
+
+ tmp = read_reg_dw(adapter, 0x308);
+ write_reg_dw(adapter, 0x308, 0x4000 | tmp);
+
+ adapter->dw_sram_type = 0x10000;
+
+ sll_detect_sram_size(adapter);
+
+ dprintk("%s sram length = %d, sram type= %x\n", __FUNCTION__, sram_length(adapter), adapter->dw_sram_type);
+
+ sram_set_media_dest(adapter, 1);
+ sram_set_net_dest(adapter, 1);
+
+ ctrl_enable_smc(adapter, 0);
+
+ sram_set_cai_dest(adapter, 2);
+ sram_set_cao_dest(adapter, 2);
+
+ dma_enable_disable_irq(adapter, 1, 0, 0);
+
+ if (eeprom_get_mac_addr(adapter, 0, adapter->mac_addr) != 0) {
+ printk("%s MAC address = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n", __FUNCTION__, adapter->mac_addr[0],
+ adapter->mac_addr[1], adapter->mac_addr[2], adapter->mac_addr[3], adapter->mac_addr[4], adapter->mac_addr[5],
+ adapter->mac_addr[6], adapter->mac_addr[7]
+ );
+
+ ca_set_mac_dst_addr_filter(adapter, adapter->mac_addr);
+ ctrl_enable_mac(adapter, 1);
+ }
+
+ spin_lock_init(&adapter->lock);
+
+out:
+ return ret;
+
+err_free_dma_queue:
+ free_dma_queue(adapter);
+err_free_irq:
+ free_irq(pdev->irq, adapter);
+err_release_adapter:
+ release_adapter(adapter);
+err_kfree:
+ pci_set_drvdata(pdev, NULL);
+ kfree(adapter);
+ goto out;
+}
+
+static void driver_halt(struct pci_dev *pdev)
+{
+ struct adapter *adapter = pci_get_drvdata(pdev);
+
+ irq_dma_enable_disable_irq(adapter, 0);
+
+ ctrl_enable_receive_data(adapter, 0);
+
+ free_adapter_object(adapter);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+static int dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct adapter *adapter = (struct adapter *) dvbdmx->priv;
+
+ dprintk("%s: PID=%d, type=%d\n", __FUNCTION__, dvbdmxfeed->pid, dvbdmxfeed->type);
+
+ open_stream(adapter, dvbdmxfeed->pid);
+
+ return 0;
+}
+
+static int dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct adapter *adapter = (struct adapter *) dvbdmx->priv;
+
+ dprintk("%s: PID=%d, type=%d\n", __FUNCTION__, dvbdmxfeed->pid, dvbdmxfeed->type);
+
+ close_stream(adapter, dvbdmxfeed->pid);
+
+ return 0;
+}
+
+/* lnb control */
+static void set_tuner_tone(struct adapter *adapter, u8 tone)
+{
+ u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc };
+ u16 ax;
+
+ dprintk("%s: %u\n", __FUNCTION__, tone);
+
+ switch (tone) {
+ case 1:
+ ax = wz_half_period_for_45_mhz[0];
+ break;
+ case 2:
+ ax = wz_half_period_for_45_mhz[1];
+ break;
+ case 3:
+ ax = wz_half_period_for_45_mhz[2];
+ break;
+ case 4:
+ ax = wz_half_period_for_45_mhz[3];
+ break;
+
+ default:
+ ax = 0;
+ }
+
+ if (ax != 0) {
+ write_reg_dw(adapter, 0x200, ((ax << 0x0f) + (ax & 0x7fff)) | 0x40000000);
+
+ } else {
+
+ write_reg_dw(adapter, 0x200, 0x40ff8000);
+ }
+}
+
+static void set_tuner_polarity(struct adapter *adapter, u8 polarity)
+{
+ u32 var;
+
+ dprintk("%s : polarity = %u \n", __FUNCTION__, polarity);
+
+ var = read_reg_dw(adapter, 0x204);
+
+ if (polarity == 0) {
+ dprintk("%s: LNB power off\n", __FUNCTION__);
+ var = var | 1;
+ };
+
+ if (polarity == 1) {
+ var = var & ~1;
+ var = var & ~4;
+ };
+
+ if (polarity == 2) {
+ var = var & ~1;
+ var = var | 4;
+ }
+
+ write_reg_dw(adapter, 0x204, var);
+}
+
+static void diseqc_send_bit(struct adapter *adapter, int data)
+{
+ set_tuner_tone(adapter, 1);
+ udelay(data ? 500 : 1000);
+ set_tuner_tone(adapter, 0);
+ udelay(data ? 1000 : 500);
+}
+
+
+static void diseqc_send_byte(struct adapter *adapter, int data)
+ {
+ int i, par = 1, d;
+
+ for (i = 7; i >= 0; i--) {
+ d = (data >> i) & 1;
+ par ^= d;
+ diseqc_send_bit(adapter, d);
+ }
+
+ diseqc_send_bit(adapter, par);
+ }
+
+
+static int send_diseqc_msg(struct adapter *adapter, int len, u8 *msg, unsigned long burst)
+{
+ int i;
+
+ set_tuner_tone(adapter, 0);
+ mdelay(16);
+
+ for (i = 0; i < len; i++)
+ diseqc_send_byte(adapter, msg[i]);
+
+ mdelay(16);
+
+ if (burst != -1) {
+ if (burst)
+ diseqc_send_byte(adapter, 0xff);
+ else {
+ set_tuner_tone(adapter, 1);
+ udelay(12500);
+ set_tuner_tone(adapter, 0);
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int flexcop_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ switch(tone) {
+ case SEC_TONE_ON:
+ set_tuner_tone(adapter, 1);
+ break;
+ case SEC_TONE_OFF:
+ set_tuner_tone(adapter, 0);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+ {
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ send_diseqc_msg(adapter, cmd->msg_len, cmd->msg, 0);
+
+ return 0;
+ }
+
+static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ send_diseqc_msg(adapter, 0, NULL, minicmd);
+
+ return 0;
+}
+
+static int flexcop_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+ {
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ dprintk("%s: SEC_VOLTAGE_13, %x\n", __FUNCTION__, SEC_VOLTAGE_13);
+ set_tuner_polarity(adapter, 1);
+ return 0;
+
+ case SEC_VOLTAGE_18:
+ dprintk("%s: SEC_VOLTAGE_18, %x\n", __FUNCTION__, SEC_VOLTAGE_18);
+ set_tuner_polarity(adapter, 2);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+static int flexcop_sleep(struct dvb_frontend* fe)
+ {
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ dprintk("%s: FE_SLEEP\n", __FUNCTION__);
+ set_tuner_polarity(adapter, 0);
+
+ if (adapter->fe_sleep) return adapter->fe_sleep(fe);
+ return 0;
+ }
+
+static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
+ {
+ printk("flexcop_i2c_func\n");
+
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm flexcop_algo = {
+ .name = "flexcop i2c algorithm",
+ .id = I2C_ALGO_BIT,
+ .master_xfer = master_xfer,
+ .functionality = flexcop_i2c_func,
+};
+
+
+
+
+static int samsung_tbmu24112_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 int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ div = params->frequency / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x84; // 0xC4
+ buf[3] = 0x08;
+
+ if (params->frequency < 1500000) buf[3] |= 0x10;
+
+ if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x30,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x35,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0xC3,
+ 0x0C, 0x00,
+ 0x0D, 0x81,
+ 0x0E, 0x23,
+ 0x0F, 0x12,
+ 0x10, 0x7E,
+ 0x11, 0x84,
+ 0x12, 0xB9,
+ 0x13, 0x88,
+ 0x14, 0x89,
+ 0x15, 0xC9,
+ 0x16, 0x00,
+ 0x17, 0x5C,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1A, 0x00,
+ 0x1C, 0x00,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x3A,
+ 0x20, 0x2E,
+ 0x21, 0x80,
+ 0x22, 0xFF,
+ 0x23, 0xC1,
+ 0x28, 0x00,
+ 0x29, 0x1E,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFE,
+ 0x34, 0x93,
+ 0xff, 0xff,
+ };
+
+static struct stv0299_config samsung_tbmu24112_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_tbmu24112_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .enhanced_tuning = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0229_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+ .pll_set = samsung_tbmu24112_pll_set,
+};
+
+
+
+static int nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ return request_firmware(fw, name, &adapter->pdev->dev);
+}
+
+
+static struct nxt2002_config samsung_tbmv_config = {
+ .demod_address = 0x0A,
+ .request_firmware = nxt2002_request_firmware,
+};
+
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+ static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
+ static u8 mt352_reset [] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
+ 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 samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+ u32 div;
+ unsigned char bs = 0;
+
+ #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+ div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
+ if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
+ if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
+
+ pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = 0xcc;
+ pllbuf[4] = bs;
+
+ return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+
+ .demod_address = 0x0f,
+ .demod_init = samsung_tdtc9251dh0_demod_init,
+ .pll_set = samsung_tdtc9251dh0_pll_set,
+};
+
+static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+ div = (params->frequency + (125/2)) / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = (div >> 0) & 0xff;
+ buf[2] = 0x84 | ((div >> 10) & 0x60);
+ buf[3] = 0x80;
+
+ if (params->frequency < 1550000)
+ buf[3] |= 0x02;
+
+ if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+
+ .demod_address = 0x0e,
+ .pll_set = skystar23_samsung_tbdu18132_pll_set,
+};
+
+
+
+
+static void frontend_init(struct adapter *skystar2)
+{
+ switch(skystar2->pdev->device) {
+ case 0x2103: // Technisat Skystar2 OR Technisat Airstar2 (DVB-T or ATSC)
+
+ // Attempt to load the Nextwave nxt2002 for ATSC support
+ skystar2->fe = nxt2002_attach(&samsung_tbmv_config, &skystar2->i2c_adap);
+ if (skystar2->fe != NULL) {
+ skystar2->fe_sleep = skystar2->fe->ops->sleep;
+ skystar2->fe->ops->sleep = flexcop_sleep;
+ break;
+ }
+
+ // try the skystar2 v2.6 first (stv0299/Samsung tbmu24112(sl1935))
+ skystar2->fe = stv0299_attach(&samsung_tbmu24112_config, &skystar2->i2c_adap);
+ if (skystar2->fe != NULL) {
+ skystar2->fe->ops->set_voltage = flexcop_set_voltage;
+ skystar2->fe_sleep = skystar2->fe->ops->sleep;
+ skystar2->fe->ops->sleep = flexcop_sleep;
+ break;
+}
+
+ // try the airstar2 (mt352/Samsung tdtc9251dh0(??))
+ skystar2->fe = mt352_attach(&samsung_tdtc9251dh0_config, &skystar2->i2c_adap);
+ if (skystar2->fe != NULL) {
+ skystar2->fe->ops->info.frequency_min = 474000000;
+ skystar2->fe->ops->info.frequency_max = 858000000;
+ break;
+ }
+
+ // try the skystar2 v2.3 (vp310/Samsung tbdu18132(tsa5059))
+ skystar2->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &skystar2->i2c_adap);
+ if (skystar2->fe != NULL) {
+ skystar2->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+ skystar2->fe->ops->diseqc_send_burst = flexcop_diseqc_send_burst;
+ skystar2->fe->ops->set_tone = flexcop_set_tone;
+ skystar2->fe->ops->set_voltage = flexcop_set_voltage;
+ skystar2->fe_sleep = skystar2->fe->ops->sleep;
+ skystar2->fe->ops->sleep = flexcop_sleep;
+ break;
+ }
+ break;
+ }
+
+ if (skystar2->fe == NULL) {
+ printk("skystar2: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ skystar2->pdev->vendor,
+ skystar2->pdev->device,
+ skystar2->pdev->subsystem_vendor,
+ skystar2->pdev->subsystem_device);
+ } else {
+ if (dvb_register_frontend(skystar2->dvb_adapter, skystar2->fe)) {
+ printk("skystar2: Frontend registration failed!\n");
+ if (skystar2->fe->ops->release)
+ skystar2->fe->ops->release(skystar2->fe);
+ skystar2->fe = NULL;
+ }
+ }
+}
+
+
+static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct adapter *adapter;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret = -ENODEV;
+
+ if (!pdev)
+ goto out;
+
+ ret = driver_initialize(pdev);
+ if (ret < 0)
+ goto out;
+
+ ret = dvb_register_adapter(&dvb_adapter, skystar2_pci_driver.name,
+ THIS_MODULE);
+ if (ret < 0) {
+ printk("%s: Error registering DVB adapter\n", __FUNCTION__);
+ goto err_halt;
+ }
+
+ adapter = pci_get_drvdata(pdev);
+
+ dvb_adapter->priv = adapter;
+ adapter->dvb_adapter = dvb_adapter;
+
+
+ init_MUTEX(&adapter->i2c_sem);
+
+
+ memset(&adapter->i2c_adap, 0, sizeof(struct i2c_adapter));
+ strcpy(adapter->i2c_adap.name, "SkyStar2");
+
+ i2c_set_adapdata(&adapter->i2c_adap, adapter);
+
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ adapter->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL;
+#else
+ adapter->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+#endif
+ adapter->i2c_adap.algo = &flexcop_algo;
+ adapter->i2c_adap.algo_data = NULL;
+ adapter->i2c_adap.id = I2C_ALGO_BIT;
+
+ ret = i2c_add_adapter(&adapter->i2c_adap);
+ if (ret < 0)
+ goto err_dvb_unregister;
+
+ dvbdemux = &adapter->demux;
+
+ dvbdemux->priv = adapter;
+ dvbdemux->filternum = N_PID_SLOTS;
+ dvbdemux->feednum = N_PID_SLOTS;
+ dvbdemux->start_feed = dvb_start_feed;
+ dvbdemux->stop_feed = dvb_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+
+ ret = dvb_dmx_init(&adapter->demux);
+ if (ret < 0)
+ goto err_i2c_del;
+
+ dmx = &dvbdemux->dmx;
+
+ adapter->hw_frontend.source = DMX_FRONTEND_0;
+ adapter->dmxdev.filternum = N_PID_SLOTS;
+ adapter->dmxdev.demux = dmx;
+ adapter->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&adapter->dmxdev, adapter->dvb_adapter);
+ if (ret < 0)
+ goto err_dmx_release;
+
+ ret = dmx->add_frontend(dmx, &adapter->hw_frontend);
+ if (ret < 0)
+ goto err_dmxdev_release;
+
+ adapter->mem_frontend.source = DMX_MEMORY_FE;
+
+ ret = dmx->add_frontend(dmx, &adapter->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &adapter->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ dvb_net_init(adapter->dvb_adapter, &adapter->dvbnet, &dvbdemux->dmx);
+
+ frontend_init(adapter);
+out:
+ return ret;
+
+err_remove_mem_frontend:
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &adapter->mem_frontend);
+err_remove_hw_frontend:
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &adapter->hw_frontend);
+err_dmxdev_release:
+ dvb_dmxdev_release(&adapter->dmxdev);
+err_dmx_release:
+ dvb_dmx_release(&adapter->demux);
+err_i2c_del:
+ i2c_del_adapter(&adapter->i2c_adap);
+err_dvb_unregister:
+ dvb_unregister_adapter(adapter->dvb_adapter);
+err_halt:
+ driver_halt(pdev);
+ goto out;
+}
+
+static void skystar2_remove(struct pci_dev *pdev)
+{
+ struct adapter *adapter = pci_get_drvdata(pdev);
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+
+ if (!adapter)
+ return;
+
+ dvb_net_release(&adapter->dvbnet);
+ dvbdemux = &adapter->demux;
+ dmx = &dvbdemux->dmx;
+
+ dmx->close(dmx);
+ dmx->remove_frontend(dmx, &adapter->hw_frontend);
+ dmx->remove_frontend(dmx, &adapter->mem_frontend);
+
+ dvb_dmxdev_release(&adapter->dmxdev);
+ dvb_dmx_release(dvbdemux);
+
+ if (adapter->fe != NULL)
+ dvb_unregister_frontend(adapter->fe);
+
+ dvb_unregister_adapter(adapter->dvb_adapter);
+
+ i2c_del_adapter(&adapter->i2c_adap);
+
+ driver_halt(pdev);
+ }
+
+static struct pci_device_id skystar2_pci_tbl[] = {
+ {0x000013d0, 0x00002103, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000},
+/* {0x000013d0, 0x00002200, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000}, UNDEFINED HARDWARE - mail linuxtv.org list */ //FCIII
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, skystar2_pci_tbl);
+
+static struct pci_driver skystar2_pci_driver = {
+ .name = "SkyStar2",
+ .id_table = skystar2_pci_tbl,
+ .probe = skystar2_probe,
+ .remove = skystar2_remove,
+};
+
+static int skystar2_init(void)
+{
+ return pci_register_driver(&skystar2_pci_driver);
+}
+
+static void skystar2_cleanup(void)
+{
+ pci_unregister_driver(&skystar2_pci_driver);
+}
+
+module_init(skystar2_init);
+module_exit(skystar2_cleanup);
+
+MODULE_DESCRIPTION("Technisat SkyStar2 DVB PCI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
new file mode 100644
index 000000000000..e7d11e0667a8
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -0,0 +1,19 @@
+config DVB_BT8XX
+ tristate "Nebula/Pinnacle PCTV/Twinhan PCI cards"
+ depends on DVB_CORE && PCI && VIDEO_BT848
+ select DVB_MT352
+ select DVB_SP887X
+ select DVB_NXT6000
+ select DVB_CX24110
+ select DVB_OR51211
+ help
+ Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+ the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards and
+ pcHDTV HD2000 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/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
new file mode 100644
index 000000000000..9da8604b9e18
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
new file mode 100644
index 000000000000..213ff7902024
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -0,0 +1,588 @@
+/*
+ * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card
+ *
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
+ *
+ * large parts based on the bttv driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.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/moduleparam.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:off).");
+
+int bt878_num;
+struct bt878 bt878[BT878_MAX];
+
+EXPORT_SYMBOL(bt878_debug);
+EXPORT_SYMBOL(bt878_verbose);
+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 if(bt878_debug) printk
+
+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("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(cpu_to_le32(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, struct pt_regs *regs)
+{
+ 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 interupt condition */
+
+
+ if (astat & (BT878_ASCERR | BT878_AOCERR)) {
+ if (bt878_verbose) {
+ printk("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
+ ("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
+ ("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 (down_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;
+ }
+ up(&bt->gpio_lock);
+ return retval;
+}
+
+EXPORT_SYMBOL(bt878_device_control);
+
+/***********************/
+/* PCI device handling */
+/***********************/
+
+static int __devinit bt878_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result;
+ unsigned char lat;
+ struct bt878 *bt;
+#if defined(__powerpc__)
+ unsigned int cmd;
+#endif
+
+ printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
+ bt878_num);
+ if (pci_enable_device(dev))
+ return -EIO;
+
+ 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;
+ }
+
+ pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt->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,
+ SA_SHIRQ | SA_INTERRUPT, "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(init_bt878(btv) < 0) {
+ bt878_remove(dev);
+ return -EIO;
+ }
+*/
+
+ if ((result = bt878_mem_alloc(bt))) {
+ printk("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("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_device_id bt878_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BROOKTREE_878,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
+
+static struct pci_driver bt878_pci_driver = {
+ .name = "bt878",
+ .id_table = bt878_pci_tbl,
+ .probe = bt878_probe,
+ .remove = bt878_remove,
+};
+
+static int bt878_pci_driver_registered = 0;
+
+/*******************************/
+/* Module management functions */
+/*******************************/
+
+static int bt878_init_module(void)
+{
+ bt878_num = 0;
+ bt878_pci_driver_registered = 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);
+/*
+ bt878_check_chipset();
+*/
+ /* later we register inside of bt878_find_audio_dma()
+ * because we may want to ignore certain cards */
+ bt878_pci_driver_registered = 1;
+ return pci_register_driver(&bt878_pci_driver);
+}
+
+static void bt878_cleanup_module(void)
+{
+ if (bt878_pci_driver_registered) {
+ bt878_pci_driver_registered = 0;
+ pci_unregister_driver(&bt878_pci_driver);
+ }
+ return;
+}
+
+module_init(bt878_init_module);
+module_exit(bt878_cleanup_module);
+
+//MODULE_AUTHOR("XXX");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h
new file mode 100644
index 000000000000..e1b9809d1b08
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/bt878.h
@@ -0,0 +1,147 @@
+/*
+ bt878.h - Bt878 audio module (register offsets)
+
+ Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.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 "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)
+
+extern int bt878_num;
+
+struct bt878 {
+ struct semaphore 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;
+ u32 *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 */
+extern __inline__ void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
+{
+ __asm__ __volatile__("stwbrx %1,0,%2":"=m"(*addr):"r"(val),
+ "r"(addr));
+ __asm__ __volatile__("eieio":::"memory");
+}
+
+#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/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
new file mode 100644
index 000000000000..eac83768dfd0
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -0,0 +1,1089 @@
+/*
+ Frontend-driver for TwinHan DST Frontend
+
+ Copyright (C) 2003 Jamie Honan
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You 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.h"
+
+struct dst_state {
+
+ struct i2c_adapter* i2c;
+
+ struct bt878* bt;
+
+ struct dvb_frontend_ops ops;
+
+ /* configuration settings */
+ const struct dst_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* private demodulator 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;
+ fe_bandwidth_t bandwidth;
+};
+
+static unsigned int dst_verbose = 0;
+module_param(dst_verbose, int, 0644);
+MODULE_PARM_DESC(dst_verbose, "verbose startup messages, default is 1 (yes)");
+static unsigned int dst_debug = 0;
+module_param(dst_debug, int, 0644);
+MODULE_PARM_DESC(dst_debug, "debug messages, default is 0 (no)");
+
+#define dprintk if (dst_debug) printk
+
+#define DST_TYPE_IS_SAT 0
+#define DST_TYPE_IS_TERR 1
+#define DST_TYPE_IS_CABLE 2
+
+#define DST_TYPE_HAS_NEWTUNE 1
+#define DST_TYPE_HAS_TS204 2
+#define DST_TYPE_HAS_SYMDIV 4
+
+#define HAS_LOCK 1
+#define ATTEMPT_TUNE 2
+#define HAS_POWER 4
+
+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)
+{
+ union dst_gpio_packet enb;
+ union dst_gpio_packet bits;
+ int err;
+
+ enb.enb.mask = mask;
+ enb.enb.enable = enbb;
+ if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) {
+ dprintk("%s: dst_gpio_enb error (err == %i, mask == 0x%02x, enb == 0x%02x)\n", __FUNCTION__, err, mask, enbb);
+ return -EREMOTEIO;
+ }
+
+ /* because complete disabling means no output, no need to do output packet */
+ if (enbb == 0)
+ return 0;
+
+ bits.outp.mask = enbb;
+ bits.outp.highvals = outhigh;
+
+ if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) {
+ dprintk("%s: dst_gpio_outb error (err == %i, enbb == 0x%02x, outhigh == 0x%02x)\n", __FUNCTION__, 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("%s: dst_gpio_inb error (err == %i)\n", __FUNCTION__, err);
+ return -EREMOTEIO;
+ }
+
+ *result = (u8) rd_packet.rd.value;
+ return 0;
+}
+
+#define DST_I2C_ENABLE 1
+#define DST_8820 2
+
+static int dst_reset8820(struct dst_state *state)
+{
+ int retval;
+ /* pull 8820 gpio pin low, wait, high, wait, then low */
+ // dprintk ("%s: reset 8820\n", __FUNCTION__);
+ retval = dst_gpio_outb(state, DST_8820, DST_8820, 0);
+ if (retval < 0)
+ return retval;
+ msleep(10);
+ retval = dst_gpio_outb(state, DST_8820, DST_8820, DST_8820);
+ if (retval < 0)
+ return retval;
+ /* wait for more feedback on what works here *
+ msleep(10);
+ retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0);
+ if (retval < 0)
+ return retval;
+ */
+ return 0;
+}
+
+static int dst_i2c_enable(struct dst_state *state)
+{
+ int retval;
+ /* pull I2C enable gpio pin low, wait */
+ // dprintk ("%s: i2c enable\n", __FUNCTION__);
+ retval = dst_gpio_outb(state, ~0, DST_I2C_ENABLE, 0);
+ if (retval < 0)
+ return retval;
+ // dprintk ("%s: i2c enable delay\n", __FUNCTION__);
+ msleep(33);
+ return 0;
+}
+
+static int dst_i2c_disable(struct dst_state *state)
+{
+ int retval;
+ /* release I2C enable gpio pin, wait */
+ // dprintk ("%s: i2c disable\n", __FUNCTION__);
+ retval = dst_gpio_outb(state, ~0, 0, 0);
+ if (retval < 0)
+ return retval;
+ // dprintk ("%s: i2c disable delay\n", __FUNCTION__);
+ msleep(33);
+ return 0;
+}
+
+static int dst_wait_dst_ready(struct dst_state *state)
+{
+ u8 reply;
+ int retval;
+ int i;
+ for (i = 0; i < 200; i++) {
+ retval = dst_gpio_inb(state, &reply);
+ if (retval < 0)
+ return retval;
+ if ((reply & DST_I2C_ENABLE) == 0) {
+ dprintk("%s: dst wait ready after %d\n", __FUNCTION__, i);
+ return 1;
+ }
+ msleep(10);
+ }
+ dprintk("%s: dst wait NOT ready after %d\n", __FUNCTION__, i);
+ return 0;
+}
+
+static 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;
+ int cnt;
+
+ if (dst_debug && dst_verbose) {
+ u8 i;
+ dprintk("%s writing", __FUNCTION__);
+ for (i = 0; i < len; i++) {
+ dprintk(" 0x%02x", data[i]);
+ }
+ dprintk("\n");
+ }
+ msleep(30);
+ for (cnt = 0; cnt < 4; cnt++) {
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+ dprintk("%s: write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, data[0]);
+ dst_i2c_disable(state);
+ msleep(500);
+ dst_i2c_enable(state);
+ msleep(500);
+ continue;
+ } else
+ break;
+ }
+ if (cnt >= 4)
+ return -EREMOTEIO;
+ return 0;
+}
+
+static 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 < 4; cnt++) {
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+ dprintk("%s: read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, ret[0]);
+ dst_i2c_disable(state);
+ dst_i2c_enable(state);
+ continue;
+ } else
+ break;
+ }
+ if (cnt >= 4)
+ return -EREMOTEIO;
+ dprintk("%s reply is 0x%x\n", __FUNCTION__, ret[0]);
+ if (dst_debug && dst_verbose) {
+ for (err = 1; err < len; err++)
+ dprintk(" 0x%x", ret[err]);
+ if (err > 1)
+ dprintk("\n");
+ }
+ return 0;
+}
+
+static int dst_set_freq(struct dst_state *state, u32 freq)
+{
+ u8 *val;
+
+ state->frequency = freq;
+
+ // dprintk("%s: set frequency %u\n", __FUNCTION__, freq);
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ freq = freq / 1000;
+ if (freq < 950 || freq > 2150)
+ return -EINVAL;
+ val = &state->tx_tuna[0];
+ val[2] = (freq >> 8) & 0x7f;
+ val[3] = (u8) freq;
+ val[4] = 1;
+ val[8] &= ~4;
+ if (freq < 1531)
+ val[8] |= 4;
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ freq = freq / 1000;
+ if (freq < 137000 || freq > 858000)
+ return -EINVAL;
+ val = &state->tx_tuna[0];
+ val[2] = (freq >> 16) & 0xff;
+ val[3] = (freq >> 8) & 0xff;
+ val[4] = (u8) freq;
+ val[5] = 0;
+ switch (state->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ val[6] = 6;
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ case BANDWIDTH_AUTO:
+ val[6] = 7;
+ break;
+
+ case BANDWIDTH_8_MHZ:
+ val[6] = 8;
+ break;
+ }
+
+ val[7] = 0;
+ val[8] = 0;
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ /* guess till will get one */
+ freq = freq / 1000;
+ val = &state->tx_tuna[0];
+ val[2] = (freq >> 16) & 0xff;
+ val[3] = (freq >> 8) & 0xff;
+ val[4] = (u8) freq;
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+static int dst_set_bandwidth(struct dst_state* state, fe_bandwidth_t bandwidth)
+{
+ u8 *val;
+
+ state->bandwidth = bandwidth;
+
+ if (state->dst_type != DST_TYPE_IS_TERR)
+ return 0;
+
+ val = &state->tx_tuna[0];
+ switch (bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ val[6] = 6;
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ val[6] = 7;
+ break;
+
+ case BANDWIDTH_8_MHZ:
+ val[6] = 8;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dst_set_inversion(struct dst_state* state, fe_spectral_inversion_t inversion)
+{
+ u8 *val;
+
+ state->inversion = inversion;
+
+ val = &state->tx_tuna[0];
+
+ val[8] &= ~0x80;
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ break;
+ case INVERSION_ON:
+ val[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)
+{
+ u8 *val;
+ u32 symcalc;
+ u64 sval;
+
+ state->symbol_rate = srate;
+
+ if (state->dst_type == DST_TYPE_IS_TERR) {
+ return 0;
+ }
+ // dprintk("%s: set srate %u\n", __FUNCTION__, srate);
+ srate /= 1000;
+ val = &state->tx_tuna[0];
+
+ if (state->type_flags & DST_TYPE_HAS_SYMDIV) {
+ sval = srate;
+ sval <<= 20;
+ do_div(sval, 88000);
+ symcalc = (u32) sval;
+ // dprintk("%s: set symcalc %u\n", __FUNCTION__, symcalc);
+ val[5] = (u8) (symcalc >> 12);
+ val[6] = (u8) (symcalc >> 4);
+ val[7] = (u8) (symcalc << 4);
+ } else {
+ val[5] = (u8) (srate >> 16) & 0x7f;
+ val[6] = (u8) (srate >> 8);
+ val[7] = (u8) srate;
+ }
+ val[8] &= ~0x20;
+ if (srate > 8000)
+ val[8] |= 0x20;
+ return 0;
+}
+
+static 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);
+}
+
+struct dst_types {
+ char *mstr;
+ int offs;
+ u8 dst_type;
+ u32 type_flags;
+};
+
+static struct dst_types dst_tlist[] = {
+ {"DST-020", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+ {"DST-030", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+ {"DST-03T", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204},
+ {"DST-MOT", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+ {"DST-CI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+ {"DSTMCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+ {"DSTFCI", 1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+ {"DCTNEW", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE},
+ {"DCT-CI", 1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE | DST_TYPE_HAS_TS204},
+ {"DTTDIG", 1, DST_TYPE_IS_TERR, 0}
+};
+
+/* DCTNEW and DCT-CI are guesses */
+
+static void dst_type_flags_print(u32 type_flags)
+{
+ printk("DST type flags :");
+ if (type_flags & DST_TYPE_HAS_NEWTUNE)
+ printk(" 0x%x newtuner", DST_TYPE_HAS_NEWTUNE);
+ if (type_flags & DST_TYPE_HAS_TS204)
+ printk(" 0x%x ts204", DST_TYPE_HAS_TS204);
+ if (type_flags & DST_TYPE_HAS_SYMDIV)
+ printk(" 0x%x symdiv", DST_TYPE_HAS_SYMDIV);
+ printk("\n");
+}
+
+static int dst_type_print(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;
+ default:
+ printk("%s: invalid dst type %d\n", __FUNCTION__, type);
+ return -EINVAL;
+ }
+ printk("DST type : %s\n", otype);
+ return 0;
+}
+
+static int dst_check_ci(struct dst_state *state)
+{
+ u8 txbuf[8];
+ u8 rxbuf[8];
+ int retval;
+ int i;
+ struct dst_types *dsp;
+ u8 use_dst_type;
+ u32 use_type_flags;
+
+ memset(txbuf, 0, sizeof(txbuf));
+ txbuf[1] = 6;
+ txbuf[7] = dst_check_sum(txbuf, 7);
+
+ dst_i2c_enable(state);
+ dst_reset8820(state);
+ retval = write_dst(state, txbuf, 8);
+ if (retval < 0) {
+ dst_i2c_disable(state);
+ dprintk("%s: write not successful, maybe no card?\n", __FUNCTION__);
+ return retval;
+ }
+ msleep(3);
+ retval = read_dst(state, rxbuf, 1);
+ dst_i2c_disable(state);
+ if (retval < 0) {
+ dprintk("%s: read not successful, maybe no card?\n", __FUNCTION__);
+ return retval;
+ }
+ if (rxbuf[0] != 0xff) {
+ dprintk("%s: write reply not 0xff, not ci (%02x)\n", __FUNCTION__, rxbuf[0]);
+ return retval;
+ }
+ if (!dst_wait_dst_ready(state))
+ return 0;
+ // dst_i2c_enable(i2c); Dimitri
+ retval = read_dst(state, rxbuf, 8);
+ dst_i2c_disable(state);
+ if (retval < 0) {
+ dprintk("%s: read not successful\n", __FUNCTION__);
+ return retval;
+ }
+ if (rxbuf[7] != dst_check_sum(rxbuf, 7)) {
+ dprintk("%s: checksum failure\n", __FUNCTION__);
+ return retval;
+ }
+ rxbuf[7] = '\0';
+ for (i = 0, dsp = &dst_tlist[0]; i < sizeof(dst_tlist) / sizeof(dst_tlist[0]); i++, dsp++) {
+ if (!strncmp(&rxbuf[dsp->offs], dsp->mstr, strlen(dsp->mstr))) {
+ use_type_flags = dsp->type_flags;
+ use_dst_type = dsp->dst_type;
+ printk("%s: recognize %s\n", __FUNCTION__, dsp->mstr);
+ break;
+ }
+ }
+ if (i >= sizeof(dst_tlist) / sizeof(dst_tlist[0])) {
+ printk("%s: unable to recognize %s or %s\n", __FUNCTION__, &rxbuf[0], &rxbuf[1]);
+ printk("%s please email linux-dvb@linuxtv.org with this type in\n", __FUNCTION__);
+ use_dst_type = DST_TYPE_IS_SAT;
+ use_type_flags = DST_TYPE_HAS_SYMDIV;
+ }
+ dst_type_print(use_dst_type);
+
+ state->type_flags = use_type_flags;
+ state->dst_type = use_dst_type;
+ dst_type_flags_print(state->type_flags);
+
+ if (state->type_flags & DST_TYPE_HAS_TS204) {
+ dst_packsize(state, 204);
+ }
+ return 0;
+}
+
+static int dst_command(struct dst_state* state, u8 * data, u8 len)
+{
+ int retval;
+ u8 reply;
+
+ dst_i2c_enable(state);
+ dst_reset8820(state);
+ retval = write_dst(state, data, len);
+ if (retval < 0) {
+ dst_i2c_disable(state);
+ dprintk("%s: write not successful\n", __FUNCTION__);
+ return retval;
+ }
+ msleep(33);
+ retval = read_dst(state, &reply, 1);
+ dst_i2c_disable(state);
+ if (retval < 0) {
+ dprintk("%s: read verify not successful\n", __FUNCTION__);
+ return retval;
+ }
+ if (reply != 0xff) {
+ dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+ return 0;
+ }
+ if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3))
+ return 0;
+ if (!dst_wait_dst_ready(state))
+ return 0;
+ // dst_i2c_enable(i2c); Per dimitri
+ retval = read_dst(state, state->rxbuffer, 8);
+ dst_i2c_disable(state);
+ if (retval < 0) {
+ dprintk("%s: read not successful\n", __FUNCTION__);
+ return 0;
+ }
+ if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+ dprintk("%s: checksum failure\n", __FUNCTION__);
+ return 0;
+ }
+ return 0;
+}
+
+static int dst_get_signal(struct dst_state* state)
+{
+ int retval;
+ u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb };
+
+ 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;
+ }
+ 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_TERR)
+ return 0;
+
+ if (state->voltage == SEC_VOLTAGE_OFF)
+ paket[4] = 0;
+ else
+ paket[4] = 1;
+ if (state->tone == SEC_TONE_ON)
+ paket[2] = state->k22;
+ else
+ paket[2] = 0;
+ paket[7] = dst_check_sum(&paket[0], 7);
+ dst_command(state, paket, 8);
+ return 0;
+}
+
+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))
+ return 0;
+ if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+ /* how to get variable length reply ???? */
+ retval = read_dst(state, state->rx_tuna, 10);
+ } else {
+ retval = read_dst(state, &state->rx_tuna[2], 8);
+ }
+ if (retval < 0) {
+ dprintk("%s: read not successful\n", __FUNCTION__);
+ return 0;
+ }
+ if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
+ dprintk("%s: checksum failure?\n", __FUNCTION__);
+ return 0;
+ }
+ } else {
+ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) {
+ dprintk("%s: checksum failure?\n", __FUNCTION__);
+ return 0;
+ }
+ }
+ if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0)
+ return 0;
+ state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3];
+
+ state->decode_lock = 1;
+ /*
+ dst->decode_n1 = (dst->rx_tuna[4] << 8) +
+ (dst->rx_tuna[5]);
+
+ dst->decode_n2 = (dst->rx_tuna[8] << 8) +
+ (dst->rx_tuna[7]);
+ */
+ state->diseq_flags |= HAS_LOCK;
+ /* dst->cur_jiff = jiffies; */
+ 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 = (struct dst_state*) fe->demodulator_priv;
+ int retval;
+ u8 reply;
+
+ dprintk("%s: type_flags 0x%x \n", __FUNCTION__, 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);
+ dst_i2c_enable(state);
+ if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+ dst_reset8820(state);
+ 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], 8);
+ }
+ if (retval < 0) {
+ dst_i2c_disable(state);
+ dprintk("%s: write not successful\n", __FUNCTION__);
+ return retval;
+ }
+ msleep(3);
+ retval = read_dst(state, &reply, 1);
+ dst_i2c_disable(state);
+ if (retval < 0) {
+ dprintk("%s: read verify not successful\n", __FUNCTION__);
+ return retval;
+ }
+ if (reply != 0xff) {
+ dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+ return 0;
+ }
+ state->diseq_flags |= ATTEMPT_TUNE;
+ return dst_get_tuna(state);
+}
+
+/*
+ * 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 = (struct dst_state*) fe->demodulator_priv;
+ u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+
+ if (state->dst_type == DST_TYPE_IS_TERR)
+ return 0;
+
+ if (cmd->msg_len == 0 || cmd->msg_len > 4)
+ return -EINVAL;
+ memcpy(&paket[3], cmd->msg, cmd->msg_len);
+ paket[7] = dst_check_sum(&paket[0], 7);
+ dst_command(state, paket, 8);
+ return 0;
+}
+
+static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ u8 *val;
+ int need_cmd;
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ state->voltage = voltage;
+
+ if (state->dst_type == DST_TYPE_IS_TERR)
+ return 0;
+
+ need_cmd = 0;
+ val = &state->tx_tuna[0];
+ val[8] &= ~0x40;
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ if ((state->diseq_flags & HAS_POWER) == 0)
+ need_cmd = 1;
+ state->diseq_flags |= HAS_POWER;
+ break;
+ case SEC_VOLTAGE_18:
+ if ((state->diseq_flags & HAS_POWER) == 0)
+ need_cmd = 1;
+ state->diseq_flags |= HAS_POWER;
+ val[8] |= 0x40;
+ break;
+ case SEC_VOLTAGE_OFF:
+ need_cmd = 1;
+ state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (need_cmd) {
+ dst_tone_power_cmd(state);
+ }
+ return 0;
+}
+
+static int dst_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ u8 *val;
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ state->tone = tone;
+
+ if (state->dst_type == DST_TYPE_IS_TERR)
+ return 0;
+
+ val = &state->tx_tuna[0];
+
+ val[8] &= ~0x1;
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ break;
+ case SEC_TONE_ON:
+ val[8] |= 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dst_tone_power_cmd(state);
+ return 0;
+}
+
+static int dst_init(struct dvb_frontend* fe)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+ static u8 ini_satci_tuna[] = { 9, 0, 3, 0xb6, 1, 0, 0x73, 0x21, 0, 0 };
+ static u8 ini_satfta_tuna[] = { 0, 0, 3, 0xb6, 1, 0x55, 0xbd, 0x50, 0, 0 };
+ static u8 ini_tvfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+ static u8 ini_tvci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+ static u8 ini_cabfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+ static u8 ini_cabci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+ state->inversion = INVERSION_ON;
+ state->voltage = SEC_VOLTAGE_13;
+ state->tone = SEC_TONE_OFF;
+ state->symbol_rate = 29473000;
+ state->fec = FEC_AUTO;
+ state->diseq_flags = 0;
+ state->k22 = 0x02;
+ state->bandwidth = BANDWIDTH_7_MHZ;
+ state->cur_jiff = jiffies;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ state->frequency = 950000;
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_satci_tuna : ini_satfta_tuna), sizeof(ini_satfta_tuna));
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ state->frequency = 137000000;
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_tvci_tuna : ini_tvfta_tuna), sizeof(ini_tvfta_tuna));
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ state->frequency = 51000000;
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_cabci_tuna : ini_cabfta_tuna), sizeof(ini_cabfta_tuna));
+ }
+
+ return 0;
+}
+
+static int dst_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ *status = 0;
+ if (state->diseq_flags & HAS_LOCK) {
+ dst_get_signal(state);
+ 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 = (struct dst_state*) fe->demodulator_priv;
+
+ dst_get_signal(state);
+ *strength = state->decode_strength;
+
+ return 0;
+}
+
+static int dst_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ dst_get_signal(state);
+ *snr = state->decode_snr;
+
+ return 0;
+}
+
+static int dst_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ dst_set_freq(state, p->frequency);
+ dst_set_inversion(state, p->inversion);
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ dst_set_fec(state, p->u.qpsk.fec_inner);
+ dst_set_symbolrate(state, p->u.qpsk.symbol_rate);
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ dst_set_bandwidth(state, p->u.ofdm.bandwidth);
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ dst_set_fec(state, p->u.qam.fec_inner);
+ dst_set_symbolrate(state, p->u.qam.symbol_rate);
+ }
+ dst_write_tuna(fe);
+
+ return 0;
+}
+
+static int dst_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+ p->frequency = state->decode_freq;
+ p->inversion = state->inversion;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ p->u.qpsk.symbol_rate = state->symbol_rate;
+ p->u.qpsk.fec_inner = dst_get_fec(state);
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ p->u.ofdm.bandwidth = state->bandwidth;
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ p->u.qam.symbol_rate = state->symbol_rate;
+ p->u.qam.fec_inner = dst_get_fec(state);
+ p->u.qam.modulation = QAM_AUTO;
+ }
+
+ return 0;
+}
+
+static void dst_release(struct dvb_frontend* fe)
+{
+ struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+ 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;
+
+struct dvb_frontend* dst_attach(const struct dst_config* config,
+ struct i2c_adapter* i2c,
+ struct bt878 *bt)
+{
+ struct dst_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dst_state*) kmalloc(sizeof(struct dst_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->bt = bt;
+
+ /* check if the demod is there */
+ if (dst_check_ci(state) < 0) goto error;
+
+ /* determine settings based on type */
+ switch (state->dst_type) {
+ case DST_TYPE_IS_TERR:
+ memcpy(&state->ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ case DST_TYPE_IS_CABLE:
+ memcpy(&state->ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ case DST_TYPE_IS_SAT:
+ memcpy(&state->ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ default:
+ printk("dst: unknown frontend type. please report to the LinuxTV.org DVB mailinglist.\n");
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops = {
+
+ .info = {
+ .name = "DST DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 137000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = dst_release,
+
+ .init = dst_init,
+
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+
+ .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 = {
+
+ .info = {
+ .name = "DST DVB-S",
+ .type = FE_QPSK,
+ .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,
+
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+
+ .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 = {
+
+ .info = {
+ .name = "DST DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ /* . symbol_rate_tolerance = ???,*/
+ .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO
+ },
+
+ .release = dst_release,
+
+ .init = dst_init,
+
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+};
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo Frontend driver");
+MODULE_AUTHOR("Jamie Honan");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dst_attach);
diff --git a/drivers/media/dvb/bt8xx/dst.h b/drivers/media/dvb/bt8xx/dst.h
new file mode 100644
index 000000000000..bcb418c5c121
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst.h
@@ -0,0 +1,40 @@
+/*
+ Frontend-driver for TwinHan DST Frontend
+
+ Copyright (C) 2003 Jamie Honan
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You 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_H
+#define DST_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include "bt878.h"
+
+struct dst_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+extern struct dvb_frontend* dst_attach(const struct dst_config* config,
+ struct i2c_adapter* i2c,
+ struct bt878 *bt);
+
+#endif // DST_H
diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/dvb/bt8xx/dst_priv.h
new file mode 100644
index 000000000000..80488aa628b4
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst_priv.h
@@ -0,0 +1,36 @@
+/*
+ * 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/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
new file mode 100644
index 000000000000..b735397f59aa
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -0,0 +1,797 @@
+/*
+ * 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.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.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).");
+
+#define dprintk( args... ) \
+ do { \
+ if (debug) printk(KERN_DEBUG args); \
+ } while (0)
+
+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;
+
+ down(&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);
+ up(&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;
+
+ down(&card->lock);
+ card->nfeeds--;
+ if (card->nfeeds == 0)
+ bt878_stop(card->bt);
+ up(&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 __init *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_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+ u32 div;
+ unsigned char bs = 0;
+ unsigned char cp = 0;
+
+ #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+ div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (params->frequency < 542000000) cp = 0xb4;
+ else if (params->frequency < 771000000) cp = 0xbc;
+ else cp = 0xf4;
+
+ if (params->frequency == 0) bs = 0x03;
+ else if (params->frequency < 443250000) bs = 0x02;
+ else bs = 0x08;
+
+ pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = cp;
+ pllbuf[4] = bs;
+
+ return 0;
+}
+
+static struct mt352_config thomson_dtt7579_config = {
+
+ .demod_address = 0x0f,
+ .demod_init = thomson_dtt7579_demod_init,
+ .pll_set = thomson_dtt7579_pll_set,
+};
+
+static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ u32 freq = params->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 */
+ printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
+
+ /* This is really the bit driving the tuner chip cx24108 */
+
+ if(freq<950000) freq=950000; /* kHz */
+ if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
+
+ /* decide which VCO to use for the input frequency */
+ for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
+ printk("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 */
+ printk("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_pll_init(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+
+static struct cx24110_config pctvsat_config = {
+
+ .demod_address = 0x55,
+ .pll_init = pinnsat_pll_init,
+ .pll_set = cx24108_pll_set,
+};
+
+
+static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ 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 + params->frequency + 83333) / 166666;
+ cfg = 0x88;
+
+ if (params->frequency < 175000000) cpump = 2;
+ else if (params->frequency < 390000000) cpump = 1;
+ else if (params->frequency < 470000000) cpump = 2;
+ else if (params->frequency < 750000000) cpump = 2;
+ else cpump = 3;
+
+ if (params->frequency < 175000000) band_select = 0x0e;
+ else if (params->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 | band_select;
+
+ 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,
+ .pll_set = microtune_mt7202dtf_pll_set,
+ .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_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+ u32 div;
+ unsigned char bs = 0;
+ unsigned char cp = 0;
+
+ #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+ div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (params->frequency < 150000000) cp = 0xB4;
+ else if (params->frequency < 173000000) cp = 0xBC;
+ else if (params->frequency < 250000000) cp = 0xB4;
+ else if (params->frequency < 400000000) cp = 0xBC;
+ else if (params->frequency < 420000000) cp = 0xF4;
+ else if (params->frequency < 470000000) cp = 0xFC;
+ else if (params->frequency < 600000000) cp = 0xBC;
+ else if (params->frequency < 730000000) cp = 0xF4;
+ else cp = 0xFC;
+
+ if (params->frequency < 150000000) bs = 0x01;
+ else if (params->frequency < 173000000) bs = 0x01;
+ else if (params->frequency < 250000000) bs = 0x02;
+ else if (params->frequency < 400000000) bs = 0x02;
+ else if (params->frequency < 420000000) bs = 0x02;
+ else if (params->frequency < 470000000) bs = 0x02;
+ else if (params->frequency < 600000000) bs = 0x08;
+ else if (params->frequency < 730000000) bs = 0x08;
+ else bs = 0x08;
+
+ pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = cp;
+ pllbuf[4] = bs;
+
+ return 0;
+}
+
+static struct mt352_config advbt771_samsung_tdtc9251dh0_config = {
+
+ .demod_address = 0x0f,
+ .demod_init = advbt771_samsung_tdtc9251dh0_demod_init,
+ .pll_set = advbt771_samsung_tdtc9251dh0_pll_set,
+};
+
+
+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 controled 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 controled 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_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+ 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 = (params->frequency + 36166667) / 166667;
+
+ buf[0] = (div >> 8) & 0x7F;
+ buf[1] = div & 0xFF;
+ buf[2] = 0x85;
+ if ((params->frequency >= 47000000) && (params->frequency < 153000000))
+ buf[3] = 0x01;
+ else if ((params->frequency >= 153000000) && (params->frequency < 430000000))
+ buf[3] = 0x02;
+ else if ((params->frequency >= 430000000) && (params->frequency < 824000000))
+ buf[3] = 0x0C;
+ else if ((params->frequency >= 824000000) && (params->frequency < 863000000))
+ buf[3] = 0x8C;
+ else
+ return -EINVAL;
+
+ i2c_transfer(card->i2c_adapter, &msg, 1);
+ return 0;
+}
+
+static struct nxt6000_config vp3021_alps_tded4_config = {
+
+ .demod_address = 0x0a,
+ .clock_inversion = 1,
+ .pll_set = vp3021_alps_tded4_pll_set,
+};
+
+
+static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
+{
+ switch(type) {
+#ifdef BTTV_DVICO_DVBT_LITE
+ case BTTV_DVICO_DVBT_LITE:
+ card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ card->fe->ops->info.frequency_min = 174000000;
+ card->fe->ops->info.frequency_max = 862000000;
+ break;
+ }
+ break;
+#endif
+
+#ifdef BTTV_TWINHAN_VP3021
+ case BTTV_TWINHAN_VP3021:
+#else
+ case BTTV_NEBULA_DIGITV:
+#endif
+ card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ break;
+ }
+ break;
+
+ case BTTV_AVDVBT_761:
+ card->fe = sp887x_attach(&microtune_mt7202dtf_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ break;
+ }
+ break;
+
+ case BTTV_AVDVBT_771:
+ card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ card->fe->ops->info.frequency_min = 174000000;
+ card->fe->ops->info.frequency_max = 862000000;
+ break;
+ }
+ break;
+
+ case BTTV_TWINHAN_DST:
+ card->fe = dst_attach(&dst_config, card->i2c_adapter, card->bt);
+ if (card->fe != NULL) {
+ break;
+ }
+ break;
+
+ case BTTV_PINNACLESAT:
+ card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ break;
+ }
+ break;
+
+ case BTTV_PC_HDTV:
+ card->fe = or51211_attach(&or51211_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ break;
+ }
+ break;
+ }
+
+ if (card->fe == NULL) {
+ printk("dvb-bt8xx: 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)) {
+ printk("dvb-bt8xx: Frontend registration failed!\n");
+ if (card->fe->ops->release)
+ card->fe->ops->release(card->fe);
+ card->fe = NULL;
+ }
+ }
+}
+
+static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
+{
+ int result;
+
+ if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name,
+ THIS_MODULE)) < 0) {
+ printk("dvb_bt8xx: 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;
+
+ if ((result = dvb_dmx_init(&card->demux)) < 0) {
+ printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+ dvb_unregister_adapter(card->dvb_adapter);
+ return result;
+ }
+
+ card->dmxdev.filternum = 256;
+ card->dmxdev.demux = &card->demux.dmx;
+ card->dmxdev.capabilities = 0;
+
+ if ((result = dvb_dmxdev_init(&card->dmxdev, card->dvb_adapter)) < 0) {
+ printk("dvb_bt8xx: dvb_dmxdev_init failed (errno = %d)\n", result);
+
+ dvb_dmx_release(&card->demux);
+ dvb_unregister_adapter(card->dvb_adapter);
+ return result;
+ }
+
+ card->fe_hw.source = DMX_FRONTEND_0;
+
+ if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
+ printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+ dvb_dmxdev_release(&card->dmxdev);
+ dvb_dmx_release(&card->demux);
+ dvb_unregister_adapter(card->dvb_adapter);
+ return result;
+ }
+
+ card->fe_mem.source = DMX_MEMORY_FE;
+
+ if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem)) < 0) {
+ printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+ dvb_dmxdev_release(&card->dmxdev);
+ dvb_dmx_release(&card->demux);
+ dvb_unregister_adapter(card->dvb_adapter);
+ return result;
+ }
+
+ if ((result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
+ printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+ 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);
+ dvb_unregister_adapter(card->dvb_adapter);
+ return result;
+ }
+
+ dvb_net_init(card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
+
+ tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
+
+ frontend_init(card, type);
+
+ return 0;
+}
+
+static int dvb_bt8xx_probe(struct device *dev)
+{
+ struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+ struct dvb_bt8xx_card *card;
+ struct pci_dev* bttv_pci_dev;
+ int ret;
+
+ if (!(card = kmalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(card, 0, sizeof(*card));
+ init_MUTEX(&card->lock);
+ card->bttv_nr = sub->core->nr;
+ strncpy(card->card_name, sub->core->name, sizeof(sub->core->name));
+ card->i2c_adapter = &sub->core->i2c_adap;
+
+ switch(sub->core->type)
+ {
+ case BTTV_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 = 0;
+ card->irq_err_ignore = 0;
+ break;
+
+#ifdef BTTV_DVICO_DVBT_LITE
+ case BTTV_DVICO_DVBT_LITE:
+#endif
+ card->gpio_mode = 0x0400C060;
+ card->op_sync_orin = 0;
+ card->irq_err_ignore = 0;
+ /* 26, 15, 14, 6, 5
+ * A_PWRDN DA_DPM DA_SBR DA_IOM_DA
+ * DA_APP(parallel) */
+ break;
+
+#ifdef BTTV_TWINHAN_VP3021
+ case BTTV_TWINHAN_VP3021:
+#else
+ case BTTV_NEBULA_DIGITV:
+#endif
+ case BTTV_AVDVBT_761:
+ card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5);
+ card->op_sync_orin = 0;
+ card->irq_err_ignore = 0;
+ /* A_PWRDN DA_SBR DA_APP (high speed serial) */
+ break;
+
+ case BTTV_AVDVBT_771: //case 0x07711461:
+ card->gpio_mode = 0x0400402B;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = 0;
+ /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/
+ break;
+
+ case BTTV_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_PC_HDTV:
+ card->gpio_mode = 0x0100EC7B;
+ card->op_sync_orin = 0;
+ card->irq_err_ignore = 0;
+ break;
+
+ default:
+ printk(KERN_WARNING "dvb_bt8xx: 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))) {
+ printk("dvb_bt8xx: no pci device for card %d\n", card->bttv_nr);
+ kfree(card);
+ return -EFAULT;
+ }
+
+ if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
+ printk("dvb_bt8xx: unable to determine DMA core of card %d,\n",
+ card->bttv_nr);
+ printk("dvb_bt8xx: if you have the ALSA bt87x audio driver "
+ "installed, try removing it.\n");
+
+ kfree(card);
+ return -EFAULT;
+
+ }
+
+ init_MUTEX(&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(dev, card);
+ return 0;
+}
+
+static int dvb_bt8xx_remove(struct device *dev)
+{
+ struct dvb_bt8xx_card *card = dev_get_drvdata(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_unregister_adapter(card->dvb_adapter);
+
+ kfree(card);
+
+ return 0;
+}
+
+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/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
new file mode 100644
index 000000000000..80ef189f930f
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -0,0 +1,59 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.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 "dvbdev.h"
+#include "dvb_net.h"
+#include "bttv.h"
+#include "mt352.h"
+#include "sp887x.h"
+#include "dst.h"
+#include "nxt6000.h"
+#include "cx24110.h"
+#include "or51211.h"
+
+struct dvb_bt8xx_card {
+ struct semaphore 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/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig
new file mode 100644
index 000000000000..226714085f58
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/Kconfig
@@ -0,0 +1,85 @@
+config DVB_CINERGYT2
+ tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
+ depends on DVB_CORE && USB
+ help
+ Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+ Say Y if you own such a device and want to use it.
+
+
+config DVB_CINERGYT2_TUNING
+ bool "sophisticated fine-tuning for CinergyT2 cards"
+ depends on DVB_CINERGYT2
+ help
+ Here you can fine-tune some parameters of the CinergyT2 driver.
+
+ Normally you don't need to touch this, but in exotic setups you
+ may fine-tune your setup and adjust e.g. DMA buffer sizes for
+ a particular application.
+
+
+config DVB_CINERGYT2_STREAM_URB_COUNT
+ int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
+ depends on DVB_CINERGYT2_TUNING
+ default "32"
+ help
+ USB Request Blocks for Highspeed Stream transfers are scheduled in
+ a queue for the Host Controller.
+
+ Usually the default value is a safe choice.
+
+ You may increase this number if you are using this device in a
+ Server Environment with many high-traffic USB Highspeed devices
+ sharing the same USB bus.
+
+
+config DVB_CINERGYT2_STREAM_BUF_SIZE
+ int "Size of URB Stream Buffers for Highspeed Transfers"
+ depends on DVB_CINERGYT2_TUNING
+ default "512"
+ help
+ Should be a multiple of native buffer size of 512 bytes.
+ Default value is a safe choice.
+
+ You may increase this number if you are using this device in a
+ Server Environment with many high-traffic USB Highspeed devices
+ sharing the same USB bus.
+
+
+config DVB_CINERGYT2_QUERY_INTERVAL
+ int "Status update interval [milliseconds]"
+ depends on DVB_CINERGYT2_TUNING
+ default "250"
+ help
+ This is the interval for status readouts from the demodulator.
+ You may try lower values if you need more responsive signal quality
+ measurements.
+
+ Please keep in mind that these updates cause traffic on the tuner
+ control bus and thus may or may not affect receiption sensitivity.
+
+ The default value should be a safe choice for common applications.
+
+
+config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+ bool "Register the onboard IR Remote Control Receiver as Input Device"
+ depends on DVB_CINERGYT2_TUNING
+ default "yes"
+ help
+ Enable this option if you want to use the onboard Infrared Remote
+ Control Receiver as Linux-Input device.
+
+ Right now only the keycode table for the default Remote Control
+ delivered with the device is supported, please see the driver
+ source code to find out how to add support for other controls.
+
+
+config DVB_CINERGYT2_RC_QUERY_INTERVAL
+ int "Infrared Remote Controller update interval [milliseconds]"
+ depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+ default "100"
+ help
+ If you have a very fast-repeating remote control you can try lower
+ values, for normal consumer receivers the default value should be
+ a safe choice.
+
diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile
new file mode 100644
index 000000000000..c51aece20f9f
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
new file mode 100644
index 000000000000..f1f539761371
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -0,0 +1,965 @@
+/*
+ * TerraTec Cinergy T²/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ * Holger Waechtler <holger@qanu.de>
+ *
+ * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/dvb/frontend.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+
+#ifdef CONFIG_DVB_CINERGYT2_TUNING
+ #define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
+ #define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
+ #define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL)
+ #ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+ #define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL)
+ #define ENABLE_RC (1)
+ #endif
+#else
+ #define STREAM_URB_COUNT (32)
+ #define STREAM_BUF_SIZE (512) /* bytes */
+ #define ENABLE_RC (1)
+ #define RC_QUERY_INTERVAL (100) /* milliseconds */
+ #define QUERY_INTERVAL (333) /* milliseconds */
+#endif
+
+#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
+
+static int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level, args...) \
+do { \
+ if ((debug & level)) { \
+ printk("%s: %s(): ", __stringify(KBUILD_MODNAME), \
+ __FUNCTION__); \
+ printk(args); } \
+} while (0)
+
+enum cinergyt2_ep1_cmd {
+ CINERGYT2_EP1_PID_TABLE_RESET = 0x01,
+ CINERGYT2_EP1_PID_SETUP = 0x02,
+ CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03,
+ CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04,
+ CINERGYT2_EP1_GET_TUNER_STATUS = 0x05,
+ CINERGYT2_EP1_START_SCAN = 0x06,
+ CINERGYT2_EP1_CONTINUE_SCAN = 0x07,
+ CINERGYT2_EP1_GET_RC_EVENTS = 0x08,
+ CINERGYT2_EP1_SLEEP_MODE = 0x09
+};
+
+struct dvbt_set_parameters_msg {
+ uint8_t cmd;
+ uint32_t freq;
+ uint8_t bandwidth;
+ uint16_t tps;
+ uint8_t flags;
+} __attribute__((packed));
+
+struct dvbt_get_status_msg {
+ uint32_t freq;
+ uint8_t bandwidth;
+ uint16_t tps;
+ uint8_t flags;
+ uint16_t gain;
+ uint8_t snr;
+ uint32_t viterbi_error_rate;
+ uint32_t rs_error_rate;
+ uint32_t uncorrected_block_count;
+ uint8_t lock_bits;
+ uint8_t prev_lock_bits;
+} __attribute__((packed));
+
+static struct dvb_frontend_info cinergyt2_fe_info = {
+ .name = DRIVER_NAME,
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS
+};
+
+struct cinergyt2 {
+ struct dvb_demux demux;
+ struct usb_device *udev;
+ struct semaphore sem;
+ struct dvb_adapter *adapter;
+ struct dvb_device *fedev;
+ struct dmxdev dmxdev;
+ struct dvb_net dvbnet;
+
+ int streaming;
+ int sleeping;
+
+ struct dvbt_set_parameters_msg param;
+ struct dvbt_get_status_msg status;
+ struct work_struct query_work;
+
+ wait_queue_head_t poll_wq;
+ int pending_fe_events;
+
+ void *streambuf;
+ dma_addr_t streambuf_dmahandle;
+ struct urb *stream_urb [STREAM_URB_COUNT];
+
+#ifdef ENABLE_RC
+ struct input_dev rc_input_dev;
+ struct work_struct rc_query_work;
+ int rc_input_event;
+#endif
+};
+
+enum {
+ CINERGYT2_RC_EVENT_TYPE_NONE = 0x00,
+ CINERGYT2_RC_EVENT_TYPE_NEC = 0x01,
+ CINERGYT2_RC_EVENT_TYPE_RC5 = 0x02
+};
+
+struct cinergyt2_rc_event {
+ char type;
+ uint32_t value;
+} __attribute__((packed));
+
+static const uint32_t rc_keys [] = {
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xfe01eb04, KEY_POWER,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xfd02eb04, KEY_1,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xfc03eb04, KEY_2,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xfb04eb04, KEY_3,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xfa05eb04, KEY_4,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf906eb04, KEY_5,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf807eb04, KEY_6,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf708eb04, KEY_7,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf609eb04, KEY_8,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf50aeb04, KEY_9,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf30ceb04, KEY_0,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf40beb04, KEY_VIDEO,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf20deb04, KEY_REFRESH,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf10eeb04, KEY_SELECT,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xf00feb04, KEY_EPG,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xef10eb04, KEY_UP,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xeb14eb04, KEY_DOWN,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xee11eb04, KEY_LEFT,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xec13eb04, KEY_RIGHT,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xed12eb04, KEY_OK,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xea15eb04, KEY_TEXT,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe916eb04, KEY_INFO,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe817eb04, KEY_RED,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe718eb04, KEY_GREEN,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe619eb04, KEY_YELLOW,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe51aeb04, KEY_BLUE,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe31ceb04, KEY_VOLUMEUP,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe11eeb04, KEY_VOLUMEDOWN,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe21deb04, KEY_MUTE,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe41beb04, KEY_CHANNELUP,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xe01feb04, KEY_CHANNELDOWN,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xbf40eb04, KEY_PAUSE,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xb34ceb04, KEY_PLAY,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xa758eb04, KEY_RECORD,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xab54eb04, KEY_PREVIOUS,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xb748eb04, KEY_STOP,
+ CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT
+};
+
+static int cinergyt2_command (struct cinergyt2 *cinergyt2,
+ char *send_buf, int send_buf_len,
+ char *recv_buf, int recv_buf_len)
+{
+ int actual_len;
+ char dummy;
+ int ret;
+
+ ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1),
+ send_buf, send_buf_len, &actual_len, 1000);
+
+ if (ret)
+ dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret);
+
+ if (!recv_buf)
+ recv_buf = &dummy;
+
+ ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1),
+ recv_buf, recv_buf_len, &actual_len, 1000);
+
+ if (ret)
+ dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret);
+
+ return ret ? ret : actual_len;
+}
+
+static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable)
+{
+ char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
+ cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+}
+
+static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep)
+{
+ char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 };
+ cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+ cinergyt2->sleeping = sleep;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs);
+
+static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb)
+{
+ int err;
+
+ usb_fill_bulk_urb(urb,
+ cinergyt2->udev,
+ usb_rcvbulkpipe(cinergyt2->udev, 0x2),
+ urb->transfer_buffer,
+ STREAM_BUF_SIZE,
+ cinergyt2_stream_irq,
+ cinergyt2);
+
+ if ((err = usb_submit_urb(urb, GFP_ATOMIC)))
+ dprintk(1, "urb submission failed (err = %i)!\n", err);
+
+ return err;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs)
+{
+ struct cinergyt2 *cinergyt2 = urb->context;
+
+ if (urb->actual_length > 0)
+ dvb_dmx_swfilter(&cinergyt2->demux,
+ urb->transfer_buffer, urb->actual_length);
+
+ if (cinergyt2->streaming)
+ cinergyt2_submit_stream_urb(cinergyt2, urb);
+}
+
+static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+ int i;
+
+ for (i=0; i<STREAM_URB_COUNT; i++)
+ if (cinergyt2->stream_urb[i])
+ usb_free_urb(cinergyt2->stream_urb[i]);
+
+ pci_free_consistent(NULL, STREAM_URB_COUNT*STREAM_BUF_SIZE,
+ cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
+}
+
+static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+ int i;
+
+ cinergyt2->streambuf = pci_alloc_consistent(NULL,
+ STREAM_URB_COUNT*STREAM_BUF_SIZE,
+ &cinergyt2->streambuf_dmahandle);
+ if (!cinergyt2->streambuf) {
+ dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
+ return -ENOMEM;
+ }
+
+ memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE);
+
+ for (i=0; i<STREAM_URB_COUNT; i++) {
+ struct urb *urb;
+
+ if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) {
+ dprintk(1, "failed to alloc consistent stream urbs, bailing out!\n");
+ cinergyt2_free_stream_urbs(cinergyt2);
+ return -ENOMEM;
+ }
+
+ urb->transfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE;
+ urb->transfer_buffer_length = STREAM_BUF_SIZE;
+
+ cinergyt2->stream_urb[i] = urb;
+ }
+
+ return 0;
+}
+
+static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+ int i;
+
+ cinergyt2_control_stream_transfer(cinergyt2, 0);
+
+ for (i=0; i<STREAM_URB_COUNT; i++)
+ if (cinergyt2->stream_urb[i])
+ usb_kill_urb(cinergyt2->stream_urb[i]);
+}
+
+static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+ int i, err;
+
+ for (i=0; i<STREAM_URB_COUNT; i++) {
+ if ((err = cinergyt2_submit_stream_urb(cinergyt2, cinergyt2->stream_urb[i]))) {
+ cinergyt2_stop_stream_xfer(cinergyt2);
+ dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err);
+ return err;
+ }
+ }
+
+ cinergyt2_control_stream_transfer(cinergyt2, 1);
+ return 0;
+}
+
+static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct cinergyt2 *cinergyt2 = demux->priv;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if (cinergyt2->streaming == 0)
+ cinergyt2_start_stream_xfer(cinergyt2);
+
+ cinergyt2->streaming++;
+ up(&cinergyt2->sem);
+ return 0;
+}
+
+static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct cinergyt2 *cinergyt2 = demux->priv;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if (--cinergyt2->streaming == 0)
+ cinergyt2_stop_stream_xfer(cinergyt2);
+
+ up(&cinergyt2->sem);
+ return 0;
+}
+
+/**
+ * convert linux-dvb frontend parameter set into TPS.
+ * See ETSI ETS-300744, section 4.6.2, table 9 for details.
+ *
+ * This function is probably reusable and may better get placed in a support
+ * library.
+ *
+ * We replace errornous fields by default TPS fields (the ones with value 0).
+ */
+static uint16_t compute_tps (struct dvb_frontend_parameters *p)
+{
+ struct dvb_ofdm_parameters *op = &p->u.ofdm;
+ uint16_t tps = 0;
+
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 7);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 7);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 7);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 7);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ default:
+ /* tps |= (0 << 7) */;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 4);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 4);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 4);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 4);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ default:
+ /* tps |= (0 << 4) */;
+ }
+
+ switch (op->constellation) {
+ case QAM_16:
+ tps |= (1 << 13);
+ break;
+ case QAM_64:
+ tps |= (2 << 13);
+ break;
+ case QPSK:
+ default:
+ /* tps |= (0 << 13) */;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 0);
+ break;
+ case TRANSMISSION_MODE_2K:
+ default:
+ /* tps |= (0 << 0) */;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 2);
+ break;
+ case GUARD_INTERVAL_1_32:
+ default:
+ /* tps |= (0 << 2) */;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_1:
+ tps |= (1 << 10);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 10);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 10);
+ break;
+ case HIERARCHY_NONE:
+ default:
+ /* tps |= (0 << 10) */;
+ }
+
+ return tps;
+}
+
+static int cinergyt2_open (struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct cinergyt2 *cinergyt2 = dvbdev->priv;
+ int err;
+
+ if ((err = dvb_generic_open(inode, file)))
+ return err;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ cinergyt2_sleep(cinergyt2, 0);
+ schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+ }
+
+ up(&cinergyt2->sem);
+ return 0;
+}
+
+static int cinergyt2_release (struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct cinergyt2 *cinergyt2 = dvbdev->priv;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ cancel_delayed_work(&cinergyt2->query_work);
+ flush_scheduled_work();
+ cinergyt2_sleep(cinergyt2, 1);
+ }
+
+ up(&cinergyt2->sem);
+
+ return dvb_generic_release(inode, file);
+}
+
+static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct cinergyt2 *cinergyt2 = dvbdev->priv;
+ poll_wait(file, &cinergyt2->poll_wq, wait);
+ return (POLLIN | POLLRDNORM | POLLPRI);
+}
+
+
+static int cinergyt2_ioctl (struct inode *inode, struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct cinergyt2 *cinergyt2 = dvbdev->priv;
+ struct dvbt_get_status_msg *stat = &cinergyt2->status;
+ fe_status_t status = 0;
+
+ switch (cmd) {
+ case FE_GET_INFO:
+ return copy_to_user((void __user*) arg, &cinergyt2_fe_info,
+ sizeof(struct dvb_frontend_info));
+
+ case FE_READ_STATUS:
+ if (0xffff - le16_to_cpu(stat->gain) > 30)
+ status |= FE_HAS_SIGNAL;
+ if (stat->lock_bits & (1 << 6))
+ status |= FE_HAS_LOCK;
+ if (stat->lock_bits & (1 << 5))
+ status |= FE_HAS_SYNC;
+ if (stat->lock_bits & (1 << 4))
+ status |= FE_HAS_CARRIER;
+ if (stat->lock_bits & (1 << 1))
+ status |= FE_HAS_VITERBI;
+
+ return copy_to_user((void __user*) arg, &status, sizeof(status));
+
+ case FE_READ_BER:
+ return put_user(le32_to_cpu(stat->viterbi_error_rate),
+ (__u32 __user *) arg);
+
+ case FE_READ_SIGNAL_STRENGTH:
+ return put_user(0xffff - le16_to_cpu(stat->gain),
+ (__u16 __user *) arg);
+
+ case FE_READ_SNR:
+ return put_user((stat->snr << 8) | stat->snr,
+ (__u16 __user *) arg);
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ /* UNC are already converted to host byte order... */
+ return put_user(stat->uncorrected_block_count,
+ (__u32 __user *) arg);
+
+ case FE_SET_FRONTEND:
+ {
+ struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+ struct dvb_frontend_parameters p;
+ int err;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ return -EPERM;
+
+ if (copy_from_user(&p, (void __user*) arg, sizeof(p)))
+ return -EFAULT;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+ param->tps = cpu_to_le16(compute_tps(&p));
+ param->freq = cpu_to_le32(p.frequency / 1000);
+ param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+
+ stat->lock_bits = 0;
+ cinergyt2->pending_fe_events++;
+ wake_up_interruptible(&cinergyt2->poll_wq);
+
+ err = cinergyt2_command(cinergyt2,
+ (char *) param, sizeof(*param),
+ NULL, 0);
+
+ up(&cinergyt2->sem);
+
+ return (err < 0) ? err : 0;
+ }
+
+ case FE_GET_FRONTEND:
+ /**
+ * trivial to implement (see struct dvbt_get_status_msg).
+ * equivalent to FE_READ ioctls, but needs
+ * TPS -> linux-dvb parameter set conversion. Feel free
+ * to implement this and send us a patch if you need this
+ * functionality.
+ */
+ break;
+
+ case FE_GET_EVENT:
+ {
+ /**
+ * for now we only fill the status field. the parameters
+ * are trivial to fill as soon FE_GET_FRONTEND is done.
+ */
+ struct dvb_frontend_event __user *e = (void __user *) arg;
+ if (cinergyt2->pending_fe_events == 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+ wait_event_interruptible(cinergyt2->poll_wq,
+ cinergyt2->pending_fe_events > 0);
+ }
+ cinergyt2->pending_fe_events = 0;
+ return cinergyt2_ioctl(inode, file, FE_READ_STATUS,
+ (unsigned long) &e->status);
+ }
+
+ default:
+ ;
+ }
+
+ return -EINVAL;
+}
+
+static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct cinergyt2 *cinergyt2 = dvbdev->priv;
+ int ret = 0;
+
+ lock_kernel();
+
+ if (vma->vm_flags & (VM_WRITE | VM_EXEC)) {
+ ret = -EPERM;
+ goto bailout;
+ }
+
+ if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) {
+ ret = -EINVAL;
+ goto bailout;
+ }
+
+ vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+ vma->vm_file = file;
+
+ ret = remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot) ? -EAGAIN : 0;
+bailout:
+ unlock_kernel();
+ return ret;
+}
+
+static struct file_operations cinergyt2_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = cinergyt2_ioctl,
+ .poll = cinergyt2_poll,
+ .open = cinergyt2_open,
+ .release = cinergyt2_release,
+ .mmap = cinergyt2_mmap
+};
+
+static struct dvb_device cinergyt2_fe_template = {
+ .users = ~0,
+ .writers = 1,
+ .readers = (~0)-1,
+ .fops = &cinergyt2_fops
+};
+
+#ifdef ENABLE_RC
+static void cinergyt2_query_rc (void *data)
+{
+ struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+ char buf [1] = { CINERGYT2_EP1_GET_RC_EVENTS };
+ struct cinergyt2_rc_event rc_events[12];
+ int n, len;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return;
+
+ len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
+ (char *) rc_events, sizeof(rc_events));
+
+ for (n=0; len>0 && n<(len/sizeof(rc_events[0])); n++) {
+ int i;
+
+ if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
+ rc_events[n].value == ~0)
+ {
+ /**
+ * keyrepeat bit. If we would handle this properly
+ * we would need to emit down events as long the
+ * keyrepeat goes, a up event if no further
+ * repeat bits occur. Would need a timer to implement
+ * and no other driver does this, so we simply
+ * emit the last key up/down sequence again.
+ */
+ } else {
+ cinergyt2->rc_input_event = KEY_MAX;
+ for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3) {
+ if (rc_keys[i+0] == rc_events[n].type &&
+ rc_keys[i+1] == rc_events[n].value)
+ {
+ cinergyt2->rc_input_event = rc_keys[i+2];
+ break;
+ }
+ }
+ }
+
+ if (cinergyt2->rc_input_event != KEY_MAX) {
+ input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1);
+ input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0);
+ input_sync(&cinergyt2->rc_input_dev);
+ }
+ }
+
+ schedule_delayed_work(&cinergyt2->rc_query_work,
+ msecs_to_jiffies(RC_QUERY_INTERVAL));
+
+ up(&cinergyt2->sem);
+}
+#endif
+
+static void cinergyt2_query (void *data)
+{
+ struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+ char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ struct dvbt_get_status_msg *s = &cinergyt2->status;
+ uint8_t lock_bits;
+ uint32_t unc;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return;
+
+ unc = s->uncorrected_block_count;
+ lock_bits = s->lock_bits;
+
+ cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s));
+
+ unc += le32_to_cpu(s->uncorrected_block_count);
+ s->uncorrected_block_count = unc;
+
+ if (lock_bits != s->lock_bits) {
+ wake_up_interruptible(&cinergyt2->poll_wq);
+ cinergyt2->pending_fe_events++;
+ }
+
+ schedule_delayed_work(&cinergyt2->query_work,
+ msecs_to_jiffies(QUERY_INTERVAL));
+
+ up(&cinergyt2->sem);
+}
+
+static int cinergyt2_probe (struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct cinergyt2 *cinergyt2;
+ int i, err;
+
+ if (!(cinergyt2 = kmalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
+ dprintk(1, "out of memory?!?\n");
+ return -ENOMEM;
+ }
+
+ memset (cinergyt2, 0, sizeof (struct cinergyt2));
+ usb_set_intfdata (intf, (void *) cinergyt2);
+
+ init_MUTEX(&cinergyt2->sem);
+ init_waitqueue_head (&cinergyt2->poll_wq);
+ INIT_WORK(&cinergyt2->query_work, cinergyt2_query, cinergyt2);
+
+ cinergyt2->udev = interface_to_usbdev(intf);
+ cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+
+ if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) {
+ dprintk(1, "unable to allocate stream urbs\n");
+ kfree(cinergyt2);
+ return -ENOMEM;
+ }
+
+ dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE);
+
+ cinergyt2->demux.priv = cinergyt2;
+ cinergyt2->demux.filternum = 256;
+ cinergyt2->demux.feednum = 256;
+ cinergyt2->demux.start_feed = cinergyt2_start_feed;
+ cinergyt2->demux.stop_feed = cinergyt2_stop_feed;
+ cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+
+ if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) {
+ dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err);
+ goto bailout;
+ }
+
+ cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum;
+ cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx;
+ cinergyt2->dmxdev.capabilities = 0;
+
+ if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, cinergyt2->adapter)) < 0) {
+ dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err);
+ goto bailout;
+ }
+
+ if (dvb_net_init(cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx))
+ dprintk(1, "dvb_net_init() failed!\n");
+
+ dvb_register_device(cinergyt2->adapter, &cinergyt2->fedev,
+ &cinergyt2_fe_template, cinergyt2,
+ DVB_DEVICE_FRONTEND);
+
+#ifdef ENABLE_RC
+ init_input_dev(&cinergyt2->rc_input_dev);
+
+ cinergyt2->rc_input_dev.evbit[0] = BIT(EV_KEY);
+ cinergyt2->rc_input_dev.keycodesize = sizeof(unsigned char);
+ cinergyt2->rc_input_dev.keycodemax = KEY_MAX;
+ cinergyt2->rc_input_dev.name = DRIVER_NAME " remote control";
+
+ for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3)
+ set_bit(rc_keys[i+2], cinergyt2->rc_input_dev.keybit);
+
+ input_register_device(&cinergyt2->rc_input_dev);
+
+ cinergyt2->rc_input_event = KEY_MAX;
+
+ INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2);
+ schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+ return 0;
+
+bailout:
+ dvb_dmxdev_release(&cinergyt2->dmxdev);
+ dvb_dmx_release(&cinergyt2->demux);
+ dvb_unregister_adapter (cinergyt2->adapter);
+ cinergyt2_free_stream_urbs (cinergyt2);
+ kfree(cinergyt2);
+ return -ENOMEM;
+}
+
+static void cinergyt2_disconnect (struct usb_interface *intf)
+{
+ struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+ if (down_interruptible(&cinergyt2->sem))
+ return;
+
+#ifdef ENABLE_RC
+ cancel_delayed_work(&cinergyt2->rc_query_work);
+ flush_scheduled_work();
+ input_unregister_device(&cinergyt2->rc_input_dev);
+#endif
+
+ cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
+ dvb_net_release(&cinergyt2->dvbnet);
+ dvb_dmxdev_release(&cinergyt2->dmxdev);
+ dvb_dmx_release(&cinergyt2->demux);
+ dvb_unregister_device(cinergyt2->fedev);
+ dvb_unregister_adapter(cinergyt2->adapter);
+
+ cinergyt2_free_stream_urbs(cinergyt2);
+ up(&cinergyt2->sem);
+ kfree(cinergyt2);
+}
+
+static int cinergyt2_suspend (struct usb_interface *intf, u32 state)
+{
+ struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if (state > 0) { /* state 0 seems to mean DEVICE_PM_ON */
+ struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+#ifdef ENABLE_RC
+ cancel_delayed_work(&cinergyt2->rc_query_work);
+#endif
+ cancel_delayed_work(&cinergyt2->query_work);
+ if (cinergyt2->streaming)
+ cinergyt2_stop_stream_xfer(cinergyt2);
+ flush_scheduled_work();
+ cinergyt2_sleep(cinergyt2, 1);
+ }
+
+ up(&cinergyt2->sem);
+ return 0;
+}
+
+static int cinergyt2_resume (struct usb_interface *intf)
+{
+ struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+ struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+
+ if (down_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
+ if (!cinergyt2->sleeping) {
+ cinergyt2_sleep(cinergyt2, 0);
+ cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0);
+ if (cinergyt2->streaming)
+ cinergyt2_start_stream_xfer(cinergyt2);
+ schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+ }
+
+#ifdef ENABLE_RC
+ schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+ up(&cinergyt2->sem);
+ return 0;
+}
+
+static const struct usb_device_id cinergyt2_table [] __devinitdata = {
+ { USB_DEVICE(0x0ccd, 0x0038) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(usb, cinergyt2_table);
+
+static struct usb_driver cinergyt2_driver = {
+ .owner = THIS_MODULE,
+ .name = "cinergyT2",
+ .probe = cinergyt2_probe,
+ .disconnect = cinergyt2_disconnect,
+ .suspend = cinergyt2_suspend,
+ .resume = cinergyt2_resume,
+ .id_table = cinergyt2_table
+};
+
+static int __init cinergyt2_init (void)
+{
+ int err;
+
+ if ((err = usb_register(&cinergyt2_driver)) < 0)
+ dprintk(1, "usb_register() failed! (err %i)\n", err);
+
+ return err;
+}
+
+static void __exit cinergyt2_exit (void)
+{
+ usb_deregister(&cinergyt2_driver);
+}
+
+module_init (cinergyt2_init);
+module_exit (cinergyt2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
+
diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig
new file mode 100644
index 000000000000..74dfc73ae5b0
--- /dev/null
+++ b/drivers/media/dvb/dibusb/Kconfig
@@ -0,0 +1,62 @@
+config DVB_DIBUSB
+ tristate "DiBcom USB DVB-T devices (see help for a complete device list)"
+ depends on DVB_CORE && USB
+ select FW_LOADER
+ select DVB_DIB3000MB
+ select DVB_DIB3000MC
+ select DVB_MT352
+ help
+ Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by
+ DiBcom (http://www.dibcom.fr) and C&E.
+
+ Devices supported by this driver:
+
+ TwinhanDTV USB-Ter (VP7041)
+ TwinhanDTV Magic Box (VP7041e)
+ KWorld/JetWay/ADSTech V-Stream XPERT DTV - DVB-T USB1.1 and USB2.0
+ Hama DVB-T USB-Box
+ DiBcom reference devices (non-public)
+ Ultima Electronic/Artec T1 USB TVBOX
+ Compro Videomate DVB-U2000 - DVB-T USB
+ Grandtec DVB-T USB
+ Avermedia AverTV DVBT USB
+ Artec T1 USB1.1 and USB2.0 boxes
+ Yakumo/Typhoon DVB-T USB2.0
+ Hanftek UMT-010 USB2.0
+ Hauppauge WinTV NOVA-T USB2
+
+ The VP7041 seems to be identical to "CTS Portable" (Chinese
+ Television System).
+
+ These devices can be understood as budget ones, they "only" deliver
+ (a part of) the MPEG2 transport stream.
+
+ A firmware is needed to get the device working. See Documentation/dvb/README.dibusb
+ details.
+
+ Say Y if you own such a device and want to use it. You should build it as
+ a module.
+
+config DVB_DIBUSB_MISDESIGNED_DEVICES
+ bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs"
+ depends on DVB_DIBUSB
+ help
+ Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their
+ USB1.1/USB2.0 devices.
+ So comes that they identify with the default Vendor and Product ID of the Cypress
+ CY7C64613 (AN2235) or Cypress FX2.
+
+ Affected device IDs:
+ 0x0574:0x2235 (Artec T1 USB1.1, cold)
+ 0x04b4:0x8613 (Artec T1 USB2.0, cold)
+ 0x0574:0x1002 (Artec T1 USB2.0, warm)
+ 0x0574:0x2131 (aged DiBcom USB1.1 test device)
+
+ Say Y if your device has one of the mentioned IDs.
+
+config DVB_DIBCOM_DEBUG
+ bool "Enable extended debug support for DiBcom USB device"
+ depends on DVB_DIBUSB
+ help
+ Say Y if you want to enable debuging. See modinfo dvb-dibusb for
+ debug levels.
diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile
new file mode 100644
index 000000000000..e941c508624e
--- /dev/null
+++ b/drivers/media/dvb/dibusb/Makefile
@@ -0,0 +1,11 @@
+dvb-dibusb-objs = dvb-dibusb-core.o \
+ dvb-dibusb-dvb.o \
+ dvb-dibusb-fe-i2c.o \
+ dvb-dibusb-firmware.o \
+ dvb-dibusb-remote.o \
+ dvb-dibusb-usb.o \
+ dvb-fe-dtt200u.o
+
+obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-core.c b/drivers/media/dvb/dibusb/dvb-dibusb-core.c
new file mode 100644
index 000000000000..26235f9247e4
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-core.c
@@ -0,0 +1,558 @@
+/*
+ * Driver for mobile USB Budget DVB-T devices based on reference
+ * design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * dvb-dibusb-core.c
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DiBcom, which has
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ * Remote control code added by David Matthews (dm@prolingua.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, version 2.
+ *
+ * Acknowledgements
+ *
+ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ * sources, on which this driver (and the dib3000mb/mc/p frontends) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/moduleparam.h>
+
+/* debug */
+int dvb_dibusb_debug;
+module_param_named(debug, dvb_dibusb_debug, int, 0644);
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define DBSTATUS ""
+#else
+#define DBSTATUS " (debugging is not enabled)"
+#endif
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS);
+#undef DBSTATUS
+
+static int pid_parse;
+module_param(pid_parse, int, 0644);
+MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0");
+
+static int rc_query_interval = 100;
+module_param(rc_query_interval, int, 0644);
+MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)");
+
+static int rc_key_repeat_count = 2;
+module_param(rc_key_repeat_count, int, 0644);
+MODULE_PARM_DESC(rc_key_repeat_count, "how many key repeats will be dropped before passing the key event again (default: 2)");
+
+/* Vendor IDs */
+#define USB_VID_ADSTECH 0x06e1
+#define USB_VID_ANCHOR 0x0547
+#define USB_VID_AVERMEDIA 0x14aa
+#define USB_VID_COMPRO 0x185b
+#define USB_VID_COMPRO_UNK 0x145f
+#define USB_VID_CYPRESS 0x04b4
+#define USB_VID_DIBCOM 0x10b8
+#define USB_VID_EMPIA 0xeb1a
+#define USB_VID_GRANDTEC 0x5032
+#define USB_VID_HANFTEK 0x15f4
+#define USB_VID_HAUPPAUGE 0x2040
+#define USB_VID_HYPER_PALTEK 0x1025
+#define USB_VID_IMC_NETWORKS 0x13d3
+#define USB_VID_TWINHAN 0x1822
+#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+
+/* Product IDs */
+#define USB_PID_ADSTECH_USB2_COLD 0xa333
+#define USB_PID_ADSTECH_USB2_WARM 0xa334
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
+#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
+#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
+#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
+#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
+#define USB_PID_TWINHAN_VP7041_COLD 0x3201
+#define USB_PID_TWINHAN_VP7041_WARM 0x3202
+#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
+#define USB_PID_YAKUMO_DTT200U_COLD 0x0201
+#define USB_PID_YAKUMO_DTT200U_WARM 0x0301
+#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
+#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
+
+/* USB Driver stuff
+ * table of devices that this driver is working with
+ *
+ * ATTENTION: Never ever change the order of this table, the particular
+ * devices depend on this order
+ *
+ * Each entry is used as a reference in the device_struct. Currently this is
+ * the only non-redundant way of assigning USB ids to actual devices I'm aware
+ * of, because there is only one place in the code where the assignment of
+ * vendor and product id is done, here.
+ */
+static struct usb_device_id dib_table [] = {
+/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_COLD)},
+/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB_WARM)},
+/* 02 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_COLD) },
+/* 03 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_YAKUMO_DTT200U_WARM) },
+
+/* 04 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) },
+/* 05 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) },
+/* 06 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) },
+/* 07 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) },
+/* 08 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) },
+/* 09 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) },
+/* 10 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) },
+/* 11 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) },
+/* 12 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) },
+/* 13 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) },
+/* 14 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) },
+/* 15 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) },
+/* 16 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) },
+/* 17 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) },
+/* 18 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) },
+/* 19 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_COLD) },
+/* 20 */ { USB_DEVICE(USB_VID_IMC_NETWORKS, USB_PID_TWINHAN_VP7041_WARM) },
+/* 21 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) },
+/* 22 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) },
+/* 23 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
+/* 24 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
+/* 25 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
+/* 26 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
+/* 27 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
+
+/* 28 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) },
+/* 29 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) },
+
+/* 30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) },
+/* 31 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) },
+/* 32 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) },
+/* 33 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) },
+/*
+ * activate the following define when you have one of the devices and want to
+ * build it from build-2.6 in dvb-kernel
+ */
+// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+/* 34 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
+/* 35 */ { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) },
+/* 36 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) },
+/* 37 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_DIBCOM_ANCHOR_2135_COLD) },
+#endif
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dib_table);
+
+static struct dibusb_usb_controller dibusb_usb_ctrl[] = {
+ { .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },
+ { .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },
+ { .name = "Cypress FX2", .cpu_cs_register = 0xe600 },
+};
+
+struct dibusb_tuner dibusb_tuner[] = {
+ { DIBUSB_TUNER_CABLE_THOMSON,
+ 0x61
+ },
+ { DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
+ 0x60
+ },
+ { DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
+ 0x61
+ },
+ { DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
+ 0x60
+ },
+};
+
+static struct dibusb_demod dibusb_demod[] = {
+ { DIBUSB_DIB3000MB,
+ 16,
+ { 0x8, 0 },
+ },
+ { DIBUSB_DIB3000MC,
+ 32,
+ { 0x9, 0xa, 0xb, 0xc },
+ },
+ { DIBUSB_MT352,
+ 254,
+ { 0xf, 0 },
+ },
+ { DTT200U_FE,
+ 8,
+ { 0xff,0 }, /* there is no i2c bus in this device */
+ }
+};
+
+static struct dibusb_device_class dibusb_device_classes[] = {
+ { .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0],
+ .firmware = "dvb-dibusb-5.0.0.11.fw",
+ .pipe_cmd = 0x01, .pipe_data = 0x02,
+ .urb_count = 7, .urb_buffer_size = 4096,
+ DIBUSB_RC_NEC_PROTOCOL,
+ &dibusb_demod[DIBUSB_DIB3000MB],
+ &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+ },
+ { DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1],
+ "dvb-dibusb-an2235-1.fw",
+ 0x01, 0x02,
+ 7, 4096,
+ DIBUSB_RC_NEC_PROTOCOL,
+ &dibusb_demod[DIBUSB_DIB3000MB],
+ &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+ },
+ { DIBUSB2_0,&dibusb_usb_ctrl[2],
+ "dvb-dibusb-6.0.0.5.fw",
+ 0x01, 0x06,
+ 7, 4096,
+ DIBUSB_RC_NEC_PROTOCOL,
+ &dibusb_demod[DIBUSB_DIB3000MC],
+ &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
+ },
+ { UMT2_0, &dibusb_usb_ctrl[2],
+ "dvb-dibusb-umt-2.fw",
+ 0x01, 0x06,
+ 20, 512,
+ DIBUSB_RC_NO,
+ &dibusb_demod[DIBUSB_MT352],
+ &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P],
+ },
+ { DIBUSB2_0B,&dibusb_usb_ctrl[2],
+ "dvb-dibusb-adstech-usb2-1.fw",
+ 0x01, 0x06,
+ 7, 4096,
+ DIBUSB_RC_NEC_PROTOCOL,
+ &dibusb_demod[DIBUSB_DIB3000MB],
+ &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+ },
+ { NOVAT_USB2,&dibusb_usb_ctrl[2],
+ "dvb-dibusb-nova-t-1.fw",
+ 0x01, 0x06,
+ 7, 4096,
+ DIBUSB_RC_HAUPPAUGE_PROTO,
+ &dibusb_demod[DIBUSB_DIB3000MC],
+ &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
+ },
+ { DTT200U,&dibusb_usb_ctrl[2],
+ "dvb-dtt200u-1.fw",
+ 0x01, 0x02,
+ 7, 4096,
+ DIBUSB_RC_NO,
+ &dibusb_demod[DTT200U_FE],
+ NULL, /* no explicit tuner/pll-programming necessary (it has the ENV57H1XD5) */
+ },
+};
+
+static struct dibusb_usb_device dibusb_devices[] = {
+ { "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[19], &dib_table[21], NULL},
+ { &dib_table[20], &dib_table[22], NULL},
+ },
+ { "KWorld V-Stream XPERT DTV - DVB-T USB1.1",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[11], NULL },
+ { &dib_table[12], NULL },
+ },
+ { "Grandtec USB1.1 DVB-T",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[13], &dib_table[15], NULL },
+ { &dib_table[14], &dib_table[16], NULL },
+ },
+ { "DiBcom USB1.1 DVB-T reference design (MOD3000)",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[7], NULL },
+ { &dib_table[8], NULL },
+ },
+ { "Artec T1 USB1.1 TVBOX with AN2135",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[23], NULL },
+ { &dib_table[24], NULL },
+ },
+ { "Artec T1 USB1.1 TVBOX with AN2235",
+ &dibusb_device_classes[DIBUSB1_1_AN2235],
+ { &dib_table[25], NULL },
+ { &dib_table[26], NULL },
+ },
+ { "Avermedia AverTV DVBT USB1.1",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[0], NULL },
+ { &dib_table[1], NULL },
+ },
+ { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[4], &dib_table[6], NULL},
+ { &dib_table[5], NULL },
+ },
+ { "Unkown USB1.1 DVB-T device ???? please report the name to the author",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[17], NULL },
+ { &dib_table[18], NULL },
+ },
+ { "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
+ &dibusb_device_classes[DIBUSB2_0],
+ { &dib_table[9], NULL },
+ { &dib_table[10], NULL },
+ },
+ { "Artec T1 USB2.0 TVBOX (please report the warm ID)",
+ &dibusb_device_classes[DIBUSB2_0],
+ { &dib_table[27], NULL },
+ { NULL },
+ },
+ { "Hauppauge WinTV NOVA-T USB2",
+ &dibusb_device_classes[NOVAT_USB2],
+ { &dib_table[30], NULL },
+ { &dib_table[31], NULL },
+ },
+ { "DTT200U (Yakumo/Hama/Typhoon) DVB-T USB2.0",
+ &dibusb_device_classes[DTT200U],
+ { &dib_table[2], NULL },
+ { &dib_table[3], NULL },
+ },
+ { "Hanftek UMT-010 DVB-T USB2.0",
+ &dibusb_device_classes[UMT2_0],
+ { &dib_table[28], NULL },
+ { &dib_table[29], NULL },
+ },
+ { "KWorld/ADSTech Instant DVB-T USB 2.0",
+ &dibusb_device_classes[DIBUSB2_0B],
+ { &dib_table[32], NULL },
+ { &dib_table[33], NULL }, /* device ID with default DIBUSB2_0-firmware */
+ },
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+ { "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)",
+ &dibusb_device_classes[DIBUSB1_1_AN2235],
+ { &dib_table[34], NULL },
+ { NULL },
+ },
+ { "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)",
+ &dibusb_device_classes[DTT200U],
+ { &dib_table[35], NULL },
+ { &dib_table[36], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */
+ },
+ { "DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs",
+ &dibusb_device_classes[DIBUSB1_1],
+ { &dib_table[37], NULL },
+ { NULL },
+ },
+#endif
+};
+
+static int dibusb_exit(struct usb_dibusb *dib)
+{
+ deb_info("init_state before exiting everything: %x\n",dib->init_state);
+ dibusb_remote_exit(dib);
+ dibusb_fe_exit(dib);
+ dibusb_i2c_exit(dib);
+ dibusb_dvb_exit(dib);
+ dibusb_urb_exit(dib);
+ deb_info("init_state should be zero now: %x\n",dib->init_state);
+ dib->init_state = DIBUSB_STATE_INIT;
+ kfree(dib);
+ return 0;
+}
+
+static int dibusb_init(struct usb_dibusb *dib)
+{
+ int ret = 0;
+ sema_init(&dib->usb_sem, 1);
+ sema_init(&dib->i2c_sem, 1);
+
+ dib->init_state = DIBUSB_STATE_INIT;
+
+ if ((ret = dibusb_urb_init(dib)) ||
+ (ret = dibusb_dvb_init(dib)) ||
+ (ret = dibusb_i2c_init(dib))) {
+ dibusb_exit(dib);
+ return ret;
+ }
+
+ if ((ret = dibusb_fe_init(dib)))
+ err("could not initialize a frontend.");
+
+ if ((ret = dibusb_remote_init(dib)))
+ err("could not initialize remote control.");
+
+ return 0;
+}
+
+static struct dibusb_usb_device * dibusb_device_class_quirk(struct usb_device *udev, struct dibusb_usb_device *dev)
+{
+ int i;
+
+ /* Quirk for the Kworld/ADSTech Instant USB2.0 device. It has the same USB
+ * IDs like the USB1.1 KWorld after loading the firmware. Which is a bad
+ * idea and make this quirk necessary.
+ */
+ if (dev->dev_cl->id == DIBUSB1_1 && udev->speed == USB_SPEED_HIGH) {
+ info("this seems to be the Kworld/ADSTech Instant USB2.0 device or equal.");
+ for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
+ if (dibusb_devices[i].dev_cl->id == DIBUSB2_0B) {
+ dev = &dibusb_devices[i];
+ break;
+ }
+ }
+ }
+
+ return dev;
+}
+
+static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold)
+{
+ int i,j;
+ struct dibusb_usb_device *dev = NULL;
+ *cold = -1;
+
+ for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
+ for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) {
+ deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct);
+ if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+ dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+ *cold = 1;
+ dev = &dibusb_devices[i];
+ break;
+ }
+ }
+
+ if (dev != NULL)
+ break;
+
+ for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) {
+ deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct);
+ if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+ dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+ *cold = 0;
+ dev = &dibusb_devices[i];
+ break;
+ }
+ }
+ }
+
+ if (dev != NULL)
+ dev = dibusb_device_class_quirk(udev,dev);
+
+ return dev;
+}
+
+/*
+ * USB
+ */
+static int dibusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_dibusb *dib = NULL;
+ struct dibusb_usb_device *dibdev = NULL;
+
+ int ret = -ENOMEM,cold=0;
+
+ if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) {
+ err("something went very wrong, "
+ "unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct));
+ return -ENODEV;
+ }
+
+ if (cold == 1) {
+ info("found a '%s' in cold state, will try to load a firmware",dibdev->name);
+ ret = dibusb_loadfirmware(udev,dibdev);
+ } else {
+ info("found a '%s' in warm state.",dibdev->name);
+ dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL);
+ if (dib == NULL) {
+ err("no memory");
+ return ret;
+ }
+ memset(dib,0,sizeof(struct usb_dibusb));
+
+ dib->udev = udev;
+ dib->dibdev = dibdev;
+
+ /* store parameters to structures */
+ dib->rc_query_interval = rc_query_interval;
+ dib->pid_parse = pid_parse;
+ dib->rc_key_repeat_count = rc_key_repeat_count;
+
+ usb_set_intfdata(intf, dib);
+
+ ret = dibusb_init(dib);
+ }
+
+ if (ret == 0)
+ info("%s successfully initialized and connected.",dibdev->name);
+ else
+ info("%s error while loading driver (%d)",dibdev->name,ret);
+ return ret;
+}
+
+static void dibusb_disconnect(struct usb_interface *intf)
+{
+ struct usb_dibusb *dib = usb_get_intfdata(intf);
+ const char *name = DRIVER_DESC;
+
+ usb_set_intfdata(intf,NULL);
+ if (dib != NULL && dib->dibdev != NULL) {
+ name = dib->dibdev->name;
+ dibusb_exit(dib);
+ }
+ info("%s successfully deinitialized and disconnected.",name);
+
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver dibusb_driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_DESC,
+ .probe = dibusb_probe,
+ .disconnect = dibusb_disconnect,
+ .id_table = dib_table,
+};
+
+/* module stuff */
+static int __init usb_dibusb_init(void)
+{
+ int result;
+ if ((result = usb_register(&dibusb_driver))) {
+ err("usb_register failed. Error number %d",result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit usb_dibusb_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&dibusb_driver);
+}
+
+module_init (usb_dibusb_init);
+module_exit (usb_dibusb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
new file mode 100644
index 000000000000..04e54ec093f0
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
@@ -0,0 +1,185 @@
+/*
+ * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the
+ * linux-dvb API.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+#include <linux/version.h>
+
+static u32 urb_compl_count;
+
+/*
+ * MPEG2 TS DVB stuff
+ */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+ struct usb_dibusb *dib = urb->context;
+
+ deb_ts("urb complete feedcount: %d, status: %d, length: %d\n",dib->feedcount,urb->status,
+ urb->actual_length);
+
+ urb_compl_count++;
+ if (urb_compl_count % 1000 == 0)
+ deb_info("%d urbs completed so far.\n",urb_compl_count);
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ deb_ts("urb completition error %d.", urb->status);
+ break;
+ }
+
+ if (dib->feedcount > 0 && urb->actual_length > 0) {
+ if (dib->init_state & DIBUSB_STATE_DVB)
+ dvb_dmx_swfilter(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length);
+ } else
+ deb_ts("URB dropped because of feedcount.\n");
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+}
+
+static int dibusb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ struct usb_dibusb *dib = dvbdmxfeed->demux->priv;
+ int newfeedcount;
+
+ if (dib == NULL)
+ return -ENODEV;
+
+ newfeedcount = dib->feedcount + (onoff ? 1 : -1);
+
+ /*
+ * stop feed before setting a new pid if there will be no pid anymore
+ */
+ if (newfeedcount == 0) {
+ deb_ts("stop feeding\n");
+ if (dib->xfer_ops.fifo_ctrl != NULL) {
+ if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) {
+ err("error while inhibiting fifo.");
+ return -ENODEV;
+ }
+ }
+ dibusb_streaming(dib,0);
+ }
+
+ dib->feedcount = newfeedcount;
+
+ /* activate the pid on the device specific pid_filter */
+ deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off");
+ if (dib->pid_parse && dib->xfer_ops.pid_ctrl != NULL)
+ dib->xfer_ops.pid_ctrl(dib->fe,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
+
+ /*
+ * start the feed if this was the first pid to set and there is still a pid
+ * for reception.
+ */
+ if (dib->feedcount == onoff && dib->feedcount > 0) {
+
+ deb_ts("controlling pid parser\n");
+ if (dib->xfer_ops.pid_parse != NULL) {
+ if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) {
+ err("could not handle pid_parser");
+ }
+ }
+
+ deb_ts("start feeding\n");
+ if (dib->xfer_ops.fifo_ctrl != NULL) {
+ if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) {
+ err("error while enabling fifo.");
+ return -ENODEV;
+ }
+ }
+ dibusb_streaming(dib,1);
+ }
+ return 0;
+}
+
+static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
+ return dibusb_ctrl_feed(dvbdmxfeed,1);
+}
+
+static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
+ return dibusb_ctrl_feed(dvbdmxfeed,0);
+}
+
+int dibusb_dvb_init(struct usb_dibusb *dib)
+{
+ int ret;
+
+ urb_compl_count = 0;
+
+ if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC,
+ THIS_MODULE)) < 0) {
+ deb_info("dvb_register_adapter failed: error %d", ret);
+ goto err;
+ }
+ dib->adapter->priv = dib;
+
+/* i2c is done in dibusb_i2c_init */
+
+ dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+ dib->demux.priv = (void *)dib;
+ /* get pidcount from demod */
+ dib->demux.feednum = dib->demux.filternum = 255;
+ dib->demux.start_feed = dibusb_start_feed;
+ dib->demux.stop_feed = dibusb_stop_feed;
+ dib->demux.write_to_decoder = NULL;
+ if ((ret = dvb_dmx_init(&dib->demux)) < 0) {
+ err("dvb_dmx_init failed: error %d",ret);
+ goto err_dmx;
+ }
+
+ dib->dmxdev.filternum = dib->demux.filternum;
+ dib->dmxdev.demux = &dib->demux.dmx;
+ dib->dmxdev.capabilities = 0;
+ if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) {
+ err("dvb_dmxdev_init failed: error %d",ret);
+ goto err_dmx_dev;
+ }
+
+ dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx);
+
+ goto success;
+err_dmx_dev:
+ dvb_dmx_release(&dib->demux);
+err_dmx:
+ dvb_unregister_adapter(dib->adapter);
+err:
+ return ret;
+success:
+ dib->init_state |= DIBUSB_STATE_DVB;
+ return 0;
+}
+
+int dibusb_dvb_exit(struct usb_dibusb *dib)
+{
+ if (dib->init_state & DIBUSB_STATE_DVB) {
+ dib->init_state &= ~DIBUSB_STATE_DVB;
+ deb_info("unregistering DVB part\n");
+ dvb_net_release(&dib->dvb_net);
+ dib->demux.dmx.close(&dib->demux.dmx);
+ dvb_dmxdev_release(&dib->dmxdev);
+ dvb_dmx_release(&dib->demux);
+ dvb_unregister_adapter(dib->adapter);
+ }
+ return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
new file mode 100644
index 000000000000..2ed89488c7c4
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
@@ -0,0 +1,582 @@
+/*
+ * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for attaching, initializing of an appropriate
+ * demodulator/frontend. I2C-stuff is also located here.
+ *
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+
+static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr,
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+ u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
+ /* write only ? */
+ int wo = (rbuf == NULL || rlen == 0),
+ len = 2 + wlen + (wo ? 0 : 2);
+
+ sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
+ sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
+
+ memcpy(&sndbuf[2],wbuf,wlen);
+
+ if (!wo) {
+ sndbuf[wlen+2] = (rlen >> 8) & 0xff;
+ sndbuf[wlen+3] = rlen & 0xff;
+ }
+
+ return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen);
+}
+
+/*
+ * I2C master xfer function
+ */
+static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+{
+ struct usb_dibusb *dib = i2c_get_adapdata(adap);
+ int i;
+
+ if (down_interruptible(&dib->i2c_sem) < 0)
+ return -EAGAIN;
+
+ if (num > 2)
+ warn("more than 2 i2c messages at a time is not handled yet. TODO.");
+
+ for (i = 0; i < num; i++) {
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,
+ msg[i+1].buf,msg[i+1].len) < 0)
+ break;
+ i++;
+ } else
+ if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
+ break;
+ }
+
+ up(&dib->i2c_sem);
+ return i;
+}
+
+static u32 dibusb_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dibusb_algo = {
+ .name = "DiBcom USB i2c algorithm",
+ .id = I2C_ALGO_BIT,
+ .master_xfer = dibusb_i2c_xfer,
+ .functionality = dibusb_i2c_func,
+};
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe);
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe);
+static int dibusb_general_pll_init(struct dvb_frontend *fe, u8 pll_buf[5]);
+static int dibusb_general_pll_set(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters* params, u8 pll_buf[5]);
+
+static struct mt352_config mt352_hanftek_umt_010_config = {
+ .demod_address = 0x1e,
+ .demod_init = dibusb_general_demod_init,
+ .pll_set = dibusb_general_pll_set,
+};
+
+static int dibusb_tuner_quirk(struct usb_dibusb *dib)
+{
+ switch (dib->dibdev->dev_cl->id) {
+ case DIBUSB1_1: /* some these device have the ENV77H11D5 and some the THOMSON CABLE */
+ case DIBUSB1_1_AN2235: { /* actually its this device, but in warm state they are indistinguishable */
+ struct dibusb_tuner *t;
+ u8 b[2] = { 0,0 } ,b2[1];
+ struct i2c_msg msg[2] = {
+ { .flags = 0, .buf = b, .len = 2 },
+ { .flags = I2C_M_RD, .buf = b2, .len = 1},
+ };
+
+ t = &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5];
+
+ msg[0].addr = msg[1].addr = t->pll_addr;
+
+ if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+ dib->xfer_ops.tuner_pass_ctrl(dib->fe,1,t->pll_addr);
+ dibusb_i2c_xfer(&dib->i2c_adap,msg,2);
+ if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+ dib->xfer_ops.tuner_pass_ctrl(dib->fe,0,t->pll_addr);
+
+ if (b2[0] == 0xfe)
+ info("this device has the Thomson Cable onboard. Which is default.");
+ else {
+ dib->tuner = t;
+ info("this device has the Panasonic ENV77H11D5 onboard.");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+int dibusb_fe_init(struct usb_dibusb* dib)
+{
+ struct dib3000_config demod_cfg;
+ int i;
+
+ if (dib->init_state & DIBUSB_STATE_I2C) {
+ for (i = 0; i < sizeof(dib->dibdev->dev_cl->demod->i2c_addrs) / sizeof(unsigned char) &&
+ dib->dibdev->dev_cl->demod->i2c_addrs[i] != 0; i++) {
+
+ demod_cfg.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+ demod_cfg.pll_addr = dibusb_general_pll_addr;
+ demod_cfg.pll_set = dibusb_general_pll_set;
+ demod_cfg.pll_init = dibusb_general_pll_init;
+
+ deb_info("demod id: %d %d\n",dib->dibdev->dev_cl->demod->id,DTT200U_FE);
+
+ switch (dib->dibdev->dev_cl->demod->id) {
+ case DIBUSB_DIB3000MB:
+ dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+ break;
+ case DIBUSB_DIB3000MC:
+ dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+ break;
+ case DIBUSB_MT352:
+ mt352_hanftek_umt_010_config.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+ dib->fe = mt352_attach(&mt352_hanftek_umt_010_config, &dib->i2c_adap);
+ break;
+ case DTT200U_FE:
+ dib->fe = dtt200u_fe_attach(dib,&dib->xfer_ops);
+ break;
+ }
+ if (dib->fe != NULL) {
+ info("found demodulator at i2c address 0x%x",dib->dibdev->dev_cl->demod->i2c_addrs[i]);
+ break;
+ }
+ }
+ /* if a frontend was found */
+ if (dib->fe != NULL) {
+ if (dib->fe->ops->sleep != NULL)
+ dib->fe_sleep = dib->fe->ops->sleep;
+ dib->fe->ops->sleep = dibusb_hw_sleep;
+
+ if (dib->fe->ops->init != NULL )
+ dib->fe_init = dib->fe->ops->init;
+ dib->fe->ops->init = dibusb_hw_wakeup;
+
+ /* setting the default tuner */
+ dib->tuner = dib->dibdev->dev_cl->tuner;
+
+ /* check which tuner is mounted on this device, in case this is unsure */
+ dibusb_tuner_quirk(dib);
+ }
+ }
+ if (dib->fe == NULL) {
+ err("A frontend driver was not found for device '%s'.",
+ dib->dibdev->name);
+ return -ENODEV;
+ } else {
+ if (dvb_register_frontend(dib->adapter, dib->fe)) {
+ err("Frontend registration failed.");
+ if (dib->fe->ops->release)
+ dib->fe->ops->release(dib->fe);
+ dib->fe = NULL;
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+int dibusb_fe_exit(struct usb_dibusb *dib)
+{
+ if (dib->fe != NULL)
+ dvb_unregister_frontend(dib->fe);
+ return 0;
+}
+
+int dibusb_i2c_init(struct usb_dibusb *dib)
+{
+ int ret = 0;
+
+ dib->adapter->priv = dib;
+
+ strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+ dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+ dib->i2c_adap.algo = &dibusb_algo;
+ dib->i2c_adap.algo_data = NULL;
+ dib->i2c_adap.id = I2C_ALGO_BIT;
+
+ i2c_set_adapdata(&dib->i2c_adap, dib);
+
+ if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0)
+ err("could not add i2c adapter");
+
+ dib->init_state |= DIBUSB_STATE_I2C;
+
+ return ret;
+}
+
+int dibusb_i2c_exit(struct usb_dibusb *dib)
+{
+ if (dib->init_state & DIBUSB_STATE_I2C)
+ i2c_del_adapter(&dib->i2c_adap);
+ dib->init_state &= ~DIBUSB_STATE_I2C;
+ return 0;
+}
+
+
+/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */
+static int thomson_cable_eu_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+ u32 tfreq = (fep->frequency + 36125000) / 62500;
+ int vu,p0,p1,p2;
+
+ if (fep->frequency > 403250000)
+ vu = 1, p2 = 1, p1 = 0, p0 = 1;
+ else if (fep->frequency > 115750000)
+ vu = 0, p2 = 1, p1 = 1, p0 = 0;
+ else if (fep->frequency > 44250000)
+ vu = 0, p2 = 0, p1 = 1, p0 = 1;
+ else
+ return -EINVAL;
+
+ pllbuf[0] = (tfreq >> 8) & 0x7f;
+ pllbuf[1] = tfreq & 0xff;
+ pllbuf[2] = 0x8e;
+ pllbuf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0;
+ return 0;
+}
+
+static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+ u32 freq_khz = fep->frequency / 1000;
+ u32 tfreq = ((freq_khz + 36125)*6 + 500) / 1000;
+ u8 TA, T210, R210, ctrl1, cp210, p4321;
+ if (freq_khz > 858000) {
+ err("frequency cannot be larger than 858 MHz.");
+ return -EINVAL;
+ }
+
+ // contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0
+ TA = 1;
+ T210 = 0;
+ R210 = 0x2;
+ ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210;
+
+// ******** CHARGE PUMP CONFIG vs RF FREQUENCIES *****************
+ if (freq_khz < 470000)
+ cp210 = 2; // VHF Low and High band ch E12 to E4 to E12
+ else if (freq_khz < 526000)
+ cp210 = 4; // UHF band Ch E21 to E27
+ else // if (freq < 862000000)
+ cp210 = 5; // UHF band ch E28 to E69
+
+//********************* BW select *******************************
+ if (freq_khz < 153000)
+ p4321 = 1; // BW selected for VHF low
+ else if (freq_khz < 470000)
+ p4321 = 2; // BW selected for VHF high E5 to E12
+ else // if (freq < 862000000)
+ p4321 = 4; // BW selection for UHF E21 to E69
+
+ pllbuf[0] = (tfreq >> 8) & 0xff;
+ pllbuf[1] = (tfreq >> 0) & 0xff;
+ pllbuf[2] = 0xff & ctrl1;
+ pllbuf[3] = (cp210 << 5) | (p4321);
+
+ return 0;
+}
+
+/*
+ * 7 6 5 4 3 2 1 0
+ * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0
+ *
+ * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8
+ * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0
+ *
+ * Control byte 1 1 T/A=1 T2 T1 T0 R2 R1 R0
+ * 1 T/A=0 0 0 ATC AL2 AL1 AL0
+ *
+ * Control byte 2 CP2 CP1 CP0 BS5 BS4 BS3 BS2 BS1
+ *
+ * MA0/1 = programmable address bits
+ * R/~W = read/write bit (0 for writing)
+ * N14-0 = programmable LO frequency
+ *
+ * T/A = test AGC bit (0 = next 6 bits AGC setting,
+ * 1 = next 6 bits test and reference divider ratio settings)
+ * T2-0 = test bits
+ * R2-0 = reference divider ratio and programmable frequency step
+ * ATC = AGC current setting and time constant
+ * ATC = 0: AGC current = 220nA, AGC time constant = 2s
+ * ATC = 1: AGC current = 9uA, AGC time constant = 50ms
+ * AL2-0 = AGC take-over point bits
+ * CP2-0 = charge pump current
+ * BS5-1 = PMOS ports control bits;
+ * BSn = 0 corresponding port is off, high-impedance state (at power-on)
+ * BSn = 1 corresponding port is on
+ */
+static int panasonic_cofdm_env77h11d5_tda6650_init(struct dvb_frontend *fe, u8 pllbuf[4])
+{
+ pllbuf[0] = 0x0b;
+ pllbuf[1] = 0xf5;
+ pllbuf[2] = 0x85;
+ pllbuf[3] = 0xab;
+ return 0;
+}
+
+static int panasonic_cofdm_env77h11d5_tda6650_set (struct dvb_frontend_parameters *fep,u8 pllbuf[4])
+{
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ // determine charge pump
+ tuner_frequency = fep->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 (fep->frequency < 49000000)
+ return -EINVAL;
+ else if (fep->frequency < 161000000)
+ band = 1;
+ else if (fep->frequency < 444000000)
+ band = 2;
+ else if (fep->frequency < 861000000)
+ band = 4;
+ else
+ return -EINVAL;
+
+ // setup PLL filter
+ switch (fep->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ case BANDWIDTH_7_MHZ:
+ filter = 0;
+ break;
+ case BANDWIDTH_8_MHZ:
+ filter = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ // calculate divisor
+ // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+ tuner_frequency = (((fep->frequency / 1000) * 6) + 217496) / 1000;
+
+ // setup tuner buffer
+ pllbuf[0] = (tuner_frequency >> 8) & 0x7f;
+ pllbuf[1] = tuner_frequency & 0xff;
+ pllbuf[2] = 0xca;
+ pllbuf[3] = (cp << 5) | (filter << 3) | band;
+ return 0;
+}
+
+/*
+ * 7 6 5 4 3 2 1 0
+ * Address Byte 1 1 0 0 0 MA1 MA0 R/~W=0
+ *
+ * Program divider byte 1 0 n14 n13 n12 n11 n10 n9 n8
+ * Program divider byte 2 n7 n6 n5 n4 n3 n2 n1 n0
+ *
+ * Control byte 1 CP T2 T1 T0 RSA RSB OS
+ *
+ * Band Switch byte X X X P4 P3 P2 P1 P0
+ *
+ * Auxiliary byte ATC AL2 AL1 AL0 0 0 0 0
+ *
+ * Address: MA1 MA0 Address
+ * 0 0 c0
+ * 0 1 c2 (always valid)
+ * 1 0 c4
+ * 1 1 c6
+ */
+static int lg_tdtp_e102p_tua6034(struct dvb_frontend_parameters* fep, u8 pllbuf[4])
+{
+ u32 div;
+ u8 p210, p3;
+
+#define TUNER_MUL 62500
+
+ div = (fep->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+// div = ((fep->frequency/1000 + 36166) * 6) / 1000;
+
+ if (fep->frequency < 174500000)
+ p210 = 1; // not supported by the tdtp_e102p
+ else if (fep->frequency < 230000000) // VHF
+ p210 = 2;
+ else
+ p210 = 4;
+
+ if (fep->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ p3 = 0;
+ else
+ p3 = 1;
+
+ pllbuf[0] = (div >> 8) & 0x7f;
+ pllbuf[1] = div & 0xff;
+ pllbuf[2] = 0xce;
+// pllbuf[2] = 0xcc;
+ pllbuf[3] = (p3 << 3) | p210;
+
+ return 0;
+}
+
+static int lg_tdtp_e102p_mt352_demod_init(struct dvb_frontend *fe)
+{
+ static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d };
+ static u8 mt352_reset[] = { 0x50, 0x80 };
+ static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 };
+ static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 };
+
+ static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff };
+ static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff };
+ static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 };
+ static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 };
+ static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f };
+
+ static u8 mt352_acq_ctl[] = { 0x53, 0x50 };
+ static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 };
+
+ mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+ mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio));
+
+ 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_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1));
+ mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2));
+ mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3));
+ mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4));
+ mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5));
+
+ mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl));
+ mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1));
+
+ return 0;
+}
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe)
+{
+ struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+ switch (dib->dibdev->dev_cl->id) {
+ case UMT2_0:
+ return lg_tdtp_e102p_mt352_demod_init(fe);
+ default: /* other device classes do not have device specific demod inits */
+ break;
+ }
+ return 0;
+}
+
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe)
+{
+ struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+ return dib->tuner->pll_addr;
+}
+
+static int dibusb_pll_i2c_helper(struct usb_dibusb *dib, u8 pll_buf[5], u8 buf[4])
+{
+ if (pll_buf == NULL) {
+ struct i2c_msg msg = {
+ .addr = dib->tuner->pll_addr,
+ .flags = 0,
+ .buf = buf,
+ .len = sizeof(buf)
+ };
+ if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+ msleep(1);
+ } else {
+ pll_buf[0] = dib->tuner->pll_addr << 1;
+ memcpy(&pll_buf[1],buf,4);
+ }
+
+ return 0;
+}
+
+static int dibusb_general_pll_init(struct dvb_frontend *fe,
+ u8 pll_buf[5])
+{
+ struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+ u8 buf[4];
+ int ret=0;
+ switch (dib->tuner->id) {
+ case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+ ret = panasonic_cofdm_env77h11d5_tda6650_init(fe,buf);
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
+
+static int dibusb_general_pll_set(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep, u8 pll_buf[5])
+{
+ struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+ u8 buf[4];
+ int ret=0;
+
+ switch (dib->tuner->id) {
+ case DIBUSB_TUNER_CABLE_THOMSON:
+ ret = thomson_cable_eu_pll_set(fep, buf);
+ break;
+ case DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5:
+ ret = panasonic_cofdm_env57h1xd5_pll_set(fep, buf);
+ break;
+ case DIBUSB_TUNER_CABLE_LG_TDTP_E102P:
+ ret = lg_tdtp_e102p_tua6034(fep, buf);
+ break;
+ case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+ ret = panasonic_cofdm_env77h11d5_tda6650_set(fep,buf);
+ break;
+ default:
+ warn("no pll programming routine found for tuner %d.\n",dib->tuner->id);
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c
new file mode 100644
index 000000000000..504ba47afdf3
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c
@@ -0,0 +1,87 @@
+/*
+ * dvb-dibusb-firmware.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for downloading the firmware to the device.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/firmware.h>
+#include <linux/usb.h>
+
+/*
+ * load a firmware packet to the device
+ */
+static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+ 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
+}
+
+int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev)
+{
+ const struct firmware *fw = NULL;
+ u16 addr;
+ u8 *b,*p;
+ int ret = 0,i;
+
+ if ((ret = request_firmware(&fw, dibdev->dev_cl->firmware, &udev->dev)) != 0) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ dibdev->dev_cl->firmware);
+ return ret;
+ }
+
+ info("downloading firmware from file '%s'.",dibdev->dev_cl->firmware);
+
+ p = kmalloc(fw->size,GFP_KERNEL);
+ if (p != NULL) {
+ u8 reset;
+ /*
+ * you cannot use the fw->data as buffer for
+ * usb_control_msg, a new buffer has to be
+ * created
+ */
+ memcpy(p,fw->data,fw->size);
+
+ /* stop the CPU */
+ reset = 1;
+ if ((ret = dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1)) != 1)
+ err("could not stop the USB controller CPU.");
+ for(i = 0; p[i+3] == 0 && i < fw->size; ) {
+ b = (u8 *) &p[i];
+ addr = *((u16 *) &b[1]);
+
+ ret = dibusb_writemem(udev,addr,&b[4],b[0]);
+
+ if (ret != b[0]) {
+ err("error while transferring firmware "
+ "(transferred size: %d, block size: %d)",
+ ret,b[0]);
+ ret = -EINVAL;
+ break;
+ }
+ i += 5 + b[0];
+ }
+ /* length in ret */
+ if (ret > 0)
+ ret = 0;
+ /* restart the CPU */
+ reset = 0;
+ if (ret || dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1) != 1) {
+ err("could not restart the USB controller CPU.");
+ ret = -EINVAL;
+ }
+
+ kfree(p);
+ } else {
+ ret = -ENOMEM;
+ }
+ release_firmware(fw);
+
+ return ret;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
new file mode 100644
index 000000000000..9dc8b15517b7
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
@@ -0,0 +1,316 @@
+/*
+ * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for handling the event device on the software
+ * side and the remote control on the hardware side.
+ */
+#include "dvb-dibusb.h"
+
+/* Table to map raw key codes to key events. This should not be hard-wired
+ into the kernel. */
+static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] =
+{
+ /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
+ { 0x00, 0xff, 0x16, KEY_POWER },
+ { 0x00, 0xff, 0x10, KEY_MUTE },
+ { 0x00, 0xff, 0x03, KEY_1 },
+ { 0x00, 0xff, 0x01, KEY_2 },
+ { 0x00, 0xff, 0x06, KEY_3 },
+ { 0x00, 0xff, 0x09, KEY_4 },
+ { 0x00, 0xff, 0x1d, KEY_5 },
+ { 0x00, 0xff, 0x1f, KEY_6 },
+ { 0x00, 0xff, 0x0d, KEY_7 },
+ { 0x00, 0xff, 0x19, KEY_8 },
+ { 0x00, 0xff, 0x1b, KEY_9 },
+ { 0x00, 0xff, 0x15, KEY_0 },
+ { 0x00, 0xff, 0x05, KEY_CHANNELUP },
+ { 0x00, 0xff, 0x02, KEY_CHANNELDOWN },
+ { 0x00, 0xff, 0x1e, KEY_VOLUMEUP },
+ { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN },
+ { 0x00, 0xff, 0x11, KEY_RECORD },
+ { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
+ { 0x00, 0xff, 0x14, KEY_PLAY },
+ { 0x00, 0xff, 0x1a, KEY_STOP },
+ { 0x00, 0xff, 0x40, KEY_REWIND },
+ { 0x00, 0xff, 0x12, KEY_FASTFORWARD },
+ { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
+ { 0x00, 0xff, 0x4c, KEY_PAUSE },
+ { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */
+ { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
+ /* additional keys TwinHan VisionPlus, the Artec seemingly not have */
+ { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */
+ { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */
+ { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */
+ { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */
+ { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */
+ { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */
+ /* Key codes for the KWorld/ADSTech/JetWay remote. */
+ { 0x86, 0x6b, 0x12, KEY_POWER },
+ { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */
+ { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */
+ { 0x86, 0x6b, 0x0b, KEY_EPG },
+ { 0x86, 0x6b, 0x10, KEY_MUTE },
+ { 0x86, 0x6b, 0x01, KEY_1 },
+ { 0x86, 0x6b, 0x02, KEY_2 },
+ { 0x86, 0x6b, 0x03, KEY_3 },
+ { 0x86, 0x6b, 0x04, KEY_4 },
+ { 0x86, 0x6b, 0x05, KEY_5 },
+ { 0x86, 0x6b, 0x06, KEY_6 },
+ { 0x86, 0x6b, 0x07, KEY_7 },
+ { 0x86, 0x6b, 0x08, KEY_8 },
+ { 0x86, 0x6b, 0x09, KEY_9 },
+ { 0x86, 0x6b, 0x0a, KEY_0 },
+ { 0x86, 0x6b, 0x18, KEY_ZOOM },
+ { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */
+ { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */
+ { 0x86, 0x6b, 0x00, KEY_UNDO },
+ { 0x86, 0x6b, 0x1d, KEY_RECORD },
+ { 0x86, 0x6b, 0x0d, KEY_STOP },
+ { 0x86, 0x6b, 0x0e, KEY_PAUSE },
+ { 0x86, 0x6b, 0x16, KEY_PLAY },
+ { 0x86, 0x6b, 0x11, KEY_BACK },
+ { 0x86, 0x6b, 0x19, KEY_FORWARD },
+ { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */
+ { 0x86, 0x6b, 0x15, KEY_ESC },
+ { 0x86, 0x6b, 0x1a, KEY_UP },
+ { 0x86, 0x6b, 0x1e, KEY_DOWN },
+ { 0x86, 0x6b, 0x1f, KEY_LEFT },
+ { 0x86, 0x6b, 0x1b, KEY_RIGHT },
+};
+
+/* Hauppauge NOVA-T USB2 keys */
+static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = {
+ { 0xddf, KEY_GOTO },
+ { 0xdef, KEY_POWER },
+ { 0xce7, KEY_TV },
+ { 0xcc7, KEY_VIDEO },
+ { 0xccf, KEY_AUDIO },
+ { 0xcd7, KEY_MEDIA },
+ { 0xcdf, KEY_EPG },
+ { 0xca7, KEY_UP },
+ { 0xc67, KEY_RADIO },
+ { 0xcb7, KEY_LEFT },
+ { 0xd2f, KEY_OK },
+ { 0xcbf, KEY_RIGHT },
+ { 0xcff, KEY_BACK },
+ { 0xcaf, KEY_DOWN },
+ { 0xc6f, KEY_MENU },
+ { 0xc87, KEY_VOLUMEUP },
+ { 0xc8f, KEY_VOLUMEDOWN },
+ { 0xc97, KEY_CHANNEL },
+ { 0xc7f, KEY_MUTE },
+ { 0xd07, KEY_CHANNELUP },
+ { 0xd0f, KEY_CHANNELDOWN },
+ { 0xdbf, KEY_RECORD },
+ { 0xdb7, KEY_STOP },
+ { 0xd97, KEY_REWIND },
+ { 0xdaf, KEY_PLAY },
+ { 0xda7, KEY_FASTFORWARD },
+ { 0xd27, KEY_LAST }, /* Skip backwards */
+ { 0xd87, KEY_PAUSE },
+ { 0xcf7, KEY_NEXT },
+ { 0xc07, KEY_0 },
+ { 0xc0f, KEY_1 },
+ { 0xc17, KEY_2 },
+ { 0xc1f, KEY_3 },
+ { 0xc27, KEY_4 },
+ { 0xc2f, KEY_5 },
+ { 0xc37, KEY_6 },
+ { 0xc3f, KEY_7 },
+ { 0xc47, KEY_8 },
+ { 0xc4f, KEY_9 },
+ { 0xc57, KEY_KPASTERISK },
+ { 0xc77, KEY_GRAVE }, /* # */
+ { 0xc5f, KEY_RED },
+ { 0xd77, KEY_GREEN },
+ { 0xdc7, KEY_YELLOW },
+ { 0xd4f, KEY_BLUE},
+};
+
+static int dibusb_key2event_nec(struct usb_dibusb *dib,u8 rb[5])
+{
+ int i;
+ switch (rb[0]) {
+ case DIBUSB_RC_NEC_KEY_PRESSED:
+ /* rb[1-3] is the actual key, rb[4] is a checksum */
+ deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ rb[1], rb[2], rb[3], rb[4]);
+
+ if ((0xff - rb[3]) != rb[4]) {
+ deb_rc("remote control checksum failed.\n");
+ break;
+ }
+
+ /* See if we can match the raw key code. */
+ for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) {
+ if (nec_rc_keys[i].c0 == rb[1] &&
+ nec_rc_keys[i].c1 == rb[2] &&
+ nec_rc_keys[i].c2 == rb[3]) {
+
+ dib->last_event = nec_rc_keys[i].key;
+ return 1;
+ }
+ }
+ break;
+ case DIBUSB_RC_NEC_KEY_REPEATED:
+ /* rb[1]..rb[4] are always zero.*/
+ /* Repeats often seem to occur so for the moment just ignore this. */
+ return 0;
+ case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[4])
+{
+ u16 raw;
+ int i,state;
+ switch (rb[0]) {
+ case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
+ raw = ((rb[1] & 0x0f) << 8) | rb[2];
+
+ state = !!(rb[1] & 0x40);
+
+ deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state);
+ for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) {
+ if (haupp_rc_keys[i].raw == raw) {
+ if (dib->last_event == haupp_rc_keys[i].key &&
+ dib->last_state == state) {
+ deb_rc("key repeat\n");
+ return 0;
+ } else {
+ dib->last_event = haupp_rc_keys[i].key;
+ dib->last_state = state;
+ return 1;
+ }
+ }
+ }
+
+ break;
+ case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY:
+ default:
+ break;
+ }
+ return -1;
+}
+
+/*
+ * Read the remote control and feed the appropriate event.
+ * NEC protocol is used for remote controls
+ */
+static int dibusb_read_remote_control(struct usb_dibusb *dib)
+{
+ u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5];
+ int ret,event = 0;
+
+ if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5)))
+ return ret;
+
+ switch (dib->dibdev->dev_cl->remote_type) {
+ case DIBUSB_RC_NEC_PROTOCOL:
+ event = dibusb_key2event_nec(dib,rb);
+ break;
+ case DIBUSB_RC_HAUPPAUGE_PROTO:
+ event = dibusb_key2event_hauppauge(dib,rb);
+ default:
+ break;
+ }
+
+ /* key repeat */
+ if (event == 0)
+ if (++dib->repeat_key_count < dib->rc_key_repeat_count) {
+ deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count);
+ event = -1; /* skip this key repeat */
+ }
+
+ if (event == 1 || event == 0) {
+ deb_rc("Translated key 0x%04x\n",event);
+
+ /* Signal down and up events for this key. */
+ input_report_key(&dib->rc_input_dev, dib->last_event, 1);
+ input_report_key(&dib->rc_input_dev, dib->last_event, 0);
+ input_sync(&dib->rc_input_dev);
+
+ if (event == 1)
+ dib->repeat_key_count = 0;
+ }
+ return 0;
+}
+
+/* Remote-control poll function - called every dib->rc_query_interval ms to see
+ whether the remote control has received anything. */
+static void dibusb_remote_query(void *data)
+{
+ struct usb_dibusb *dib = (struct usb_dibusb *) data;
+ /* TODO: need a lock here. We can simply skip checking for the remote control
+ if we're busy. */
+ dibusb_read_remote_control(dib);
+ schedule_delayed_work(&dib->rc_query_work,
+ msecs_to_jiffies(dib->rc_query_interval));
+}
+
+int dibusb_remote_init(struct usb_dibusb *dib)
+{
+ int i;
+
+ if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+ return 0;
+
+ /* Initialise the remote-control structures.*/
+ init_input_dev(&dib->rc_input_dev);
+
+ dib->rc_input_dev.evbit[0] = BIT(EV_KEY);
+ dib->rc_input_dev.keycodesize = sizeof(unsigned char);
+ dib->rc_input_dev.keycodemax = KEY_MAX;
+ dib->rc_input_dev.name = DRIVER_DESC " remote control";
+
+ switch (dib->dibdev->dev_cl->remote_type) {
+ case DIBUSB_RC_NEC_PROTOCOL:
+ for (i=0; i<sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++)
+ set_bit(nec_rc_keys[i].key, dib->rc_input_dev.keybit);
+ break;
+ case DIBUSB_RC_HAUPPAUGE_PROTO:
+ for (i=0; i<sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++)
+ set_bit(haupp_rc_keys[i].key, dib->rc_input_dev.keybit);
+ break;
+ default:
+ break;
+ }
+
+
+ input_register_device(&dib->rc_input_dev);
+
+ INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib);
+
+ /* Start the remote-control polling. */
+ if (dib->rc_query_interval < 40)
+ dib->rc_query_interval = 100; /* default */
+
+ info("schedule remote query interval to %d msecs.",dib->rc_query_interval);
+ schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval));
+
+ dib->init_state |= DIBUSB_STATE_REMOTE;
+
+ return 0;
+}
+
+int dibusb_remote_exit(struct usb_dibusb *dib)
+{
+ if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+ return 0;
+
+ if (dib->init_state & DIBUSB_STATE_REMOTE) {
+ cancel_delayed_work(&dib->rc_query_work);
+ flush_scheduled_work();
+ input_unregister_device(&dib->rc_input_dev);
+ }
+ dib->init_state &= ~DIBUSB_STATE_REMOTE;
+ return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c
new file mode 100644
index 000000000000..642f0596a5ba
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c
@@ -0,0 +1,303 @@
+/*
+ * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the
+ * usb specific stuff.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/version.h>
+#include <linux/pci.h>
+
+int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf,
+ u16 rlen)
+{
+ int actlen,ret = -ENOMEM;
+
+ if (wbuf == NULL || wlen == 0)
+ return -EINVAL;
+
+ if ((ret = down_interruptible(&dib->usb_sem)))
+ return ret;
+
+ debug_dump(wbuf,wlen);
+
+ ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev,
+ dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen,
+ DIBUSB_I2C_TIMEOUT);
+
+ if (ret)
+ err("bulk message failed: %d (%d/%d)",ret,wlen,actlen);
+ else
+ ret = actlen != wlen ? -1 : 0;
+
+ /* an answer is expected, and no error before */
+ if (!ret && rbuf && rlen) {
+ ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev,
+ dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen,
+ DIBUSB_I2C_TIMEOUT);
+
+ if (ret)
+ err("recv bulk message failed: %d",ret);
+ else {
+ deb_alot("rlen: %d\n",rlen);
+ debug_dump(rbuf,actlen);
+ }
+ }
+
+ up(&dib->usb_sem);
+ return ret;
+}
+
+/*
+ * Cypress controls
+ */
+int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len)
+{
+ return dibusb_readwrite_usb(dib,buf,len,NULL,0);
+}
+
+#if 0
+/*
+ * #if 0'ing the following functions as they are not in use _now_,
+ * but probably will be sometime.
+ */
+/*
+ * do not use this, just a workaround for a bug,
+ * which will hopefully never occur :).
+ */
+int dibusb_interrupt_read_loop(struct usb_dibusb *dib)
+{
+ u8 b[1] = { DIBUSB_REQ_INTR_READ };
+ return dibusb_write_usb(dib,b,1);
+}
+#endif
+
+/*
+ * ioctl for the firmware
+ */
+static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen)
+{
+ u8 b[34];
+ int size = plen > 32 ? 32 : plen;
+ memset(b,0,34);
+ b[0] = DIBUSB_REQ_SET_IOCTL;
+ b[1] = cmd;
+
+ if (size > 0)
+ memcpy(&b[2],param,size);
+
+ return dibusb_write_usb(dib,b,34); //2+size);
+}
+
+/*
+ * ioctl for power control
+ */
+int dibusb_hw_wakeup(struct dvb_frontend *fe)
+{
+ struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+ u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP };
+ deb_info("dibusb-device is getting up.\n");
+
+ switch (dib->dibdev->dev_cl->id) {
+ case DTT200U:
+ break;
+ default:
+ dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+ break;
+ }
+
+ if (dib->fe_init)
+ return dib->fe_init(fe);
+
+ return 0;
+}
+
+int dibusb_hw_sleep(struct dvb_frontend *fe)
+{
+ struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+ u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP };
+ deb_info("dibusb-device is going to bed.\n");
+ /* workaround, something is wrong, when dibusb 1.1 device are going to bed too late */
+ switch (dib->dibdev->dev_cl->id) {
+ case DIBUSB1_1:
+ case NOVAT_USB2:
+ case DTT200U:
+ break;
+ default:
+ dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+ break;
+ }
+ if (dib->fe_sleep)
+ return dib->fe_sleep(fe);
+
+ return 0;
+}
+
+int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode)
+{
+ u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode };
+ return dibusb_readwrite_usb(dib,b,2,NULL,0);
+}
+
+static int dibusb_urb_kill(struct usb_dibusb *dib)
+{
+ int i;
+deb_info("trying to kill urbs\n");
+ if (dib->init_state & DIBUSB_STATE_URB_SUBMIT) {
+ for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+ deb_info("killing URB no. %d.\n",i);
+
+ /* stop the URB */
+ usb_kill_urb(dib->urb_list[i]);
+ }
+ } else
+ deb_info(" URBs not killed.\n");
+ dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT;
+ return 0;
+}
+
+static int dibusb_urb_submit(struct usb_dibusb *dib)
+{
+ int i,ret;
+ if (dib->init_state & DIBUSB_STATE_URB_INIT) {
+ for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+ deb_info("submitting URB no. %d\n",i);
+ if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) {
+ err("could not submit buffer urb no. %d - get them all back\n",i);
+ dibusb_urb_kill(dib);
+ return ret;
+ }
+ dib->init_state |= DIBUSB_STATE_URB_SUBMIT;
+ }
+ }
+ return 0;
+}
+
+int dibusb_streaming(struct usb_dibusb *dib,int onoff)
+{
+ if (onoff)
+ dibusb_urb_submit(dib);
+ else
+ dibusb_urb_kill(dib);
+
+ switch (dib->dibdev->dev_cl->id) {
+ case DIBUSB2_0:
+ case DIBUSB2_0B:
+ case NOVAT_USB2:
+ case UMT2_0:
+ if (onoff)
+ return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0);
+ else
+ return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int dibusb_urb_init(struct usb_dibusb *dib)
+{
+ int i,bufsize,def_pid_parse = 1;
+
+ /*
+ * when reloading the driver w/o replugging the device
+ * a timeout occures, this helps
+ */
+ usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+ usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+ usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data));
+
+ /* allocate the array for the data transfer URBs */
+ dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL);
+ if (dib->urb_list == NULL)
+ return -ENOMEM;
+ memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *));
+
+ dib->init_state |= DIBUSB_STATE_URB_LIST;
+
+ bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size;
+ deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize);
+ /* allocate the actual buffer for the URBs */
+ if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) {
+ deb_info("not enough memory.\n");
+ return -ENOMEM;
+ }
+ deb_info("allocation complete\n");
+ memset(dib->buffer,0,bufsize);
+
+ dib->init_state |= DIBUSB_STATE_URB_BUF;
+
+ /* allocate and submit the URBs */
+ for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+ if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) {
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb( dib->urb_list[i], dib->udev,
+ usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data),
+ &dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size],
+ dib->dibdev->dev_cl->urb_buffer_size,
+ dibusb_urb_complete, dib);
+
+ dib->urb_list[i]->transfer_flags = 0;
+
+ dib->init_state |= DIBUSB_STATE_URB_INIT;
+ }
+
+ /* dib->pid_parse here contains the value of the module parameter */
+ /* decide if pid parsing can be deactivated:
+ * is possible (by device type) and wanted (by user)
+ */
+ switch (dib->dibdev->dev_cl->id) {
+ case DIBUSB2_0:
+ case DIBUSB2_0B:
+ if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) {
+ def_pid_parse = 0;
+ info("running at HIGH speed, will deliver the complete TS.");
+ } else
+ info("will use pid_parsing.");
+ break;
+ default:
+ break;
+ }
+ /* from here on it contains the device and user decision */
+ dib->pid_parse = def_pid_parse;
+
+ return 0;
+}
+
+int dibusb_urb_exit(struct usb_dibusb *dib)
+{
+ int i;
+
+ dibusb_urb_kill(dib);
+
+ if (dib->init_state & DIBUSB_STATE_URB_LIST) {
+ for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+ if (dib->urb_list[i] != NULL) {
+ deb_info("freeing URB no. %d.\n",i);
+ /* free the URBs */
+ usb_free_urb(dib->urb_list[i]);
+ }
+ }
+ /* free the urb array */
+ kfree(dib->urb_list);
+ dib->init_state &= ~DIBUSB_STATE_URB_LIST;
+ }
+
+ if (dib->init_state & DIBUSB_STATE_URB_BUF)
+ pci_free_consistent(NULL,
+ dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count,
+ dib->buffer,dib->dma_handle);
+
+ dib->init_state &= ~DIBUSB_STATE_URB_BUF;
+ dib->init_state &= ~DIBUSB_STATE_URB_INIT;
+ return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h
new file mode 100644
index 000000000000..52cd35dd9d83
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb.h
@@ -0,0 +1,327 @@
+/*
+ * dvb-dibusb.h
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2.
+ *
+ * for more information see dvb-dibusb-core.c .
+ */
+#ifndef __DVB_DIBUSB_H__
+#define __DVB_DIBUSB_H__
+
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/usb.h>
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+
+#include "dib3000.h"
+#include "mt352.h"
+
+/* debug */
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+ do { if ((dvb_dibusb_debug & level)) { printk(args); } } while (0)
+
+#define debug_dump(b,l) {\
+ int i; \
+ for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
+ deb_xfer("\n");\
+}
+
+#else
+#define dprintk(args...)
+#define debug_dump(b,l)
+#endif
+
+extern int dvb_dibusb_debug;
+
+/* Version information */
+#define DRIVER_VERSION "0.3"
+#define DRIVER_DESC "DiBcom based USB Budget DVB-T device"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_alot(args...) dprintk(0x04,args)
+#define deb_ts(args...) dprintk(0x08,args)
+#define deb_err(args...) dprintk(0x10,args)
+#define deb_rc(args...) dprintk(0x20,args)
+
+/* generic log methods - taken from usb.h */
+#undef err
+#define err(format, arg...) printk(KERN_ERR "dvb-dibusb: " format "\n" , ## arg)
+#undef info
+#define info(format, arg...) printk(KERN_INFO "dvb-dibusb: " format "\n" , ## arg)
+#undef warn
+#define warn(format, arg...) printk(KERN_WARNING "dvb-dibusb: " format "\n" , ## arg)
+
+struct dibusb_usb_controller {
+ const char *name; /* name of the usb controller */
+ u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */
+};
+
+typedef enum {
+ DIBUSB1_1 = 0,
+ DIBUSB1_1_AN2235,
+ DIBUSB2_0,
+ UMT2_0,
+ DIBUSB2_0B,
+ NOVAT_USB2,
+ DTT200U,
+} dibusb_class_t;
+
+typedef enum {
+ DIBUSB_TUNER_CABLE_THOMSON = 0,
+ DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
+ DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
+ DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
+} dibusb_tuner_t;
+
+typedef enum {
+ DIBUSB_DIB3000MB = 0,
+ DIBUSB_DIB3000MC,
+ DIBUSB_MT352,
+ DTT200U_FE,
+} dibusb_demodulator_t;
+
+typedef enum {
+ DIBUSB_RC_NO = 0,
+ DIBUSB_RC_NEC_PROTOCOL,
+ DIBUSB_RC_HAUPPAUGE_PROTO,
+} dibusb_remote_t;
+
+struct dibusb_tuner {
+ dibusb_tuner_t id;
+
+ u8 pll_addr; /* tuner i2c address */
+};
+extern struct dibusb_tuner dibusb_tuner[];
+
+#define DIBUSB_POSSIBLE_I2C_ADDR_NUM 4
+struct dibusb_demod {
+ dibusb_demodulator_t id;
+
+ int pid_filter_count; /* counter of the internal pid_filter */
+ u8 i2c_addrs[DIBUSB_POSSIBLE_I2C_ADDR_NUM]; /* list of possible i2c addresses of the demod */
+};
+
+#define DIBUSB_MAX_TUNER_NUM 2
+struct dibusb_device_class {
+ dibusb_class_t id;
+
+ const struct dibusb_usb_controller *usb_ctrl; /* usb controller */
+ const char *firmware; /* valid firmware filenames */
+
+ int pipe_cmd; /* command pipe (read/write) */
+ int pipe_data; /* data pipe */
+
+ int urb_count; /* number of data URBs to be submitted */
+ int urb_buffer_size; /* the size of the buffer for each URB */
+
+ dibusb_remote_t remote_type; /* does this device have a ir-receiver */
+
+ struct dibusb_demod *demod; /* which demodulator is mount */
+ struct dibusb_tuner *tuner; /* which tuner can be found here */
+};
+
+#define DIBUSB_ID_MAX_NUM 15
+struct dibusb_usb_device {
+ const char *name; /* real name of the box */
+ struct dibusb_device_class *dev_cl; /* which dibusb_device_class is this device part of */
+
+ struct usb_device_id *cold_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at pre firmware state */
+ struct usb_device_id *warm_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at post firmware state */
+};
+
+/* a PID for the pid_filter list, when in use */
+struct dibusb_pid
+{
+ int index;
+ u16 pid;
+ int active;
+};
+
+struct usb_dibusb {
+ /* usb */
+ struct usb_device * udev;
+
+ struct dibusb_usb_device * dibdev;
+
+#define DIBUSB_STATE_INIT 0x000
+#define DIBUSB_STATE_URB_LIST 0x001
+#define DIBUSB_STATE_URB_BUF 0x002
+#define DIBUSB_STATE_URB_INIT 0x004
+#define DIBUSB_STATE_DVB 0x008
+#define DIBUSB_STATE_I2C 0x010
+#define DIBUSB_STATE_REMOTE 0x020
+#define DIBUSB_STATE_URB_SUBMIT 0x040
+ int init_state;
+
+ int feedcount;
+ struct dib_fe_xfer_ops xfer_ops;
+
+ struct dibusb_tuner *tuner;
+
+ struct urb **urb_list;
+ u8 *buffer;
+ dma_addr_t dma_handle;
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+
+ /* locking */
+ struct semaphore usb_sem;
+ struct semaphore i2c_sem;
+
+ /* dvb */
+ struct dvb_adapter *adapter;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvb_net;
+ struct dvb_frontend* fe;
+
+ int (*fe_sleep) (struct dvb_frontend *);
+ int (*fe_init) (struct dvb_frontend *);
+
+ /* remote control */
+ struct input_dev rc_input_dev;
+ struct work_struct rc_query_work;
+ int last_event;
+ int last_state; /* for Hauppauge RC protocol */
+ int repeat_key_count;
+ int rc_key_repeat_count; /* module parameter */
+
+ /* module parameters */
+ int pid_parse;
+ int rc_query_interval;
+};
+
+/* commonly used functions in the separated files */
+
+/* dvb-dibusb-firmware.c */
+int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev);
+
+/* dvb-dibusb-remote.c */
+int dibusb_remote_exit(struct usb_dibusb *dib);
+int dibusb_remote_init(struct usb_dibusb *dib);
+
+/* dvb-dibusb-fe-i2c.c */
+int dibusb_fe_init(struct usb_dibusb* dib);
+int dibusb_fe_exit(struct usb_dibusb *dib);
+int dibusb_i2c_init(struct usb_dibusb *dib);
+int dibusb_i2c_exit(struct usb_dibusb *dib);
+
+/* dvb-dibusb-dvb.c */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs);
+int dibusb_dvb_init(struct usb_dibusb *dib);
+int dibusb_dvb_exit(struct usb_dibusb *dib);
+
+/* dvb-dibusb-usb.c */
+int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf,
+ u16 rlen);
+int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len);
+
+int dibusb_hw_wakeup(struct dvb_frontend *);
+int dibusb_hw_sleep(struct dvb_frontend *);
+int dibusb_set_streaming_mode(struct usb_dibusb *,u8);
+int dibusb_streaming(struct usb_dibusb *,int);
+
+int dibusb_urb_init(struct usb_dibusb *);
+int dibusb_urb_exit(struct usb_dibusb *);
+
+/* dvb-fe-dtt200u.c */
+struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *,struct dib_fe_xfer_ops *);
+
+/* i2c and transfer stuff */
+#define DIBUSB_I2C_TIMEOUT 5000
+
+/*
+ * protocol of all dibusb related devices
+ */
+
+/*
+ * bulk msg to/from endpoint 0x01
+ *
+ * general structure:
+ * request_byte parameter_bytes
+ */
+
+#define DIBUSB_REQ_START_READ 0x00
+#define DIBUSB_REQ_START_DEMOD 0x01
+
+/*
+ * i2c read
+ * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word
+ * bulk read: byte_buffer (length_word bytes)
+ */
+#define DIBUSB_REQ_I2C_READ 0x02
+
+/*
+ * i2c write
+ * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes
+ */
+#define DIBUSB_REQ_I2C_WRITE 0x03
+
+/*
+ * polling the value of the remote control
+ * bulk write: 0x04
+ * bulk read: byte_buffer (5 bytes)
+ *
+ * first byte of byte_buffer shows the status (0x00, 0x01, 0x02)
+ */
+#define DIBUSB_REQ_POLL_REMOTE 0x04
+
+#define DIBUSB_RC_NEC_EMPTY 0x00
+#define DIBUSB_RC_NEC_KEY_PRESSED 0x01
+#define DIBUSB_RC_NEC_KEY_REPEATED 0x02
+
+/* additional status values for Hauppauge Remote Control Protocol */
+#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01
+#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03
+
+/* streaming mode:
+ * bulk write: 0x05 mode_byte
+ *
+ * mode_byte is mostly 0x00
+ */
+#define DIBUSB_REQ_SET_STREAMING_MODE 0x05
+
+/* interrupt the internal read loop, when blocking */
+#define DIBUSB_REQ_INTR_READ 0x06
+
+/* io control
+ * 0x07 cmd_byte param_bytes
+ *
+ * param_bytes can be up to 32 bytes
+ *
+ * cmd_byte function parameter name
+ * 0x00 power mode
+ * 0x00 sleep
+ * 0x01 wakeup
+ *
+ * 0x01 enable streaming
+ * 0x02 disable streaming
+ *
+ *
+ */
+#define DIBUSB_REQ_SET_IOCTL 0x07
+
+/* IOCTL commands */
+
+/* change the power mode in firmware */
+#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00
+#define DIBUSB_IOCTL_POWER_SLEEP 0x00
+#define DIBUSB_IOCTL_POWER_WAKEUP 0x01
+
+/* modify streaming of the FX2 */
+#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01
+#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02
+
+#endif
diff --git a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c
new file mode 100644
index 000000000000..1872aa6d200a
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c
@@ -0,0 +1,263 @@
+/*
+ * dvb-dtt200u-fe.c is a driver which implements the frontend-part of the
+ * Yakumo/Typhoon/Hama USB2.0 boxes. It is hard-wired to the dibusb-driver as
+ * it uses the usb-transfer functions directly (maybe creating a
+ * generic-dvb-usb-lib for all usb-drivers will be reduce some more code.)
+ *
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * see dvb-dibusb-core.c for copyright details.
+ */
+
+/* guessed protocol description (reverse engineered):
+ * read
+ * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1
+ * 81 - <TS_LOCK> <current frequency divided by 250000>
+ * 82 - crash - do not touch
+ * 83 - crash - do not touch
+ * 84 - remote control
+ * 85 - crash - do not touch (OK, stop testing here)
+ * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal)
+ * 89 - noise-to-signal
+ * 8a - unkown 1 byte - signal_strength
+ * 8c - ber ???
+ * 8d - ber
+ * 8e - unc
+ *
+ * write
+ * 02 - bandwidth
+ * 03 - frequency (divided by 250000)
+ * 04 - pid table (index pid(7:0) pid(12:8))
+ * 05 - reset the pid table
+ * 08 - demod transfer enabled or not (FX2 transfer is enabled by default)
+ */
+
+#include "dvb-dibusb.h"
+#include "dvb_frontend.h"
+
+struct dtt200u_fe_state {
+ struct usb_dibusb *dib;
+
+ struct dvb_frontend_parameters fep;
+ struct dvb_frontend frontend;
+};
+
+#define moan(which,what) info("unexpected value in '%s' for cmd '%02x' - please report to linux-dvb@linuxtv.org",which,what)
+
+static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 bw[1] = { 0x81 };
+ u8 br[3] = { 0 };
+// u8 bdeb[5] = { 0 };
+
+ dibusb_readwrite_usb(state->dib,bw,1,br,3);
+ switch (br[0]) {
+ case 0x01:
+ *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ break;
+ case 0x00:
+ *stat = 0;
+ break;
+ default:
+ moan("br[0]",0x81);
+ break;
+ }
+
+// bw[0] = 0x88;
+// dibusb_readwrite_usb(state->dib,bw,1,bdeb,5);
+
+// deb_info("%02x: %02x %02x %02x %02x %02x\n",bw[0],bdeb[0],bdeb[1],bdeb[2],bdeb[3],bdeb[4]);
+
+ return 0;
+}
+static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 bw[1] = { 0x8d };
+ *ber = 0;
+ dibusb_readwrite_usb(state->dib,bw,1,(u8*) ber, 3);
+ return 0;
+}
+
+static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 bw[1] = { 0x8c };
+ *unc = 0;
+ dibusb_readwrite_usb(state->dib,bw,1,(u8*) unc, 3);
+ return 0;
+}
+
+static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 bw[1] = { 0x8a };
+ u8 b;
+ dibusb_readwrite_usb(state->dib,bw,1,&b, 1);
+ *strength = (b << 8) | b;
+ return 0;
+}
+
+static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 bw[1] = { 0x89 };
+ u8 br[1] = { 0 };
+ dibusb_readwrite_usb(state->dib,bw,1,br,1);
+ *snr = ((0xff - br[0]) << 8) | (0xff - br[0]);
+ return 0;
+}
+
+static int dtt200u_fe_init(struct dvb_frontend* fe)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u8 b[] = { 0x01 };
+ return dibusb_write_usb(state->dib,b,1);
+}
+
+static int dtt200u_fe_sleep(struct dvb_frontend* fe)
+{
+ return dtt200u_fe_init(fe);
+}
+
+static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1500;
+ tune->step_size = 166667;
+ tune->max_drift = 166667 * 2;
+ return 0;
+}
+
+static int dtt200u_fe_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ u16 freq = fep->frequency / 250000;
+ u8 bw,bwbuf[2] = { 0x03, 0 }, freqbuf[3] = { 0x02, 0, 0 };
+
+ switch (fep->u.ofdm.bandwidth) {
+ case BANDWIDTH_8_MHZ: bw = 8; break;
+ case BANDWIDTH_7_MHZ: bw = 7; break;
+ case BANDWIDTH_6_MHZ: bw = 6; break;
+ case BANDWIDTH_AUTO: return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ deb_info("set_frontend\n");
+
+ bwbuf[1] = bw;
+ dibusb_write_usb(state->dib,bwbuf,2);
+
+ freqbuf[1] = freq & 0xff;
+ freqbuf[2] = (freq >> 8) & 0xff;
+ dibusb_write_usb(state->dib,freqbuf,3);
+
+ memcpy(&state->fep,fep,sizeof(struct dvb_frontend_parameters));
+
+ return 0;
+}
+
+static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dtt200u_fe_state *state = fe->demodulator_priv;
+ memcpy(fep,&state->fep,sizeof(struct dvb_frontend_parameters));
+ return 0;
+}
+
+static void dtt200u_fe_release(struct dvb_frontend* fe)
+{
+ struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static int dtt200u_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+ struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+ u8 b_pid[4];
+ pid = onoff ? pid : 0;
+
+ b_pid[0] = 0x04;
+ b_pid[1] = index;
+ b_pid[2] = pid & 0xff;
+ b_pid[3] = (pid >> 8) & 0xff;
+
+ dibusb_write_usb(state->dib,b_pid,4);
+ return 0;
+}
+
+static int dtt200u_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+ struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+ u8 b_streaming[2] = { 0x08, onoff };
+ u8 b_rst_pid[1] = { 0x05 };
+
+ dibusb_write_usb(state->dib,b_streaming,2);
+
+ if (!onoff)
+ dibusb_write_usb(state->dib,b_rst_pid,1);
+ return 0;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops;
+
+struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfer_ops *xfer_ops)
+{
+ struct dtt200u_fe_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dtt200u_fe_state*) kmalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ memset(state,0,sizeof(struct dtt200u_fe_state));
+
+ deb_info("attaching frontend dtt200u\n");
+
+ state->dib = dib;
+
+ state->frontend.ops = &dtt200u_fe_ops;
+ state->frontend.demodulator_priv = state;
+
+ xfer_ops->fifo_ctrl = dtt200u_fifo_control;
+ xfer_ops->pid_ctrl = dtt200u_pid_control;
+
+ goto success;
+error:
+ return NULL;
+success:
+ return &state->frontend;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops = {
+ .info = {
+ .name = "DTT200U (Yakumo/Typhoon/Hama) DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 250000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dtt200u_fe_release,
+
+ .init = dtt200u_fe_init,
+ .sleep = dtt200u_fe_sleep,
+
+ .set_frontend = dtt200u_fe_set_frontend,
+ .get_frontend = dtt200u_fe_get_frontend,
+ .get_tune_settings = dtt200u_fe_get_tune_settings,
+
+ .read_status = dtt200u_fe_read_status,
+ .read_ber = dtt200u_fe_read_ber,
+ .read_signal_strength = dtt200u_fe_read_signal_strength,
+ .read_snr = dtt200u_fe_read_snr,
+ .read_ucblocks = dtt200u_fe_read_unc_blocks,
+};
diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig
new file mode 100644
index 000000000000..a9a7b3421048
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Kconfig
@@ -0,0 +1,11 @@
+config DVB_CORE
+ tristate "DVB Core Support"
+ depends on DVB
+ select CRC32
+ help
+ DVB core utility functions for device handling, software fallbacks etc.
+ Say Y when you have a DVB card and want to use it. Say Y if your want
+ to build your drivers outside the kernel, but need the DVB core. All
+ in-kernel drivers will select this automatically if needed.
+ If unsure say N.
+
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
new file mode 100644
index 000000000000..c6baac20f529
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
+ dvb_ca_en50221.o dvb_frontend.o \
+ dvb_net.o dvb_ringbuffer.o
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
new file mode 100644
index 000000000000..fb55eaa5c8e7
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -0,0 +1,301 @@
+/*
+ * demux.h
+ *
+ * Copyright (c) 2002 Convergence GmbH
+ *
+ * based on code:
+ * Copyright (c) 2000 Nokia Research Center
+ * Tampere, FINLAND
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 __DEMUX_H
+#define __DEMUX_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/time.h>
+
+/*--------------------------------------------------------------------------*/
+/* Common definitions */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter.
+ */
+
+#ifndef DMX_MAX_FILTER_SIZE
+#define DMX_MAX_FILTER_SIZE 18
+#endif
+
+/*
+ * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter.
+ */
+
+#ifndef DMX_MAX_SECFEED_SIZE
+#define DMX_MAX_SECFEED_SIZE 4096
+#endif
+
+
+/*
+ * enum dmx_success: Success codes for the Demux Callback API.
+ */
+
+enum dmx_success {
+ DMX_OK = 0, /* Received Ok */
+ DMX_LENGTH_ERROR, /* Incorrect length */
+ DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
+ DMX_CRC_ERROR, /* Incorrect CRC */
+ DMX_FRAME_ERROR, /* Frame alignment error */
+ DMX_FIFO_ERROR, /* Receiver FIFO overrun */
+ DMX_MISSED_ERROR /* Receiver missed packet */
+} ;
+
+/*--------------------------------------------------------------------------*/
+/* TS packet reception */
+/*--------------------------------------------------------------------------*/
+
+/* TS filter type for set() */
+
+#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */
+#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS
+ payload (<=184 bytes per packet) to callback */
+#define TS_DECODER 4 /* send stream to built-in decoder (if present) */
+
+/* PES type for filters which write to built-in decoder */
+/* these should be kept identical to the types in dmx.h */
+
+enum dmx_ts_pes
+{ /* also send packets to decoder (if it exists) */
+ DMX_TS_PES_AUDIO0,
+ DMX_TS_PES_VIDEO0,
+ DMX_TS_PES_TELETEXT0,
+ DMX_TS_PES_SUBTITLE0,
+ DMX_TS_PES_PCR0,
+
+ DMX_TS_PES_AUDIO1,
+ DMX_TS_PES_VIDEO1,
+ DMX_TS_PES_TELETEXT1,
+ DMX_TS_PES_SUBTITLE1,
+ DMX_TS_PES_PCR1,
+
+ DMX_TS_PES_AUDIO2,
+ DMX_TS_PES_VIDEO2,
+ DMX_TS_PES_TELETEXT2,
+ DMX_TS_PES_SUBTITLE2,
+ DMX_TS_PES_PCR2,
+
+ DMX_TS_PES_AUDIO3,
+ DMX_TS_PES_VIDEO3,
+ DMX_TS_PES_TELETEXT3,
+ DMX_TS_PES_SUBTITLE3,
+ DMX_TS_PES_PCR3,
+
+ DMX_TS_PES_OTHER
+};
+
+#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0
+#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0
+#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0
+#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0
+#define DMX_TS_PES_PCR DMX_TS_PES_PCR0
+
+
+struct dmx_ts_feed {
+ int is_filtering; /* Set to non-zero when filtering in progress */
+ struct dmx_demux *parent; /* Back-pointer */
+ void *priv; /* Pointer to private data of the API client */
+ int (*set) (struct dmx_ts_feed *feed,
+ u16 pid,
+ int type,
+ enum dmx_ts_pes pes_type,
+ size_t callback_length,
+ size_t circular_buffer_size,
+ int descramble,
+ struct timespec timeout);
+ int (*start_filtering) (struct dmx_ts_feed* feed);
+ int (*stop_filtering) (struct dmx_ts_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Section reception */
+/*--------------------------------------------------------------------------*/
+
+struct dmx_section_filter {
+ u8 filter_value [DMX_MAX_FILTER_SIZE];
+ u8 filter_mask [DMX_MAX_FILTER_SIZE];
+ u8 filter_mode [DMX_MAX_FILTER_SIZE];
+ struct dmx_section_feed* parent; /* Back-pointer */
+ void* priv; /* Pointer to private data of the API client */
+};
+
+struct dmx_section_feed {
+ int is_filtering; /* Set to non-zero when filtering in progress */
+ struct dmx_demux* parent; /* Back-pointer */
+ void* priv; /* Pointer to private data of the API client */
+
+ int check_crc;
+ u32 crc_val;
+
+ u8 *secbuf;
+ u8 secbuf_base[DMX_MAX_SECFEED_SIZE];
+ u16 secbufp, seclen, tsfeedp;
+
+ int (*set) (struct dmx_section_feed* feed,
+ u16 pid,
+ size_t circular_buffer_size,
+ int descramble,
+ int check_crc);
+ int (*allocate_filter) (struct dmx_section_feed* feed,
+ struct dmx_section_filter** filter);
+ int (*release_filter) (struct dmx_section_feed* feed,
+ struct dmx_section_filter* filter);
+ int (*start_filtering) (struct dmx_section_feed* feed);
+ int (*stop_filtering) (struct dmx_section_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Callback functions */
+/*--------------------------------------------------------------------------*/
+
+typedef int (*dmx_ts_cb) ( const u8 * buffer1,
+ size_t buffer1_length,
+ const u8 * buffer2,
+ size_t buffer2_length,
+ struct dmx_ts_feed* source,
+ enum dmx_success success);
+
+typedef int (*dmx_section_cb) ( const u8 * buffer1,
+ size_t buffer1_len,
+ const u8 * buffer2,
+ size_t buffer2_len,
+ struct dmx_section_filter * source,
+ enum dmx_success success);
+
+/*--------------------------------------------------------------------------*/
+/* DVB Front-End */
+/*--------------------------------------------------------------------------*/
+
+enum dmx_frontend_source {
+ DMX_MEMORY_FE,
+ DMX_FRONTEND_0,
+ DMX_FRONTEND_1,
+ DMX_FRONTEND_2,
+ DMX_FRONTEND_3,
+ DMX_STREAM_0, /* external stream input, e.g. LVDS */
+ DMX_STREAM_1,
+ DMX_STREAM_2,
+ DMX_STREAM_3
+};
+
+struct dmx_frontend {
+ struct list_head connectivity_list; /* List of front-ends that can
+ be connected to a particular
+ demux */
+ void* priv; /* Pointer to private data of the API client */
+ enum dmx_frontend_source source;
+};
+
+/*--------------------------------------------------------------------------*/
+/* MPEG-2 TS Demux */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Flags OR'ed in the capabilites field of struct dmx_demux.
+ */
+
+#define DMX_TS_FILTERING 1
+#define DMX_PES_FILTERING 2
+#define DMX_SECTION_FILTERING 4
+#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */
+#define DMX_CRC_CHECKING 16
+#define DMX_TS_DESCRAMBLING 32
+#define DMX_SECTION_PAYLOAD_DESCRAMBLING 64
+#define DMX_MAC_ADDRESS_DESCRAMBLING 128
+
+/*
+ * Demux resource type identifier.
+*/
+
+/*
+ * DMX_FE_ENTRY(): Casts elements in the list of registered
+ * front-ends from the generic type struct list_head
+ * to the type * struct dmx_frontend
+ *.
+*/
+
+#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list)
+
+struct dmx_demux {
+ u32 capabilities; /* Bitfield of capability flags */
+ struct dmx_frontend* frontend; /* Front-end connected to the demux */
+ struct list_head reg_list; /* List of registered demuxes */
+ void* priv; /* Pointer to private data of the API client */
+ int users; /* Number of users */
+ int (*open) (struct dmx_demux* demux);
+ int (*close) (struct dmx_demux* demux);
+ int (*write) (struct dmx_demux* demux, const char* buf, size_t count);
+ int (*allocate_ts_feed) (struct dmx_demux* demux,
+ struct dmx_ts_feed** feed,
+ dmx_ts_cb callback);
+ int (*release_ts_feed) (struct dmx_demux* demux,
+ struct dmx_ts_feed* feed);
+ int (*allocate_section_feed) (struct dmx_demux* demux,
+ struct dmx_section_feed** feed,
+ dmx_section_cb callback);
+ int (*release_section_feed) (struct dmx_demux* demux,
+ struct dmx_section_feed* feed);
+ int (*descramble_mac_address) (struct dmx_demux* demux,
+ u8* buffer1,
+ size_t buffer1_length,
+ u8* buffer2,
+ size_t buffer2_length,
+ u16 pid);
+ int (*descramble_section_payload) (struct dmx_demux* demux,
+ u8* buffer1,
+ size_t buffer1_length,
+ u8* buffer2, size_t buffer2_length,
+ u16 pid);
+ int (*add_frontend) (struct dmx_demux* demux,
+ struct dmx_frontend* frontend);
+ int (*remove_frontend) (struct dmx_demux* demux,
+ struct dmx_frontend* frontend);
+ struct list_head* (*get_frontends) (struct dmx_demux* demux);
+ int (*connect_frontend) (struct dmx_demux* demux,
+ struct dmx_frontend* frontend);
+ int (*disconnect_frontend) (struct dmx_demux* demux);
+
+ int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids);
+
+ int (*get_stc) (struct dmx_demux* demux, unsigned int num,
+ u64 *stc, unsigned int *base);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Demux directory */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_DIR_ENTRY(): Casts elements in the list of registered
+ * demuxes from the generic type struct list_head* to the type struct dmx_demux
+ *.
+ */
+
+#define DMX_DIR_ENTRY(list) list_entry(list, struct dmx_demux, reg_list)
+
+#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
new file mode 100644
index 000000000000..1863f1dfb00c
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -0,0 +1,1137 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
+ * & Marcus Metzler <marcus@convergence.de>
+ for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include "dmxdev.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk if (debug) printk
+
+static inline struct dmxdev_filter *
+dvb_dmxdev_file_to_filter(struct file *file)
+{
+ return (struct dmxdev_filter *) file->private_data;
+}
+
+static inline void dvb_dmxdev_buffer_init(struct dmxdev_buffer *buffer)
+{
+ buffer->data=NULL;
+ buffer->size=8192;
+ buffer->pread=0;
+ buffer->pwrite=0;
+ buffer->error=0;
+ init_waitqueue_head(&buffer->queue);
+}
+
+static inline int dvb_dmxdev_buffer_write(struct dmxdev_buffer *buf, const u8 *src, int len)
+{
+ int split;
+ int free;
+ int todo;
+
+ if (!len)
+ return 0;
+ if (!buf->data)
+ return 0;
+
+ free=buf->pread-buf->pwrite;
+ split=0;
+ if (free<=0) {
+ free+=buf->size;
+ split=buf->size-buf->pwrite;
+ }
+ if (len>=free) {
+ dprintk("dmxdev: buffer overflow\n");
+ return -1;
+ }
+ if (split>=len)
+ split=0;
+ todo=len;
+ if (split) {
+ memcpy(buf->data + buf->pwrite, src, split);
+ todo-=split;
+ buf->pwrite=0;
+ }
+ memcpy(buf->data + buf->pwrite, src+split, todo);
+ buf->pwrite=(buf->pwrite+todo)%buf->size;
+ return len;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_buffer *src,
+ int non_blocking, char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned long todo=count;
+ int split, avail, error;
+
+ if (!src->data)
+ return 0;
+
+ if ((error=src->error)) {
+ src->pwrite=src->pread;
+ src->error=0;
+ return error;
+ }
+
+ if (non_blocking && (src->pwrite==src->pread))
+ return -EWOULDBLOCK;
+
+ while (todo>0) {
+ if (non_blocking && (src->pwrite==src->pread))
+ return (count-todo) ? (count-todo) : -EWOULDBLOCK;
+
+ if (wait_event_interruptible(src->queue,
+ (src->pread!=src->pwrite) ||
+ (src->error))<0)
+ return count-todo;
+
+ if ((error=src->error)) {
+ src->pwrite=src->pread;
+ src->error=0;
+ return error;
+ }
+
+ split=src->size;
+ avail=src->pwrite - src->pread;
+ if (avail<0) {
+ avail+=src->size;
+ split=src->size - src->pread;
+ }
+ if (avail>todo)
+ avail=todo;
+ if (split<avail) {
+ if (copy_to_user(buf, src->data+src->pread, split))
+ return -EFAULT;
+ buf+=split;
+ src->pread=0;
+ todo-=split;
+ avail-=split;
+ }
+ if (avail) {
+ if (copy_to_user(buf, src->data+src->pread, avail))
+ return -EFAULT;
+ src->pread = (src->pread + avail) % src->size;
+ todo-=avail;
+ buf+=avail;
+ }
+ }
+ return count;
+}
+
+static struct dmx_frontend * get_fe(struct dmx_demux *demux, int type)
+{
+ struct list_head *head, *pos;
+
+ head=demux->get_frontends(demux);
+ if (!head)
+ return NULL;
+ list_for_each(pos, head)
+ if (DMX_FE_ENTRY(pos)->source==type)
+ return DMX_FE_ENTRY(pos);
+
+ return NULL;
+}
+
+static inline void dvb_dmxdev_dvr_state_set(struct dmxdev_dvr *dmxdevdvr, int state)
+{
+ spin_lock_irq(&dmxdevdvr->dev->lock);
+ dmxdevdvr->state=state;
+ spin_unlock_irq(&dmxdevdvr->dev->lock);
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+ struct dmx_frontend *front;
+
+ dprintk ("function : %s\n", __FUNCTION__);
+
+ if (down_interruptible (&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if ((file->f_flags&O_ACCMODE)==O_RDWR) {
+ if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) {
+ up(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+ dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+ dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE;
+ dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE);
+ if (!dmxdev->dvr_buffer.data) {
+ up(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ }
+
+ if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+ dmxdev->dvr_orig_fe=dmxdev->demux->frontend;
+
+ if (!dmxdev->demux->write) {
+ up(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+
+ front=get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+ if (!front) {
+ up(&dmxdev->mutex);
+ return -EINVAL;
+ }
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux, front);
+ }
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+ if (down_interruptible (&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux,
+ dmxdev->dvr_orig_fe);
+ }
+ if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+ if (dmxdev->dvr_buffer.data) {
+ void *mem=dmxdev->dvr_buffer.data;
+ mb();
+ spin_lock_irq(&dmxdev->lock);
+ dmxdev->dvr_buffer.data=NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+ }
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+ int ret;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+ if ((file->f_flags&O_ACCMODE)!=O_WRONLY)
+ return -EINVAL;
+ if (down_interruptible (&dmxdev->mutex))
+ return -ERESTARTSYS;
+ ret=dmxdev->demux->write(dmxdev->demux, buf, count);
+ up(&dmxdev->mutex);
+ return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+ int ret;
+
+ //down(&dmxdev->mutex);
+ ret= dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+ file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+ //up(&dmxdev->mutex);
+ return ret;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state)
+{
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state=state;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, unsigned long size)
+{
+ struct dmxdev_buffer *buf=&dmxdevfilter->buffer;
+ void *mem;
+
+ if (buf->size==size)
+ return 0;
+ if (dmxdevfilter->state>=DMXDEV_STATE_GO)
+ return -EBUSY;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ mem=buf->data;
+ buf->data=NULL;
+ buf->size=size;
+ buf->pwrite=buf->pread=0;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ vfree(mem);
+
+ if (buf->size) {
+ mem=vmalloc(dmxdevfilter->buffer.size);
+ if (!mem)
+ return -ENOMEM;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data=mem;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ }
+ return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(unsigned long data)
+{
+ struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *)data;
+
+ dmxdevfilter->buffer.error=-ETIMEDOUT;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
+
+ del_timer(&dmxdevfilter->timer);
+ if (para->timeout) {
+ dmxdevfilter->timer.function=dvb_dmxdev_filter_timeout;
+ dmxdevfilter->timer.data=(unsigned long) dmxdevfilter;
+ dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000;
+ add_timer(&dmxdevfilter->timer);
+ }
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_section_filter *filter, enum dmx_success success)
+{
+ struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) filter->priv;
+ int ret;
+
+ if (dmxdevfilter->buffer.error) {
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+ }
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->state!=DMXDEV_STATE_GO) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+ del_timer(&dmxdevfilter->timer);
+ dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
+ buffer1[0], buffer1[1],
+ buffer1[2], buffer1[3],
+ buffer1[4], buffer1[5]);
+ ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len);
+ if (ret==buffer1_len) {
+ ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, buffer2_len);
+ }
+ if (ret<0) {
+ dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread;
+ dmxdevfilter->buffer.error=-EOVERFLOW;
+ }
+ if (dmxdevfilter->params.sec.flags&DMX_ONESHOT)
+ dmxdevfilter->state=DMXDEV_STATE_DONE;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_ts_feed *feed, enum dmx_success success)
+{
+ struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) feed->priv;
+ struct dmxdev_buffer *buffer;
+ int ret;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output==DMX_OUT_TAP)
+ buffer=&dmxdevfilter->buffer;
+ else
+ buffer=&dmxdevfilter->dev->dvr_buffer;
+ if (buffer->error) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+ }
+ ret=dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+ if (ret==buffer1_len)
+ ret=dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+ if (ret<0) {
+ buffer->pwrite=buffer->pread;
+ buffer->error=-EOVERFLOW;
+ }
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+}
+
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ del_timer(&dmxdevfilter->timer);
+ dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+ break;
+ case DMXDEV_TYPE_PES:
+ dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* start feed associated with the specified filter */
+
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+ dvb_dmxdev_filter_state_set (filter, DMXDEV_STATE_GO);
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ return filter->feed.sec->start_filtering(filter->feed.sec);
+ break;
+ case DMXDEV_TYPE_PES:
+ return filter->feed.ts->start_filtering(filter->feed.ts);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/* restart section feed if it has filters left associated with it,
+ otherwise release the feed */
+
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+ int i;
+ struct dmxdev *dmxdev = filter->dev;
+ u16 pid = filter->params.sec.pid;
+
+ for (i=0; i<dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state>=DMXDEV_STATE_GO &&
+ dmxdev->filter[i].type==DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].pid==pid) {
+ dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+ return 0;
+ }
+
+ filter->dev->demux->release_section_feed(dmxdev->demux, filter->feed.sec);
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ if (dmxdevfilter->state<DMXDEV_STATE_GO)
+ return 0;
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ if (!dmxdevfilter->feed.sec)
+ break;
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ if (dmxdevfilter->filter.sec)
+ dmxdevfilter->feed.sec->
+ release_filter(dmxdevfilter->feed.sec,
+ dmxdevfilter->filter.sec);
+ dvb_dmxdev_feed_restart(dmxdevfilter);
+ dmxdevfilter->feed.sec=NULL;
+ break;
+ case DMXDEV_TYPE_PES:
+ if (!dmxdevfilter->feed.ts)
+ break;
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ dmxdevfilter->dev->demux->
+ release_ts_feed(dmxdevfilter->dev->demux,
+ dmxdevfilter->feed.ts);
+ dmxdevfilter->feed.ts=NULL;
+ break;
+ default:
+ if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED)
+ return 0;
+ return -EINVAL;
+ }
+ dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0;
+ return 0;
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+ if (dmxdevfilter->state<DMXDEV_STATE_SET)
+ return 0;
+
+ dmxdevfilter->type=DMXDEV_TYPE_NONE;
+ dmxdevfilter->pid=0xffff;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev *dmxdev = filter->dev;
+ void *mem;
+ int ret, i;
+
+ if (filter->state < DMXDEV_STATE_SET)
+ return -EINVAL;
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ dvb_dmxdev_filter_stop(filter);
+
+ if (!(mem = filter->buffer.data)) {
+ mem = vmalloc(filter->buffer.size);
+ spin_lock_irq(&filter->dev->lock);
+ filter->buffer.data=mem;
+ spin_unlock_irq(&filter->dev->lock);
+ if (!filter->buffer.data)
+ return -ENOMEM;
+ }
+
+ filter->buffer.pwrite = filter->buffer.pread = 0;
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ {
+ struct dmx_sct_filter_params *para=&filter->params.sec;
+ struct dmx_section_filter **secfilter=&filter->filter.sec;
+ struct dmx_section_feed **secfeed=&filter->feed.sec;
+
+ *secfilter=NULL;
+ *secfeed=NULL;
+
+ /* find active filter/feed with same PID */
+ for (i=0; i<dmxdev->filternum; i++) {
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+ dmxdev->filter[i].pid == para->pid &&
+ dmxdev->filter[i].type == DMXDEV_TYPE_SEC) {
+ *secfeed = dmxdev->filter[i].feed.sec;
+ break;
+ }
+ }
+
+ /* if no feed found, try to allocate new one */
+ if (!*secfeed) {
+ ret=dmxdev->demux->allocate_section_feed(dmxdev->demux,
+ secfeed,
+ dvb_dmxdev_section_callback);
+ if (ret<0) {
+ printk ("DVB (%s): could not alloc feed\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0,
+ (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+
+ if (ret<0) {
+ printk ("DVB (%s): could not set feed\n",
+ __FUNCTION__);
+ dvb_dmxdev_feed_restart(filter);
+ return ret;
+ }
+ } else {
+ dvb_dmxdev_feed_stop(filter);
+ }
+
+ ret=(*secfeed)->allocate_filter(*secfeed, secfilter);
+
+ if (ret < 0) {
+ dvb_dmxdev_feed_restart(filter);
+ filter->feed.sec->start_filtering(*secfeed);
+ dprintk ("could not get filter\n");
+ return ret;
+ }
+
+ (*secfilter)->priv = filter;
+
+ memcpy(&((*secfilter)->filter_value[3]),
+ &(para->filter.filter[1]), DMX_FILTER_SIZE-1);
+ memcpy(&(*secfilter)->filter_mask[3],
+ &para->filter.mask[1], DMX_FILTER_SIZE-1);
+ memcpy(&(*secfilter)->filter_mode[3],
+ &para->filter.mode[1], DMX_FILTER_SIZE-1);
+
+ (*secfilter)->filter_value[0]=para->filter.filter[0];
+ (*secfilter)->filter_mask[0]=para->filter.mask[0];
+ (*secfilter)->filter_mode[0]=para->filter.mode[0];
+ (*secfilter)->filter_mask[1]=0;
+ (*secfilter)->filter_mask[2]=0;
+
+ filter->todo = 0;
+
+ ret = filter->feed.sec->start_filtering (filter->feed.sec);
+
+ if (ret < 0)
+ return ret;
+
+ dvb_dmxdev_filter_timer(filter);
+ break;
+ }
+
+ case DMXDEV_TYPE_PES:
+ {
+ struct timespec timeout = { 0 };
+ struct dmx_pes_filter_params *para = &filter->params.pes;
+ dmx_output_t otype;
+ int ret;
+ int ts_type;
+ enum dmx_ts_pes ts_pes;
+ struct dmx_ts_feed **tsfeed = &filter->feed.ts;
+
+ filter->feed.ts = NULL;
+ otype=para->output;
+
+ ts_pes=(enum dmx_ts_pes) para->pes_type;
+
+ if (ts_pes<DMX_PES_OTHER)
+ ts_type=TS_DECODER;
+ else
+ ts_type=0;
+
+ if (otype == DMX_OUT_TS_TAP)
+ ts_type |= TS_PACKET;
+
+ if (otype == DMX_OUT_TAP)
+ ts_type |= TS_PAYLOAD_ONLY|TS_PACKET;
+
+ ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux,
+ tsfeed,
+ dvb_dmxdev_ts_callback);
+ if (ret<0)
+ return ret;
+
+ (*tsfeed)->priv = (void *) filter;
+
+ ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes,
+ 188, 32768, 0, timeout);
+
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, *tsfeed);
+ return ret;
+ }
+
+ ret = filter->feed.ts->start_filtering(filter->feed.ts);
+
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+ return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+ int i;
+ struct dmxdev_filter *dmxdevfilter;
+
+ if (!dmxdev->filter)
+ return -EINVAL;
+
+ if (down_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ for (i=0; i<dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state==DMXDEV_STATE_FREE)
+ break;
+
+ if (i==dmxdev->filternum) {
+ up(&dmxdev->mutex);
+ return -EMFILE;
+ }
+
+ dmxdevfilter=&dmxdev->filter[i];
+ sema_init(&dmxdevfilter->mutex, 1);
+ dmxdevfilter->dvbdev=dmxdev->dvbdev;
+ file->private_data=dmxdevfilter;
+
+ dvb_dmxdev_buffer_init(&dmxdevfilter->buffer);
+ dmxdevfilter->type=DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ dmxdevfilter->feed.ts=NULL;
+ init_timer(&dmxdevfilter->timer);
+
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter)
+{
+ if (down_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if (dmxdevfilter->buffer.data) {
+ void *mem=dmxdevfilter->buffer.data;
+
+ spin_lock_irq(&dmxdev->lock);
+ dmxdevfilter->buffer.data=NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+ wake_up(&dmxdevfilter->buffer.queue);
+ up(&dmxdevfilter->mutex);
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+static inline void invert_mode(dmx_filter_t *filter)
+{
+ int i;
+
+ for (i=0; i<DMX_FILTER_SIZE; i++)
+ filter->mode[i]^=0xff;
+}
+
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_sct_filter_params *params)
+{
+ dprintk ("function : %s\n", __FUNCTION__);
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ dmxdevfilter->type=DMXDEV_TYPE_SEC;
+ dmxdevfilter->pid=params->pid;
+ memcpy(&dmxdevfilter->params.sec,
+ params, sizeof(struct dmx_sct_filter_params));
+ invert_mode(&dmxdevfilter->params.sec.filter);
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags&DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_pes_filter_params *params)
+{
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ if (params->pes_type>DMX_PES_OTHER || params->pes_type<0)
+ return -EINVAL;
+
+ dmxdevfilter->type=DMXDEV_TYPE_PES;
+ dmxdevfilter->pid=params->pid;
+ memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params));
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags&DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+ struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ int result, hcount;
+ int done=0;
+
+ if (dfil->todo<=0) {
+ hcount=3+dfil->todo;
+ if (hcount>count)
+ hcount=count;
+ result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+ buf, hcount, ppos);
+ if (result<0) {
+ dfil->todo=0;
+ return result;
+ }
+ if (copy_from_user(dfil->secheader-dfil->todo, buf, result))
+ return -EFAULT;
+ buf+=result;
+ done=result;
+ count-=result;
+ dfil->todo-=result;
+ if (dfil->todo>-3)
+ return done;
+ dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff;
+ if (!count)
+ return done;
+ }
+ if (count>dfil->todo)
+ count=dfil->todo;
+ result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+ if (result<0)
+ return result;
+ dfil->todo-=result;
+ return (result+done);
+}
+
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+ int ret=0;
+
+ if (down_interruptible(&dmxdevfilter->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdevfilter->type==DMXDEV_TYPE_SEC)
+ ret=dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+ else
+ ret=dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+ file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+
+ up(&dmxdevfilter->mutex);
+ return ret;
+}
+
+
+static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+ struct dmxdev *dmxdev=dmxdevfilter->dev;
+ unsigned long arg=(unsigned long) parg;
+ int ret=0;
+
+ if (down_interruptible (&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_START:
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ if (dmxdevfilter->state<DMXDEV_STATE_SET)
+ ret = -EINVAL;
+ else
+ ret = dvb_dmxdev_filter_start(dmxdevfilter);
+ up(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_STOP:
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret=dvb_dmxdev_filter_stop(dmxdevfilter);
+ up(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_FILTER:
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter,
+ (struct dmx_sct_filter_params *)parg);
+ up(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PES_FILTER:
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret=dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter,
+ (struct dmx_pes_filter_params *)parg);
+ up(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_BUFFER_SIZE:
+ if (down_interruptible(&dmxdevfilter->mutex)) {
+ up(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret=dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+ up(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_EVENT:
+ break;
+
+ case DMX_GET_PES_PIDS:
+ if (!dmxdev->demux->get_pes_pids) {
+ ret=-EINVAL;
+ break;
+ }
+ dmxdev->demux->get_pes_pids(dmxdev->demux, (u16 *)parg);
+ break;
+
+ case DMX_GET_STC:
+ if (!dmxdev->demux->get_stc) {
+ ret=-EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->get_stc(dmxdev->demux,
+ ((struct dmx_stc *)parg)->num,
+ &((struct dmx_stc *)parg)->stc,
+ &((struct dmx_stc *)parg)->base);
+ break;
+
+ default:
+ ret=-EINVAL;
+ }
+ up(&dmxdev->mutex);
+ return ret;
+}
+
+static int dvb_demux_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+
+static unsigned int dvb_demux_poll (struct file *file, poll_table *wait)
+{
+ struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+ unsigned int mask = 0;
+
+ if (!dmxdevfilter)
+ return -EINVAL;
+
+ poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+ dmxdevfilter->state != DMXDEV_STATE_DONE &&
+ dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+ return 0;
+
+ if (dmxdevfilter->buffer.error)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdevfilter->buffer.pread != dmxdevfilter->buffer.pwrite)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+ return mask;
+}
+
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+ struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+ return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+}
+
+
+static struct file_operations dvb_demux_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_demux_read,
+ .ioctl = dvb_demux_ioctl,
+ .open = dvb_demux_open,
+ .release = dvb_demux_release,
+ .poll = dvb_demux_poll,
+};
+
+
+static struct dvb_device dvbdev_demux = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+ .fops = &dvb_demux_fops
+};
+
+
+static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+ int ret=0;
+
+ if (down_interruptible (&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_SET_BUFFER_SIZE:
+ // FIXME: implement
+ ret=0;
+ break;
+
+ default:
+ ret=-EINVAL;
+ }
+ up(&dmxdev->mutex);
+ return ret;
+}
+
+
+static int dvb_dvr_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+
+static unsigned int dvb_dvr_poll (struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+ struct dmxdev *dmxdev = (struct dmxdev *) dvbdev->priv;
+ unsigned int mask = 0;
+
+ dprintk ("function : %s\n", __FUNCTION__);
+
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+ if ((file->f_flags&O_ACCMODE) == O_RDONLY) {
+ if (dmxdev->dvr_buffer.error)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ } else
+ mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+ return mask;
+}
+
+
+static struct file_operations dvb_dvr_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_dvr_read,
+ .write = dvb_dvr_write,
+ .ioctl = dvb_dvr_ioctl,
+ .open = dvb_dvr_open,
+ .release = dvb_dvr_release,
+ .poll = dvb_dvr_poll,
+};
+
+static struct dvb_device dvbdev_dvr = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+ .fops = &dvb_dvr_fops
+};
+
+int
+dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+ int i;
+
+ if (dmxdev->demux->open(dmxdev->demux) < 0)
+ return -EUSERS;
+
+ dmxdev->filter = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_filter));
+ if (!dmxdev->filter)
+ return -ENOMEM;
+
+ dmxdev->dvr = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_dvr));
+ if (!dmxdev->dvr) {
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ return -ENOMEM;
+ }
+
+ sema_init(&dmxdev->mutex, 1);
+ spin_lock_init(&dmxdev->lock);
+ for (i=0; i<dmxdev->filternum; i++) {
+ dmxdev->filter[i].dev=dmxdev;
+ dmxdev->filter[i].buffer.data=NULL;
+ dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+ dmxdev->dvr[i].dev=dmxdev;
+ dmxdev->dvr[i].buffer.data=NULL;
+ dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+ dvb_dmxdev_dvr_state_set(&dmxdev->dvr[i], DMXDEV_STATE_FREE);
+ }
+
+ dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX);
+ dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR);
+
+ dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void
+dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+ dvb_unregister_device(dmxdev->dvbdev);
+ dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+ vfree(dmxdev->filter);
+ dmxdev->filter=NULL;
+ vfree(dmxdev->dvr);
+ dmxdev->dvr=NULL;
+ dmxdev->demux->close(dmxdev->demux);
+}
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
new file mode 100644
index 000000000000..395a9cd75012
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -0,0 +1,128 @@
+/*
+ * dmxdev.h
+ *
+ * Copyright (C) 2000 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 Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 _DMXDEV_H_
+#define _DMXDEV_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <asm/semaphore.h>
+
+#include <linux/dvb/dmx.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+
+enum dmxdevype {
+ DMXDEV_TYPE_NONE,
+ DMXDEV_TYPE_SEC,
+ DMXDEV_TYPE_PES,
+};
+
+enum dmxdev_state {
+ DMXDEV_STATE_FREE,
+ DMXDEV_STATE_ALLOCATED,
+ DMXDEV_STATE_SET,
+ DMXDEV_STATE_GO,
+ DMXDEV_STATE_DONE,
+ DMXDEV_STATE_TIMEDOUT
+};
+
+struct dmxdev_buffer {
+ u8 *data;
+ int size;
+ int pread;
+ int pwrite;
+ wait_queue_head_t queue;
+ int error;
+};
+
+struct dmxdev_filter {
+ struct dvb_device *dvbdev;
+
+ union {
+ struct dmx_section_filter *sec;
+ } filter;
+
+ union {
+ struct dmx_ts_feed *ts;
+ struct dmx_section_feed *sec;
+ } feed;
+
+ union {
+ struct dmx_sct_filter_params sec;
+ struct dmx_pes_filter_params pes;
+ } params;
+
+ int type;
+ enum dmxdev_state state;
+ struct dmxdev *dev;
+ struct dmxdev_buffer buffer;
+
+ struct semaphore mutex;
+
+ /* only for sections */
+ struct timer_list timer;
+ int todo;
+ u8 secheader[3];
+
+ u16 pid;
+};
+
+
+struct dmxdev_dvr {
+ int state;
+ struct dmxdev *dev;
+ struct dmxdev_buffer buffer;
+};
+
+
+struct dmxdev {
+ struct dvb_device *dvbdev;
+ struct dvb_device *dvr_dvbdev;
+
+ struct dmxdev_filter *filter;
+ struct dmxdev_dvr *dvr;
+ struct dmx_demux *demux;
+
+ int filternum;
+ int capabilities;
+#define DMXDEV_CAP_DUPLEX 1
+ struct dmx_frontend *dvr_orig_fe;
+
+ struct dmxdev_buffer dvr_buffer;
+#define DVR_BUFFER_SIZE (10*188*1024)
+
+ struct semaphore mutex;
+ spinlock_t lock;
+};
+
+
+int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
+void dvb_dmxdev_release(struct dmxdev *dmxdev);
+
+#endif /* _DMXDEV_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 000000000000..c1ea89f2880c
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1778 @@
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/rwsem.h>
+
+#include "dvb_ca_en50221.h"
+#include "dvb_ringbuffer.h"
+
+static int dvb_ca_en50221_debug;
+
+module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
+MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
+
+#define dprintk if (dvb_ca_en50221_debug) printk
+
+#define INIT_TIMEOUT_SECS 5
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA 0
+#define CTRLIF_COMMAND 1
+#define CTRLIF_STATUS 1
+#define CTRLIF_SIZE_LOW 2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC 1 /* Host control */
+#define CMDREG_SW 2 /* Size write */
+#define CMDREG_SR 4 /* Size read */
+#define CMDREG_RS 8 /* Reset interface */
+#define CMDREG_FRIE 0x40 /* Enable FR interrupt */
+#define CMDREG_DAIE 0x80 /* Enable DA interrupt */
+#define IRQEN (CMDREG_DAIE)
+
+#define STATUSREG_RE 1 /* read error */
+#define STATUSREG_WE 2 /* write error */
+#define STATUSREG_FR 0x40 /* module free */
+#define STATUSREG_DA 0x80 /* data available */
+#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */
+
+
+#define DVB_CA_SLOTSTATE_NONE 0
+#define DVB_CA_SLOTSTATE_UNINITIALISED 1
+#define DVB_CA_SLOTSTATE_RUNNING 2
+#define DVB_CA_SLOTSTATE_INVALID 3
+#define DVB_CA_SLOTSTATE_WAITREADY 4
+#define DVB_CA_SLOTSTATE_VALIDATE 5
+#define DVB_CA_SLOTSTATE_WAITFR 6
+#define DVB_CA_SLOTSTATE_LINKINIT 7
+
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+
+ /* current state of the CAM */
+ int slot_state;
+
+ /* Number of CAMCHANGES that have occurred since last processing */
+ atomic_t camchange_count;
+
+ /* Type of last CAMCHANGE */
+ int camchange_type;
+
+ /* base address of CAM config */
+ u32 config_base;
+
+ /* value to write into Config Control register */
+ u8 config_option;
+
+ /* if 1, the CAM supports DA IRQs */
+ u8 da_irq_supported:1;
+
+ /* size of the buffer to use when talking to the CAM */
+ int link_buf_size;
+
+ /* semaphore for syncing access to slot structure */
+ struct rw_semaphore sem;
+
+ /* buffer for incoming packets */
+ struct dvb_ringbuffer rx_buffer;
+
+ /* timer used during various states of the slot */
+ unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+
+ /* pointer back to the public data structure */
+ struct dvb_ca_en50221 *pub;
+
+ /* the DVB device */
+ struct dvb_device *dvbdev;
+
+ /* Flags describing the interface (DVB_CA_FLAG_*) */
+ u32 flags;
+
+ /* number of slots supported by this CA interface */
+ unsigned int slot_count;
+
+ /* information on each slot */
+ struct dvb_ca_slot *slot_info;
+
+ /* wait queues for read() and write() operations */
+ wait_queue_head_t wait_queue;
+
+ /* PID of the monitoring thread */
+ pid_t thread_pid;
+
+ /* Wait queue used when shutting thread down */
+ wait_queue_head_t thread_queue;
+
+ /* Flag indicating when thread should exit */
+ unsigned int exit:1;
+
+ /* Flag indicating if the CA device is open */
+ unsigned int open:1;
+
+ /* Flag indicating the thread should wake up now */
+ unsigned int wakeup:1;
+
+ /* Delay the main thread should use */
+ unsigned long delay;
+
+ /* Slot to start looking for data to read from in the next user-space read operation */
+ int next_read_slot;
+};
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+
+
+/**
+ * Safely find needle in haystack.
+ *
+ * @param haystack Buffer to look in.
+ * @param hlen Number of bytes in haystack.
+ * @param needle Buffer to find.
+ * @param nlen Number of bytes in needle.
+ * @return Pointer into haystack needle was found at, or NULL if not found.
+ */
+static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen)
+{
+ int i;
+
+ if (hlen < nlen)
+ return NULL;
+
+ for (i = 0; i <= hlen - nlen; i++) {
+ if (!strncmp(haystack + i, needle, nlen))
+ return haystack + i;
+ }
+
+ return NULL;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 physical interface functions */
+
+
+/**
+ * Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
+{
+ int slot_status;
+ int cam_present_now;
+ int cam_changed;
+
+ /* IRQ mode */
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
+ return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
+ }
+
+ /* poll mode */
+ slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
+
+ cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
+ cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
+ if (!cam_changed) {
+ int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
+ cam_changed = (cam_present_now != cam_present_old);
+ }
+
+ if (cam_changed) {
+ if (!cam_present_now) {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ } else {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ }
+ atomic_set(&ca->slot_info[slot].camchange_count, 1);
+ } else {
+ if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+ (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+ // move to validate state if reset is completed
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ }
+ }
+
+ return cam_changed;
+}
+
+
+/**
+ * Wait for flags to become set on the STATUS register on a CAM interface,
+ * checking for errors and timeout.
+ *
+ * @param ca CA instance.
+ * @param slot Slot on interface.
+ * @param waitfor Flags to wait for.
+ * @param timeout_ms Timeout in milliseconds.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
+ u8 waitfor, int timeout_hz)
+{
+ unsigned long timeout;
+ unsigned long start;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ /* loop until timeout elapsed */
+ start = jiffies;
+ timeout = jiffies + timeout_hz;
+ while (1) {
+ /* read the status and check for error */
+ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (res < 0)
+ return -EIO;
+
+ /* if we got the flags, it was successful! */
+ if (res & waitfor) {
+ dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start);
+ return 0;
+ }
+
+ /* check for timeout */
+ if (time_after(jiffies, timeout)) {
+ break;
+ }
+
+ /* wait for a bit */
+ msleep(1);
+ }
+
+ dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+ /* if we get here, we've timed out */
+ return -ETIMEDOUT;
+}
+
+
+/**
+ * Initialise the link layer connection to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
+{
+ int ret;
+ int buf_size;
+ u8 buf[2];
+
+ dprintk("%s\n", __FUNCTION__);
+
+ /* we'll be determining these during this function */
+ ca->slot_info[slot].da_irq_supported = 0;
+
+ /* set the host link buffer size temporarily. it will be overwritten with the
+ * real negotiated size later. */
+ ca->slot_info[slot].link_buf_size = 2;
+
+ /* read the buffer size from the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
+ return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
+ return ret;
+ if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
+ return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+ return ret;
+
+ /* store it, and choose the minimum of our buffer and the CAM's buffer size */
+ buf_size = (buf[0] << 8) | buf[1];
+ if (buf_size > HOST_LINK_BUF_SIZE)
+ buf_size = HOST_LINK_BUF_SIZE;
+ ca->slot_info[slot].link_buf_size = buf_size;
+ buf[0] = buf_size >> 8;
+ buf[1] = buf_size & 0xff;
+ dprintk("Chosen link buffer size of %i\n", buf_size);
+
+ /* write the buffer size to the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0)
+ return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0)
+ return ret;
+ if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2)
+ return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+ return ret;
+
+ /* success */
+ return 0;
+}
+
+/**
+ * Read a tuple from attribute memory.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ * @param address Address to read from. Updated.
+ * @param tupleType Tuple id byte. Updated.
+ * @param tupleLength Tuple length. Updated.
+ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
+ int *address, int *tupleType, int *tupleLength, u8 * tuple)
+{
+ int i;
+ int _tupleType;
+ int _tupleLength;
+ int _address = *address;
+
+ /* grab the next tuple length and type */
+ if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0)
+ return _tupleType;
+ if (_tupleType == 0xff) {
+ dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
+ *address += 2;
+ *tupleType = _tupleType;
+ *tupleLength = 0;
+ return 0;
+ }
+ if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0)
+ return _tupleLength;
+ _address += 4;
+
+ dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
+
+ /* read in the whole tuple */
+ for (i = 0; i < _tupleLength; i++) {
+ tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
+ dprintk(" 0x%02x: 0x%02x %c\n",
+ i, tuple[i] & 0xff,
+ ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+ }
+ _address += (_tupleLength * 2);
+
+ // success
+ *tupleType = _tupleType;
+ *tupleLength = _tupleLength;
+ *address = _address;
+ return 0;
+}
+
+
+/**
+ * Parse attribute memory of a CAM module, extracting Config register, and checking
+ * it is a DVB CAM module.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
+{
+ int address = 0;
+ int tupleLength;
+ int tupleType;
+ u8 tuple[257];
+ char *dvb_str;
+ int rasz;
+ int status;
+ int got_cftableentry = 0;
+ int end_chain = 0;
+ int i;
+ u16 manfid = 0;
+ u16 devid = 0;
+
+
+ // CISTPL_DEVICE_0A
+ if ((status =
+ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+ return status;
+ if (tupleType != 0x1D)
+ return -EINVAL;
+
+
+
+ // CISTPL_DEVICE_0C
+ if ((status =
+ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+ return status;
+ if (tupleType != 0x1C)
+ return -EINVAL;
+
+
+
+ // CISTPL_VERS_1
+ if ((status =
+ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+ return status;
+ if (tupleType != 0x15)
+ return -EINVAL;
+
+
+
+ // CISTPL_MANFID
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+ &tupleLength, tuple)) < 0)
+ return status;
+ if (tupleType != 0x20)
+ return -EINVAL;
+ if (tupleLength != 4)
+ return -EINVAL;
+ manfid = (tuple[1] << 8) | tuple[0];
+ devid = (tuple[3] << 8) | tuple[2];
+
+
+
+ // CISTPL_CONFIG
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+ &tupleLength, tuple)) < 0)
+ return status;
+ if (tupleType != 0x1A)
+ return -EINVAL;
+ if (tupleLength < 3)
+ return -EINVAL;
+
+ /* extract the configbase */
+ rasz = tuple[0] & 3;
+ if (tupleLength < (3 + rasz + 14))
+ return -EINVAL;
+ ca->slot_info[slot].config_base = 0;
+ for (i = 0; i < rasz + 1; i++) {
+ ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
+ }
+
+ /* check it contains the correct DVB string */
+ dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
+ if (dvb_str == NULL)
+ return -EINVAL;
+ if (tupleLength < ((dvb_str - (char *) tuple) + 12))
+ return -EINVAL;
+
+ /* is it a version we support? */
+ if (strncmp(dvb_str + 8, "1.00", 4)) {
+ printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
+ ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
+ return -EINVAL;
+ }
+
+ /* process the CFTABLE_ENTRY tuples, and any after those */
+ while ((!end_chain) && (address < 0x1000)) {
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+ &tupleLength, tuple)) < 0)
+ return status;
+ switch (tupleType) {
+ case 0x1B: // CISTPL_CFTABLE_ENTRY
+ if (tupleLength < (2 + 11 + 17))
+ break;
+
+ /* if we've already parsed one, just use it */
+ if (got_cftableentry)
+ break;
+
+ /* get the config option */
+ ca->slot_info[slot].config_option = tuple[0] & 0x3f;
+
+ /* OK, check it contains the correct strings */
+ if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+ (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL))
+ break;
+
+ got_cftableentry = 1;
+ break;
+
+ case 0x14: // CISTPL_NO_LINK
+ break;
+
+ case 0xFF: // CISTPL_END
+ end_chain = 1;
+ break;
+
+ default: /* Unknown tuple type - just skip this tuple and move to the next one */
+ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType,
+ tupleLength);
+ break;
+ }
+ }
+
+ if ((address > 0x1000) || (!got_cftableentry))
+ return -EINVAL;
+
+ dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+ manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
+
+ // success!
+ return 0;
+}
+
+
+/**
+ * Set CAM's configoption correctly.
+ *
+ * @param ca CA instance.
+ * @param slot Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
+{
+ int configoption;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ /* set the config option */
+ ca->pub->write_attribute_mem(ca->pub, slot,
+ ca->slot_info[slot].config_base,
+ ca->slot_info[slot].config_option);
+
+ /* check it */
+ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+ dprintk("Set configoption 0x%x, read configoption 0x%x\n",
+ ca->slot_info[slot].config_option, configoption & 0x3f);
+
+ /* fine! */
+ return 0;
+
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It reads a buffer of
+ * data from the CAM. The data can either be stored in a supplied buffer, or
+ * automatically be added to the slot's rx_buffer.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to read from.
+ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal fragment.
+ * @param ecount Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * @return Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
+{
+ int bytes_read;
+ int status;
+ u8 buf[HOST_LINK_BUF_SIZE];
+ int i;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ /* check if we have space for a link buf in the rx_buffer */
+ if (ebuf == NULL) {
+ int buf_free;
+
+ down_read(&ca->slot_info[slot].sem);
+ if (ca->slot_info[slot].rx_buffer.data == NULL) {
+ up_read(&ca->slot_info[slot].sem);
+ status = -EIO;
+ goto exit;
+ }
+ buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+ up_read(&ca->slot_info[slot].sem);
+
+ if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ status = -EAGAIN;
+ goto exit;
+ }
+ }
+
+ /* check if there is data available */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+ goto exit;
+ if (!(status & STATUSREG_DA)) {
+ /* no data */
+ status = 0;
+ goto exit;
+ }
+
+ /* read the amount of data */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
+ goto exit;
+ bytes_read = status << 8;
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
+ goto exit;
+ bytes_read |= status;
+
+ /* check it will fit */
+ if (ebuf == NULL) {
+ if (bytes_read > ca->slot_info[slot].link_buf_size) {
+ printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+ ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (bytes_read < 2) {
+ printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+ ca->dvbdev->adapter->num);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ } else {
+ if (bytes_read > ecount) {
+ printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+ ca->dvbdev->adapter->num);
+ status = -EIO;
+ goto exit;
+ }
+ }
+
+ /* fill the buffer */
+ for (i = 0; i < bytes_read; i++) {
+ /* read byte and check */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
+ goto exit;
+
+ /* OK, store it in the buffer */
+ buf[i] = status;
+ }
+
+ /* check for read error (RE should now be 0) */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+ goto exit;
+ if (status & STATUSREG_RE) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+
+ /* OK, add it to the receive buffer, or copy into external buffer if supplied */
+ if (ebuf == NULL) {
+ down_read(&ca->slot_info[slot].sem);
+ if (ca->slot_info[slot].rx