summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2011-03-11 11:44:42 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2011-03-11 11:44:42 +1100
commita70f6fa2344e0b79044a86dbaa9bd4a241a9e285 (patch)
tree525914668415f409838cfa75c7699a4141b56075
parent92926da930f8067bc78604067dba2c853c80e6c0 (diff)
parent7fac413095873b36f4ae85cf2e2cefdf7c0d033e (diff)
Merge remote-tracking branch 'v4l-dvb/master'
-rw-r--r--Documentation/DocBook/media-entities.tmpl8
-rw-r--r--Documentation/DocBook/v4l/common.xml2
-rw-r--r--Documentation/DocBook/v4l/compat.xml31
-rw-r--r--Documentation/DocBook/v4l/dev-capture.xml13
-rw-r--r--Documentation/DocBook/v4l/dev-output.xml13
-rw-r--r--Documentation/DocBook/v4l/func-mmap.xml10
-rw-r--r--Documentation/DocBook/v4l/func-munmap.xml3
-rw-r--r--Documentation/DocBook/v4l/io.xml283
-rw-r--r--Documentation/DocBook/v4l/pixfmt-nv12m.xml154
-rw-r--r--Documentation/DocBook/v4l/pixfmt-yuv420m.xml162
-rw-r--r--Documentation/DocBook/v4l/pixfmt.xml118
-rw-r--r--Documentation/DocBook/v4l/planar-apis.xml62
-rw-r--r--Documentation/DocBook/v4l/v4l2.xml30
-rw-r--r--Documentation/DocBook/v4l/videodev2.h.xml141
-rw-r--r--Documentation/DocBook/v4l/vidioc-enum-fmt.xml2
-rw-r--r--Documentation/DocBook/v4l/vidioc-g-fmt.xml15
-rw-r--r--Documentation/DocBook/v4l/vidioc-qbuf.xml24
-rw-r--r--Documentation/DocBook/v4l/vidioc-querybuf.xml14
-rw-r--r--Documentation/DocBook/v4l/vidioc-querycap.xml18
-rw-r--r--Documentation/dvb/get_dvb_firmware8
-rw-r--r--Documentation/feature-removal-schedule.txt36
-rw-r--r--Documentation/video4linux/README.ivtv3
-rw-r--r--Documentation/video4linux/gspca.txt1
-rw-r--r--drivers/media/common/tuners/tda9887.c9
-rw-r--r--drivers/media/common/tuners/tea5761.c33
-rw-r--r--drivers/media/common/tuners/tuner-types.c21
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c14
-rw-r--r--drivers/media/common/tuners/xc5000.c56
-rw-r--r--drivers/media/common/tuners/xc5000.h1
-rw-r--r--drivers/media/dvb/Kconfig2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig8
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700.h2
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c47
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c1381
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h7
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c2
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h2
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c550
-rw-r--r--drivers/media/dvb/dvb-usb/technisat-usb2.c807
-rw-r--r--drivers/media/dvb/firewire/Kconfig8
-rw-r--r--drivers/media/dvb/firewire/Makefile5
-rw-r--r--drivers/media/dvb/firewire/firedtv-1394.c300
-rw-r--r--drivers/media/dvb/firewire/firedtv-avc.c15
-rw-r--r--drivers/media/dvb/firewire/firedtv-dvb.c135
-rw-r--r--drivers/media/dvb/firewire/firedtv-fe.c8
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c146
-rw-r--r--drivers/media/dvb/firewire/firedtv.h45
-rw-r--r--drivers/media/dvb/frontends/Kconfig15
-rw-r--r--drivers/media/dvb/frontends/Makefile2
-rw-r--r--drivers/media/dvb/frontends/dib0090.c1583
-rw-r--r--drivers/media/dvb/frontends/dib0090.h31
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c1945
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h96
-rw-r--r--drivers/media/dvb/frontends/dib8000.c821
-rw-r--r--drivers/media/dvb/frontends/dib8000.h20
-rw-r--r--drivers/media/dvb/frontends/dib9000.c2351
-rw-r--r--drivers/media/dvb/frontends/dib9000.h131
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c279
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h152
-rw-r--r--drivers/media/dvb/frontends/ds3000.c645
-rw-r--r--drivers/media/dvb/frontends/ds3000.h3
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c79
-rw-r--r--drivers/media/dvb/frontends/stv0367.c3441
-rw-r--r--drivers/media/dvb/frontends/stv0367.h66
-rw-r--r--drivers/media/dvb/frontends/stv0367_priv.h212
-rw-r--r--drivers/media/dvb/frontends/stv0367_regs.h3614
-rw-r--r--drivers/media/dvb/frontends/stv0900.h2
-rw-r--r--drivers/media/dvb/frontends/stv0900_core.c27
-rw-r--r--drivers/media/dvb/frontends/stv090x.c295
-rw-r--r--drivers/media/dvb/frontends/stv090x.h16
-rw-r--r--drivers/media/dvb/frontends/stv090x_reg.h16
-rw-r--r--drivers/media/dvb/ngene/Makefile3
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c179
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c236
-rw-r--r--drivers/media/dvb/ngene/ngene-dvb.c71
-rw-r--r--drivers/media/dvb/ngene/ngene.h24
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c1
-rw-r--r--drivers/media/radio/Kconfig4
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-wl1273.c360
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c1
-rw-r--r--drivers/media/radio/wl128x/Kconfig17
-rw-r--r--drivers/media/radio/wl128x/Makefile6
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h244
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c1677
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.h402
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.c847
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.h59
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.c425
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.h37
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c580
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.h33
-rw-r--r--drivers/media/rc/keymaps/Makefile1
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-usb2.c93
-rw-r--r--drivers/media/video/Kconfig45
-rw-r--r--drivers/media/video/Makefile8
-rw-r--r--drivers/media/video/adv7343.c167
-rw-r--r--drivers/media/video/adv7343_regs.h8
-rw-r--r--drivers/media/video/au0828/au0828-cards.c3
-rw-r--r--drivers/media/video/bt819.c129
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c2
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c373
-rw-r--r--drivers/media/video/cs5345.c87
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c92
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c175
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h12
-rw-r--r--drivers/media/video/cx18/cx18-controls.c285
-rw-r--r--drivers/media/video/cx18/cx18-controls.h7
-rw-r--r--drivers/media/video/cx18/cx18-driver.c31
-rw-r--r--drivers/media/video/cx18/cx18-driver.h2
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c32
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c24
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c5
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h5
-rw-r--r--drivers/media/video/cx18/cx18-streams.c16
-rw-r--r--drivers/media/video/cx18/cx23418.h2
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c4
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c14
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c246
-rw-r--r--drivers/media/video/cx231xx/cx231xx-core.c16
-rw-r--r--drivers/media/video/cx231xx/cx231xx-i2c.c31
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c20
-rw-r--r--drivers/media/video/cx231xx/cx231xx.h7
-rw-r--r--drivers/media/video/cx23885/Kconfig12
-rw-r--r--drivers/media/video/cx23885/Makefile1
-rw-r--r--drivers/media/video/cx23885/altera-ci.c832
-rw-r--r--drivers/media/video/cx23885/altera-ci.h100
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c110
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c37
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c172
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h1
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c7
-rw-r--r--drivers/media/video/cx23885/cx23885.h7
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c118
-rw-r--r--drivers/media/video/cx88/cx88-cards.c24
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c23
-rw-r--r--drivers/media/video/cx88/cx88-input.c1
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c8
-rw-r--r--drivers/media/video/cx88/cx88-video.c78
-rw-r--r--drivers/media/video/cx88/cx88.h14
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c33
-rw-r--r--drivers/media/video/gspca/Kconfig10
-rw-r--r--drivers/media/video/gspca/Makefile2
-rw-r--r--drivers/media/video/gspca/cpia1.c6
-rw-r--r--drivers/media/video/gspca/ov519.c90
-rw-r--r--drivers/media/video/gspca/ov534.c980
-rw-r--r--drivers/media/video/gspca/sn9c20x.c40
-rw-r--r--drivers/media/video/gspca/sonixj.c353
-rw-r--r--drivers/media/video/gspca/vicam.c381
-rw-r--r--drivers/media/video/gspca/zc3xx.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c7
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c52
-rw-r--r--drivers/media/video/mem2mem_testdev.c227
-rw-r--r--drivers/media/video/mx3_camera.c415
-rw-r--r--drivers/media/video/noon010pc30.c792
-rw-r--r--drivers/media/video/omap1_camera.c66
-rw-r--r--drivers/media/video/omap24xxcam.c1
-rw-r--r--drivers/media/video/ov9740.c1009
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c60
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c9
-rw-r--r--drivers/media/video/pwc/pwc-if.c38
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c1032
-rw-r--r--drivers/media/video/pwc/pwc.h3
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c550
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c872
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h134
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c199
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h29
-rw-r--r--drivers/media/video/saa7110.c115
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c43
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c35
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c4
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c1
-rw-r--r--drivers/media/video/saa7134/saa7134.h1
-rw-r--r--drivers/media/video/saa7164/saa7164-api.c10
-rw-r--r--drivers/media/video/saa7164/saa7164-buffer.c16
-rw-r--r--drivers/media/video/saa7164/saa7164-bus.c8
-rw-r--r--drivers/media/video/saa7164/saa7164-cmd.c10
-rw-r--r--drivers/media/video/saa7164/saa7164-core.c8
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c4
-rw-r--r--drivers/media/video/saa7164/saa7164-encoder.c8
-rw-r--r--drivers/media/video/saa7164/saa7164-fw.c2
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c8
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c276
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c6
-rw-r--r--drivers/media/video/soc_camera.c131
-rw-r--r--drivers/media/video/soc_mediabus.c14
-rw-r--r--drivers/media/video/tlv320aic23b.c74
-rw-r--r--drivers/media/video/tuner-core.c1205
-rw-r--r--drivers/media/video/tvp514x.c236
-rw-r--r--drivers/media/video/tvp5150.c199
-rw-r--r--drivers/media/video/tvp7002.c117
-rw-r--r--drivers/media/video/v4l2-common.c1
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c244
-rw-r--r--drivers/media/video/v4l2-ctrls.c2
-rw-r--r--drivers/media/video/v4l2-ioctl.c493
-rw-r--r--drivers/media/video/v4l2-mem2mem.c232
-rw-r--r--drivers/media/video/via-camera.c64
-rw-r--r--drivers/media/video/videobuf2-core.c1804
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c185
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c292
-rw-r--r--drivers/media/video/videobuf2-memops.c235
-rw-r--r--drivers/media/video/videobuf2-vmalloc.c132
-rw-r--r--drivers/media/video/vivi.c585
-rw-r--r--drivers/media/video/vpx3220.c137
-rw-r--r--drivers/media/video/wm8775.c126
-rw-r--r--drivers/mfd/Kconfig2
-rw-r--r--drivers/mfd/wl1273-core.c149
-rw-r--r--drivers/staging/Kconfig8
-rw-r--r--drivers/staging/Makefile5
-rw-r--r--drivers/staging/altera-stapl/Kconfig8
-rw-r--r--drivers/staging/altera-stapl/Makefile3
-rw-r--r--drivers/staging/altera-stapl/altera-comp.c142
-rw-r--r--drivers/staging/altera-stapl/altera-exprt.h33
-rw-r--r--drivers/staging/altera-stapl/altera-jtag.c1020
-rw-r--r--drivers/staging/altera-stapl/altera-jtag.h113
-rw-r--r--drivers/staging/altera-stapl/altera-lpt.c70
-rw-r--r--drivers/staging/altera-stapl/altera.c2527
-rw-r--r--drivers/staging/cx25821/Kconfig1
-rw-r--r--drivers/staging/cx25821/cx25821-alsa.c2
-rw-r--r--drivers/staging/cx25821/cx25821-core.c16
-rw-r--r--drivers/staging/cx25821/cx25821-video.c9
-rw-r--r--drivers/staging/cx25821/cx25821.h3
-rw-r--r--drivers/staging/cxd2099/Kconfig11
-rw-r--r--drivers/staging/cxd2099/Makefile5
-rw-r--r--drivers/staging/cxd2099/TODO12
-rw-r--r--drivers/staging/cxd2099/cxd2099.c574
-rw-r--r--drivers/staging/cxd2099/cxd2099.h41
-rw-r--r--drivers/staging/dabusb/Kconfig14
-rw-r--r--drivers/staging/dabusb/Makefile2
-rw-r--r--drivers/staging/dabusb/TODO5
-rw-r--r--drivers/staging/dabusb/dabusb.c914
-rw-r--r--drivers/staging/dabusb/dabusb.h85
-rw-r--r--drivers/staging/easycap/easycap_ioctl.c5
-rw-r--r--drivers/staging/se401/Kconfig13
-rw-r--r--drivers/staging/se401/Makefile1
-rw-r--r--drivers/staging/se401/TODO5
-rw-r--r--drivers/staging/se401/se401.c1492
-rw-r--r--drivers/staging/se401/se401.h236
-rw-r--r--drivers/staging/se401/videodev.h318
-rw-r--r--drivers/staging/tm6000/tm6000-alsa.c13
-rw-r--r--drivers/staging/tm6000/tm6000-cards.c72
-rw-r--r--drivers/staging/tm6000/tm6000-core.c298
-rw-r--r--drivers/staging/tm6000/tm6000-regs.h63
-rw-r--r--drivers/staging/tm6000/tm6000-stds.c33
-rw-r--r--drivers/staging/tm6000/tm6000-video.c327
-rw-r--r--drivers/staging/tm6000/tm6000.h23
-rw-r--r--drivers/staging/usbvideo/Kconfig15
-rw-r--r--drivers/staging/usbvideo/Makefile2
-rw-r--r--drivers/staging/usbvideo/TODO5
-rw-r--r--drivers/staging/usbvideo/usbvideo.c2230
-rw-r--r--drivers/staging/usbvideo/usbvideo.h395
-rw-r--r--drivers/staging/usbvideo/vicam.c952
-rw-r--r--drivers/staging/usbvideo/videodev.h318
-rw-r--r--drivers/video/matrox/matroxfb_base.c3
-rw-r--r--include/linux/mfd/wl1273-core.h2
-rw-r--r--include/linux/videodev2.h141
-rw-r--r--include/media/noon010pc30.h28
-rw-r--r--include/media/rc-map.h1
-rw-r--r--include/media/s5p_fimc.h (renamed from include/media/s3c_fimc.h)20
-rw-r--r--include/media/soc_camera.h22
-rw-r--r--include/media/soc_mediabus.h1
-rw-r--r--include/media/tuner.h16
-rw-r--r--include/media/v4l2-chip-ident.h4
-rw-r--r--include/media/v4l2-ioctl.h16
-rw-r--r--include/media/v4l2-mem2mem.h56
-rw-r--r--include/media/v4l2-subdev.h2
-rw-r--r--include/media/videobuf2-core.h380
-rw-r--r--include/media/videobuf2-dma-contig.h29
-rw-r--r--include/media/videobuf2-dma-sg.h32
-rw-r--r--include/media/videobuf2-memops.h45
-rw-r--r--include/media/videobuf2-vmalloc.h20
-rw-r--r--include/media/wm8775.h9
-rw-r--r--include/staging/altera.h49
-rw-r--r--sound/soc/codecs/Kconfig2
-rw-r--r--sound/soc/codecs/wl1273.c11
279 files changed, 41726 insertions, 15507 deletions
diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl
index be34dcbe0d90..d2f99e5a3a2f 100644
--- a/Documentation/DocBook/media-entities.tmpl
+++ b/Documentation/DocBook/media-entities.tmpl
@@ -129,6 +129,7 @@
<!ENTITY v4l2-audioout "struct&nbsp;<link linkend='v4l2-audioout'>v4l2_audioout</link>">
<!ENTITY v4l2-bt-timings "struct&nbsp;<link linkend='v4l2-bt-timings'>v4l2_bt_timings</link>">
<!ENTITY v4l2-buffer "struct&nbsp;<link linkend='v4l2-buffer'>v4l2_buffer</link>">
+<!ENTITY v4l2-plane "struct&nbsp;<link linkend='v4l2-plane'>v4l2_plane</link>">
<!ENTITY v4l2-capability "struct&nbsp;<link linkend='v4l2-capability'>v4l2_capability</link>">
<!ENTITY v4l2-captureparm "struct&nbsp;<link linkend='v4l2-captureparm'>v4l2_captureparm</link>">
<!ENTITY v4l2-clip "struct&nbsp;<link linkend='v4l2-clip'>v4l2_clip</link>">
@@ -167,6 +168,8 @@
<!ENTITY v4l2-output "struct&nbsp;<link linkend='v4l2-output'>v4l2_output</link>">
<!ENTITY v4l2-outputparm "struct&nbsp;<link linkend='v4l2-outputparm'>v4l2_outputparm</link>">
<!ENTITY v4l2-pix-format "struct&nbsp;<link linkend='v4l2-pix-format'>v4l2_pix_format</link>">
+<!ENTITY v4l2-pix-format-mplane "struct&nbsp;<link linkend='v4l2-pix-format-mplane'>v4l2_pix_format_mplane</link>">
+<!ENTITY v4l2-plane-pix-format "struct&nbsp;<link linkend='v4l2-plane-pix-format'>v4l2_plane_pix_format</link>">
<!ENTITY v4l2-queryctrl "struct&nbsp;<link linkend='v4l2-queryctrl'>v4l2_queryctrl</link>">
<!ENTITY v4l2-querymenu "struct&nbsp;<link linkend='v4l2-querymenu'>v4l2_querymenu</link>">
<!ENTITY v4l2-rect "struct&nbsp;<link linkend='v4l2-rect'>v4l2_rect</link>">
@@ -202,6 +205,7 @@
<!-- Subsections -->
<!ENTITY sub-biblio SYSTEM "v4l/biblio.xml">
<!ENTITY sub-common SYSTEM "v4l/common.xml">
+<!ENTITY sub-planar-apis SYSTEM "v4l/planar-apis.xml">
<!ENTITY sub-compat SYSTEM "v4l/compat.xml">
<!ENTITY sub-controls SYSTEM "v4l/controls.xml">
<!ENTITY sub-dev-capture SYSTEM "v4l/dev-capture.xml">
@@ -233,6 +237,7 @@
<!ENTITY sub-io SYSTEM "v4l/io.xml">
<!ENTITY sub-grey SYSTEM "v4l/pixfmt-grey.xml">
<!ENTITY sub-nv12 SYSTEM "v4l/pixfmt-nv12.xml">
+<!ENTITY sub-nv12m SYSTEM "v4l/pixfmt-nv12m.xml">
<!ENTITY sub-nv16 SYSTEM "v4l/pixfmt-nv16.xml">
<!ENTITY sub-packed-rgb SYSTEM "v4l/pixfmt-packed-rgb.xml">
<!ENTITY sub-packed-yuv SYSTEM "v4l/pixfmt-packed-yuv.xml">
@@ -247,6 +252,7 @@
<!ENTITY sub-yuv410 SYSTEM "v4l/pixfmt-yuv410.xml">
<!ENTITY sub-yuv411p SYSTEM "v4l/pixfmt-yuv411p.xml">
<!ENTITY sub-yuv420 SYSTEM "v4l/pixfmt-yuv420.xml">
+<!ENTITY sub-yuv420m SYSTEM "v4l/pixfmt-yuv420m.xml">
<!ENTITY sub-yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
<!ENTITY sub-yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
<!ENTITY sub-yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
@@ -333,6 +339,7 @@
<!ENTITY write SYSTEM "v4l/func-write.xml">
<!ENTITY grey SYSTEM "v4l/pixfmt-grey.xml">
<!ENTITY nv12 SYSTEM "v4l/pixfmt-nv12.xml">
+<!ENTITY nv12m SYSTEM "v4l/pixfmt-nv12m.xml">
<!ENTITY nv16 SYSTEM "v4l/pixfmt-nv16.xml">
<!ENTITY packed-rgb SYSTEM "v4l/pixfmt-packed-rgb.xml">
<!ENTITY packed-yuv SYSTEM "v4l/pixfmt-packed-yuv.xml">
@@ -347,6 +354,7 @@
<!ENTITY yuv410 SYSTEM "v4l/pixfmt-yuv410.xml">
<!ENTITY yuv411p SYSTEM "v4l/pixfmt-yuv411p.xml">
<!ENTITY yuv420 SYSTEM "v4l/pixfmt-yuv420.xml">
+<!ENTITY yuv420m SYSTEM "v4l/pixfmt-yuv420m.xml">
<!ENTITY yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
<!ENTITY yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
<!ENTITY yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
diff --git a/Documentation/DocBook/v4l/common.xml b/Documentation/DocBook/v4l/common.xml
index cea23e1c4fc6..dbab79c215c1 100644
--- a/Documentation/DocBook/v4l/common.xml
+++ b/Documentation/DocBook/v4l/common.xml
@@ -846,6 +846,8 @@ conversion routine or library for integration into applications.</para>
</section>
</section>
+ &sub-planar-apis;
+
<section id="crop">
<title>Image Cropping, Insertion and Scaling</title>
diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml
index c9ce61d981f5..4d74bf24e114 100644
--- a/Documentation/DocBook/v4l/compat.xml
+++ b/Documentation/DocBook/v4l/compat.xml
@@ -1711,8 +1711,8 @@ ioctl would enumerate the available audio inputs. An ioctl to
determine the current audio input, if more than one combines with the
current video input, did not exist. So
<constant>VIDIOC_G_AUDIO</constant> was renamed to
-<constant>VIDIOC_G_AUDIO_OLD</constant>, this ioctl will be removed in
-the future. The &VIDIOC-ENUMAUDIO; ioctl was added to enumerate
+<constant>VIDIOC_G_AUDIO_OLD</constant>, this ioctl was removed on
+Kernel 2.6.39. The &VIDIOC-ENUMAUDIO; ioctl was added to enumerate
audio inputs, while &VIDIOC-G-AUDIO; now reports the current audio
input.</para>
<para>The same changes were made to &VIDIOC-G-AUDOUT; and
@@ -1726,7 +1726,7 @@ must be updated to successfully compile again.</para>
<para>The &VIDIOC-OVERLAY; ioctl was incorrectly defined with
write-read parameter. It was changed to write-only, while the write-read
version was renamed to <constant>VIDIOC_OVERLAY_OLD</constant>. The old
-ioctl will be removed in the future. Until further the "videodev"
+ioctl was removed on Kernel 2.6.39. Until further the "videodev"
kernel module will automatically translate to the new version, so drivers
must be recompiled, but not applications.</para>
</listitem>
@@ -1744,7 +1744,7 @@ surface can be seen.</para>
defined with write-only parameter, inconsistent with other ioctls
modifying their argument. They were changed to write-read, while a
<constant>_OLD</constant> suffix was added to the write-only versions.
-The old ioctls will be removed in the future. Drivers and
+The old ioctls were removed on Kernel 2.6.39. Drivers and
applications assuming a constant parameter need an update.</para>
</listitem>
</orderedlist>
@@ -1815,8 +1815,8 @@ yet to be addressed, for details see <xref
<para>The &VIDIOC-CROPCAP; ioctl was incorrectly defined
with read-only parameter. It is now defined as write-read ioctl, while
the read-only version was renamed to
-<constant>VIDIOC_CROPCAP_OLD</constant>. The old ioctl will be removed
-in the future.</para>
+<constant>VIDIOC_CROPCAP_OLD</constant>. The old ioctl was removed
+on Kernel 2.6.39.</para>
</listitem>
</orderedlist>
</section>
@@ -2353,6 +2353,25 @@ that used it. It was originally scheduled for removal in 2.6.35.
</listitem>
</orderedlist>
</section>
+ <section>
+ <title>V4L2 in Linux 2.6.38</title>
+ <orderedlist>
+ <listitem>
+ <para>Multi-planar API added. Does not affect the compatibility of
+ current drivers and applications. See
+ <link linkend="planar-apis">multi-planar API</link>
+ for details.</para>
+ </listitem>
+ </orderedlist>
+ </section>
+ <section>
+ <title>V4L2 in Linux 2.6.39</title>
+ <orderedlist>
+ <listitem>
+ <para>The old VIDIOC_*_OLD symbols and V4L1 support were removed.</para>
+ </listitem>
+ </orderedlist>
+ </section>
<section id="other">
<title>Relation of V4L2 to other Linux multimedia APIs</title>
diff --git a/Documentation/DocBook/v4l/dev-capture.xml b/Documentation/DocBook/v4l/dev-capture.xml
index 32807e43f170..2237c661f26a 100644
--- a/Documentation/DocBook/v4l/dev-capture.xml
+++ b/Documentation/DocBook/v4l/dev-capture.xml
@@ -18,7 +18,8 @@ files are used for video output devices.</para>
<title>Querying Capabilities</title>
<para>Devices supporting the video capture interface set the
-<constant>V4L2_CAP_VIDEO_CAPTURE</constant> flag in the
+<constant>V4L2_CAP_VIDEO_CAPTURE</constant> or
+<constant>V4L2_CAP_VIDEO_CAPTURE_MPLANE</constant> flag in the
<structfield>capabilities</structfield> field of &v4l2-capability;
returned by the &VIDIOC-QUERYCAP; ioctl. As secondary device functions
they may also support the <link linkend="overlay">video overlay</link>
@@ -64,9 +65,11 @@ linkend="crop" />.</para>
<para>To query the current image format applications set the
<structfield>type</structfield> field of a &v4l2-format; to
-<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant> and call the
+<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant> or
+<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE</constant> and call the
&VIDIOC-G-FMT; ioctl with a pointer to this structure. Drivers fill
-the &v4l2-pix-format; <structfield>pix</structfield> member of the
+the &v4l2-pix-format; <structfield>pix</structfield> or the
+&v4l2-pix-format-mplane; <structfield>pix_mp</structfield> member of the
<structfield>fmt</structfield> union.</para>
<para>To request different parameters applications set the
@@ -84,8 +87,8 @@ adjust the parameters and finally return the actual parameters as
without disabling I/O or possibly time consuming hardware
preparations.</para>
- <para>The contents of &v4l2-pix-format; are discussed in <xref
-linkend="pixfmt" />. See also the specification of the
+ <para>The contents of &v4l2-pix-format; and &v4l2-pix-format-mplane;
+are discussed in <xref linkend="pixfmt" />. See also the specification of the
<constant>VIDIOC_G_FMT</constant>, <constant>VIDIOC_S_FMT</constant>
and <constant>VIDIOC_TRY_FMT</constant> ioctls for details. Video
capture devices must implement both the
diff --git a/Documentation/DocBook/v4l/dev-output.xml b/Documentation/DocBook/v4l/dev-output.xml
index 63c3c20e5a72..919e22c53854 100644
--- a/Documentation/DocBook/v4l/dev-output.xml
+++ b/Documentation/DocBook/v4l/dev-output.xml
@@ -17,7 +17,8 @@ files are used for video capture devices.</para>
<title>Querying Capabilities</title>
<para>Devices supporting the video output interface set the
-<constant>V4L2_CAP_VIDEO_OUTPUT</constant> flag in the
+<constant>V4L2_CAP_VIDEO_OUTPUT</constant> or
+<constant>V4L2_CAP_VIDEO_OUTPUT_MPLANE</constant> flag in the
<structfield>capabilities</structfield> field of &v4l2-capability;
returned by the &VIDIOC-QUERYCAP; ioctl. As secondary device functions
they may also support the <link linkend="raw-vbi">raw VBI
@@ -60,9 +61,11 @@ linkend="crop" />.</para>
<para>To query the current image format applications set the
<structfield>type</structfield> field of a &v4l2-format; to
-<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant> and call the
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant> or
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant> and call the
&VIDIOC-G-FMT; ioctl with a pointer to this structure. Drivers fill
-the &v4l2-pix-format; <structfield>pix</structfield> member of the
+the &v4l2-pix-format; <structfield>pix</structfield> or the
+&v4l2-pix-format-mplane; <structfield>pix_mp</structfield> member of the
<structfield>fmt</structfield> union.</para>
<para>To request different parameters applications set the
@@ -80,8 +83,8 @@ adjust the parameters and finally return the actual parameters as
without disabling I/O or possibly time consuming hardware
preparations.</para>
- <para>The contents of &v4l2-pix-format; are discussed in <xref
-linkend="pixfmt" />. See also the specification of the
+ <para>The contents of &v4l2-pix-format; and &v4l2-pix-format-mplane;
+are discussed in <xref linkend="pixfmt" />. See also the specification of the
<constant>VIDIOC_G_FMT</constant>, <constant>VIDIOC_S_FMT</constant>
and <constant>VIDIOC_TRY_FMT</constant> ioctls for details. Video
output devices must implement both the
diff --git a/Documentation/DocBook/v4l/func-mmap.xml b/Documentation/DocBook/v4l/func-mmap.xml
index 2e2fc3933aea..786732b64bbd 100644
--- a/Documentation/DocBook/v4l/func-mmap.xml
+++ b/Documentation/DocBook/v4l/func-mmap.xml
@@ -45,7 +45,10 @@ just specify a <constant>NULL</constant> pointer here.</para>
<listitem>
<para>Length of the memory area to map. This must be the
same value as returned by the driver in the &v4l2-buffer;
-<structfield>length</structfield> field.</para>
+<structfield>length</structfield> field for the
+single-planar API, and the same value as returned by the driver
+in the &v4l2-plane; <structfield>length</structfield> field for the
+multi-planar API.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -106,7 +109,10 @@ flag.</para>
<listitem>
<para>Offset of the buffer in device memory. This must be the
same value as returned by the driver in the &v4l2-buffer;
-<structfield>m</structfield> union <structfield>offset</structfield> field.</para>
+<structfield>m</structfield> union <structfield>offset</structfield> field for
+the single-planar API, and the same value as returned by the driver
+in the &v4l2-plane; <structfield>m</structfield> union
+<structfield>mem_offset</structfield> field for the multi-planar API.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/Documentation/DocBook/v4l/func-munmap.xml b/Documentation/DocBook/v4l/func-munmap.xml
index 502ed49323b0..e2c4190f9bb6 100644
--- a/Documentation/DocBook/v4l/func-munmap.xml
+++ b/Documentation/DocBook/v4l/func-munmap.xml
@@ -37,7 +37,8 @@
<para>Length of the mapped buffer. This must be the same
value as given to <function>mmap()</function> and returned by the
driver in the &v4l2-buffer; <structfield>length</structfield>
-field.</para>
+field for the single-planar API and in the &v4l2-plane;
+<structfield>length</structfield> field for the multi-planar API.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/Documentation/DocBook/v4l/io.xml b/Documentation/DocBook/v4l/io.xml
index d424886beda0..227e7ac45a06 100644
--- a/Documentation/DocBook/v4l/io.xml
+++ b/Documentation/DocBook/v4l/io.xml
@@ -121,18 +121,22 @@ mapped.</para>
<para>Before applications can access the buffers they must map
them into their address space with the &func-mmap; function. The
location of the buffers in device memory can be determined with the
-&VIDIOC-QUERYBUF; ioctl. The <structfield>m.offset</structfield> and
-<structfield>length</structfield> returned in a &v4l2-buffer; are
-passed as sixth and second parameter to the
-<function>mmap()</function> function. The offset and length values
-must not be modified. Remember the buffers are allocated in physical
-memory, as opposed to virtual memory which can be swapped out to disk.
-Applications should free the buffers as soon as possible with the
-&func-munmap; function.</para>
+&VIDIOC-QUERYBUF; ioctl. In the single-planar API case, the
+<structfield>m.offset</structfield> and <structfield>length</structfield>
+returned in a &v4l2-buffer; are passed as sixth and second parameter to the
+<function>mmap()</function> function. When using the multi-planar API,
+struct &v4l2-buffer; contains an array of &v4l2-plane; structures, each
+containing its own <structfield>m.offset</structfield> and
+<structfield>length</structfield>. When using the multi-planar API, every
+plane of every buffer has to be mapped separately, so the number of
+calls to &func-mmap; should be equal to number of buffers times number of
+planes in each buffer. The offset and length values must not be modified.
+Remember, the buffers are allocated in physical memory, as opposed to virtual
+memory, which can be swapped out to disk. Applications should free the buffers
+as soon as possible with the &func-munmap; function.</para>
<example>
- <title>Mapping buffers</title>
-
+ <title>Mapping buffers in the single-planar API</title>
<programlisting>
&v4l2-requestbuffers; reqbuf;
struct {
@@ -141,63 +145,145 @@ struct {
} *buffers;
unsigned int i;
-memset (&amp;reqbuf, 0, sizeof (reqbuf));
+memset(&amp;reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 20;
if (-1 == ioctl (fd, &VIDIOC-REQBUFS;, &amp;reqbuf)) {
if (errno == EINVAL)
- printf ("Video capturing or mmap-streaming is not supported\n");
+ printf("Video capturing or mmap-streaming is not supported\n");
else
- perror ("VIDIOC_REQBUFS");
+ perror("VIDIOC_REQBUFS");
- exit (EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
/* We want at least five buffers. */
if (reqbuf.count &lt; 5) {
/* You may need to free the buffers here. */
- printf ("Not enough buffer memory\n");
- exit (EXIT_FAILURE);
+ printf("Not enough buffer memory\n");
+ exit(EXIT_FAILURE);
}
-buffers = calloc (reqbuf.count, sizeof (*buffers));
-assert (buffers != NULL);
+buffers = calloc(reqbuf.count, sizeof(*buffers));
+assert(buffers != NULL);
for (i = 0; i &lt; reqbuf.count; i++) {
&v4l2-buffer; buffer;
- memset (&amp;buffer, 0, sizeof (buffer));
+ memset(&amp;buffer, 0, sizeof(buffer));
buffer.type = reqbuf.type;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
if (-1 == ioctl (fd, &VIDIOC-QUERYBUF;, &amp;buffer)) {
- perror ("VIDIOC_QUERYBUF");
- exit (EXIT_FAILURE);
+ perror("VIDIOC_QUERYBUF");
+ exit(EXIT_FAILURE);
}
buffers[i].length = buffer.length; /* remember for munmap() */
- buffers[i].start = mmap (NULL, buffer.length,
- PROT_READ | PROT_WRITE, /* recommended */
- MAP_SHARED, /* recommended */
- fd, buffer.m.offset);
+ buffers[i].start = mmap(NULL, buffer.length,
+ PROT_READ | PROT_WRITE, /* recommended */
+ MAP_SHARED, /* recommended */
+ fd, buffer.m.offset);
if (MAP_FAILED == buffers[i].start) {
/* If you do not exit here you should unmap() and free()
the buffers mapped so far. */
- perror ("mmap");
- exit (EXIT_FAILURE);
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* Cleanup. */
+
+for (i = 0; i &lt; reqbuf.count; i++)
+ munmap(buffers[i].start, buffers[i].length);
+ </programlisting>
+ </example>
+
+ <example>
+ <title>Mapping buffers in the multi-planar API</title>
+ <programlisting>
+&v4l2-requestbuffers; reqbuf;
+/* Our current format uses 3 planes per buffer */
+#define FMT_NUM_PLANES = 3;
+
+struct {
+ void *start[FMT_NUM_PLANES];
+ size_t length[FMT_NUM_PLANES];
+} *buffers;
+unsigned int i, j;
+
+memset(&amp;reqbuf, 0, sizeof(reqbuf));
+reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+reqbuf.memory = V4L2_MEMORY_MMAP;
+reqbuf.count = 20;
+
+if (ioctl(fd, &VIDIOC-REQBUFS;, &amp;reqbuf) &lt; 0) {
+ if (errno == EINVAL)
+ printf("Video capturing or mmap-streaming is not supported\n");
+ else
+ perror("VIDIOC_REQBUFS");
+
+ exit(EXIT_FAILURE);
+}
+
+/* We want at least five buffers. */
+
+if (reqbuf.count &lt; 5) {
+ /* You may need to free the buffers here. */
+ printf("Not enough buffer memory\n");
+ exit(EXIT_FAILURE);
+}
+
+buffers = calloc(reqbuf.count, sizeof(*buffers));
+assert(buffers != NULL);
+
+for (i = 0; i &lt; reqbuf.count; i++) {
+ &v4l2-buffer; buffer;
+ &v4l2-plane; planes[FMT_NUM_PLANES];
+
+ memset(&amp;buffer, 0, sizeof(buffer));
+ buffer.type = reqbuf.type;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ buffer.index = i;
+ /* length in struct v4l2_buffer in multi-planar API stores the size
+ * of planes array. */
+ buffer.length = FMT_NUM_PLANES;
+ buffer.m.planes = planes;
+
+ if (ioctl(fd, &VIDIOC-QUERYBUF;, &amp;buffer) &lt; 0) {
+ perror("VIDIOC_QUERYBUF");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Every plane has to be mapped separately */
+ for (j = 0; j &lt; FMT_NUM_PLANES; j++) {
+ buffers[i].length[j] = buffer.m.planes[j].length; /* remember for munmap() */
+
+ buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
+ PROT_READ | PROT_WRITE, /* recommended */
+ MAP_SHARED, /* recommended */
+ fd, buffer.m.planes[j].m.offset);
+
+ if (MAP_FAILED == buffers[i].start[j]) {
+ /* If you do not exit here you should unmap() and free()
+ the buffers and planes mapped so far. */
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
}
}
/* Cleanup. */
for (i = 0; i &lt; reqbuf.count; i++)
- munmap (buffers[i].start, buffers[i].length);
+ for (j = 0; j &lt; FMT_NUM_PLANES; j++)
+ munmap(buffers[i].start[j], buffers[i].length[j]);
</programlisting>
</example>
@@ -286,13 +372,13 @@ pointer method (not only memory mapping) is supported must be
determined by calling the &VIDIOC-REQBUFS; ioctl.</para>
<para>This I/O method combines advantages of the read/write and
-memory mapping methods. Buffers are allocated by the application
+memory mapping methods. Buffers (planes) are allocated by the application
itself, and can reside for example in virtual or shared memory. Only
pointers to data are exchanged, these pointers and meta-information
-are passed in &v4l2-buffer;. The driver must be switched
-into user pointer I/O mode by calling the &VIDIOC-REQBUFS; with the
-desired buffer type. No buffers are allocated beforehands,
-consequently they are not indexed and cannot be queried like mapped
+are passed in &v4l2-buffer; (or in &v4l2-plane; in the multi-planar API case).
+The driver must be switched into user pointer I/O mode by calling the
+&VIDIOC-REQBUFS; with the desired buffer type. No buffers (planes) are allocated
+beforehand, consequently they are not indexed and cannot be queried like mapped
buffers with the <constant>VIDIOC_QUERYBUF</constant> ioctl.</para>
<example>
@@ -316,7 +402,7 @@ if (ioctl (fd, &VIDIOC-REQBUFS;, &amp;reqbuf) == -1) {
</programlisting>
</example>
- <para>Buffer addresses and sizes are passed on the fly with the
+ <para>Buffer (plane) addresses and sizes are passed on the fly with the
&VIDIOC-QBUF; ioctl. Although buffers are commonly cycled,
applications can pass different addresses and sizes at each
<constant>VIDIOC_QBUF</constant> call. If required by the hardware the
@@ -396,11 +482,18 @@ rest should be evident.</para>
<title>Buffers</title>
<para>A buffer contains data exchanged by application and
-driver using one of the Streaming I/O methods. Only pointers to
-buffers are exchanged, the data itself is not copied. These pointers,
-together with meta-information like timestamps or field parity, are
-stored in a struct <structname>v4l2_buffer</structname>, argument to
-the &VIDIOC-QUERYBUF;, &VIDIOC-QBUF; and &VIDIOC-DQBUF; ioctl.</para>
+driver using one of the Streaming I/O methods. In the multi-planar API, the
+data is held in planes, while the buffer structure acts as a container
+for the planes. Only pointers to buffers (planes) are exchanged, the data
+itself is not copied. These pointers, together with meta-information like
+timestamps or field parity, are stored in a struct
+<structname>v4l2_buffer</structname>, argument to
+the &VIDIOC-QUERYBUF;, &VIDIOC-QBUF; and &VIDIOC-DQBUF; ioctl.
+In the multi-planar API, some plane-specific members of struct
+<structname>v4l2_buffer</structname>, such as pointers and sizes for each
+plane, are stored in struct <structname>v4l2_plane</structname> instead.
+In that case, struct <structname>v4l2_buffer</structname> contains an array of
+plane structures.</para>
<para>Nominally timestamps refer to the first data byte transmitted.
In practice however the wide range of hardware covered by the V4L2 API
@@ -551,26 +644,40 @@ in accordance with the selected I/O method.</entry>
<entry></entry>
<entry>__u32</entry>
<entry><structfield>offset</structfield></entry>
- <entry>When <structfield>memory</structfield> is
-<constant>V4L2_MEMORY_MMAP</constant> this is the offset of the buffer
-from the start of the device memory. The value is returned by the
-driver and apart of serving as parameter to the &func-mmap; function
-not useful for applications. See <xref linkend="mmap" /> for details.</entry>
+ <entry>For the single-planar API and when
+<structfield>memory</structfield> is <constant>V4L2_MEMORY_MMAP</constant> this
+is the offset of the buffer from the start of the device memory. The value is
+returned by the driver and apart of serving as parameter to the &func-mmap;
+function not useful for applications. See <xref linkend="mmap" /> for details
+ </entry>
</row>
<row>
<entry></entry>
<entry>unsigned long</entry>
<entry><structfield>userptr</structfield></entry>
- <entry>When <structfield>memory</structfield> is
-<constant>V4L2_MEMORY_USERPTR</constant> this is a pointer to the
-buffer (casted to unsigned long type) in virtual memory, set by the
-application. See <xref linkend="userp" /> for details.</entry>
+ <entry>For the single-planar API and when
+<structfield>memory</structfield> is <constant>V4L2_MEMORY_USERPTR</constant>
+this is a pointer to the buffer (casted to unsigned long type) in virtual
+memory, set by the application. See <xref linkend="userp" /> for details.
+ </entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry>struct v4l2_plane</entry>
+ <entry><structfield>*planes</structfield></entry>
+ <entry>When using the multi-planar API, contains a userspace pointer
+ to an array of &v4l2-plane;. The size of the array should be put
+ in the <structfield>length</structfield> field of this
+ <structname>v4l2_buffer</structname> structure.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>length</structfield></entry>
<entry></entry>
- <entry>Size of the buffer (not the payload) in bytes.</entry>
+ <entry>Size of the buffer (not the payload) in bytes for the
+ single-planar API. For the multi-planar API should contain the
+ number of elements in the <structfield>planes</structfield> array.
+ </entry>
</row>
<row>
<entry>__u32</entry>
@@ -596,6 +703,66 @@ should set this to 0.</entry>
</tgroup>
</table>
+ <table frame="none" pgwide="1" id="v4l2-plane">
+ <title>struct <structname>v4l2_plane</structname></title>
+ <tgroup cols="4">
+ &cs-ustr;
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>bytesused</structfield></entry>
+ <entry></entry>
+ <entry>The number of bytes occupied by data in the plane
+ (its payload).</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>length</structfield></entry>
+ <entry></entry>
+ <entry>Size in bytes of the plane (not its payload).</entry>
+ </row>
+ <row>
+ <entry>union</entry>
+ <entry><structfield>m</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry>__u32</entry>
+ <entry><structfield>mem_offset</structfield></entry>
+ <entry>When the memory type in the containing &v4l2-buffer; is
+ <constant>V4L2_MEMORY_MMAP</constant>, this is the value that
+ should be passed to &func-mmap;, similar to the
+ <structfield>offset</structfield> field in &v4l2-buffer;.</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry>__unsigned long</entry>
+ <entry><structfield>userptr</structfield></entry>
+ <entry>When the memory type in the containing &v4l2-buffer; is
+ <constant>V4L2_MEMORY_USERPTR</constant>, this is a userspace
+ pointer to the memory allocated for this plane by an application.
+ </entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>data_offset</structfield></entry>
+ <entry></entry>
+ <entry>Offset in bytes to video data in the plane, if applicable.
+ </entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved[11]</structfield></entry>
+ <entry></entry>
+ <entry>Reserved for future use. Should be zeroed by an
+ application.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<table frame="none" pgwide="1" id="v4l2-buf-type">
<title>enum v4l2_buf_type</title>
<tgroup cols="3">
@@ -604,13 +771,27 @@ should set this to 0.</entry>
<row>
<entry><constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant></entry>
<entry>1</entry>
- <entry>Buffer of a video capture stream, see <xref
+ <entry>Buffer of a single-planar video capture stream, see <xref
+ linkend="capture" />.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE</constant>
+ </entry>
+ <entry>9</entry>
+ <entry>Buffer of a multi-planar video capture stream, see <xref
linkend="capture" />.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant></entry>
<entry>2</entry>
- <entry>Buffer of a video output stream, see <xref
+ <entry>Buffer of a single-planar video output stream, see <xref
+ linkend="output" />.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant>
+ </entry>
+ <entry>10</entry>
+ <entry>Buffer of a multi-planar video output stream, see <xref
linkend="output" />.</entry>
</row>
<row>
diff --git a/Documentation/DocBook/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/v4l/pixfmt-nv12m.xml
new file mode 100644
index 000000000000..c9e166d9ded8
--- /dev/null
+++ b/Documentation/DocBook/v4l/pixfmt-nv12m.xml
@@ -0,0 +1,154 @@
+ <refentry id="V4L2-PIX-FMT-NV12M">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_NV12M ('NV12M')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname> <constant>V4L2_PIX_FMT_NV12M</constant></refname>
+ <refpurpose>Variation of <constant>V4L2_PIX_FMT_NV12</constant> with planes
+ non contiguous in memory. </refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a multi-planar, two-plane version of the YUV 4:2:0 format.
+The three components are separated into two sub-images or planes.
+<constant>V4L2_PIX_FMT_NV12M</constant> differs from <constant>V4L2_PIX_FMT_NV12
+</constant> in that the two planes are non-contiguous in memory, i.e. the chroma
+plane do not necessarily immediately follows the luma plane.
+The luminance data occupies the first plane. The Y plane has one byte per pixel.
+In the second plane there is a chrominance data with alternating chroma samples.
+The CbCr plane is the same width, in bytes, as the Y plane (and of the image),
+but is half as tall in pixels. Each CbCr pair belongs to four pixels. For example,
+Cb<subscript>0</subscript>/Cr<subscript>0</subscript> belongs to
+Y'<subscript>00</subscript>, Y'<subscript>01</subscript>,
+Y'<subscript>10</subscript>, Y'<subscript>11</subscript>. </para>
+
+ <para><constant>V4L2_PIX_FMT_NV12M</constant> is intended to be
+used only in drivers and applications that support the multi-planar API,
+described in <xref linkend="planar-apis"/>. </para>
+
+ <para>If the Y plane has pad bytes after each row, then the
+CbCr plane has as many pad bytes after its rows.</para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_NV12M</constant> 4 &times; 4 pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="5" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start0&nbsp;+&nbsp;0:</entry>
+ <entry>Y'<subscript>00</subscript></entry>
+ <entry>Y'<subscript>01</subscript></entry>
+ <entry>Y'<subscript>02</subscript></entry>
+ <entry>Y'<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;4:</entry>
+ <entry>Y'<subscript>10</subscript></entry>
+ <entry>Y'<subscript>11</subscript></entry>
+ <entry>Y'<subscript>12</subscript></entry>
+ <entry>Y'<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;8:</entry>
+ <entry>Y'<subscript>20</subscript></entry>
+ <entry>Y'<subscript>21</subscript></entry>
+ <entry>Y'<subscript>22</subscript></entry>
+ <entry>Y'<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;12:</entry>
+ <entry>Y'<subscript>30</subscript></entry>
+ <entry>Y'<subscript>31</subscript></entry>
+ <entry>Y'<subscript>32</subscript></entry>
+ <entry>Y'<subscript>33</subscript></entry>
+ </row>
+ <row>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>start1&nbsp;+&nbsp;0:</entry>
+ <entry>Cb<subscript>00</subscript></entry>
+ <entry>Cr<subscript>00</subscript></entry>
+ <entry>Cb<subscript>01</subscript></entry>
+ <entry>Cr<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start1&nbsp;+&nbsp;4:</entry>
+ <entry>Cb<subscript>10</subscript></entry>
+ <entry>Cr<subscript>10</subscript></entry>
+ <entry>Cb<subscript>11</subscript></entry>
+ <entry>Cr<subscript>11</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+
+ <formalpara>
+ <title>Color Sample Location.</title>
+ <para>
+ <informaltable frame="none">
+ <tgroup cols="7" align="center">
+ <tbody valign="top">
+ <row>
+ <entry></entry>
+ <entry>0</entry><entry></entry><entry>1</entry><entry></entry>
+ <entry>2</entry><entry></entry><entry>3</entry>
+ </row>
+ <row>
+ <entry>0</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry><entry>C</entry><entry></entry><entry></entry>
+ <entry></entry><entry>C</entry><entry></entry>
+ </row>
+ <row>
+ <entry>1</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>2</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry><entry>C</entry><entry></entry><entry></entry>
+ <entry></entry><entry>C</entry><entry></entry>
+ </row>
+ <row>
+ <entry>3</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+ </refentry>
+
+ <!--
+Local Variables:
+mode: sgml
+sgml-parent-document: "pixfmt.sgml"
+indent-tabs-mode: nil
+End:
+ -->
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv420m.xml b/Documentation/DocBook/v4l/pixfmt-yuv420m.xml
new file mode 100644
index 000000000000..f5d8f57495c8
--- /dev/null
+++ b/Documentation/DocBook/v4l/pixfmt-yuv420m.xml
@@ -0,0 +1,162 @@
+ <refentry id="V4L2-PIX-FMT-YUV420M">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_YUV420M ('YU12M')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname> <constant>V4L2_PIX_FMT_YUV420M</constant></refname>
+ <refpurpose>Variation of <constant>V4L2_PIX_FMT_YUV420</constant>
+ with planes non contiguous in memory. </refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a multi-planar format, as opposed to a packed format.
+The three components are separated into three sub- images or planes.
+
+The Y plane is first. The Y plane has one byte per pixel. The Cb data
+constitutes the second plane which is half the width and half
+the height of the Y plane (and of the image). Each Cb belongs to four
+pixels, a two-by-two square of the image. For example,
+Cb<subscript>0</subscript> belongs to Y'<subscript>00</subscript>,
+Y'<subscript>01</subscript>, Y'<subscript>10</subscript>, and
+Y'<subscript>11</subscript>. The Cr data, just like the Cb plane, is
+in the third plane. </para>
+
+ <para>If the Y plane has pad bytes after each row, then the Cb
+and Cr planes have half as many pad bytes after their rows. In other
+words, two Cx rows (including padding) is exactly as long as one Y row
+(including padding).</para>
+
+ <para><constant>V4L2_PIX_FMT_NV12M</constant> is intended to be
+used only in drivers and applications that support the multi-planar API,
+described in <xref linkend="planar-apis"/>. </para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_YVU420M</constant> 4 &times; 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="5" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start0&nbsp;+&nbsp;0:</entry>
+ <entry>Y'<subscript>00</subscript></entry>
+ <entry>Y'<subscript>01</subscript></entry>
+ <entry>Y'<subscript>02</subscript></entry>
+ <entry>Y'<subscript>03</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;4:</entry>
+ <entry>Y'<subscript>10</subscript></entry>
+ <entry>Y'<subscript>11</subscript></entry>
+ <entry>Y'<subscript>12</subscript></entry>
+ <entry>Y'<subscript>13</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;8:</entry>
+ <entry>Y'<subscript>20</subscript></entry>
+ <entry>Y'<subscript>21</subscript></entry>
+ <entry>Y'<subscript>22</subscript></entry>
+ <entry>Y'<subscript>23</subscript></entry>
+ </row>
+ <row>
+ <entry>start0&nbsp;+&nbsp;12:</entry>
+ <entry>Y'<subscript>30</subscript></entry>
+ <entry>Y'<subscript>31</subscript></entry>
+ <entry>Y'<subscript>32</subscript></entry>
+ <entry>Y'<subscript>33</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start1&nbsp;+&nbsp;0:</entry>
+ <entry>Cb<subscript>00</subscript></entry>
+ <entry>Cb<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start1&nbsp;+&nbsp;2:</entry>
+ <entry>Cb<subscript>10</subscript></entry>
+ <entry>Cb<subscript>11</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start2&nbsp;+&nbsp;0:</entry>
+ <entry>Cr<subscript>00</subscript></entry>
+ <entry>Cr<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start2&nbsp;+&nbsp;2:</entry>
+ <entry>Cr<subscript>10</subscript></entry>
+ <entry>Cr<subscript>11</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+
+ <formalpara>
+ <title>Color Sample Location.</title>
+ <para>
+ <informaltable frame="none">
+ <tgroup cols="7" align="center">
+ <tbody valign="top">
+ <row>
+ <entry></entry>
+ <entry>0</entry><entry></entry><entry>1</entry><entry></entry>
+ <entry>2</entry><entry></entry><entry>3</entry>
+ </row>
+ <row>
+ <entry>0</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry><entry>C</entry><entry></entry><entry></entry>
+ <entry></entry><entry>C</entry><entry></entry>
+ </row>
+ <row>
+ <entry>1</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>2</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry><entry>C</entry><entry></entry><entry></entry>
+ <entry></entry><entry>C</entry><entry></entry>
+ </row>
+ <row>
+ <entry>3</entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry><entry></entry>
+ <entry>Y</entry><entry></entry><entry>Y</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+ </refentry>
+
+ <!--
+Local Variables:
+mode: sgml
+sgml-parent-document: "pixfmt.sgml"
+indent-tabs-mode: nil
+End:
+ -->
diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml
index cfffc88d7383..f8436dcb7414 100644
--- a/Documentation/DocBook/v4l/pixfmt.xml
+++ b/Documentation/DocBook/v4l/pixfmt.xml
@@ -2,12 +2,16 @@
<para>The V4L2 API was primarily designed for devices exchanging
image data with applications. The
-<structname>v4l2_pix_format</structname> structure defines the format
-and layout of an image in memory. Image formats are negotiated with
-the &VIDIOC-S-FMT; ioctl. (The explanations here focus on video
+<structname>v4l2_pix_format</structname> and <structname>v4l2_pix_format_mplane
+</structname> structures define the format and layout of an image in memory.
+The former is used with the single-planar API, while the latter is used with the
+multi-planar version (see <xref linkend="planar-apis"/>). Image formats are
+negotiated with the &VIDIOC-S-FMT; ioctl. (The explanations here focus on video
capturing and output, for overlay frame buffer formats see also
&VIDIOC-G-FBUF;.)</para>
+<section>
+ <title>Single-planar format structure</title>
<table pgwide="1" frame="none" id="v4l2-pix-format">
<title>struct <structname>v4l2_pix_format</structname></title>
<tgroup cols="3">
@@ -106,6 +110,98 @@ set this field to zero.</entry>
</tbody>
</tgroup>
</table>
+</section>
+
+<section>
+ <title>Multi-planar format structures</title>
+ <para>The <structname>v4l2_plane_pix_format</structname> structures define
+ size and layout for each of the planes in a multi-planar format.
+ The <structname>v4l2_pix_format_mplane</structname> structure contains
+ information common to all planes (such as image width and height) and
+ an array of <structname>v4l2_plane_pix_format</structname> structures,
+ describing all planes of that format.</para>
+ <table pgwide="1" frame="none" id="v4l2-plane-pix-format">
+ <title>struct <structname>vl42_plane_pix_format</structname></title>
+ <tgroup cols="3">
+ &cs-str;
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>sizeimage</structfield></entry>
+ <entry>Maximum size in bytes required for image data in this plane.
+ </entry>
+ </row>
+ <row>
+ <entry>__u16</entry>
+ <entry><structfield>bytesperline</structfield></entry>
+ <entry>Distance in bytes between the leftmost pixels in two adjacent
+ lines.</entry>
+ </row>
+ <row>
+ <entry>__u16</entry>
+ <entry><structfield>reserved[7]</structfield></entry>
+ <entry>Reserved for future extensions. Should be zeroed by the
+ application.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table pgwide="1" frame="none" id="v4l2-pix-format-mplane">
+ <title>struct <structname>v4l2_pix_format_mplane</structname></title>
+ <tgroup cols="3">
+ &cs-str;
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>width</structfield></entry>
+ <entry>Image width in pixels.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>height</structfield></entry>
+ <entry>Image height in pixels.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>pixelformat</structfield></entry>
+ <entry>The pixel format. Both single- and multi-planar four character
+codes can be used.</entry>
+ </row>
+ <row>
+ <entry>&v4l2-field;</entry>
+ <entry><structfield>field</structfield></entry>
+ <entry>See &v4l2-pix-format;.</entry>
+ </row>
+ <row>
+ <entry>&v4l2-colorspace;</entry>
+ <entry><structfield>colorspace</structfield></entry>
+ <entry>See &v4l2-pix-format;.</entry>
+ </row>
+ <row>
+ <entry>&v4l2-plane-pix-format;</entry>
+ <entry><structfield>plane_fmt[VIDEO_MAX_PLANES]</structfield></entry>
+ <entry>An array of structures describing format of each plane this
+ pixel format consists of. The number of valid entries in this array
+ has to be put in the <structfield>num_planes</structfield>
+ field.</entry>
+ </row>
+ <row>
+ <entry>__u8</entry>
+ <entry><structfield>num_planes</structfield></entry>
+ <entry>Number of planes (i.e. separate memory buffers) for this format
+ and the number of valid entries in the
+ <structfield>plane_fmt</structfield> array.</entry>
+ </row>
+ <row>
+ <entry>__u8</entry>
+ <entry><structfield>reserved[11]</structfield></entry>
+ <entry>Reserved for future extensions. Should be zeroed by the
+ application.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+</section>
<section>
<title>Standard Image Formats</title>
@@ -142,11 +238,19 @@ leftmost pixel of the second row from the top, and so on. The last row
has just as many pad bytes after it as the other rows.</para>
<para>In V4L2 each format has an identifier which looks like
-<constant>PIX_FMT_XXX</constant>, defined in the <filename>videodev2.h</filename>
-header file. These identifiers
-represent <link linkend="v4l2-fourcc">four character codes</link>
+<constant>PIX_FMT_XXX</constant>, defined in the <link
+linkend="videodev">videodev.h</link> header file. These identifiers
+represent <link linkend="v4l2-fourcc">four character (FourCC) codes</link>
which are also listed below, however they are not the same as those
used in the Windows world.</para>
+
+ <para>For some formats, data is stored in separate, discontiguous
+memory buffers. Those formats are identified by a separate set of FourCC codes
+and are referred to as "multi-planar formats". For example, a YUV422 frame is
+normally stored in one memory buffer, but it can also be placed in two or three
+separate buffers, with Y component in one buffer and CbCr components in another
+in the 2-planar version or with each component in its own buffer in the
+3-planar case. Those sub-buffers are referred to as "planes".</para>
</section>
<section id="colorspaces">
@@ -599,10 +703,12 @@ information.</para>
&sub-vyuy;
&sub-y41p;
&sub-yuv420;
+ &sub-yuv420m;
&sub-yuv410;
&sub-yuv422p;
&sub-yuv411p;
&sub-nv12;
+ &sub-nv12m;
&sub-nv16;
</section>
diff --git a/Documentation/DocBook/v4l/planar-apis.xml b/Documentation/DocBook/v4l/planar-apis.xml
new file mode 100644
index 000000000000..878ce2040488
--- /dev/null
+++ b/Documentation/DocBook/v4l/planar-apis.xml
@@ -0,0 +1,62 @@
+<section id="planar-apis">
+ <title>Single- and multi-planar APIs</title>
+
+ <para>Some devices require data for each input or output video frame
+ to be placed in discontiguous memory buffers. In such cases, one
+ video frame has to be addressed using more than one memory address, i.e. one
+ pointer per "plane". A plane is a sub-buffer of the current frame. For
+ examples of such formats see <xref linkend="pixfmt" />.</para>
+
+ <para>Initially, V4L2 API did not support multi-planar buffers and a set of
+ extensions has been introduced to handle them. Those extensions constitute
+ what is being referred to as the "multi-planar API".</para>
+
+ <para>Some of the V4L2 API calls and structures are interpreted differently,
+ depending on whether single- or multi-planar API is being used. An application
+ can choose whether to use one or the other by passing a corresponding buffer
+ type to its ioctl calls. Multi-planar versions of buffer types are suffixed
+ with an `_MPLANE' string. For a list of available multi-planar buffer types
+ see &v4l2-buf-type;.
+ </para>
+
+ <section>
+ <title>Multi-planar formats</title>
+ <para>Multi-planar API introduces new multi-planar formats. Those formats
+ use a separate set of FourCC codes. It is important to distinguish between
+ the multi-planar API and a multi-planar format. Multi-planar API calls can
+ handle all single-planar formats as well (as long as they are passed in
+ multi-planar API structures), while the single-planar API cannot
+ handle multi-planar formats.</para>
+ </section>
+
+ <section>
+ <title>Calls that distinguish between single and multi-planar APIs</title>
+ <variablelist>
+ <varlistentry>
+ <term>&VIDIOC-QUERYCAP;</term>
+ <listitem><para>Two additional multi-planar capabilities are added. They can
+ be set together with non-multi-planar ones for devices that handle
+ both single- and multi-planar formats.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&VIDIOC-G-FMT;, &VIDIOC-S-FMT;, &VIDIOC-TRY-FMT;</term>
+ <listitem><para>New structures for describing multi-planar formats are added:
+ &v4l2-pix-format-mplane; and &v4l2-plane-pix-format;. Drivers may
+ define new multi-planar formats, which have distinct FourCC codes from
+ the existing single-planar ones.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&VIDIOC-QBUF;, &VIDIOC-DQBUF;, &VIDIOC-QUERYBUF;</term>
+ <listitem><para>A new &v4l2-plane; structure for describing planes is added.
+ Arrays of this structure are passed in the new
+ <structfield>m.planes</structfield> field of &v4l2-buffer;.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&VIDIOC-REQBUFS;</term>
+ <listitem><para>Will allocate multi-planar buffers as requested.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </section>
+</section>
diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml
index 9288af96de34..7859d7d9da39 100644
--- a/Documentation/DocBook/v4l/v4l2.xml
+++ b/Documentation/DocBook/v4l/v4l2.xml
@@ -85,6 +85,17 @@ Remote Controller chapter.</contrib>
</address>
</affiliation>
</author>
+
+ <author>
+ <firstname>Pawel</firstname>
+ <surname>Osciak</surname>
+ <contrib>Designed and documented the multi-planar API.</contrib>
+ <affiliation>
+ <address>
+ <email>pawel AT osciak.com</email>
+ </address>
+ </affiliation>
+ </author>
</authorgroup>
<copyright>
@@ -102,7 +113,8 @@ Remote Controller chapter.</contrib>
<year>2010</year>
<year>2011</year>
<holder>Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin
-Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab</holder>
+Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab,
+ Pawel Osciak</holder>
</copyright>
<legalnotice>
<para>Except when explicitly stated as GPL, programming examples within
@@ -116,6 +128,22 @@ structs, ioctls) must be noted in more detail in the history chapter
applications. -->
<revision>
+ <revnumber>2.6.39</revnumber>
+ <date>2011-03-01</date>
+ <authorinitials>mcc</authorinitials>
+ <revremark>Removed VIDIOC_*_OLD from videodev2.h header and update it to reflect latest changes.
+ </revremark>
+ </revision>
+
+ <revision>
+ <revnumber>2.6.38</revnumber>
+ <date>2011-01-16</date>
+ <authorinitials>po</authorinitials>
+ <revremark>Added the <link linkend="planar-apis">multi-planar API</link>.
+ </revremark>
+ </revision>
+
+ <revision>
<revnumber>2.6.37</revnumber>
<date>2010-08-06</date>
<authorinitials>hv</authorinitials>
diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml
index 325b23b6964c..2b796a2ee98a 100644
--- a/Documentation/DocBook/v4l/videodev2.h.xml
+++ b/Documentation/DocBook/v4l/videodev2.h.xml
@@ -71,6 +71,7 @@
* Moved from videodev.h
*/
#define VIDEO_MAX_FRAME 32
+#define VIDEO_MAX_PLANES 8
#ifndef __KERNEL__
@@ -158,9 +159,23 @@ enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> {
/* Experimental */
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
#endif
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
+#define V4L2_TYPE_IS_MULTIPLANAR(type) \
+ ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+
+#define V4L2_TYPE_IS_OUTPUT(type) \
+ ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \
+ || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \
+ || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+
enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link> {
V4L2_TUNER_RADIO = 1,
V4L2_TUNER_ANALOG_TV = 2,
@@ -246,6 +261,11 @@ struct <link linkend="v4l2-capability">v4l2_capability</link> {
#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */
#define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */
+/* Is a video capture device that supports multiplanar formats */
+#define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000
+/* Is a video output device that supports multiplanar formats */
+#define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000
+
#define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
#define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
#define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
@@ -320,6 +340,13 @@ struct <link linkend="v4l2-pix-format">v4l2_pix_format</link> {
#define <link linkend="V4L2-PIX-FMT-NV16">V4L2_PIX_FMT_NV16</link> v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define <link linkend="V4L2-PIX-FMT-NV61">V4L2_PIX_FMT_NV61</link> v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */
+/* two non contiguous planes - one Y, one Cr + Cb interleaved */
+#define <link linkend="V4L2-PIX-FMT-NV12M">V4L2_PIX_FMT_NV12M</link> v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */
+#define <link linkend="V4L2-PIX-FMT-NV12MT">V4L2_PIX_FMT_NV12MT</link> v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */
+
+/* three non contiguous planes - Y, Cb, Cr */
+#define <link linkend="V4L2-PIX-FMT-YUV420M">V4L2_PIX_FMT_YUV420M</link> v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */
+
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define <link linkend="V4L2-PIX-FMT-SBGGR8">V4L2_PIX_FMT_SBGGR8</link> v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define <link linkend="V4L2-PIX-FMT-SGBRG8">V4L2_PIX_FMT_SGBRG8</link> v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
@@ -518,6 +545,62 @@ struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> {
__u32 reserved[2];
};
+/**
+ * struct <link linkend="v4l2-plane">v4l2_plane</link> - plane info for multi-planar buffers
+ * @bytesused: number of bytes occupied by data in the plane (payload)
+ * @length: size of this plane (NOT the payload) in bytes
+ * @mem_offset: when memory in the associated struct <link linkend="v4l2-buffer">v4l2_buffer</link> is
+ * V4L2_MEMORY_MMAP, equals the offset from the start of
+ * the device memory for this plane (or is a "cookie" that
+ * should be passed to mmap() called on the video node)
+ * @userptr: when memory is V4L2_MEMORY_USERPTR, a userspace pointer
+ * pointing to this plane
+ * @data_offset: offset in the plane to the start of data; usually 0,
+ * unless there is a header in front of the data
+ *
+ * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
+ * with two planes can have one plane for Y, and another for interleaved CbCr
+ * components. Each plane can reside in a separate memory buffer, or even in
+ * a completely separate memory node (e.g. in embedded devices).
+ */
+struct <link linkend="v4l2-plane">v4l2_plane</link> {
+ __u32 bytesused;
+ __u32 length;
+ union {
+ __u32 mem_offset;
+ unsigned long userptr;
+ } m;
+ __u32 data_offset;
+ __u32 reserved[11];
+};
+
+/**
+ * struct <link linkend="v4l2-buffer">v4l2_buffer</link> - video buffer info
+ * @index: id number of the buffer
+ * @type: buffer type (type == *_MPLANE for multiplanar buffers)
+ * @bytesused: number of bytes occupied by data in the buffer (payload);
+ * unused (set to 0) for multiplanar buffers
+ * @flags: buffer informational flags
+ * @field: field order of the image in the buffer
+ * @timestamp: frame timestamp
+ * @timecode: frame timecode
+ * @sequence: sequence count of this frame
+ * @memory: the method, in which the actual video data is passed
+ * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
+ * offset from the start of the device memory for this plane,
+ * (or a "cookie" that should be passed to mmap() as offset)
+ * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
+ * a userspace pointer pointing to this buffer
+ * @planes: for multiplanar buffers; userspace pointer to the array of plane
+ * info structs for this buffer
+ * @length: size in bytes of the buffer (NOT its payload) for single-plane
+ * buffers (when type != *_MPLANE); number of elements in the
+ * planes array for multi-plane buffers
+ * @input: input number from which the video data has has been captured
+ *
+ * Contains data exchanged by application and driver using one of the Streaming
+ * I/O methods.
+ */
struct <link linkend="v4l2-buffer">v4l2_buffer</link> {
__u32 index;
enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
@@ -533,6 +616,7 @@ struct <link linkend="v4l2-buffer">v4l2_buffer</link> {
union {
__u32 offset;
unsigned long userptr;
+ struct <link linkend="v4l2-plane">v4l2_plane</link> *planes;
} m;
__u32 length;
__u32 input;
@@ -1623,12 +1707,56 @@ struct <link linkend="v4l2-mpeg-vbi-fmt-ivtv">v4l2_mpeg_vbi_fmt_ivtv</link> {
* A G G R E G A T E S T R U C T U R E S
*/
-/* Stream data format
+/**
+ * struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link> - additional, per-plane format definition
+ * @sizeimage: maximum size in bytes required for data, for which
+ * this plane will be used
+ * @bytesperline: distance in bytes between the leftmost pixels in two
+ * adjacent lines
+ */
+struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link> {
+ __u32 sizeimage;
+ __u16 bytesperline;
+ __u16 reserved[7];
+} __attribute__ ((packed));
+
+/**
+ * struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link> - multiplanar format definition
+ * @width: image width in pixels
+ * @height: image height in pixels
+ * @pixelformat: little endian four character code (fourcc)
+ * @field: field order (for interlaced video)
+ * @colorspace: supplemental to pixelformat
+ * @plane_fmt: per-plane information
+ * @num_planes: number of planes for this format
+ */
+struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link> {
+ __u32 width;
+ __u32 height;
+ __u32 pixelformat;
+ enum <link linkend="v4l2-field">v4l2_field</link> field;
+ enum <link linkend="v4l2-colorspace">v4l2_colorspace</link> colorspace;
+
+ struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link> plane_fmt[VIDEO_MAX_PLANES];
+ __u8 num_planes;
+ __u8 reserved[11];
+} __attribute__ ((packed));
+
+/**
+ * struct <link linkend="v4l2-format">v4l2_format</link> - stream data format
+ * @type: type of the data stream
+ * @pix: definition of an image format
+ * @pix_mp: definition of a multiplanar image format
+ * @win: definition of an overlaid image
+ * @vbi: raw VBI capture or output parameters
+ * @sliced: sliced VBI capture or output parameters
+ * @raw_data: placeholder for future extensions and custom formats
*/
struct <link linkend="v4l2-format">v4l2_format</link> {
enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
union {
struct <link linkend="v4l2-pix-format">v4l2_pix_format</link> pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
+ struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link> pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
struct <link linkend="v4l2-window">v4l2_window</link> win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct <link linkend="v4l2-vbi-format">v4l2_vbi_format</link> vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct <link linkend="v4l2-sliced-vbi-format">v4l2_sliced_vbi_format</link> sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
@@ -1636,7 +1764,6 @@ struct <link linkend="v4l2-format">v4l2_format</link> {
} fmt;
};
-
/* Stream type-dependent parameters
*/
struct <link linkend="v4l2-streamparm">v4l2_streamparm</link> {
@@ -1809,16 +1936,6 @@ struct <link linkend="v4l2-dbg-chip-ident">v4l2_dbg_chip_ident</link> {
/* Reminder: when adding new ioctls please add support for them to
drivers/media/video/v4l2-compat-ioctl32.c as well! */
-#ifdef __OLD_VIDIOC_
-/* for compatibility, will go away some day */
-#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int)
-#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct <link linkend="v4l2-streamparm">v4l2_streamparm</link>)
-#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct <link linkend="v4l2-control">v4l2_control</link>)
-#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct <link linkend="v4l2-audio">v4l2_audio</link>)
-#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct <link linkend="v4l2-audioout">v4l2_audioout</link>)
-#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct <link linkend="v4l2-cropcap">v4l2_cropcap</link>)
-#endif
-
#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */
#endif /* __LINUX_VIDEODEV2_H */
diff --git a/Documentation/DocBook/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/v4l/vidioc-enum-fmt.xml
index 960d44615ca6..71d373b6d36a 100644
--- a/Documentation/DocBook/v4l/vidioc-enum-fmt.xml
+++ b/Documentation/DocBook/v4l/vidioc-enum-fmt.xml
@@ -76,7 +76,9 @@ pixelformat</structfield> field.</entry>
<entry>Type of the data stream, set by the application.
Only these types are valid here:
<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant>,
+<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE</constant>,
<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant>,
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant>,
<constant>V4L2_BUF_TYPE_VIDEO_OVERLAY</constant>, and custom (driver
defined) types with code <constant>V4L2_BUF_TYPE_PRIVATE</constant>
and higher.</entry>
diff --git a/Documentation/DocBook/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/v4l/vidioc-g-fmt.xml
index 7c7d1b72c40d..a4ae59b664eb 100644
--- a/Documentation/DocBook/v4l/vidioc-g-fmt.xml
+++ b/Documentation/DocBook/v4l/vidioc-g-fmt.xml
@@ -60,11 +60,13 @@ application.</para>
<structfield>type</structfield> field of a struct
<structname>v4l2_format</structname> to the respective buffer (stream)
type. For example video capture devices use
-<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant>. When the application
+<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant> or
+<constant>V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE</constant>. When the application
calls the <constant>VIDIOC_G_FMT</constant> ioctl with a pointer to
this structure the driver fills the respective member of the
<structfield>fmt</structfield> union. In case of video capture devices
-that is the &v4l2-pix-format; <structfield>pix</structfield> member.
+that is either the &v4l2-pix-format; <structfield>pix</structfield> or
+the &v4l2-pix-format-mplane; <structfield>pix_mp</structfield> member.
When the requested buffer type is not supported drivers return an
&EINVAL;.</para>
@@ -134,6 +136,15 @@ devices.</entry>
</row>
<row>
<entry></entry>
+ <entry>&v4l2-pix-format-mplane;</entry>
+ <entry><structfield>pix_mp</structfield></entry>
+ <entry>Definition of an image format, see <xref
+ linkend="pixfmt" />, used by video capture and output
+devices that support the <link linkend="planar-apis">multi-planar
+version of the API</link>.</entry>
+ </row>
+ <row>
+ <entry></entry>
<entry>&v4l2-window;</entry>
<entry><structfield>win</structfield></entry>
<entry>Definition of an overlaid image, see <xref
diff --git a/Documentation/DocBook/v4l/vidioc-qbuf.xml b/Documentation/DocBook/v4l/vidioc-qbuf.xml
index ab691ebf3b93..f2b11f8a4031 100644
--- a/Documentation/DocBook/v4l/vidioc-qbuf.xml
+++ b/Documentation/DocBook/v4l/vidioc-qbuf.xml
@@ -64,7 +64,8 @@ zero to the number of buffers allocated with &VIDIOC-REQBUFS;
contents of the struct <structname>v4l2_buffer</structname> returned
by a &VIDIOC-QUERYBUF; ioctl will do as well. When the buffer is
intended for output (<structfield>type</structfield> is
-<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant> or
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant>,
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant>, or
<constant>V4L2_BUF_TYPE_VBI_OUTPUT</constant>) applications must also
initialize the <structfield>bytesused</structfield>,
<structfield>field</structfield> and
@@ -75,7 +76,11 @@ supports capturing from specific video inputs and you want to specify a video
input, then <structfield>flags</structfield> should be set to
<constant>V4L2_BUF_FLAG_INPUT</constant> and the field
<structfield>input</structfield> must be initialized to the desired input.
-The <structfield>reserved</structfield> field must be set to 0.
+The <structfield>reserved</structfield> field must be set to 0. When using
+the <link linkend="planar-apis">multi-planar API</link>, the
+<structfield>m.planes</structfield> field must contain a userspace pointer
+to a filled-in array of &v4l2-plane; and the <structfield>length</structfield>
+field must be set to the number of elements in that array.
</para>
<para>To enqueue a <link linkend="mmap">memory mapped</link>
@@ -93,10 +98,13 @@ structure the driver sets the
buffer applications set the <structfield>memory</structfield>
field to <constant>V4L2_MEMORY_USERPTR</constant>, the
<structfield>m.userptr</structfield> field to the address of the
-buffer and <structfield>length</structfield> to its size.
-When <constant>VIDIOC_QBUF</constant> is called with a pointer to this
-structure the driver sets the <constant>V4L2_BUF_FLAG_QUEUED</constant>
-flag and clears the <constant>V4L2_BUF_FLAG_MAPPED</constant> and
+buffer and <structfield>length</structfield> to its size. When the multi-planar
+API is used, <structfield>m.userptr</structfield> and
+<structfield>length</structfield> members of the passed array of &v4l2-plane;
+have to be used instead. When <constant>VIDIOC_QBUF</constant> is called with
+a pointer to this structure the driver sets the
+<constant>V4L2_BUF_FLAG_QUEUED</constant> flag and clears the
+<constant>V4L2_BUF_FLAG_MAPPED</constant> and
<constant>V4L2_BUF_FLAG_DONE</constant> flags in the
<structfield>flags</structfield> field, or it returns an error code.
This ioctl locks the memory pages of the buffer in physical memory,
@@ -115,7 +123,9 @@ remaining fields or returns an error code. The driver may also set
<constant>V4L2_BUF_FLAG_ERROR</constant> in the <structfield>flags</structfield>
field. It indicates a non-critical (recoverable) streaming error. In such case
the application may continue as normal, but should be aware that data in the
-dequeued buffer might be corrupted.</para>
+dequeued buffer might be corrupted. When using the multi-planar API, the
+planes array does not have to be passed; the <structfield>m.planes</structfield>
+member must be set to NULL in that case.</para>
<para>By default <constant>VIDIOC_DQBUF</constant> blocks when no
buffer is in the outgoing queue. When the
diff --git a/Documentation/DocBook/v4l/vidioc-querybuf.xml b/Documentation/DocBook/v4l/vidioc-querybuf.xml
index e649805a4908..5c104d42d31c 100644
--- a/Documentation/DocBook/v4l/vidioc-querybuf.xml
+++ b/Documentation/DocBook/v4l/vidioc-querybuf.xml
@@ -61,6 +61,10 @@ buffer at any time after buffers have been allocated with the
to the number of buffers allocated with &VIDIOC-REQBUFS;
(&v4l2-requestbuffers; <structfield>count</structfield>) minus one.
The <structfield>reserved</structfield> field should to set to 0.
+When using the <link linkend="planar-apis">multi-planar API</link>, the
+<structfield>m.planes</structfield> field must contain a userspace pointer to an
+array of &v4l2-plane; and the <structfield>length</structfield> field has
+to be set to the number of elements in that array.
After calling <constant>VIDIOC_QUERYBUF</constant> with a pointer to
this structure drivers return an error code or fill the rest of
the structure.</para>
@@ -70,11 +74,13 @@ the structure.</para>
<constant>V4L2_BUF_FLAG_QUEUED</constant> and
<constant>V4L2_BUF_FLAG_DONE</constant> flags will be valid. The
<structfield>memory</structfield> field will be set to the current
-I/O method, the <structfield>m.offset</structfield>
+I/O method. For the single-planar API, the <structfield>m.offset</structfield>
contains the offset of the buffer from the start of the device memory,
-the <structfield>length</structfield> field its size. The driver may
-or may not set the remaining fields and flags, they are meaningless in
-this context.</para>
+the <structfield>length</structfield> field its size. For the multi-planar API,
+fields <structfield>m.mem_offset</structfield> and
+<structfield>length</structfield> in the <structfield>m.planes</structfield>
+array elements will be used instead. The driver may or may not set the remaining
+fields and flags, they are meaningless in this context.</para>
<para>The <structname>v4l2_buffer</structname> structure is
specified in <xref linkend="buffer" />.</para>
diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml
index d499da93a450..f29f1b86213c 100644
--- a/Documentation/DocBook/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/v4l/vidioc-querycap.xml
@@ -142,16 +142,30 @@ this array to zero.</entry>
<row>
<entry><constant>V4L2_CAP_VIDEO_CAPTURE</constant></entry>
<entry>0x00000001</entry>
- <entry>The device supports the <link
+ <entry>The device supports the single-planar API through the <link
linkend="capture">Video Capture</link> interface.</entry>
</row>
<row>
+ <entry><constant>V4L2_CAP_VIDEO_CAPTURE_MPLANE</constant></entry>
+ <entry>0x00001000</entry>
+ <entry>The device supports the
+ <link linkend="planar-apis">multi-planar API</link> through the
+ <link linkend="capture">Video Capture</link> interface.</entry>
+ </row>
+ <row>
<entry><constant>V4L2_CAP_VIDEO_OUTPUT</constant></entry>
<entry>0x00000002</entry>
- <entry>The device supports the <link
+ <entry>The device supports the single-planar API through the <link
linkend="output">Video Output</link> interface.</entry>
</row>
<row>
+ <entry><constant>V4L2_CAP_VIDEO_OUTPUT_MPLANE</constant></entry>
+ <entry>0x00002000</entry>
+ <entry>The device supports the
+ <link linkend="planar-apis">multi-planar API</link> through the
+ <link linkend="output">Video Output</link> interface.</entry>
+ </row>
+ <row>
<entry><constant>V4L2_CAP_VIDEO_OVERLAY</constant></entry>
<entry>0x00000004</entry>
<entry>The device supports the <link
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index 59690de8ebfe..3348d313fbe0 100644
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -556,6 +556,9 @@ sub ngene {
my $hash1 = "d798d5a757121174f0dbc5f2833c0c85";
my $file2 = "ngene_17.fw";
my $hash2 = "26b687136e127b8ac24b81e0eeafc20b";
+ my $url2 = "http://l4m-daten.de/downloads/firmware/dvb-s2/linux/all/";
+ my $file3 = "ngene_18.fw";
+ my $hash3 = "ebce3ea769a53e3e0b0197c3b3f127e3";
checkstandard();
@@ -565,7 +568,10 @@ sub ngene {
wgetfile($file2, $url . $file2);
verify($file2, $hash2);
- "$file1, $file2";
+ wgetfile($file3, $url2 . $file3);
+ verify($file3, $hash3);
+
+ "$file1, $file2, $file3";
}
sub az6027{
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index b840ec3c875d..a4ab80329b38 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -97,42 +97,6 @@ Who: Pavel Machek <pavel@ucw.cz>
---------------------------
-What: Video4Linux obsolete drivers using V4L1 API
-When: kernel 2.6.39
-Files: drivers/staging/se401/* drivers/staging/usbvideo/*
-Check: drivers/staging/se401/se401.c drivers/staging/usbvideo/usbvideo.c
-Why: There are some drivers still using V4L1 API, despite all efforts we've done
- to migrate. Those drivers are for obsolete hardware that the old maintainer
- didn't care (or not have the hardware anymore), and that no other developer
- could find any hardware to buy. They probably have no practical usage today,
- and people with such old hardware could probably keep using an older version
- of the kernel. Those drivers will be moved to staging on 2.6.38 and, if nobody
- cares enough to port and test them with V4L2 API, they'll be removed on 2.6.39.
-Who: Mauro Carvalho Chehab <mchehab@infradead.org>
-
----------------------------
-
-What: Video4Linux: Remove obsolete ioctl's
-When: kernel 2.6.39
-Files: include/media/videodev2.h
-Why: Some ioctl's were defined wrong on 2.6.2 and 2.6.6, using the wrong
- type of R/W arguments. They were fixed, but the old ioctl names are
- still there, maintained to avoid breaking binary compatibility:
- #define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int)
- #define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm)
- #define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control)
- #define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio)
- #define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout)
- #define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap)
- There's no sense on preserving those forever, as it is very doubtful
- that someone would try to use a such old binary with a modern kernel.
- Removing them will allow us to remove some magic done at the V4L ioctl
- handler.
-
-Who: Mauro Carvalho Chehab <mchehab@infradead.org>
-
----------------------------
-
What: sys_sysctl
When: September 2010
Option: CONFIG_SYSCTL_SYSCALL
diff --git a/Documentation/video4linux/README.ivtv b/Documentation/video4linux/README.ivtv
index 42b06686eb78..2579b5b709ed 100644
--- a/Documentation/video4linux/README.ivtv
+++ b/Documentation/video4linux/README.ivtv
@@ -36,8 +36,7 @@ Additional features for the PVR-350 (CX23415 based):
* Provides comprehensive OSD (On Screen Display: ie. graphics overlaying the
video signal)
* Provides a framebuffer (allowing X applications to appear on the video
- device) (this framebuffer is not yet part of the kernel. In the meantime it
- is available from www.ivtvdriver.org).
+ device)
* Supports raw YUV output.
IMPORTANT: In case of problems first read this page:
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 261776e0c5e1..dc72fff2eb12 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -199,6 +199,7 @@ spca500 06bd:0404 Agfa CL20
spca500 06be:0800 Optimedia
sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom
spca506 06e1:a190 ADS Instant VCD
+ov534 06f8:3002 Hercules Blog Webcam
ov534_9 06f8:3003 Hercules Dualpix HD Weblog
sonixj 06f8:3004 Hercules Classic Silver
sonixj 06f8:3008 Hercules Deluxe Optical Glass
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c
index bf14bd79e2fc..cdb645d57438 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/common/tuners/tda9887.c
@@ -36,6 +36,8 @@ struct tda9887_priv {
unsigned int mode;
unsigned int audmode;
v4l2_std_id std;
+
+ bool standby;
};
/* ---------------------------------------------------------------------- */
@@ -568,7 +570,7 @@ static void tda9887_configure(struct dvb_frontend *fe)
tda9887_do_config(fe);
tda9887_set_insmod(fe);
- if (priv->mode == T_STANDBY)
+ if (priv->standby)
priv->data[1] |= cForcedMuteAudioON;
tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
@@ -616,7 +618,7 @@ static void tda9887_standby(struct dvb_frontend *fe)
{
struct tda9887_priv *priv = fe->analog_demod_priv;
- priv->mode = T_STANDBY;
+ priv->standby = true;
tda9887_configure(fe);
}
@@ -626,6 +628,7 @@ static void tda9887_set_params(struct dvb_frontend *fe,
{
struct tda9887_priv *priv = fe->analog_demod_priv;
+ priv->standby = false;
priv->mode = params->mode;
priv->audmode = params->audmode;
priv->std = params->std;
@@ -686,7 +689,7 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
return NULL;
case 1:
fe->analog_demod_priv = priv;
- priv->mode = T_STANDBY;
+ priv->standby = true;
tuner_info("tda988[5/6/7] found\n");
break;
default:
diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/common/tuners/tea5761.c
index 925399dffbed..bf78cb9fc52c 100644
--- a/drivers/media/common/tuners/tea5761.c
+++ b/drivers/media/common/tuners/tea5761.c
@@ -23,6 +23,7 @@ struct tea5761_priv {
struct tuner_i2c_props i2c_props;
u32 frequency;
+ bool standby;
};
/*****************************************************************************/
@@ -135,18 +136,19 @@ static void tea5761_status_dump(unsigned char *buffer)
}
/* Freq should be specifyed at 62.5 Hz */
-static int set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
+static int __set_radio_freq(struct dvb_frontend *fe,
+ unsigned int freq,
+ bool mono)
{
struct tea5761_priv *priv = fe->tuner_priv;
- unsigned int frq = params->frequency;
+ unsigned int frq = freq;
unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 };
unsigned div;
int rc;
tuner_dbg("radio freq counter %d\n", frq);
- if (params->mode == T_STANDBY) {
+ if (priv->standby) {
tuner_dbg("TEA5761 set to standby mode\n");
buffer[5] |= TEA5761_TNCTRL_MU;
} else {
@@ -154,7 +156,7 @@ static int set_radio_freq(struct dvb_frontend *fe,
}
- if (params->audmode == V4L2_TUNER_MODE_MONO) {
+ if (mono) {
tuner_dbg("TEA5761 set to mono\n");
buffer[5] |= TEA5761_TNCTRL_MST;
} else {
@@ -176,6 +178,26 @@ static int set_radio_freq(struct dvb_frontend *fe,
return 0;
}
+static int set_radio_freq(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tea5761_priv *priv = fe->analog_demod_priv;
+
+ priv->standby = false;
+
+ return __set_radio_freq(fe, params->frequency,
+ params->audmode == V4L2_TUNER_MODE_MONO);
+}
+
+static int set_radio_sleep(struct dvb_frontend *fe)
+{
+ struct tea5761_priv *priv = fe->analog_demod_priv;
+
+ priv->standby = true;
+
+ return __set_radio_freq(fe, priv->frequency, false);
+}
+
static int tea5761_read_status(struct dvb_frontend *fe, char *buffer)
{
struct tea5761_priv *priv = fe->tuner_priv;
@@ -284,6 +306,7 @@ static struct dvb_tuner_ops tea5761_tuner_ops = {
.name = "tea5761", // Philips TEA5761HN FM Radio
},
.set_analog_params = set_radio_freq,
+ .sleep = set_radio_sleep,
.release = tea5761_release,
.get_frequency = tea5761_get_frequency,
.get_status = tea5761_get_status,
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index 58a513bcd747..afba6dc5e080 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -971,6 +971,22 @@ static struct tuner_params tuner_tena_9533_di_params[] = {
},
};
+/* ------------ TUNER_TENA_TNF_5337 - Tena tnf5337MFD STD M/N ------------ */
+
+static struct tuner_range tuner_tena_tnf_5337_ntsc_ranges[] = {
+ { 16 * 166.25 /*MHz*/, 0x86, 0x01, },
+ { 16 * 466.25 /*MHz*/, 0x86, 0x02, },
+ { 16 * 999.99 , 0x86, 0x08, },
+};
+
+static struct tuner_params tuner_tena_tnf_5337_params[] = {
+ {
+ .type = TUNER_PARAM_TYPE_NTSC,
+ .ranges = tuner_tena_tnf_5337_ntsc_ranges,
+ .count = ARRAY_SIZE(tuner_tena_tnf_5337_ntsc_ranges),
+ },
+};
+
/* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */
static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = {
@@ -1842,6 +1858,11 @@ struct tunertype tuners[] = {
.params = tuner_philips_fq1236_mk5_params,
.count = ARRAY_SIZE(tuner_philips_fq1236_mk5_params),
},
+ [TUNER_TENA_TNF_5337] = { /* Tena 5337 MFD */
+ .name = "Tena TNF5337 MFD",
+ .params = tuner_tena_tnf_5337_params,
+ .count = ARRAY_SIZE(tuner_tena_tnf_5337_params),
+ },
};
EXPORT_SYMBOL(tuners);
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index b6ce528e1889..efcbc3ed0aea 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -685,7 +685,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type,
{
struct xc2028_data *priv = fe->tuner_priv;
struct firmware_properties new_fw;
- int rc = 0, is_retry = 0;
+ int rc = 0, retry_count = 0;
u16 version, hwmodel;
v4l2_std_id std0;
@@ -855,9 +855,9 @@ read_not_reliable:
fail:
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
- if (!is_retry) {
+ if (retry_count < 8) {
msleep(50);
- is_retry = 1;
+ retry_count++;
tuner_dbg("Retrying firmware load\n");
goto retry;
}
@@ -933,7 +933,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
* that xc2028 will be in a safe state.
* Maybe this might also be needed for DTV.
*/
- if (new_mode == T_ANALOG_TV) {
+ if (new_mode == V4L2_TUNER_ANALOG_TV) {
rc = send_seq(priv, {0x00, 0x00});
/* Analog modes require offset = 0 */
@@ -1054,7 +1054,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe,
if (priv->ctrl.input1)
type |= INPUT1;
return generic_set_freq(fe, (625l * p->frequency) / 10,
- T_RADIO, type, 0, 0);
+ V4L2_TUNER_RADIO, type, 0, 0);
}
/* if std is not defined, choose one */
@@ -1069,7 +1069,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe,
p->std |= parse_audio_std_option();
return generic_set_freq(fe, 62500l * p->frequency,
- T_ANALOG_TV, type, p->std, 0);
+ V4L2_TUNER_ANALOG_TV, type, p->std, 0);
}
static int xc2028_set_params(struct dvb_frontend *fe,
@@ -1174,7 +1174,7 @@ static int xc2028_set_params(struct dvb_frontend *fe,
}
return generic_set_freq(fe, p->frequency,
- T_DIGITAL_TV, type, 0, demod);
+ V4L2_TUNER_DIGITAL_TV, type, 0, demod);
}
static int xc2028_sleep(struct dvb_frontend *fe)
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index 76ac5cd84af7..1e28f7dcb26b 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -65,7 +65,7 @@ struct xc5000_priv {
};
/* Misc Defines */
-#define MAX_TV_STANDARD 23
+#define MAX_TV_STANDARD 24
#define XC_MAX_I2C_WRITE_LENGTH 64
/* Signal Types */
@@ -92,6 +92,8 @@ struct xc5000_priv {
#define XREG_IF_OUT 0x05
#define XREG_SEEK_MODE 0x07
#define XREG_POWER_DOWN 0x0A /* Obsolete */
+/* Set the output amplitude - SIF for analog, DTVP/DTVN for digital */
+#define XREG_OUTPUT_AMP 0x0B
#define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */
#define XREG_SMOOTHEDCVBS 0x0E
#define XREG_XTALFREQ 0x0F
@@ -173,6 +175,7 @@ struct XC_TV_STANDARD {
#define DTV7 20
#define FM_Radio_INPUT2 21
#define FM_Radio_INPUT1 22
+#define FM_Radio_INPUT1_MONO 23
static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
{"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
@@ -197,7 +200,8 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
{"DTV7/8", 0x00C0, 0x801B},
{"DTV7", 0x00C0, 0x8007},
{"FM Radio-INPUT2", 0x9802, 0x9002},
- {"FM Radio-INPUT1", 0x0208, 0x9002}
+ {"FM Radio-INPUT1", 0x0208, 0x9002},
+ {"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
};
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
@@ -683,6 +687,24 @@ static int xc5000_set_params(struct dvb_frontend *fe,
return -EINVAL;
}
priv->rf_mode = XC_RF_MODE_AIR;
+ } else if (fe->ops.info.type == FE_QAM) {
+ dprintk(1, "%s() QAM\n", __func__);
+ switch (params->u.qam.modulation) {
+ case QAM_16:
+ case QAM_32:
+ case QAM_64:
+ case QAM_128:
+ case QAM_256:
+ case QAM_AUTO:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->bandwidth = BANDWIDTH_8_MHZ;
+ priv->video_standard = DTV7_8;
+ priv->freq_hz = params->frequency - 2750000;
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
} else {
printk(KERN_ERR "xc5000 modulation type not supported!\n");
return -EINVAL;
@@ -714,6 +736,8 @@ static int xc5000_set_params(struct dvb_frontend *fe,
return -EIO;
}
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
+
xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
if (debug)
@@ -818,6 +842,8 @@ tune_channel:
return -EREMOTEIO;
}
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
+
xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
if (debug)
@@ -845,6 +871,8 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
radio_input = FM_Radio_INPUT1;
else if (priv->radio_input == XC5000_RADIO_FM2)
radio_input = FM_Radio_INPUT2;
+ else if (priv->radio_input == XC5000_RADIO_FM1_MONO)
+ radio_input = FM_Radio_INPUT1_MONO;
else {
dprintk(1, "%s() unknown radio input %d\n", __func__,
priv->radio_input);
@@ -871,6 +899,12 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
return -EREMOTEIO;
}
+ if ((priv->radio_input == XC5000_RADIO_FM1) ||
+ (priv->radio_input == XC5000_RADIO_FM2))
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
+ else if (priv->radio_input == XC5000_RADIO_FM1_MONO)
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x06);
+
xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
return 0;
@@ -1021,6 +1055,23 @@ static int xc5000_release(struct dvb_frontend *fe)
return 0;
}
+static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ struct xc5000_config *p = priv_cfg;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (p->if_khz)
+ priv->if_khz = p->if_khz;
+
+ if (p->radio_input)
+ priv->radio_input = p->radio_input;
+
+ return 0;
+}
+
+
static const struct dvb_tuner_ops xc5000_tuner_ops = {
.info = {
.name = "Xceive XC5000",
@@ -1033,6 +1084,7 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = {
.init = xc5000_init,
.sleep = xc5000_sleep,
+ .set_config = xc5000_set_config,
.set_params = xc5000_set_params,
.set_analog_params = xc5000_set_analog_params,
.get_frequency = xc5000_get_frequency,
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index 3756e73649be..e2957451b532 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -40,6 +40,7 @@ struct xc5000_config {
#define XC5000_RADIO_NOT_CONFIGURED 0
#define XC5000_RADIO_FM1 1
#define XC5000_RADIO_FM2 2
+#define XC5000_RADIO_FM1_MONO 3
/* For each bridge framework, when it attaches either analog or digital,
* it has to store a reference back to its _core equivalent structure,
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index 161ccfd471cb..ee214c3b63d7 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -65,7 +65,7 @@ comment "Supported SDMC DM1105 Adapters"
source "drivers/media/dvb/dm1105/Kconfig"
comment "Supported FireWire (IEEE 1394) Adapters"
- depends on DVB_CORE && IEEE1394
+ depends on DVB_CORE && FIREWIRE
source "drivers/media/dvb/firewire/Kconfig"
comment "Supported Earthsoft PT1 Adapters"
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index f9f19be77181..3b860504bf04 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -239,7 +239,6 @@ struct analog_demod_ops {
void (*set_params)(struct dvb_frontend *fe,
struct analog_parameters *params);
int (*has_signal)(struct dvb_frontend *fe);
- int (*is_stereo)(struct dvb_frontend *fe);
int (*get_afc)(struct dvb_frontend *fe);
void (*tuner_status)(struct dvb_frontend *fe);
void (*standby)(struct dvb_frontend *fe);
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 3d48ba019342..fe4f894183ff 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -358,3 +358,11 @@ config DVB_USB_LME2510
select DVB_IX2505V if !DVB_FE_CUSTOMISE
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
+
+config DVB_USB_TECHNISAT_USB2
+ tristate "Technisat DVB-S/S2 USB2.0 support"
+ depends on DVB_USB
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Technisat USB2 DVB-S/S2 device
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 5b1d12f2d591..4bac13da0c39 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -91,6 +91,9 @@ obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
dvb-usb-lmedm04-objs = lmedm04.o
obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
+dvb-usb-technisat-usb2-objs = technisat-usb2.o
+obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
+
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index 3537d65c04bc..b2a87f2c2c3e 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -32,6 +32,7 @@ extern int dvb_usb_dib0700_debug;
// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
+#define REQUEST_SET_I2C_PARAM 0x10
#define REQUEST_SET_RC 0x11
#define REQUEST_NEW_I2C_READ 0x12
#define REQUEST_NEW_I2C_WRITE 0x13
@@ -61,6 +62,7 @@ extern struct i2c_algorithm dib0700_i2c_algo;
extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold);
extern int dib0700_change_protocol(struct rc_dev *dev, u64 rc_type);
+extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz);
extern int dib0700_device_count;
extern int dvb_usb_dib0700_ir_proto;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 98ffb40728e3..b79af68c54ae 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -186,7 +186,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
msg[i].len,
USB_CTRL_GET_TIMEOUT);
if (result < 0) {
- err("i2c read error (status = %d)\n", result);
+ deb_info("i2c read error (status = %d)\n", result);
break;
}
@@ -215,7 +215,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
0, 0, buf, msg[i].len + 4,
USB_CTRL_GET_TIMEOUT);
if (result < 0) {
- err("i2c write error (status = %d)\n", result);
+ deb_info("i2c write error (status = %d)\n", result);
break;
}
}
@@ -328,6 +328,31 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
return dib0700_ctrl_wr(d, b, 10);
}
+int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
+{
+ u16 divider;
+ u8 b[8];
+
+ if (scl_kHz == 0)
+ return -EINVAL;
+
+ b[0] = REQUEST_SET_I2C_PARAM;
+ divider = (u16) (30000 / scl_kHz);
+ b[2] = (u8) (divider >> 8);
+ b[3] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz);
+ b[4] = (u8) (divider >> 8);
+ b[5] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
+ b[6] = (u8) (divider >> 8);
+ b[7] = (u8) (divider & 0xff);
+
+ deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
+ (b[2] << 8) | (b[3]), (b[4] << 8) | b[5], (b[6] << 8) | b[7], scl_kHz);
+ return dib0700_ctrl_wr(d, b, 8);
+}
+
+
int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
{
switch (clk_MHz) {
@@ -459,10 +484,20 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
- if (onoff)
- st->channel_state |= 1 << adap->id;
- else
- st->channel_state &= ~(1 << adap->id);
+ st->channel_state &= ~0x3;
+ if ((adap->stream.props.endpoint != 2)
+ && (adap->stream.props.endpoint != 3)) {
+ deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->stream.props.endpoint);
+ if (onoff)
+ st->channel_state |= 1 << (adap->id);
+ else
+ st->channel_state |= 1 << ~(adap->id);
+ } else {
+ if (onoff)
+ st->channel_state |= 1 << (adap->stream.props.endpoint-2);
+ else
+ st->channel_state |= 1 << (3-adap->stream.props.endpoint);
+ }
b[2] |= st->channel_state;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 193cdb77b76a..97af266d7f1d 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -12,6 +12,7 @@
#include "dib7000m.h"
#include "dib7000p.h"
#include "dib8000.h"
+#include "dib9000.h"
#include "mt2060.h"
#include "mt2266.h"
#include "tuner-xc2028.h"
@@ -29,6 +30,7 @@ MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplif
struct dib0700_adapter_state {
int (*set_param_save) (struct dvb_frontend *, struct dvb_frontend_parameters *);
+ const struct firmware *frontend_firmware;
};
/* Hauppauge Nova-T 500 (aka Bristol)
@@ -1243,13 +1245,13 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index,
u16 pid, int onoff)
{
- return dib8000_pid_filter(adapter->fe, index, pid, onoff);
+ return dib8000_pid_filter(adapter->fe, index, pid, onoff);
}
static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter,
- int onoff)
+ int onoff)
{
- return dib8000_pid_filter_ctrl(adapter->fe, onoff);
+ return dib8000_pid_filter_ctrl(adapter->fe, onoff);
}
/* STK807x */
@@ -1321,11 +1323,11 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
/* STK8096GP */
struct dibx000_agc_config dib8090_agc_config[2] = {
- {
+ {
BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1,
- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
+ * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
| (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
@@ -1362,12 +1364,12 @@ struct dibx000_agc_config dib8090_agc_config[2] = {
51,
0,
- },
- {
+ },
+ {
BAND_CBAND,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1,
- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
+ * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
| (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
@@ -1404,135 +1406,153 @@ struct dibx000_agc_config dib8090_agc_config[2] = {
51,
0,
- }
+ }
};
static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = {
- 54000, 13500,
- 1, 18, 3, 1, 0,
- 0, 0, 1, 1, 2,
- (3 << 14) | (1 << 12) | (599 << 0),
- (0 << 25) | 0,
- 20199727,
- 12000000,
+ 54000, 13500,
+ 1, 18, 3, 1, 0,
+ 0, 0, 1, 1, 2,
+ (3 << 14) | (1 << 12) | (599 << 0),
+ (0 << 25) | 0,
+ 20199727,
+ 12000000,
};
static int dib8090_get_adc_power(struct dvb_frontend *fe)
{
- return dib8000_get_adc_power(fe, 1);
+ return dib8000_get_adc_power(fe, 1);
}
-static struct dib8000_config dib809x_dib8000_config = {
- .output_mpeg2_in_188_bytes = 1,
-
- .agc_config_count = 2,
- .agc = dib8090_agc_config,
- .agc_control = dib0090_dcc_freq,
- .pll = &dib8090_pll_config_12mhz,
- .tuner_is_baseband = 1,
-
- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
-
- .hostbus_diversity = 1,
- .div_cfg = 0x31,
- .output_mode = OUTMODE_MPEG2_FIFO,
- .drives = 0x2d98,
- .diversity_delay = 144,
- .refclksel = 3,
+static struct dib8000_config dib809x_dib8000_config[2] = {
+ {
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc_config_count = 2,
+ .agc = dib8090_agc_config,
+ .agc_control = dib0090_dcc_freq,
+ .pll = &dib8090_pll_config_12mhz,
+ .tuner_is_baseband = 1,
+
+ .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
+
+ .hostbus_diversity = 1,
+ .div_cfg = 0x31,
+ .output_mode = OUTMODE_MPEG2_FIFO,
+ .drives = 0x2d98,
+ .diversity_delay = 48,
+ .refclksel = 3,
+ }, {
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc_config_count = 2,
+ .agc = dib8090_agc_config,
+ .agc_control = dib0090_dcc_freq,
+ .pll = &dib8090_pll_config_12mhz,
+ .tuner_is_baseband = 1,
+
+ .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
+
+ .hostbus_diversity = 1,
+ .div_cfg = 0x31,
+ .output_mode = OUTMODE_DIVERSITY,
+ .drives = 0x2d08,
+ .diversity_delay = 1,
+ .refclksel = 3,
+ }
+};
+
+static struct dib0090_wbd_slope dib8090_wbd_table[] = {
+ /* max freq ; cold slope ; cold offset ; warm slope ; warm offset ; wbd gain */
+ { 120, 0, 500, 0, 500, 4 }, /* CBAND */
+ { 170, 0, 450, 0, 450, 4 }, /* CBAND */
+ { 380, 48, 373, 28, 259, 6 }, /* VHF */
+ { 860, 34, 700, 36, 616, 6 }, /* high UHF */
+ { 0xFFFF, 34, 700, 36, 616, 6 }, /* default */
};
static struct dib0090_config dib809x_dib0090_config = {
- .io.pll_bypass = 1,
- .io.pll_range = 1,
- .io.pll_prediv = 1,
- .io.pll_loopdiv = 20,
- .io.adc_clock_ratio = 8,
- .io.pll_int_loop_filt = 0,
- .io.clock_khz = 12000,
- .reset = dib80xx_tuner_reset,
- .sleep = dib80xx_tuner_sleep,
- .clkouttobamse = 1,
- .analog_output = 1,
- .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS,
- .wbd_vhf_offset = 100,
- .wbd_cband_offset = 450,
- .use_pwm_agc = 1,
- .clkoutdrive = 1,
- .get_adc_power = dib8090_get_adc_power,
- .freq_offset_khz_uhf = 0,
+ .io.pll_bypass = 1,
+ .io.pll_range = 1,
+ .io.pll_prediv = 1,
+ .io.pll_loopdiv = 20,
+ .io.adc_clock_ratio = 8,
+ .io.pll_int_loop_filt = 0,
+ .io.clock_khz = 12000,
+ .reset = dib80xx_tuner_reset,
+ .sleep = dib80xx_tuner_sleep,
+ .clkouttobamse = 1,
+ .analog_output = 1,
+ .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS,
+ .use_pwm_agc = 1,
+ .clkoutdrive = 1,
+ .get_adc_power = dib8090_get_adc_power,
+ .freq_offset_khz_uhf = -63,
.freq_offset_khz_vhf = -143,
+ .wbd = dib8090_wbd_table,
+ .fref_clock_ratio = 6,
};
static int dib8096_set_param_override(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fep)
{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct dib0700_adapter_state *state = adap->priv;
- u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
- u16 offset;
- int ret = 0;
- enum frontend_tune_state tune_state = CT_SHUTDOWN;
- u16 ltgain, rf_gain_limit;
-
- ret = state->set_param_save(fe, fep);
- if (ret < 0)
- return ret;
-
- switch (band) {
- case BAND_VHF:
- offset = 100;
- break;
- case BAND_UHF:
- offset = 550;
- break;
- default:
- offset = 0;
- break;
- }
- offset += (dib0090_get_wbd_offset(fe) * 8 * 18 / 33 + 1) / 2;
- dib8000_set_wbd_ref(fe, offset);
-
-
- if (band == BAND_CBAND) {
- deb_info("tuning in CBAND - soft-AGC startup\n");
- /* TODO specific wbd target for dib0090 - needed for startup ? */
- dib0090_set_tune_state(fe, CT_AGC_START);
- do {
- ret = dib0090_gain_control(fe);
- msleep(ret);
- tune_state = dib0090_get_tune_state(fe);
- if (tune_state == CT_AGC_STEP_0)
- dib8000_set_gpio(fe, 6, 0, 1);
- else if (tune_state == CT_AGC_STEP_1) {
- dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, &ltgain);
- if (rf_gain_limit == 0)
- dib8000_set_gpio(fe, 6, 0, 0);
- }
- } while (tune_state < CT_AGC_STOP);
- dib0090_pwm_gain_reset(fe);
- dib8000_pwm_agc_reset(fe);
- dib8000_set_tune_state(fe, CT_DEMOD_START);
- } else {
- deb_info("not tuning in CBAND - standard AGC startup\n");
- dib0090_pwm_gain_reset(fe);
- }
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dib0700_adapter_state *state = adap->priv;
+ u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
+ u16 target;
+ int ret = 0;
+ enum frontend_tune_state tune_state = CT_SHUTDOWN;
+ u16 ltgain, rf_gain_limit;
+
+ ret = state->set_param_save(fe, fep);
+ if (ret < 0)
+ return ret;
+
+ target = (dib0090_get_wbd_offset(fe) * 8 * 18 / 33 + 1) / 2;
+ dib8000_set_wbd_ref(fe, target);
+
+
+ if (band == BAND_CBAND) {
+ deb_info("tuning in CBAND - soft-AGC startup\n");
+ dib0090_set_tune_state(fe, CT_AGC_START);
+ do {
+ ret = dib0090_gain_control(fe);
+ msleep(ret);
+ tune_state = dib0090_get_tune_state(fe);
+ if (tune_state == CT_AGC_STEP_0)
+ dib8000_set_gpio(fe, 6, 0, 1);
+ else if (tune_state == CT_AGC_STEP_1) {
+ dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, &ltgain);
+ if (rf_gain_limit == 0)
+ dib8000_set_gpio(fe, 6, 0, 0);
+ }
+ } while (tune_state < CT_AGC_STOP);
+ dib0090_pwm_gain_reset(fe);
+ dib8000_pwm_agc_reset(fe);
+ dib8000_set_tune_state(fe, CT_DEMOD_START);
+ } else {
+ deb_info("not tuning in CBAND - standard AGC startup\n");
+ dib0090_pwm_gain_reset(fe);
+ }
- return 0;
+ return 0;
}
static int dib809x_tuner_attach(struct dvb_usb_adapter *adap)
{
- struct dib0700_adapter_state *st = adap->priv;
- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
- if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
- return -ENODEV;
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
- st->set_param_save = adap->fe->ops.tuner_ops.set_params;
- adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
- return 0;
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ return 0;
}
static int stk809x_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1554,11 +1574,931 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap)
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80);
- adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config);
+ adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c;
+ struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe, 1);
+
+ if (fe_slave) {
+ tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1);
+ if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
+ fe_slave->dvb = adap->fe->dvb;
+ fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override;
+ }
+ tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+
+ return 0;
+}
+
+static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe_slave;
+
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(1000);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80);
+
+ adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]);
+ if (adap->fe == NULL)
+ return -ENODEV;
+
+ fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]);
+ dib8000_set_slave_frontend(adap->fe, fe_slave);
+
+ return fe_slave == NULL ? -ENODEV : 0;
+}
+
+/* STK9090M */
+static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+{
+ return dib9000_fw_pid_filter(adapter->fe, index, pid, onoff);
+}
+
+static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+{
+ return dib9000_fw_pid_filter_ctrl(adapter->fe, onoff);
+}
+
+static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff)
+{
+ return dib9000_set_gpio(fe, 5, 0, !onoff);
+}
+
+static int dib90x0_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+ return dib9000_set_gpio(fe, 0, 0, onoff);
+}
+
+static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len)
+{
+ u8 wb[4] = { 0xc >> 8, 0xc & 0xff, 0, 0 };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ {.addr = 0x1e >> 1, .flags = 0, .buf = wb, .len = 2},
+ {.addr = 0x1e >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2},
+ };
+ u8 index_data;
+
+ dibx000_i2c_set_speed(i2c, 250);
+
+ if (i2c_transfer(i2c, msg, 2) != 2)
+ return -EIO;
+
+ switch (rb[0] << 8 | rb[1]) {
+ case 0:
+ deb_info("Found DiB0170 rev1: This version of DiB0170 is not supported any longer.\n");
+ return -EIO;
+ case 1:
+ deb_info("Found DiB0170 rev2");
+ break;
+ case 2:
+ deb_info("Found DiB0190 rev2");
+ break;
+ default:
+ deb_info("DiB01x0 not found");
+ return -EIO;
+ }
+
+ for (index_data = 0; index_data < len; index_data += 2) {
+ wb[2] = (data[index_data + 1] >> 8) & 0xff;
+ wb[3] = (data[index_data + 1]) & 0xff;
+
+ if (data[index_data] == 0) {
+ wb[0] = (data[index_data] >> 8) & 0xff;
+ wb[1] = (data[index_data]) & 0xff;
+ msg[0].len = 2;
+ if (i2c_transfer(i2c, msg, 2) != 2)
+ return -EIO;
+ wb[2] |= rb[0];
+ wb[3] |= rb[1] & ~(3 << 4);
+ }
+
+ wb[0] = (data[index_data] >> 8)&0xff;
+ wb[1] = (data[index_data])&0xff;
+ msg[0].len = 4;
+ if (i2c_transfer(i2c, &msg[0], 1) != 1)
+ return -EIO;
+ }
+ return 0;
+}
+
+static struct dib9000_config stk9090m_config = {
+ .output_mpeg2_in_188_bytes = 1,
+ .output_mode = OUTMODE_MPEG2_FIFO,
+ .vcxo_timer = 279620,
+ .timing_frequency = 20452225,
+ .demod_clock_khz = 60000,
+ .xtal_clock_khz = 30000,
+ .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0),
+ .subband = {
+ 2,
+ {
+ { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0008 } }, /* GPIO 3 to 1 for VHF */
+ { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0000 } }, /* GPIO 3 to 0 for UHF */
+ { 0 },
+ },
+ },
+ .gpio_function = {
+ { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 },
+ { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 },
+ },
+};
+
+static struct dib9000_config nim9090md_config[2] = {
+ {
+ .output_mpeg2_in_188_bytes = 1,
+ .output_mode = OUTMODE_MPEG2_FIFO,
+ .vcxo_timer = 279620,
+ .timing_frequency = 20452225,
+ .demod_clock_khz = 60000,
+ .xtal_clock_khz = 30000,
+ .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0),
+ }, {
+ .output_mpeg2_in_188_bytes = 1,
+ .output_mode = OUTMODE_DIVERSITY,
+ .vcxo_timer = 279620,
+ .timing_frequency = 20452225,
+ .demod_clock_khz = 60000,
+ .xtal_clock_khz = 30000,
+ .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0),
+ .subband = {
+ 2,
+ {
+ { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0006 } }, /* GPIO 1 and 2 to 1 for VHF */
+ { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0000 } }, /* GPIO 1 and 2 to 0 for UHF */
+ { 0 },
+ },
+ },
+ .gpio_function = {
+ { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 },
+ { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 },
+ },
+ }
+};
+
+static struct dib0090_config dib9090_dib0090_config = {
+ .io.pll_bypass = 0,
+ .io.pll_range = 1,
+ .io.pll_prediv = 1,
+ .io.pll_loopdiv = 8,
+ .io.adc_clock_ratio = 8,
+ .io.pll_int_loop_filt = 0,
+ .io.clock_khz = 30000,
+ .reset = dib90x0_tuner_reset,
+ .sleep = dib90x0_tuner_sleep,
+ .clkouttobamse = 0,
+ .analog_output = 0,
+ .use_pwm_agc = 0,
+ .clkoutdrive = 0,
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = 0,
+};
+
+static struct dib0090_config nim9090md_dib0090_config[2] = {
+ {
+ .io.pll_bypass = 0,
+ .io.pll_range = 1,
+ .io.pll_prediv = 1,
+ .io.pll_loopdiv = 8,
+ .io.adc_clock_ratio = 8,
+ .io.pll_int_loop_filt = 0,
+ .io.clock_khz = 30000,
+ .reset = dib90x0_tuner_reset,
+ .sleep = dib90x0_tuner_sleep,
+ .clkouttobamse = 1,
+ .analog_output = 0,
+ .use_pwm_agc = 0,
+ .clkoutdrive = 0,
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = 0,
+ }, {
+ .io.pll_bypass = 0,
+ .io.pll_range = 1,
+ .io.pll_prediv = 1,
+ .io.pll_loopdiv = 8,
+ .io.adc_clock_ratio = 8,
+ .io.pll_int_loop_filt = 0,
+ .io.clock_khz = 30000,
+ .reset = dib90x0_tuner_reset,
+ .sleep = dib90x0_tuner_sleep,
+ .clkouttobamse = 0,
+ .analog_output = 0,
+ .use_pwm_agc = 0,
+ .clkoutdrive = 0,
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = 0,
+ }
+};
+
+
+static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *state = adap->priv;
+ struct dib0700_state *st = adap->dev->priv;
+ u32 fw_version;
+
+ /* Make use of the new i2c functions from FW 1.20 */
+ dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL);
+ if (fw_version >= 0x10200)
+ st->fw_use_new_i2c_api = 1;
+ dib0700_set_i2c_speed(adap->dev, 340);
+
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80);
+
+ if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) {
+ deb_info("%s: Upload failed. (file not found?)\n", __func__);
+ return -ENODEV;
+ } else {
+ deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size);
+ }
+ stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size;
+ stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data;
+
+ adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int dib9090_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *state = adap->priv;
+ struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe);
+ u16 data_dib190[10] = {
+ 1, 0x1374,
+ 2, 0x01a2,
+ 7, 0x0020,
+ 0, 0x00ef,
+ 8, 0x0486,
+ };
+
+ if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &dib9090_dib0090_config) == NULL)
+ return -ENODEV;
+ i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0)
+ return -ENODEV;
+ dib0700_set_i2c_speed(adap->dev, 2000);
+ if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ return -ENODEV;
+ release_firmware(state->frontend_firmware);
+ return 0;
+}
+
+static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *state = adap->priv;
+ struct dib0700_state *st = adap->dev->priv;
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe_slave;
+ u32 fw_version;
+
+ /* Make use of the new i2c functions from FW 1.20 */
+ dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL);
+ if (fw_version >= 0x10200)
+ st->fw_use_new_i2c_api = 1;
+ dib0700_set_i2c_speed(adap->dev, 340);
+
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) {
+ deb_info("%s: Upload failed. (file not found?)\n", __func__);
+ return -EIO;
+ } else {
+ deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size);
+ }
+ nim9090md_config[0].microcode_B_fe_size = state->frontend_firmware->size;
+ nim9090md_config[0].microcode_B_fe_buffer = state->frontend_firmware->data;
+ nim9090md_config[1].microcode_B_fe_size = state->frontend_firmware->size;
+ nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data;
+
+ dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80);
+ adap->fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]);
+
+ if (adap->fe == NULL)
+ return -ENODEV;
+
+ i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0);
+ dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82);
+
+ fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]);
+ dib9000_set_slave_frontend(adap->fe, fe_slave);
+
+ return fe_slave == NULL ? -ENODEV : 0;
+}
+
+static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *state = adap->priv;
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe_slave;
+ u16 data_dib190[10] = {
+ 1, 0x5374,
+ 2, 0x01ae,
+ 7, 0x0020,
+ 0, 0x00ef,
+ 8, 0x0406,
+ };
+ i2c = dib9000_get_tuner_interface(adap->fe);
+ if (dvb_attach(dib0090_fw_register, adap->fe, i2c, &nim9090md_dib0090_config[0]) == NULL)
+ return -ENODEV;
+ i2c = dib9000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0);
+ if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0)
+ return -ENODEV;
+ dib0700_set_i2c_speed(adap->dev, 2000);
+ if (dib9000_firmware_post_pll_init(adap->fe) < 0)
+ return -ENODEV;
+
+ fe_slave = dib9000_get_slave_frontend(adap->fe, 1);
+ if (fe_slave != NULL) {
+ i2c = dib9000_get_component_bus_interface(adap->fe);
+ dib9000_set_i2c_adapter(fe_slave, i2c);
+
+ i2c = dib9000_get_tuner_interface(fe_slave);
+ if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL)
+ return -ENODEV;
+ fe_slave->dvb = adap->fe->dvb;
+ dib9000_fw_set_component_bus_speed(adap->fe, 2000);
+ if (dib9000_firmware_post_pll_init(fe_slave) < 0)
+ return -ENODEV;
+ }
+ release_firmware(state->frontend_firmware);
+
+ return 0;
+}
+
+/* NIM7090 */
+struct dib7090p_best_adc {
+ u32 timf;
+ u32 pll_loopdiv;
+ u32 pll_prediv;
+};
+
+static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc)
+{
+ u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1;
+
+ u16 xtal = 12000;
+ u32 fcp_min = 1900; /* PLL Minimum Frequency comparator KHz */
+ u32 fcp_max = 20000; /* PLL Maximum Frequency comparator KHz */
+ u32 fdem_max = 76000;
+ u32 fdem_min = 69500;
+ u32 fcp = 0, fs = 0, fdem = 0;
+ u32 harmonic_id = 0;
+
+ adc->pll_loopdiv = loopdiv;
+ adc->pll_prediv = prediv;
+ adc->timf = 0;
+
+ deb_info("bandwidth = %d fdem_min =%d", fe->dtv_property_cache.bandwidth_hz, fdem_min);
+
+ /* Find Min and Max prediv */
+ while ((xtal/max_prediv) >= fcp_min)
+ max_prediv++;
+
+ max_prediv--;
+ min_prediv = max_prediv;
+ while ((xtal/min_prediv) <= fcp_max) {
+ min_prediv--;
+ if (min_prediv == 1)
+ break;
+ }
+ deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv);
+
+ min_prediv = 2;
+
+ for (prediv = min_prediv ; prediv < max_prediv; prediv++) {
+ fcp = xtal / prediv;
+ if (fcp > fcp_min && fcp < fcp_max) {
+ for (loopdiv = 1 ; loopdiv < 64 ; loopdiv++) {
+ fdem = ((xtal/prediv) * loopdiv);
+ fs = fdem / 4;
+ /* test min/max system restrictions */
+
+ if ((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) {
+ spur = 0;
+ /* test fs harmonics positions */
+ for (harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) {
+ if (((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) {
+ spur = 1;
+ break;
+ }
+ }
+
+ if (!spur) {
+ adc->pll_loopdiv = loopdiv;
+ adc->pll_prediv = prediv;
+ adc->timf = 2396745143UL/fdem*(1 << 9);
+ adc->timf += ((2396745143UL%fdem) << 9)/fdem;
+ deb_info("loopdiv=%i prediv=%i timf=%i", loopdiv, prediv, adc->timf);
+ break;
+ }
+ }
+ }
+ }
+ if (!spur)
+ break;
+ }
+
+
+ if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+static int dib7090_agc_startup(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dib0700_adapter_state *state = adap->priv;
+ struct dibx000_bandwidth_config pll;
+ u16 target;
+ struct dib7090p_best_adc adc;
+ int ret;
+
+ ret = state->set_param_save(fe, fep);
+ if (ret < 0)
+ return ret;
+
+ memset(&pll, 0, sizeof(struct dibx000_bandwidth_config));
+ dib0090_pwm_gain_reset(fe);
+ target = (dib0090_get_wbd_offset(fe) * 8 + 1) / 2;
+ dib7000p_set_wbd_ref(fe, target);
+
+ if (dib7090p_get_best_sampling(fe, &adc) == 0) {
+ pll.pll_ratio = adc.pll_loopdiv;
+ pll.pll_prediv = adc.pll_prediv;
+
+ dib7000p_update_pll(fe, &pll);
+ dib7000p_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf);
+ }
+ return 0;
+}
+
+static struct dib0090_wbd_slope dib7090_wbd_table[] = {
+ { 380, 81, 850, 64, 540, 4},
+ { 860, 51, 866, 21, 375, 4},
+ {1700, 0, 250, 0, 100, 6},
+ {2600, 0, 250, 0, 100, 6},
+ { 0xFFFF, 0, 0, 0, 0, 0},
+};
+
+struct dibx000_agc_config dib7090_agc_config[2] = {
+ {
+ .band_caps = BAND_UHF,
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ .inv_gain = 687,
+ .time_stabiliz = 10,
+
+ .alpha_level = 0,
+ .thlock = 118,
+
+ .wbd_inv = 0,
+ .wbd_ref = 1200,
+ .wbd_sel = 3,
+ .wbd_alpha = 5,
+
+ .agc1_max = 65535,
+ .agc1_min = 0,
+
+ .agc2_max = 65535,
+ .agc2_min = 0,
+
+ .agc1_pt1 = 0,
+ .agc1_pt2 = 32,
+ .agc1_pt3 = 114,
+ .agc1_slope1 = 143,
+ .agc1_slope2 = 144,
+ .agc2_pt1 = 114,
+ .agc2_pt2 = 227,
+ .agc2_slope1 = 116,
+ .agc2_slope2 = 117,
+
+ .alpha_mant = 18,
+ .alpha_exp = 0,
+ .beta_mant = 20,
+ .beta_exp = 59,
+
+ .perform_agc_softsplit = 0,
+ } , {
+ .band_caps = BAND_FM | BAND_VHF | BAND_CBAND,
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ .inv_gain = 732,
+ .time_stabiliz = 10,
+
+ .alpha_level = 0,
+ .thlock = 118,
+
+ .wbd_inv = 0,
+ .wbd_ref = 1200,
+ .wbd_sel = 3,
+ .wbd_alpha = 5,
+
+ .agc1_max = 65535,
+ .agc1_min = 0,
+
+ .agc2_max = 65535,
+ .agc2_min = 0,
+
+ .agc1_pt1 = 0,
+ .agc1_pt2 = 0,
+ .agc1_pt3 = 98,
+ .agc1_slope1 = 0,
+ .agc1_slope2 = 167,
+ .agc1_pt1 = 98,
+ .agc2_pt2 = 255,
+ .agc2_slope1 = 104,
+ .agc2_slope2 = 0,
+
+ .alpha_mant = 18,
+ .alpha_exp = 0,
+ .beta_mant = 20,
+ .beta_exp = 59,
+
+ .perform_agc_softsplit = 0,
+ }
+};
+
+static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = {
+ 60000, 15000,
+ 1, 5, 0, 0, 0,
+ 0, 0, 1, 1, 2,
+ (3 << 14) | (1 << 12) | (524 << 0),
+ (0 << 25) | 0,
+ 20452225,
+ 15000000,
+};
+
+static struct dib7000p_config nim7090_dib7000p_config = {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 1,
+ .update_lna = NULL,
+
+ .agc_config_count = 2,
+ .agc = dib7090_agc_config,
+
+ .bw = &dib7090_clock_config_12_mhz,
+
+ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .pwm_freq_div = 0,
+
+ .agc_control = dib7090_agc_restart,
+
+ .spur_protect = 0,
+ .disable_sample_and_hold = 0,
+ .enable_current_mirror = 0,
+ .diversity_delay = 0,
+
+ .output_mode = OUTMODE_MPEG2_FIFO,
+ .enMpegOutput = 1,
+};
+
+static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = {
+ {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 1,
+ .update_lna = NULL,
+
+ .agc_config_count = 2,
+ .agc = dib7090_agc_config,
+
+ .bw = &dib7090_clock_config_12_mhz,
+
+ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .pwm_freq_div = 0,
+
+ .agc_control = dib7090_agc_restart,
+
+ .spur_protect = 0,
+ .disable_sample_and_hold = 0,
+ .enable_current_mirror = 0,
+ .diversity_delay = 0,
+
+ .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK,
+ .default_i2c_addr = 0x90,
+ .enMpegOutput = 1,
+ }, {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 1,
+ .update_lna = NULL,
+
+ .agc_config_count = 2,
+ .agc = dib7090_agc_config,
+
+ .bw = &dib7090_clock_config_12_mhz,
+
+ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .pwm_freq_div = 0,
+
+ .agc_control = dib7090_agc_restart,
+
+ .spur_protect = 0,
+ .disable_sample_and_hold = 0,
+ .enable_current_mirror = 0,
+ .diversity_delay = 0,
+
+ .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK,
+ .default_i2c_addr = 0x92,
+ .enMpegOutput = 0,
+ }
+};
+
+static const struct dib0090_config nim7090_dib0090_config = {
+ .io.clock_khz = 12000,
+ .io.pll_bypass = 0,
+ .io.pll_range = 0,
+ .io.pll_prediv = 3,
+ .io.pll_loopdiv = 6,
+ .io.adc_clock_ratio = 0,
+ .io.pll_int_loop_filt = 0,
+ .reset = dib7090_tuner_sleep,
+ .sleep = dib7090_tuner_sleep,
+
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = 0,
+
+ .get_adc_power = dib7090_get_adc_power,
+
+ .clkouttobamse = 1,
+ .analog_output = 0,
+
+ .wbd_vhf_offset = 0,
+ .wbd_cband_offset = 0,
+ .use_pwm_agc = 1,
+ .clkoutdrive = 0,
+
+ .fref_clock_ratio = 0,
+
+ .wbd = dib7090_wbd_table,
+
+ .ls_cfg_pad_drv = 0,
+ .data_tx_drv = 0,
+ .low_if = NULL,
+ .in_soc = 1,
+};
+
+static const struct dib0090_config tfe7090pvr_dib0090_config[2] = {
+ {
+ .io.clock_khz = 12000,
+ .io.pll_bypass = 0,
+ .io.pll_range = 0,
+ .io.pll_prediv = 3,
+ .io.pll_loopdiv = 6,
+ .io.adc_clock_ratio = 0,
+ .io.pll_int_loop_filt = 0,
+ .reset = dib7090_tuner_sleep,
+ .sleep = dib7090_tuner_sleep,
+
+ .freq_offset_khz_uhf = 50,
+ .freq_offset_khz_vhf = 70,
+
+ .get_adc_power = dib7090_get_adc_power,
+
+ .clkouttobamse = 1,
+ .analog_output = 0,
+
+ .wbd_vhf_offset = 0,
+ .wbd_cband_offset = 0,
+ .use_pwm_agc = 1,
+ .clkoutdrive = 0,
+
+ .fref_clock_ratio = 0,
+
+ .wbd = dib7090_wbd_table,
+
+ .ls_cfg_pad_drv = 0,
+ .data_tx_drv = 0,
+ .low_if = NULL,
+ .in_soc = 1,
+ }, {
+ .io.clock_khz = 12000,
+ .io.pll_bypass = 0,
+ .io.pll_range = 0,
+ .io.pll_prediv = 3,
+ .io.pll_loopdiv = 6,
+ .io.adc_clock_ratio = 0,
+ .io.pll_int_loop_filt = 0,
+ .reset = dib7090_tuner_sleep,
+ .sleep = dib7090_tuner_sleep,
+
+ .freq_offset_khz_uhf = -50,
+ .freq_offset_khz_vhf = -70,
+
+ .get_adc_power = dib7090_get_adc_power,
+
+ .clkouttobamse = 1,
+ .analog_output = 0,
+
+ .wbd_vhf_offset = 0,
+ .wbd_cband_offset = 0,
+ .use_pwm_agc = 1,
+ .clkoutdrive = 0,
+
+ .fref_clock_ratio = 0,
+
+ .wbd = dib7090_wbd_table,
+
+ .ls_cfg_pad_drv = 0,
+ .data_tx_drv = 0,
+ .low_if = NULL,
+ .in_soc = 1,
+ }
+};
+
+static int nim7090_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) {
+ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
+ return -ENODEV;
+ }
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config);
return adap->fe == NULL ? -ENODEV : 0;
}
+static int nim7090_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &nim7090_dib0090_config) == NULL)
+ return -ENODEV;
+
+ dib7000p_set_gpio(adap->fe, 8, 0, 1);
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ return 0;
+}
+
+static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_state *st = adap->dev->priv;
+
+ /* The TFE7090 requires the dib0700 to not be in master mode */
+ st->disable_streaming_master_mode = 1;
+
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(20);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ /* initialize IC 0 */
+ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) {
+ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
+ return -ENODEV;
+ }
+
+ dib0700_set_i2c_speed(adap->dev, 340);
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
+
+ dib7090_slave_reset(adap->fe);
+
+ if (adap->fe == NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap)
+{
+ struct i2c_adapter *i2c;
+
+ if (adap->dev->adapter[0].fe == NULL) {
+ err("the master dib7090 has to be initialized first");
+ return -ENODEV; /* the master device has not been initialized */
+ }
+
+ i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1);
+ if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) {
+ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
+ return -ENODEV;
+ }
+
+ adap->fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]);
+ dib0700_set_i2c_speed(adap->dev, 200);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL)
+ return -ENODEV;
+
+ dib7000p_set_gpio(adap->fe, 8, 0, 1);
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ return 0;
+}
+
+static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe);
+
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL)
+ return -ENODEV;
+
+ dib7000p_set_gpio(adap->fe, 8, 0, 1);
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib7090_agc_startup;
+ return 0;
+}
+
/* STK7070PD */
static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
{
@@ -1856,6 +2796,12 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) },
{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DIVERSITY) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090M) },
+/* 70 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM8096MD) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090MD) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM7090) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) },
+ { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -2465,7 +3411,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{ "DiBcom STK7770P reference design",
{ &dib0700_usb_id_table[59], NULL },
@@ -2477,6 +3423,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
&dib0700_usb_id_table[60], NULL},
{ NULL },
},
+ { "TechniSat AirStar TeleStick 2",
+ { &dib0700_usb_id_table[74], NULL },
+ { NULL },
+ },
},
.rc.core = {
@@ -2619,6 +3569,205 @@ struct dvb_usb_device_properties dib0700_devices[] = {
RC_TYPE_NEC,
.change_protocol = dib0700_change_protocol,
},
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = dib90x0_pid_filter,
+ .pid_filter_ctrl = dib90x0_pid_filter_ctrl,
+ .frontend_attach = stk9090m_frontend_attach,
+ .tuner_attach = dib9090_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom STK9090M reference design",
+ { &dib0700_usb_id_table[69], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
+ .frontend_attach = nim8096md_frontend_attach,
+ .tuner_attach = nim8096md_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom NIM8096MD reference design",
+ { &dib0700_usb_id_table[70], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = dib90x0_pid_filter,
+ .pid_filter_ctrl = dib90x0_pid_filter_ctrl,
+ .frontend_attach = nim9090md_frontend_attach,
+ .tuner_attach = nim9090md_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom NIM9090MD reference design",
+ { &dib0700_usb_id_table[71], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
+ .frontend_attach = nim7090_frontend_attach,
+ .tuner_attach = nim7090_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom NIM7090 reference design",
+ { &dib0700_usb_id_table[72], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
+ .frontend_attach = tfe7090pvr_frontend0_attach,
+ .tuner_attach = tfe7090pvr_tuner0_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
+ .frontend_attach = tfe7090pvr_frontend1_attach,
+ .tuner_attach = tfe7090pvr_tuner1_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom TFE7090PVR reference design",
+ { &dib0700_usb_id_table[73], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .module_name = "dib0700",
+ .rc_query = dib0700_rc_query_old_firmware,
+ .allowed_protos = RC_TYPE_RC5 |
+ RC_TYPE_RC6 |
+ RC_TYPE_NEC,
+ .change_protocol = dib0700_change_protocol,
+ },
},
};
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 1a6310b61923..3a8b7446b7b0 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -106,8 +106,13 @@
#define USB_PID_DIBCOM_STK807XP 0x1f90
#define USB_PID_DIBCOM_STK807XPVR 0x1f98
#define USB_PID_DIBCOM_STK8096GP 0x1fa0
+#define USB_PID_DIBCOM_NIM8096MD 0x1fa8
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
#define USB_PID_DIBCOM_STK7770P 0x1e80
+#define USB_PID_DIBCOM_NIM7090 0x1bb2
+#define USB_PID_DIBCOM_TFE7090PVR 0x1bb4
+#define USB_PID_DIBCOM_NIM9090M 0x2383
+#define USB_PID_DIBCOM_NIM9090MD 0x2384
#define USB_PID_DPOSH_M9206_COLD 0x9206
#define USB_PID_DPOSH_M9206_WARM 0xa090
#define USB_PID_E3C_EC168 0x1689
@@ -312,4 +317,6 @@
#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac
#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001
#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002
+#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004
+#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index 23005b3cf30b..f511418b144a 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -246,7 +246,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d)
dev->map_name = d->props.rc.core.rc_codes;
dev->change_protocol = d->props.rc.core.change_protocol;
dev->allowed_protos = d->props.rc.core.allowed_protos;
- dev->driver_type = RC_DRIVER_SCANCODE;
+ dev->driver_type = d->props.rc.core.driver_type;
usb_to_input_id(d->udev, &dev->input_id);
dev->input_name = "IR-receiver inside an USB DVB receiver";
dev->input_phys = d->rc_phys;
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index 65fa9268e7f7..76a80968482a 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -181,6 +181,7 @@ struct dvb_rc_legacy {
* @rc_codes: name of rc codes table
* @protocol: type of protocol(s) currently used by the driver
* @allowed_protos: protocol(s) supported by the driver
+ * @driver_type: Used to point if a device supports raw mode
* @change_protocol: callback to change protocol
* @rc_query: called to query an event event.
* @rc_interval: time in ms between two queries.
@@ -190,6 +191,7 @@ struct dvb_rc {
char *rc_codes;
u64 protocol;
u64 allowed_protos;
+ enum rc_driver_type driver_type;
int (*change_protocol)(struct rc_dev *dev, u64 rc_type);
char *module_name;
int (*rc_query) (struct dvb_usb_device *d);
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 2c307ba0d28b..98cf30270f7d 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -1,15 +1,16 @@
/* DVB USB framework compliant Linux driver for the
-* DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
-* TeVii S600, S630, S650,
-* Prof 1100, 7500 Cards
-* Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by)
-*
-* This program is free software; you can redistribute it and/or modify it
-* under the terms of the GNU General Public License as published by the
-* Free Software Foundation, version 2.
-*
-* see Documentation/dvb/README.dvb-usb for more information
-*/
+ * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
+ * TeVii S600, S630, S650, S660, S480,
+ * Prof 1100, 7500,
+ * Geniatech SU3000 Cards
+ * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
#include "dw2102.h"
#include "si21xx.h"
#include "stv0299.h"
@@ -55,6 +56,14 @@
#define USB_PID_TEVII_S660 0xd660
#endif
+#ifndef USB_PID_TEVII_S480_1
+#define USB_PID_TEVII_S480_1 0xd481
+#endif
+
+#ifndef USB_PID_TEVII_S480_2
+#define USB_PID_TEVII_S480_2 0xd482
+#endif
+
#ifndef USB_PID_PROF_1100
#define USB_PID_PROF_1100 0xb012
#endif
@@ -67,7 +76,9 @@
#define REG_21_SYMBOLRATE_BYTE2 0x21
/* on my own*/
#define DW2102_VOLTAGE_CTRL (0x1800)
+#define SU3000_STREAM_CTRL (0x1900)
#define DW2102_RC_QUERY (0x1a00)
+#define DW2102_LED_CTRL (0x1b00)
#define err_str "did not find the firmware file. (%s) " \
"Please see linux/Documentation/dvb/ for more details " \
@@ -78,6 +89,14 @@ struct rc_map_dvb_usb_table_table {
int rc_keys_size;
};
+struct su3000_state {
+ u8 initialized;
+};
+
+struct s6x0_state {
+ int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v);
+};
+
/* debug */
static int dvb_usb_dw2102_debug;
module_param_named(debug, dvb_usb_dw2102_debug, int, 0644);
@@ -87,7 +106,8 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))."
/* keymaps */
static int ir_keymap;
module_param_named(keymap, ir_keymap, int, 0644);
-MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ...");
+MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..."
+ " 256=none");
/* demod probe */
static int demod_probe = 1;
@@ -136,8 +156,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
/* read stv0299 register */
value = msg[0].buf[0];/* register */
for (i = 0; i < msg[1].len; i++) {
- value = value + i;
- ret = dw210x_op_rw(d->udev, 0xb5, value, 0,
+ ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0,
buf6, 2, DW210X_READ_MSG);
msg[1].buf[i] = buf6[0];
}
@@ -483,10 +502,10 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
for (j = 0; j < num; j++) {
switch (msg[j].addr) {
case (DW2102_RC_QUERY): {
- u8 ibuf[4];
+ u8 ibuf[5];
ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
- ibuf, 4, DW210X_READ_MSG);
- memcpy(msg[j].buf, ibuf + 1, 2);
+ ibuf, 5, DW210X_READ_MSG);
+ memcpy(msg[j].buf, ibuf + 3, 2);
break;
}
case (DW2102_VOLTAGE_CTRL): {
@@ -502,6 +521,15 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf, 2, DW210X_WRITE_MSG);
break;
}
+ case (DW2102_LED_CTRL): {
+ u8 obuf[2];
+
+ obuf[0] = 5;
+ obuf[1] = msg[j].buf[0];
+ ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
+ break;
+ }
/*case 0x55: cx24116
case 0x6a: stv0903
case 0x68: ds3000, stv0903
@@ -535,14 +563,15 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
i += 16;
len -= 16;
} while (len > 0);
- } else if ((udev->descriptor.idProduct == 0x7500)
- && (j < (num - 1))) {
+ } else if (j < (num - 1)) {
/* write register addr before read */
u8 obuf[msg[j].len + 2];
obuf[0] = msg[j + 1].len;
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
- ret = dw210x_op_rw(d->udev, 0x92, 0, 0,
+ ret = dw210x_op_rw(d->udev,
+ udev->descriptor.idProduct ==
+ 0x7500 ? 0x92 : 0x90, 0, 0,
obuf, msg[j].len + 2,
DW210X_WRITE_MSG);
break;
@@ -552,8 +581,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = msg[j].len + 1;
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
- ret = dw210x_op_rw(d->udev,
- (num > 1 ? 0x90 : 0x80), 0, 0,
+ ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
obuf, msg[j].len + 2,
DW210X_WRITE_MSG);
break;
@@ -561,14 +589,76 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
break;
}
}
-
- msleep(3);
}
mutex_unlock(&d->i2c_mutex);
return num;
}
+static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ u8 obuf[0x40], ibuf[0x40];
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 1:
+ switch (msg[0].addr) {
+ case SU3000_STREAM_CTRL:
+ obuf[0] = msg[0].buf[0] + 0x36;
+ obuf[1] = 3;
+ obuf[2] = 0;
+ if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0)
+ err("i2c transfer failed.");
+ break;
+ case DW2102_RC_QUERY:
+ obuf[0] = 0x10;
+ if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0)
+ err("i2c transfer failed.");
+ msg[0].buf[1] = ibuf[0];
+ msg[0].buf[0] = ibuf[1];
+ break;
+ default:
+ /* always i2c write*/
+ obuf[0] = 0x08;
+ obuf[1] = msg[0].addr;
+ obuf[2] = msg[0].len;
+
+ memcpy(&obuf[3], msg[0].buf, msg[0].len);
+
+ if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3,
+ ibuf, 1, 0) < 0)
+ err("i2c transfer failed.");
+
+ }
+ break;
+ case 2:
+ /* always i2c read */
+ obuf[0] = 0x09;
+ obuf[1] = msg[0].len;
+ obuf[2] = msg[1].len;
+ obuf[3] = msg[0].addr;
+ memcpy(&obuf[4], msg[0].buf, msg[0].len);
+
+ if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4,
+ ibuf, msg[1].len + 1, 0) < 0)
+ err("i2c transfer failed.");
+
+ memcpy(msg[1].buf, &ibuf[1], msg[1].len);
+ break;
+ default:
+ warn("more than 2 i2c messages at a time is not handled yet.");
+ break;
+ }
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
@@ -604,6 +694,11 @@ static struct i2c_algorithm s6x0_i2c_algo = {
.functionality = dw210x_i2c_func,
};
+static struct i2c_algorithm su3000_i2c_algo = {
+ .master_xfer = su3000_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
{
int i;
@@ -668,6 +763,82 @@ static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
return 0;
};
+static int su3000_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ static u8 command_start[] = {0x00};
+ static u8 command_stop[] = {0x01};
+ struct i2c_msg msg = {
+ .addr = SU3000_STREAM_CTRL,
+ .flags = 0,
+ .buf = onoff ? command_start : command_stop,
+ .len = 1
+ };
+
+ i2c_transfer(&adap->dev->i2c_adap, &msg, 1);
+
+ return 0;
+}
+
+static int su3000_power_ctrl(struct dvb_usb_device *d, int i)
+{
+ struct su3000_state *state = (struct su3000_state *)d->priv;
+ u8 obuf[] = {0xde, 0};
+
+ info("%s: %d, initialized %d\n", __func__, i, state->initialized);
+
+ if (i && !state->initialized) {
+ state->initialized = 1;
+ /* reset board */
+ dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0);
+ }
+
+ return 0;
+}
+
+static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ int i;
+ u8 obuf[] = { 0x1f, 0xf0 };
+ u8 ibuf[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0x51,
+ .flags = 0,
+ .buf = obuf,
+ .len = 2,
+ }, {
+ .addr = 0x51,
+ .flags = I2C_M_RD,
+ .buf = ibuf,
+ .len = 1,
+
+ }
+ };
+
+ for (i = 0; i < 6; i++) {
+ obuf[1] = 0xf0 + i;
+ if (i2c_transfer(&d->i2c_adap, msg, 2) != 2)
+ break;
+ else
+ mac[i] = ibuf[0];
+
+ debug_dump(mac, 6, printk);
+ }
+
+ return 0;
+}
+
+static int su3000_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ info("%s\n", __func__);
+
+ *cold = 0;
+ return 0;
+}
+
static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
static u8 command_13v[] = {0x00, 0x01};
@@ -692,6 +863,37 @@ static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
return 0;
}
+static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct dvb_usb_adapter *d =
+ (struct dvb_usb_adapter *)(fe->dvb->priv);
+ struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
+
+ dw210x_set_voltage(fe, voltage);
+ if (st->old_set_voltage)
+ st->old_set_voltage(fe, voltage);
+
+ return 0;
+}
+
+static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon)
+{
+ static u8 led_off[] = { 0 };
+ static u8 led_on[] = { 1 };
+ struct i2c_msg msg = {
+ .addr = DW2102_LED_CTRL,
+ .flags = 0,
+ .buf = led_off,
+ .len = 1
+ };
+ struct dvb_usb_adapter *udev_adap =
+ (struct dvb_usb_adapter *)(fe->dvb->priv);
+
+ if (offon)
+ msg.buf = led_on;
+ i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1);
+}
+
static struct stv0299_config sharp_z0194a_config = {
.demod_address = 0x68,
.inittab = sharp_z0194a_inittab,
@@ -771,6 +973,12 @@ static struct stv0900_config prof_7500_stv0900_config = {
.tun1_adc = 0,/* 2 Vpp */
.path1_mode = 3,
.tun1_type = 3,
+ .set_lock_led = dw210x_led_ctrl,
+};
+
+static struct ds3000_config su3000_ds3000_config = {
+ .demod_address = 0x68,
+ .ci_mode = 1,
};
static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
@@ -885,7 +1093,7 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
return -EIO;
}
-static int s6x0_frontend_attach(struct dvb_usb_adapter *d)
+static int zl100313_frontend_attach(struct dvb_usb_adapter *d)
{
d->fe = dvb_attach(mt312_attach, &zl313_config,
&d->dev->i2c_adap);
@@ -898,41 +1106,108 @@ static int s6x0_frontend_attach(struct dvb_usb_adapter *d)
}
}
+ return -EIO;
+}
+
+static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
+{
+ u8 obuf[] = {7, 1};
+
d->fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- if (dvb_attach(stb6000_attach, d->fe, 0x61,
- &d->dev->i2c_adap)) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
- info("Attached stv0288+stb6000!\n");
- return 0;
- }
- }
+
+ if (d->fe == NULL)
+ return -EIO;
+
+ if (NULL == dvb_attach(stb6000_attach, d->fe, 0x61, &d->dev->i2c_adap))
+ return -EIO;
+
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+
+ dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
+
+ info("Attached stv0288+stb6000!\n");
+
+ return 0;
+
+}
+
+static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
+{
+ struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
+ u8 obuf[] = {7, 1};
d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
&d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
- info("Attached ds3000+ds2020!\n");
- return 0;
- }
- return -EIO;
+ if (d->fe == NULL)
+ return -EIO;
+
+ st->old_set_voltage = d->fe->ops.set_voltage;
+ d->fe->ops.set_voltage = s660_set_voltage;
+
+ dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
+
+ info("Attached ds3000+ds2020!\n");
+
+ return 0;
}
static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
{
+ u8 obuf[] = {7, 1};
+
d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
&d->dev->i2c_adap, 0);
if (d->fe == NULL)
return -EIO;
+
d->fe->ops.set_voltage = dw210x_set_voltage;
+ dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
+
info("Attached STV0900+STB6100A!\n");
return 0;
}
+static int su3000_frontend_attach(struct dvb_usb_adapter *d)
+{
+ u8 obuf[3] = { 0xe, 0x80, 0 };
+ u8 ibuf[] = { 0 };
+
+ if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+ err("command 0x0e transfer failed.");
+
+ obuf[0] = 0xe;
+ obuf[1] = 0x83;
+ obuf[2] = 0;
+
+ if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+ err("command 0x0e transfer failed.");
+
+ obuf[0] = 0xe;
+ obuf[1] = 0x83;
+ obuf[2] = 1;
+
+ if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+ err("command 0x0e transfer failed.");
+
+ obuf[0] = 0x51;
+
+ if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
+ err("command 0x51 transfer failed.");
+
+ d->fe = dvb_attach(ds3000_attach, &su3000_ds3000_config,
+ &d->dev->i2c_adap);
+ if (d->fe == NULL)
+ return -EIO;
+
+ info("Attached DS3000!\n");
+
+ return 0;
+}
+
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -1067,10 +1342,49 @@ static struct rc_map_table rc_map_tbs_table[] = {
{ 0xf89b, KEY_MODE }
};
+static struct rc_map_table rc_map_su3000_table[] = {
+ { 0x25, KEY_POWER }, /* right-bottom Red */
+ { 0x0a, KEY_MUTE }, /* -/-- */
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x00, KEY_0 },
+ { 0x20, KEY_UP }, /* CH+ */
+ { 0x21, KEY_DOWN }, /* CH+ */
+ { 0x12, KEY_VOLUMEUP }, /* Brightness Up */
+ { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */
+ { 0x1f, KEY_RECORD },
+ { 0x17, KEY_PLAY },
+ { 0x16, KEY_PAUSE },
+ { 0x0b, KEY_STOP },
+ { 0x27, KEY_FASTFORWARD },/* >> */
+ { 0x26, KEY_REWIND }, /* << */
+ { 0x0d, KEY_OK }, /* Mute */
+ { 0x11, KEY_LEFT }, /* VOL- */
+ { 0x10, KEY_RIGHT }, /* VOL+ */
+ { 0x29, KEY_BACK }, /* button under 9 */
+ { 0x2c, KEY_MENU }, /* TTX */
+ { 0x2b, KEY_EPG }, /* EPG */
+ { 0x1e, KEY_RED }, /* OSD */
+ { 0x0e, KEY_GREEN }, /* Window */
+ { 0x2d, KEY_YELLOW }, /* button under << */
+ { 0x0f, KEY_BLUE }, /* bottom yellow button */
+ { 0x14, KEY_AUDIO }, /* Snapshot */
+ { 0x38, KEY_TV }, /* TV/Radio */
+ { 0x0c, KEY_ESC } /* upper Red buttton */
+};
+
static struct rc_map_dvb_usb_table_table keys_tables[] = {
{ rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) },
{ rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) },
{ rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) },
+ { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) },
};
static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -1089,7 +1403,8 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
if ((ir_keymap > 0) && (ir_keymap <= ARRAY_SIZE(keys_tables))) {
keymap = keys_tables[ir_keymap - 1].rc_keys ;
keymap_size = keys_tables[ir_keymap - 1].rc_keys_size;
- }
+ } else if (ir_keymap > ARRAY_SIZE(keys_tables))
+ return 0; /* none */
*state = REMOTE_NO_KEY_PRESSED;
if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
@@ -1125,6 +1440,11 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(0x3011, USB_PID_PROF_1100)},
{USB_DEVICE(0x9022, USB_PID_TEVII_S660)},
{USB_DEVICE(0x3034, 0x7500)},
+ {USB_DEVICE(0x1f4d, 0x3000)},
+ {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)},
+ {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)},
+ {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)},
+ {USB_DEVICE(0x1f4d, 0x3100)},
{ }
};
@@ -1184,11 +1504,6 @@ static int dw2102_load_firmware(struct usb_device *dev,
}
/* init registers */
switch (dev->descriptor.idProduct) {
- case USB_PID_PROF_1100:
- s6x0_properties.rc.legacy.rc_map_table = rc_map_tbs_table;
- s6x0_properties.rc.legacy.rc_map_size =
- ARRAY_SIZE(rc_map_tbs_table);
- break;
case USB_PID_TEVII_S650:
dw2104_properties.rc.legacy.rc_map_table = rc_map_tevii_table;
dw2104_properties.rc.legacy.rc_map_size =
@@ -1271,8 +1586,6 @@ static struct dvb_usb_device_properties dw2102_properties = {
.adapter = {
{
.frontend_attach = dw2102_frontend_attach,
- .streaming_ctrl = NULL,
- .tuner_attach = NULL,
.stream = {
.type = USB_BULK,
.count = 8,
@@ -1324,8 +1637,6 @@ static struct dvb_usb_device_properties dw2104_properties = {
.adapter = {
{
.frontend_attach = dw2104_frontend_attach,
- .streaming_ctrl = NULL,
- /*.tuner_attach = dw2104_tuner_attach,*/
.stream = {
.type = USB_BULK,
.count = 8,
@@ -1373,7 +1684,6 @@ static struct dvb_usb_device_properties dw3101_properties = {
.adapter = {
{
.frontend_attach = dw3101_frontend_attach,
- .streaming_ctrl = NULL,
.tuner_attach = dw3101_tuner_attach,
.stream = {
.type = USB_BULK,
@@ -1399,6 +1709,7 @@ static struct dvb_usb_device_properties dw3101_properties = {
static struct dvb_usb_device_properties s6x0_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
+ .size_of_priv = sizeof(struct s6x0_state),
.firmware = "dvb-usb-s630.fw",
.no_reconnect = 1,
@@ -1416,9 +1727,7 @@ static struct dvb_usb_device_properties s6x0_properties = {
.read_mac_address = s6x0_read_mac_address,
.adapter = {
{
- .frontend_attach = s6x0_frontend_attach,
- .streaming_ctrl = NULL,
- .tuner_attach = NULL,
+ .frontend_attach = zl100313_frontend_attach,
.stream = {
.type = USB_BULK,
.count = 8,
@@ -1431,23 +1740,41 @@ static struct dvb_usb_device_properties s6x0_properties = {
},
}
},
- .num_device_descs = 3,
+ .num_device_descs = 1,
.devices = {
{"TeVii S630 USB",
{&dw2102_table[6], NULL},
{NULL},
},
- {"Prof 1100 USB ",
- {&dw2102_table[7], NULL},
- {NULL},
- },
- {"TeVii S660 USB",
- {&dw2102_table[8], NULL},
- {NULL},
- },
}
};
+struct dvb_usb_device_properties *p1100;
+static struct dvb_usb_device_description d1100 = {
+ "Prof 1100 USB ",
+ {&dw2102_table[7], NULL},
+ {NULL},
+};
+
+struct dvb_usb_device_properties *s660;
+static struct dvb_usb_device_description d660 = {
+ "TeVii S660 USB",
+ {&dw2102_table[8], NULL},
+ {NULL},
+};
+
+static struct dvb_usb_device_description d480_1 = {
+ "TeVii S480.1 USB",
+ {&dw2102_table[12], NULL},
+ {NULL},
+};
+
+static struct dvb_usb_device_description d480_2 = {
+ "TeVii S480.2 USB",
+ {&dw2102_table[13], NULL},
+ {NULL},
+};
+
struct dvb_usb_device_properties *p7500;
static struct dvb_usb_device_description d7500 = {
"Prof 7500 USB DVB-S2",
@@ -1455,17 +1782,97 @@ static struct dvb_usb_device_description d7500 = {
{NULL},
};
+static struct dvb_usb_device_properties su3000_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .size_of_priv = sizeof(struct su3000_state),
+ .power_ctrl = su3000_power_ctrl,
+ .num_adapters = 1,
+ .identify_state = su3000_identify_state,
+ .i2c_algo = &su3000_i2c_algo,
+
+ .rc.legacy = {
+ .rc_map_table = rc_map_su3000_table,
+ .rc_map_size = ARRAY_SIZE(rc_map_su3000_table),
+ .rc_interval = 150,
+ .rc_query = dw2102_rc_query,
+ },
+
+ .read_mac_address = su3000_read_mac_address,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .adapter = {
+ {
+ .streaming_ctrl = su3000_streaming_ctrl,
+ .frontend_attach = su3000_frontend_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ }
+ }
+ },
+ .num_device_descs = 3,
+ .devices = {
+ { "SU3000HD DVB-S USB2.0",
+ { &dw2102_table[10], NULL },
+ { NULL },
+ },
+ { "Terratec Cinergy S2 USB HD",
+ { &dw2102_table[11], NULL },
+ { NULL },
+ },
+ { "X3M TV SPC1400HD PCI",
+ { &dw2102_table[14], NULL },
+ { NULL },
+ },
+ }
+};
+
static int dw2102_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ p1100 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
+ if (!p1100)
+ return -ENOMEM;
+ /* copy default structure */
+ memcpy(p1100, &s6x0_properties,
+ sizeof(struct dvb_usb_device_properties));
+ /* fill only different fields */
+ p1100->firmware = "dvb-usb-p1100.fw";
+ p1100->devices[0] = d1100;
+ p1100->rc.legacy.rc_map_table = rc_map_tbs_table;
+ p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table);
+ p1100->adapter->frontend_attach = stv0288_frontend_attach;
+
+ s660 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
+ if (!s660) {
+ kfree(p1100);
+ return -ENOMEM;
+ }
+ memcpy(s660, &s6x0_properties,
+ sizeof(struct dvb_usb_device_properties));
+ s660->firmware = "dvb-usb-s660.fw";
+ s660->num_device_descs = 3;
+ s660->devices[0] = d660;
+ s660->devices[1] = d480_1;
+ s660->devices[2] = d480_2;
+ s660->adapter->frontend_attach = ds3000_frontend_attach;
p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
- if (!p7500)
+ if (!p7500) {
+ kfree(p1100);
+ kfree(s660);
return -ENOMEM;
- /* copy default structure */
+ }
memcpy(p7500, &s6x0_properties,
sizeof(struct dvb_usb_device_properties));
- /* fill only different fields */
p7500->firmware = "dvb-usb-p7500.fw";
p7500->devices[0] = d7500;
p7500->rc.legacy.rc_map_table = rc_map_tbs_table;
@@ -1480,8 +1887,14 @@ static int dw2102_probe(struct usb_interface *intf,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &s6x0_properties,
THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, p1100,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, s660,
+ THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, p7500,
- THIS_MODULE, NULL, adapter_nr))
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &su3000_properties,
+ THIS_MODULE, NULL, adapter_nr))
return 0;
return -ENODEV;
@@ -1514,7 +1927,8 @@ module_exit(dw2102_module_exit);
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
" DVB-C 3101 USB2.0,"
- " TeVii S600, S630, S650, S660 USB2.0,"
- " Prof 1100, 7500 USB2.0 devices");
+ " TeVii S600, S630, S650, S660, S480,"
+ " Prof 1100, 7500 USB2.0,"
+ " Geniatech SU3000 devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c
new file mode 100644
index 000000000000..08f8842ad280
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c
@@ -0,0 +1,807 @@
+/*
+ * Linux driver for Technisat DVB-S/S2 USB 2.0 device
+ *
+ * Copyright (C) 2010 Patrick Boettcher,
+ * Kernel Labs Inc. PO Box 745, St James, NY 11780
+ *
+ * Development was sponsored by Technisat Digital UK Limited, whose
+ * registered office is Witan Gate House 500 - 600 Witan Gate West,
+ * Milton Keynes, MK9 1SH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ *
+ * You 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.
+ *
+ * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND
+ * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER
+ * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the
+ * GNU General Public License for more details.
+ */
+
+#define DVB_USB_LOG_PREFIX "technisat-usb2"
+#include "dvb-usb.h"
+
+#include "stv6110x.h"
+#include "stv090x.h"
+
+/* module parameters */
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "set debugging level (bit-mask: 1=info,2=eeprom,4=i2c,8=rc)." \
+ DVB_USB_DEBUG_STATUS);
+
+/* disables all LED control command and
+ * also does not start the signal polling thread */
+static int disable_led_control;
+module_param(disable_led_control, int, 0444);
+MODULE_PARM_DESC(disable_led_control,
+ "disable LED control of the device "
+ "(default: 0 - LED control is active).");
+
+/* device private data */
+struct technisat_usb2_state {
+ struct dvb_usb_device *dev;
+ struct delayed_work green_led_work;
+ u8 power_state;
+
+ u16 last_scan_code;
+};
+
+/* debug print helpers */
+#define deb_info(args...) dprintk(debug, 0x01, args)
+#define deb_eeprom(args...) dprintk(debug, 0x02, args)
+#define deb_i2c(args...) dprintk(debug, 0x04, args)
+#define deb_rc(args...) dprintk(debug, 0x08, args)
+
+/* vendor requests */
+#define SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST 0xB3
+#define SET_FRONT_END_RESET_VENDOR_REQUEST 0xB4
+#define GET_VERSION_INFO_VENDOR_REQUEST 0xB5
+#define SET_GREEN_LED_VENDOR_REQUEST 0xB6
+#define SET_RED_LED_VENDOR_REQUEST 0xB7
+#define GET_IR_DATA_VENDOR_REQUEST 0xB8
+#define SET_LED_TIMER_DIVIDER_VENDOR_REQUEST 0xB9
+#define SET_USB_REENUMERATION 0xBA
+
+/* i2c-access methods */
+#define I2C_SPEED_100KHZ_BIT 0x40
+
+#define I2C_STATUS_NAK 7
+#define I2C_STATUS_OK 8
+
+static int technisat_usb2_i2c_access(struct usb_device *udev,
+ u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
+{
+ u8 b[64];
+ int ret, actual_length;
+
+ deb_i2c("i2c-access: %02x, tx: ", device_addr);
+ debug_dump(tx, txlen, deb_i2c);
+ deb_i2c(" ");
+
+ if (txlen > 62) {
+ err("i2c TX buffer can't exceed 62 bytes (dev 0x%02x)",
+ device_addr);
+ txlen = 62;
+ }
+ if (rxlen > 62) {
+ err("i2c RX buffer can't exceed 62 bytes (dev 0x%02x)",
+ device_addr);
+ txlen = 62;
+ }
+
+ b[0] = I2C_SPEED_100KHZ_BIT;
+ b[1] = device_addr << 1;
+
+ if (rx != NULL) {
+ b[0] |= rxlen;
+ b[1] |= 1;
+ }
+
+ memcpy(&b[2], tx, txlen);
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, 0x01),
+ b, 2 + txlen,
+ NULL, 1000);
+
+ if (ret < 0) {
+ err("i2c-error: out failed %02x = %d", device_addr, ret);
+ return -ENODEV;
+ }
+
+ ret = usb_bulk_msg(udev,
+ usb_rcvbulkpipe(udev, 0x01),
+ b, 64, &actual_length, 1000);
+ if (ret < 0) {
+ err("i2c-error: in failed %02x = %d", device_addr, ret);
+ return -ENODEV;
+ }
+
+ if (b[0] != I2C_STATUS_OK) {
+ err("i2c-error: %02x = %d", device_addr, b[0]);
+ /* handle tuner-i2c-nak */
+ if (!(b[0] == I2C_STATUS_NAK &&
+ device_addr == 0x60
+ /* && device_is_technisat_usb2 */))
+ return -ENODEV;
+ }
+
+ deb_i2c("status: %d, ", b[0]);
+
+ if (rx != NULL) {
+ memcpy(rx, &b[2], rxlen);
+
+ deb_i2c("rx (%d): ", rxlen);
+ debug_dump(rx, rxlen, deb_i2c);
+ }
+
+ deb_i2c("\n");
+
+ return 0;
+}
+
+static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ int ret = 0, i;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+ /* Ensure nobody else hits the i2c bus while we're sending our
+ sequence of messages, (such as the remote control thread) */
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ if (i+1 < num && msg[i+1].flags & I2C_M_RD) {
+ ret = technisat_usb2_i2c_access(d->udev, msg[i+1].addr,
+ msg[i].buf, msg[i].len,
+ msg[i+1].buf, msg[i+1].len);
+ if (ret != 0)
+ break;
+ i++;
+ } else {
+ ret = technisat_usb2_i2c_access(d->udev, msg[i].addr,
+ msg[i].buf, msg[i].len,
+ NULL, 0);
+ if (ret != 0)
+ break;
+ }
+ }
+
+ if (ret == 0)
+ ret = i;
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 technisat_usb2_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm technisat_usb2_i2c_algo = {
+ .master_xfer = technisat_usb2_i2c_xfer,
+ .functionality = technisat_usb2_i2c_func,
+};
+
+#if 0
+static void technisat_usb2_frontend_reset(struct usb_device *udev)
+{
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ SET_FRONT_END_RESET_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 10, 0,
+ NULL, 0, 500);
+}
+#endif
+
+/* LED control */
+enum technisat_usb2_led_state {
+ LED_OFF,
+ LED_BLINK,
+ LED_ON,
+ LED_UNDEFINED
+};
+
+static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state)
+{
+ int ret;
+
+ u8 led[8] = {
+ red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
+ 0
+ };
+
+ if (disable_led_control && state != LED_OFF)
+ return 0;
+
+ switch (state) {
+ case LED_ON:
+ led[1] = 0x82;
+ break;
+ case LED_BLINK:
+ led[1] = 0x82;
+ if (red) {
+ led[2] = 0x02;
+ led[3] = 10;
+ led[4] = 10;
+ } else {
+ led[2] = 0xff;
+ led[3] = 50;
+ led[4] = 50;
+ }
+ led[5] = 1;
+ break;
+
+ default:
+ case LED_OFF:
+ led[1] = 0x80;
+ break;
+ }
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
+ red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0,
+ led, sizeof(led), 500);
+
+ mutex_unlock(&d->i2c_mutex);
+ return ret;
+}
+
+static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green)
+{
+ int ret;
+ u8 b = 0;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
+ SET_LED_TIMER_DIVIDER_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ (red << 8) | green, 0,
+ &b, 1, 500);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static void technisat_usb2_green_led_control(struct work_struct *work)
+{
+ struct technisat_usb2_state *state =
+ container_of(work, struct technisat_usb2_state, green_led_work.work);
+ struct dvb_frontend *fe = state->dev->adapter[0].fe;
+
+ if (state->power_state == 0)
+ goto schedule;
+
+ if (fe != NULL) {
+ enum fe_status status;
+
+ if (fe->ops.read_status(fe, &status) != 0)
+ goto schedule;
+
+ if (status & FE_HAS_LOCK) {
+ u32 ber;
+
+ if (fe->ops.read_ber(fe, &ber) != 0)
+ goto schedule;
+
+ if (ber > 1000)
+ technisat_usb2_set_led(state->dev, 0, LED_BLINK);
+ else
+ technisat_usb2_set_led(state->dev, 0, LED_ON);
+ } else
+ technisat_usb2_set_led(state->dev, 0, LED_OFF);
+ }
+
+schedule:
+ schedule_delayed_work(&state->green_led_work,
+ msecs_to_jiffies(500));
+}
+
+/* method to find out whether the firmware has to be downloaded or not */
+static int technisat_usb2_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc, int *cold)
+{
+ int ret;
+ u8 version[3];
+
+ /* first select the interface */
+ if (usb_set_interface(udev, 0, 1) != 0)
+ err("could not set alternate setting to 0");
+ else
+ info("set alternate setting");
+
+ *cold = 0; /* by default do not download a firmware - just in case something is wrong */
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ GET_VERSION_INFO_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ 0, 0,
+ version, sizeof(version), 500);
+
+ if (ret < 0)
+ *cold = 1;
+ else {
+ info("firmware version: %d.%d", version[1], version[2]);
+ *cold = 0;
+ }
+
+ return 0;
+}
+
+/* power control */
+static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level)
+{
+ struct technisat_usb2_state *state = d->priv;
+
+ state->power_state = level;
+
+ if (disable_led_control)
+ return 0;
+
+ /* green led is turned off in any case - will be turned on when tuning */
+ technisat_usb2_set_led(d, 0, LED_OFF);
+ /* red led is turned on all the time */
+ technisat_usb2_set_led(d, 1, LED_ON);
+ return 0;
+}
+
+/* mac address reading - from the eeprom */
+#if 0
+static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d)
+{
+ u8 reg;
+ u8 b[16];
+ int i, j;
+
+ /* full EEPROM dump */
+ for (j = 0; j < 256 * 4; j += 16) {
+ reg = j;
+ if (technisat_usb2_i2c_access(d->udev, 0x50 + j / 256, &reg, 1, b, 16) != 0)
+ break;
+
+ deb_eeprom("EEPROM: %01x%02x: ", j / 256, reg);
+ for (i = 0; i < 16; i++)
+ deb_eeprom("%02x ", b[i]);
+ deb_eeprom("\n");
+ }
+}
+#endif
+
+static u8 technisat_usb2_calc_lrc(const u8 *b, u16 length)
+{
+ u8 lrc = 0;
+ while (--length)
+ lrc ^= *b++;
+ return lrc;
+}
+
+static int technisat_usb2_eeprom_lrc_read(struct dvb_usb_device *d,
+ u16 offset, u8 *b, u16 length, u8 tries)
+{
+ u8 bo = offset & 0xff;
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0x50 | ((offset >> 8) & 0x3),
+ .buf = &bo,
+ .len = 1
+ }, {
+ .addr = 0x50 | ((offset >> 8) & 0x3),
+ .flags = I2C_M_RD,
+ .buf = b,
+ .len = length
+ }
+ };
+
+ while (tries--) {
+ int status;
+
+ if (i2c_transfer(&d->i2c_adap, msg, 2) != 2)
+ break;
+
+ status =
+ technisat_usb2_calc_lrc(b, length - 1) == b[length - 1];
+
+ if (status)
+ return 0;
+ }
+
+ return -EREMOTEIO;
+}
+
+#define EEPROM_MAC_START 0x3f8
+#define EEPROM_MAC_TOTAL 8
+static int technisat_usb2_read_mac_address(struct dvb_usb_device *d,
+ u8 mac[])
+{
+ u8 buf[EEPROM_MAC_TOTAL];
+
+ if (technisat_usb2_eeprom_lrc_read(d, EEPROM_MAC_START,
+ buf, EEPROM_MAC_TOTAL, 4) != 0)
+ return -ENODEV;
+
+ memcpy(mac, buf, 6);
+ return 0;
+}
+
+/* frontend attach */
+static int technisat_usb2_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ int i;
+ u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */
+
+ gpio[2] = 1; /* high - voltage ? */
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ gpio[0] = 1;
+ break;
+ case SEC_VOLTAGE_18:
+ gpio[0] = 1;
+ gpio[1] = 1;
+ break;
+ default:
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0)
+ return -EREMOTEIO;
+ return 0;
+}
+
+static struct stv090x_config technisat_usb2_stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 8000000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_DVBCI,
+ .ts1_clk = 13400000,
+ .ts1_tei = 1,
+
+ .repeater_level = STV090x_RPTLEVEL_64,
+
+ .tuner_bbgain = 6,
+};
+
+static struct stv6110x_config technisat_usb2_stv6110x_config = {
+ .addr = 0x60,
+ .refclk = 16000000,
+ .clk_div = 2,
+};
+
+static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
+{
+ struct usb_device *udev = a->dev->udev;
+ int ret;
+
+ a->fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
+ &a->dev->i2c_adap, STV090x_DEMODULATOR_0);
+
+ if (a->fe) {
+ struct stv6110x_devctl *ctl;
+
+ ctl = dvb_attach(stv6110x_attach,
+ a->fe,
+ &technisat_usb2_stv6110x_config,
+ &a->dev->i2c_adap);
+
+ if (ctl) {
+ technisat_usb2_stv090x_config.tuner_init = ctl->tuner_init;
+ technisat_usb2_stv090x_config.tuner_sleep = ctl->tuner_sleep;
+ technisat_usb2_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
+ technisat_usb2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+ technisat_usb2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+ technisat_usb2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ technisat_usb2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ technisat_usb2_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
+ technisat_usb2_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
+ technisat_usb2_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
+ technisat_usb2_stv090x_config.tuner_get_status = ctl->tuner_get_status;
+
+ /* call the init function once to initialize
+ tuner's clock output divider and demod's
+ master clock */
+ if (a->fe->ops.init)
+ a->fe->ops.init(a->fe);
+
+ if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0,
+ NULL, 0, 500);
+ mutex_unlock(&a->dev->i2c_mutex);
+
+ if (ret != 0)
+ err("could not set IF_CLK to external");
+
+ a->fe->ops.set_voltage = technisat_usb2_set_voltage;
+
+ /* if everything was successful assign a nice name to the frontend */
+ strlcpy(a->fe->ops.info.name, a->dev->desc->name,
+ sizeof(a->fe->ops.info.name));
+ } else {
+ dvb_frontend_detach(a->fe);
+ a->fe = NULL;
+ }
+ }
+
+ technisat_usb2_set_led_timer(a->dev, 1, 1);
+
+ return a->fe == NULL ? -ENODEV : 0;
+}
+
+/* Remote control */
+
+/* the device is giving providing raw IR-signals to the host mapping
+ * it only to one remote control is just the default implementation
+ */
+#define NOMINAL_IR_BIT_TRANSITION_TIME_US 889
+#define NOMINAL_IR_BIT_TIME_US (2 * NOMINAL_IR_BIT_TRANSITION_TIME_US)
+
+#define FIRMWARE_CLOCK_TICK 83333
+#define FIRMWARE_CLOCK_DIVISOR 256
+
+#define IR_PERCENT_TOLERANCE 15
+
+#define NOMINAL_IR_BIT_TRANSITION_TICKS ((NOMINAL_IR_BIT_TRANSITION_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK)
+#define NOMINAL_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICKS / FIRMWARE_CLOCK_DIVISOR)
+
+#define NOMINAL_IR_BIT_TIME_TICKS ((NOMINAL_IR_BIT_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK)
+#define NOMINAL_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICKS / FIRMWARE_CLOCK_DIVISOR)
+
+#define MINIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT - ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
+#define MAXIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT + ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
+
+#define MINIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT - ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
+#define MAXIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT + ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
+
+static int technisat_usb2_get_ir(struct dvb_usb_device *d)
+{
+ u8 buf[62], *b;
+ int ret;
+ struct ir_raw_event ev;
+
+ buf[0] = GET_IR_DATA_VENDOR_REQUEST;
+ buf[1] = 0x08;
+ buf[2] = 0x8f;
+ buf[3] = MINIMUM_IR_BIT_TRANSITION_TICK_COUNT;
+ buf[4] = MAXIMUM_IR_BIT_TIME_TICK_COUNT;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+ ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
+ GET_IR_DATA_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0,
+ buf, 5, 500);
+ if (ret < 0)
+ goto unlock;
+
+ buf[1] = 0;
+ buf[2] = 0;
+ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
+ GET_IR_DATA_VENDOR_REQUEST,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ 0x8080, 0,
+ buf, sizeof(buf), 500);
+
+unlock:
+ mutex_unlock(&d->i2c_mutex);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 1)
+ return 0; /* no key pressed */
+
+ /* decoding */
+ b = buf+1;
+
+#if 0
+ deb_rc("RC: %d ", ret);
+ debug_dump(b, ret, deb_rc);
+#endif
+
+ ev.pulse = 0;
+ while (1) {
+ ev.pulse = !ev.pulse;
+ ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000;
+ ir_raw_event_store(d->rc_dev, &ev);
+
+ b++;
+ if (*b == 0xff) {
+ ev.pulse = 0;
+ ev.duration = 888888*2;
+ ir_raw_event_store(d->rc_dev, &ev);
+ break;
+ }
+ }
+
+ ir_raw_event_handle(d->rc_dev);
+
+ return 1;
+}
+
+static int technisat_usb2_rc_query(struct dvb_usb_device *d)
+{
+ int ret = technisat_usb2_get_ir(d);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0)
+ return 0;
+
+ if (!disable_led_control)
+ technisat_usb2_set_led(d, 1, LED_BLINK);
+
+ return 0;
+}
+
+/* DVB-USB and USB stuff follows */
+static struct usb_device_id technisat_usb2_id_table[] = {
+ { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_DVB_S2) },
+ { 0 } /* Terminating entry */
+};
+
+/* device description */
+static struct dvb_usb_device_properties technisat_usb2_devices = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .identify_state = technisat_usb2_identify_state,
+ .firmware = "dvb-usb-SkyStar_USB_HD_FW_v17_63.HEX.fw",
+
+ .size_of_priv = sizeof(struct technisat_usb2_state),
+
+ .i2c_algo = &technisat_usb2_i2c_algo,
+
+ .power_ctrl = technisat_usb2_power_ctrl,
+ .read_mac_address = technisat_usb2_read_mac_address,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .frontend_attach = technisat_usb2_frontend_attach,
+
+ .stream = {
+ .type = USB_ISOC,
+ .count = 8,
+ .endpoint = 0x2,
+ .u = {
+ .isoc = {
+ .framesperurb = 32,
+ .framesize = 2048,
+ .interval = 3,
+ }
+ }
+ },
+
+ .size_of_priv = 0,
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "Technisat SkyStar USB HD (DVB-S/S2)",
+ { &technisat_usb2_id_table[0], NULL },
+ { NULL },
+ },
+ },
+
+ .rc.core = {
+ .rc_interval = 100,
+ .rc_codes = RC_MAP_TECHNISAT_USB2,
+ .module_name = "technisat-usb2",
+ .rc_query = technisat_usb2_rc_query,
+ .allowed_protos = RC_TYPE_ALL,
+ .driver_type = RC_DRIVER_IR_RAW,
+ }
+};
+
+static int technisat_usb2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct dvb_usb_device *dev;
+
+ if (dvb_usb_device_init(intf, &technisat_usb2_devices, THIS_MODULE,
+ &dev, adapter_nr) != 0)
+ return -ENODEV;
+
+ if (dev) {
+ struct technisat_usb2_state *state = dev->priv;
+ state->dev = dev;
+
+ if (!disable_led_control) {
+ INIT_DELAYED_WORK(&state->green_led_work,
+ technisat_usb2_green_led_control);
+ schedule_delayed_work(&state->green_led_work,
+ msecs_to_jiffies(500));
+ }
+ }
+
+ return 0;
+}
+
+static void technisat_usb2_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *dev = usb_get_intfdata(intf);
+
+ /* work and stuff was only created when the device is is hot-state */
+ if (dev != NULL) {
+ struct technisat_usb2_state *state = dev->priv;
+ if (state != NULL) {
+ cancel_delayed_work_sync(&state->green_led_work);
+ flush_scheduled_work();
+ }
+ }
+
+ dvb_usb_device_exit(intf);
+}
+
+static struct usb_driver technisat_usb2_driver = {
+ .name = "dvb_usb_technisat_usb2",
+ .probe = technisat_usb2_probe,
+ .disconnect = technisat_usb2_disconnect,
+ .id_table = technisat_usb2_id_table,
+};
+
+/* module stuff */
+static int __init technisat_usb2_module_init(void)
+{
+ int result = usb_register(&technisat_usb2_driver);
+ if (result) {
+ err("usb_register failed. Code %d", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit technisat_usb2_module_exit(void)
+{
+ usb_deregister(&technisat_usb2_driver);
+}
+
+module_init(technisat_usb2_module_init);
+module_exit(technisat_usb2_module_exit);
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@kernellabs.com>");
+MODULE_DESCRIPTION("Driver for Technisat DVB-S/S2 USB 2.0 device");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig
index 4afa29256df1..f3e9448c3955 100644
--- a/drivers/media/dvb/firewire/Kconfig
+++ b/drivers/media/dvb/firewire/Kconfig
@@ -1,6 +1,6 @@
config DVB_FIREDTV
tristate "FireDTV and FloppyDTV"
- depends on DVB_CORE && (FIREWIRE || IEEE1394)
+ depends on DVB_CORE && FIREWIRE
help
Support for DVB receivers from Digital Everywhere
which are connected via IEEE 1394 (FireWire).
@@ -13,12 +13,6 @@ config DVB_FIREDTV
if DVB_FIREDTV
-config DVB_FIREDTV_FIREWIRE
- def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m)
-
-config DVB_FIREDTV_IEEE1394
- def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m)
-
config DVB_FIREDTV_INPUT
def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m)
diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile
index da84203d51c6..357b3aab186b 100644
--- a/drivers/media/dvb/firewire/Makefile
+++ b/drivers/media/dvb/firewire/Makefile
@@ -1,9 +1,6 @@
obj-$(CONFIG_DVB_FIREDTV) += firedtv.o
-firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o
-firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o
-firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o
+firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o
firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o
ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-$(CONFIG_DVB_FIREDTV_IEEE1394) += -Idrivers/ieee1394
diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c
deleted file mode 100644
index b34ca7afb0e6..000000000000
--- a/drivers/media/dvb/firewire/firedtv-1394.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * FireDTV driver -- ieee1394 I/O backend
- *
- * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
- * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
- * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-#include <dma.h>
-#include <csr1212.h>
-#include <highlevel.h>
-#include <hosts.h>
-#include <ieee1394.h>
-#include <iso.h>
-#include <nodemgr.h>
-
-#include <dvb_demux.h>
-
-#include "firedtv.h"
-
-static LIST_HEAD(node_list);
-static DEFINE_SPINLOCK(node_list_lock);
-
-#define CIP_HEADER_SIZE 8
-#define MPEG2_TS_HEADER_SIZE 4
-#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
-
-static void rawiso_activity_cb(struct hpsb_iso *iso)
-{
- struct firedtv *f, *fdtv = NULL;
- unsigned int i, num, packet;
- unsigned char *buf;
- unsigned long flags;
- int count;
-
- spin_lock_irqsave(&node_list_lock, flags);
- list_for_each_entry(f, &node_list, list)
- if (f->backend_data == iso) {
- fdtv = f;
- break;
- }
- spin_unlock_irqrestore(&node_list_lock, flags);
-
- packet = iso->first_packet;
- num = hpsb_iso_n_ready(iso);
-
- if (!fdtv) {
- pr_err("received at unknown iso channel\n");
- goto out;
- }
-
- for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) {
- buf = dma_region_i(&iso->data_buf, unsigned char,
- iso->infos[packet].offset + CIP_HEADER_SIZE);
- count = (iso->infos[packet].len - CIP_HEADER_SIZE) /
- MPEG2_TS_SOURCE_PACKET_SIZE;
-
- /* ignore empty packet */
- if (iso->infos[packet].len <= CIP_HEADER_SIZE)
- continue;
-
- while (count--) {
- if (buf[MPEG2_TS_HEADER_SIZE] == 0x47)
- dvb_dmx_swfilter_packets(&fdtv->demux,
- &buf[MPEG2_TS_HEADER_SIZE], 1);
- else
- dev_err(fdtv->device,
- "skipping invalid packet\n");
- buf += MPEG2_TS_SOURCE_PACKET_SIZE;
- }
- }
-out:
- hpsb_iso_recv_release_packets(iso, num);
-}
-
-static inline struct node_entry *node_of(struct firedtv *fdtv)
-{
- return container_of(fdtv->device, struct unit_directory, device)->ne;
-}
-
-static int node_lock(struct firedtv *fdtv, u64 addr, void *data)
-{
- quadlet_t *d = data;
- int ret;
-
- ret = hpsb_node_lock(node_of(fdtv), addr,
- EXTCODE_COMPARE_SWAP, &d[1], d[0]);
- d[0] = d[1];
-
- return ret;
-}
-
-static int node_read(struct firedtv *fdtv, u64 addr, void *data)
-{
- return hpsb_node_read(node_of(fdtv), addr, data, 4);
-}
-
-static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
-{
- return hpsb_node_write(node_of(fdtv), addr, data, len);
-}
-
-#define FDTV_ISO_BUFFER_PACKETS 256
-#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200)
-
-static int start_iso(struct firedtv *fdtv)
-{
- struct hpsb_iso *iso_handle;
- int ret;
-
- iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host,
- FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS,
- fdtv->isochannel, HPSB_ISO_DMA_DEFAULT,
- -1, /* stat.config.irq_interval */
- rawiso_activity_cb);
- if (iso_handle == NULL) {
- dev_err(fdtv->device, "cannot initialize iso receive\n");
- return -ENOMEM;
- }
- fdtv->backend_data = iso_handle;
-
- ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0);
- if (ret != 0) {
- dev_err(fdtv->device, "cannot start iso receive\n");
- hpsb_iso_shutdown(iso_handle);
- fdtv->backend_data = NULL;
- }
- return ret;
-}
-
-static void stop_iso(struct firedtv *fdtv)
-{
- struct hpsb_iso *iso_handle = fdtv->backend_data;
-
- if (iso_handle != NULL) {
- hpsb_iso_stop(iso_handle);
- hpsb_iso_shutdown(iso_handle);
- }
- fdtv->backend_data = NULL;
-}
-
-static const struct firedtv_backend fdtv_1394_backend = {
- .lock = node_lock,
- .read = node_read,
- .write = node_write,
- .start_iso = start_iso,
- .stop_iso = stop_iso,
-};
-
-static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
- int cts, u8 *data, size_t length)
-{
- struct firedtv *f, *fdtv = NULL;
- unsigned long flags;
- int su;
-
- if (length == 0 || (data[0] & 0xf0) != 0)
- return;
-
- su = data[1] & 0x7;
-
- spin_lock_irqsave(&node_list_lock, flags);
- list_for_each_entry(f, &node_list, list)
- if (node_of(f)->host == host &&
- node_of(f)->nodeid == nodeid &&
- (f->subunit == su || (f->subunit == 0 && su == 0x7))) {
- fdtv = f;
- break;
- }
- spin_unlock_irqrestore(&node_list_lock, flags);
-
- if (fdtv)
- avc_recv(fdtv, data, length);
-}
-
-static int node_probe(struct device *dev)
-{
- struct unit_directory *ud =
- container_of(dev, struct unit_directory, device);
- struct firedtv *fdtv;
- int kv_len, err;
- void *kv_str;
-
- if (ud->model_name_kv) {
- kv_len = (ud->model_name_kv->value.leaf.len - 2) * 4;
- kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
- } else {
- kv_len = 0;
- kv_str = NULL;
- }
- fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len);
- if (!fdtv)
- return -ENOMEM;
-
- /*
- * Work around a bug in udev's path_id script: Use the fw-host's dev
- * instead of the unit directory's dev as parent of the input device.
- */
- err = fdtv_register_rc(fdtv, dev->parent->parent);
- if (err)
- goto fail_free;
-
- spin_lock_irq(&node_list_lock);
- list_add_tail(&fdtv->list, &node_list);
- spin_unlock_irq(&node_list_lock);
-
- err = avc_identify_subunit(fdtv);
- if (err)
- goto fail;
-
- err = fdtv_dvb_register(fdtv);
- if (err)
- goto fail;
-
- avc_register_remote_control(fdtv);
-
- return 0;
-fail:
- spin_lock_irq(&node_list_lock);
- list_del(&fdtv->list);
- spin_unlock_irq(&node_list_lock);
- fdtv_unregister_rc(fdtv);
-fail_free:
- kfree(fdtv);
-
- return err;
-}
-
-static int node_remove(struct device *dev)
-{
- struct firedtv *fdtv = dev_get_drvdata(dev);
-
- fdtv_dvb_unregister(fdtv);
-
- spin_lock_irq(&node_list_lock);
- list_del(&fdtv->list);
- spin_unlock_irq(&node_list_lock);
-
- fdtv_unregister_rc(fdtv);
- kfree(fdtv);
-
- return 0;
-}
-
-static int node_update(struct unit_directory *ud)
-{
- struct firedtv *fdtv = dev_get_drvdata(&ud->device);
-
- if (fdtv->isochannel >= 0)
- cmp_establish_pp_connection(fdtv, fdtv->subunit,
- fdtv->isochannel);
- return 0;
-}
-
-static struct hpsb_protocol_driver fdtv_driver = {
- .name = "firedtv",
- .id_table = fdtv_id_table,
- .update = node_update,
- .driver = {
- .probe = node_probe,
- .remove = node_remove,
- },
-};
-
-static struct hpsb_highlevel fdtv_highlevel = {
- .name = "firedtv",
- .fcp_request = fcp_request,
-};
-
-int __init fdtv_1394_init(void)
-{
- int ret;
-
- hpsb_register_highlevel(&fdtv_highlevel);
- ret = hpsb_register_protocol(&fdtv_driver);
- if (ret) {
- printk(KERN_ERR "firedtv: failed to register protocol\n");
- hpsb_unregister_highlevel(&fdtv_highlevel);
- }
- return ret;
-}
-
-void __exit fdtv_1394_exit(void)
-{
- hpsb_unregister_protocol(&fdtv_driver);
- hpsb_unregister_highlevel(&fdtv_highlevel);
-}
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index f0f1842fab60..fc5ccd8c923a 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -241,8 +241,8 @@ static int avc_write(struct firedtv *fdtv)
if (unlikely(avc_debug))
debug_fcp(fdtv->avc_data, fdtv->avc_data_length);
- err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER,
- fdtv->avc_data, fdtv->avc_data_length);
+ err = fdtv_write(fdtv, FCP_COMMAND_REGISTER,
+ fdtv->avc_data, fdtv->avc_data_length);
if (err) {
dev_err(fdtv->device, "FCP command write failed\n");
@@ -1322,7 +1322,7 @@ static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data)
mutex_lock(&fdtv->avc_mutex);
- ret = fdtv->backend->read(fdtv, addr, data);
+ ret = fdtv_read(fdtv, addr, data);
if (ret < 0)
dev_err(fdtv->device, "CMP: read I/O error\n");
@@ -1340,7 +1340,7 @@ static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
/* data[] is stack-allocated and should not be DMA-mapped. */
memcpy(fdtv->avc_data, data, 8);
- ret = fdtv->backend->lock(fdtv, addr, fdtv->avc_data);
+ ret = fdtv_lock(fdtv, addr, fdtv->avc_data);
if (ret < 0)
dev_err(fdtv->device, "CMP: lock I/O error\n");
else
@@ -1405,10 +1405,7 @@ repeat:
/* FIXME: this is for the worst case - optimize */
set_opcr_overhead_id(opcr, 0);
- /*
- * FIXME: allocate isochronous channel and bandwidth at IRM
- * fdtv->backend->alloc_resources(fdtv, channels_mask, bw);
- */
+ /* FIXME: allocate isochronous channel and bandwidth at IRM */
}
set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1);
@@ -1424,8 +1421,6 @@ repeat:
/*
* FIXME: if old_opcr.P2P_Connections > 0,
* deallocate isochronous channel and bandwidth at IRM
- * if (...)
- * fdtv->backend->dealloc_resources(fdtv, channel, bw);
*/
if (++attempts < 6) /* arbitrary limit */
diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c
index 079e8c5b0475..fd8bbbfa5c59 100644
--- a/drivers/media/dvb/firewire/firedtv-dvb.c
+++ b/drivers/media/dvb/firewire/firedtv-dvb.c
@@ -14,14 +14,9 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
#include <dmxdev.h>
#include <dvb_demux.h>
@@ -166,11 +161,11 @@ int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int fdtv_dvb_register(struct firedtv *fdtv)
+int fdtv_dvb_register(struct firedtv *fdtv, const char *name)
{
int err;
- err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type],
+ err = dvb_register_adapter(&fdtv->adapter, name,
THIS_MODULE, fdtv->device, adapter_nr);
if (err < 0)
goto fail_log;
@@ -210,7 +205,7 @@ int fdtv_dvb_register(struct firedtv *fdtv)
dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx);
- fdtv_frontend_init(fdtv);
+ fdtv_frontend_init(fdtv, name);
err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe);
if (err)
goto fail_net_release;
@@ -248,127 +243,3 @@ void fdtv_dvb_unregister(struct firedtv *fdtv)
dvb_dmx_release(&fdtv->demux);
dvb_unregister_adapter(&fdtv->adapter);
}
-
-const char *fdtv_model_names[] = {
- [FIREDTV_UNKNOWN] = "unknown type",
- [FIREDTV_DVB_S] = "FireDTV S/CI",
- [FIREDTV_DVB_C] = "FireDTV C/CI",
- [FIREDTV_DVB_T] = "FireDTV T/CI",
- [FIREDTV_DVB_S2] = "FireDTV S2 ",
-};
-
-struct firedtv *fdtv_alloc(struct device *dev,
- const struct firedtv_backend *backend,
- const char *name, size_t name_len)
-{
- struct firedtv *fdtv;
- int i;
-
- fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL);
- if (!fdtv)
- return NULL;
-
- dev_set_drvdata(dev, fdtv);
- fdtv->device = dev;
- fdtv->isochannel = -1;
- fdtv->voltage = 0xff;
- fdtv->tone = 0xff;
- fdtv->backend = backend;
-
- mutex_init(&fdtv->avc_mutex);
- init_waitqueue_head(&fdtv->avc_wait);
- mutex_init(&fdtv->demux_mutex);
- INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work);
-
- for (i = ARRAY_SIZE(fdtv_model_names); --i; )
- if (strlen(fdtv_model_names[i]) <= name_len &&
- strncmp(name, fdtv_model_names[i], name_len) == 0)
- break;
- fdtv->type = i;
-
- return fdtv;
-}
-
-#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \
- IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION)
-
-#define DIGITAL_EVERYWHERE_OUI 0x001287
-#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d
-#define AVC_SW_VERSION_ENTRY 0x010001
-
-const struct ieee1394_device_id fdtv_id_table[] = {
- {
- /* FloppyDTV S/CI and FloppyDTV S2 */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000024,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {
- /* FloppyDTV T/CI */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000025,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {
- /* FloppyDTV C/CI */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000026,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {
- /* FireDTV S/CI and FloppyDTV S2 */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000034,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {
- /* FireDTV T/CI */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000035,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {
- /* FireDTV C/CI */
- .match_flags = MATCH_FLAGS,
- .vendor_id = DIGITAL_EVERYWHERE_OUI,
- .model_id = 0x000036,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
- .version = AVC_SW_VERSION_ENTRY,
- }, {}
-};
-MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
-
-static int __init fdtv_init(void)
-{
- int ret;
-
- ret = fdtv_fw_init();
- if (ret < 0)
- return ret;
-
- ret = fdtv_1394_init();
- if (ret < 0)
- fdtv_fw_exit();
-
- return ret;
-}
-
-static void __exit fdtv_exit(void)
-{
- fdtv_1394_exit();
- fdtv_fw_exit();
-}
-
-module_init(fdtv_init);
-module_exit(fdtv_exit);
-
-MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
-MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
-MODULE_DESCRIPTION("FireDTV DVB Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("FireDTV DVB");
diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c
index d10920e2f3a2..8748a61be73d 100644
--- a/drivers/media/dvb/firewire/firedtv-fe.c
+++ b/drivers/media/dvb/firewire/firedtv-fe.c
@@ -36,14 +36,14 @@ static int fdtv_dvb_init(struct dvb_frontend *fe)
return err;
}
- return fdtv->backend->start_iso(fdtv);
+ return fdtv_start_iso(fdtv);
}
static int fdtv_sleep(struct dvb_frontend *fe)
{
struct firedtv *fdtv = fe->sec_priv;
- fdtv->backend->stop_iso(fdtv);
+ fdtv_stop_iso(fdtv);
cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel);
fdtv->isochannel = -1;
return 0;
@@ -165,7 +165,7 @@ static int fdtv_set_property(struct dvb_frontend *fe, struct dtv_property *tvp)
return 0;
}
-void fdtv_frontend_init(struct firedtv *fdtv)
+void fdtv_frontend_init(struct firedtv *fdtv, const char *name)
{
struct dvb_frontend_ops *ops = &fdtv->fe.ops;
struct dvb_frontend_info *fi = &ops->info;
@@ -266,7 +266,7 @@ void fdtv_frontend_init(struct firedtv *fdtv)
dev_err(fdtv->device, "no frontend for model type %d\n",
fdtv->type);
}
- strcpy(fi->name, fdtv_model_names[fdtv->type]);
+ strcpy(fi->name, name);
fdtv->fe.dvb = &fdtv->adapter;
fdtv->fe.sec_priv = fdtv;
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
index 7424b0493f9d..8022b743af91 100644
--- a/drivers/media/dvb/firewire/firedtv-fw.c
+++ b/drivers/media/dvb/firewire/firedtv-fw.c
@@ -9,11 +9,18 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
#include <asm/page.h>
+#include <asm/system.h>
#include <dvb_demux.h>
@@ -41,17 +48,17 @@ static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
return rcode != RCODE_COMPLETE ? -EIO : 0;
}
-static int node_lock(struct firedtv *fdtv, u64 addr, void *data)
+int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data)
{
return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
}
-static int node_read(struct firedtv *fdtv, u64 addr, void *data)
+int fdtv_read(struct firedtv *fdtv, u64 addr, void *data)
{
return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST);
}
-static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
+int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
{
return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST);
}
@@ -67,7 +74,7 @@ static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE)
#define IRQ_INTERVAL 16
-struct firedtv_receive_context {
+struct fdtv_ir_context {
struct fw_iso_context *context;
struct fw_iso_buffer buffer;
int interrupt_packet;
@@ -75,7 +82,7 @@ struct firedtv_receive_context {
char *pages[N_PAGES];
};
-static int queue_iso(struct firedtv_receive_context *ctx, int index)
+static int queue_iso(struct fdtv_ir_context *ctx, int index)
{
struct fw_iso_packet p;
@@ -92,7 +99,7 @@ static void handle_iso(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header, void *data)
{
struct firedtv *fdtv = data;
- struct firedtv_receive_context *ctx = fdtv->backend_data;
+ struct fdtv_ir_context *ctx = fdtv->ir_context;
__be32 *h, *h_end;
int length, err, i = ctx->current_packet;
char *p, *p_end;
@@ -121,9 +128,9 @@ static void handle_iso(struct fw_iso_context *context, u32 cycle,
ctx->current_packet = i;
}
-static int start_iso(struct firedtv *fdtv)
+int fdtv_start_iso(struct firedtv *fdtv)
{
- struct firedtv_receive_context *ctx;
+ struct fdtv_ir_context *ctx;
struct fw_device *device = device_of(fdtv);
int i, err;
@@ -161,7 +168,7 @@ static int start_iso(struct firedtv *fdtv)
if (err)
goto fail;
- fdtv->backend_data = ctx;
+ fdtv->ir_context = ctx;
return 0;
fail:
@@ -174,9 +181,9 @@ fail_free:
return err;
}
-static void stop_iso(struct firedtv *fdtv)
+void fdtv_stop_iso(struct firedtv *fdtv)
{
- struct firedtv_receive_context *ctx = fdtv->backend_data;
+ struct fdtv_ir_context *ctx = fdtv->ir_context;
fw_iso_context_stop(ctx->context);
fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card);
@@ -184,14 +191,6 @@ static void stop_iso(struct firedtv *fdtv)
kfree(ctx);
}
-static const struct firedtv_backend backend = {
- .lock = node_lock,
- .read = node_read,
- .write = node_write,
- .start_iso = start_iso,
- .stop_iso = stop_iso,
-};
-
static void handle_fcp(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source, int generation,
unsigned long long offset, void *payload, size_t length,
@@ -238,6 +237,14 @@ static const struct fw_address_region fcp_region = {
.end = CSR_REGISTER_BASE + CSR_FCP_END,
};
+static const char * const model_names[] = {
+ [FIREDTV_UNKNOWN] = "unknown type",
+ [FIREDTV_DVB_S] = "FireDTV S/CI",
+ [FIREDTV_DVB_C] = "FireDTV C/CI",
+ [FIREDTV_DVB_T] = "FireDTV T/CI",
+ [FIREDTV_DVB_S2] = "FireDTV S2 ",
+};
+
/* Adjust the template string if models with longer names appear. */
#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????")
@@ -245,15 +252,31 @@ static int node_probe(struct device *dev)
{
struct firedtv *fdtv;
char name[MAX_MODEL_NAME_LEN];
- int name_len, err;
-
- name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL,
- name, sizeof(name));
+ int name_len, i, err;
- fdtv = fdtv_alloc(dev, &backend, name, name_len >= 0 ? name_len : 0);
+ fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL);
if (!fdtv)
return -ENOMEM;
+ dev_set_drvdata(dev, fdtv);
+ fdtv->device = dev;
+ fdtv->isochannel = -1;
+ fdtv->voltage = 0xff;
+ fdtv->tone = 0xff;
+
+ mutex_init(&fdtv->avc_mutex);
+ init_waitqueue_head(&fdtv->avc_wait);
+ mutex_init(&fdtv->demux_mutex);
+ INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work);
+
+ name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL,
+ name, sizeof(name));
+ for (i = ARRAY_SIZE(model_names); --i; )
+ if (strlen(model_names[i]) <= name_len &&
+ strncmp(name, model_names[i], name_len) == 0)
+ break;
+ fdtv->type = i;
+
err = fdtv_register_rc(fdtv, dev);
if (err)
goto fail_free;
@@ -266,7 +289,7 @@ static int node_probe(struct device *dev)
if (err)
goto fail;
- err = fdtv_dvb_register(fdtv);
+ err = fdtv_dvb_register(fdtv, model_names[fdtv->type]);
if (err)
goto fail;
@@ -309,6 +332,60 @@ static void node_update(struct fw_unit *unit)
fdtv->isochannel);
}
+#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION)
+
+#define DIGITAL_EVERYWHERE_OUI 0x001287
+#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d
+#define AVC_SW_VERSION_ENTRY 0x010001
+
+static const struct ieee1394_device_id fdtv_id_table[] = {
+ {
+ /* FloppyDTV S/CI and FloppyDTV S2 */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000024,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {
+ /* FloppyDTV T/CI */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000025,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {
+ /* FloppyDTV C/CI */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000026,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {
+ /* FireDTV S/CI and FloppyDTV S2 */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000034,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {
+ /* FireDTV T/CI */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000035,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {
+ /* FireDTV C/CI */
+ .match_flags = MATCH_FLAGS,
+ .vendor_id = DIGITAL_EVERYWHERE_OUI,
+ .model_id = 0x000036,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
+ .version = AVC_SW_VERSION_ENTRY,
+ }, {}
+};
+MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
+
static struct fw_driver fdtv_driver = {
.driver = {
.owner = THIS_MODULE,
@@ -321,7 +398,7 @@ static struct fw_driver fdtv_driver = {
.id_table = fdtv_id_table,
};
-int __init fdtv_fw_init(void)
+static int __init fdtv_init(void)
{
int ret;
@@ -329,11 +406,24 @@ int __init fdtv_fw_init(void)
if (ret < 0)
return ret;
- return driver_register(&fdtv_driver.driver);
+ ret = driver_register(&fdtv_driver.driver);
+ if (ret < 0)
+ fw_core_remove_address_handler(&fcp_handler);
+
+ return ret;
}
-void fdtv_fw_exit(void)
+static void __exit fdtv_exit(void)
{
driver_unregister(&fdtv_driver.driver);
fw_core_remove_address_handler(&fcp_handler);
}
+
+module_init(fdtv_init);
+module_exit(fdtv_exit);
+
+MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
+MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
+MODULE_DESCRIPTION("FireDTV DVB Driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("FireDTV DVB");
diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h
index 78cc28f36914..bd00b04e079d 100644
--- a/drivers/media/dvb/firewire/firedtv.h
+++ b/drivers/media/dvb/firewire/firedtv.h
@@ -70,15 +70,7 @@ enum model_type {
struct device;
struct input_dev;
-struct firedtv;
-
-struct firedtv_backend {
- int (*lock)(struct firedtv *fdtv, u64 addr, void *data);
- int (*read)(struct firedtv *fdtv, u64 addr, void *data);
- int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len);
- int (*start_iso)(struct firedtv *fdtv);
- void (*stop_iso)(struct firedtv *fdtv);
-};
+struct fdtv_ir_context;
struct firedtv {
struct device *device;
@@ -104,12 +96,11 @@ struct firedtv {
enum model_type type;
char subunit;
char isochannel;
+ struct fdtv_ir_context *ir_context;
+
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone;
- const struct firedtv_backend *backend;
- void *backend_data;
-
struct mutex demux_mutex;
unsigned long channel_active;
u16 channel_pid[16];
@@ -118,15 +109,6 @@ struct firedtv {
u8 avc_data[512];
};
-/* firedtv-1394.c */
-#ifdef CONFIG_DVB_FIREDTV_IEEE1394
-int fdtv_1394_init(void);
-void fdtv_1394_exit(void);
-#else
-static inline int fdtv_1394_init(void) { return 0; }
-static inline void fdtv_1394_exit(void) {}
-#endif
-
/* firedtv-avc.c */
int avc_recv(struct firedtv *fdtv, void *data, size_t length);
int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat);
@@ -158,25 +140,18 @@ void fdtv_ca_release(struct firedtv *fdtv);
/* firedtv-dvb.c */
int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed);
int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed);
-int fdtv_dvb_register(struct firedtv *fdtv);
+int fdtv_dvb_register(struct firedtv *fdtv, const char *name);
void fdtv_dvb_unregister(struct firedtv *fdtv);
-struct firedtv *fdtv_alloc(struct device *dev,
- const struct firedtv_backend *backend,
- const char *name, size_t name_len);
-extern const char *fdtv_model_names[];
-extern const struct ieee1394_device_id fdtv_id_table[];
/* firedtv-fe.c */
-void fdtv_frontend_init(struct firedtv *fdtv);
+void fdtv_frontend_init(struct firedtv *fdtv, const char *name);
/* firedtv-fw.c */
-#ifdef CONFIG_DVB_FIREDTV_FIREWIRE
-int fdtv_fw_init(void);
-void fdtv_fw_exit(void);
-#else
-static inline int fdtv_fw_init(void) { return 0; }
-static inline void fdtv_fw_exit(void) {}
-#endif
+int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data);
+int fdtv_read(struct firedtv *fdtv, u64 addr, void *data);
+int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len);
+int fdtv_start_iso(struct firedtv *fdtv);
+void fdtv_stop_iso(struct firedtv *fdtv);
/* firedtv-rc.c */
#ifdef CONFIG_DVB_FIREDTV_INPUT
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index b8519ba511e5..83093d1f4f74 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -349,6 +349,14 @@ config DVB_DIB7000P
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
+config DVB_DIB9000
+ tristate "DiBcom 9000"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
config DVB_TDA10048
tristate "Philips TDA10048HN based"
depends on DVB_CORE && I2C
@@ -370,6 +378,13 @@ config DVB_EC100
help
Say Y when you want to support this frontend.
+config DVB_STV0367
+ tristate "ST STV0367 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T/C tuner module. Say Y when you want to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index b1d9525aa7e3..3b0c4bdc4b2b 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o
obj-$(CONFIG_DVB_MT312) += mt312.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
@@ -83,3 +84,4 @@ obj-$(CONFIG_DVB_DS3000) += ds3000.o
obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
+obj-$(CONFIG_DVB_STV0367) += stv0367.o
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index 65240b7801e8..52ff1a252a90 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -45,6 +45,7 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
} \
} while (0)
+#define CONFIG_SYS_DVBT
#define CONFIG_SYS_ISDBT
#define CONFIG_BAND_CBAND
#define CONFIG_BAND_VHF
@@ -76,6 +77,34 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
#define EN_SBD 0x44E9
#define EN_CAB 0x88E9
+/* Calibration defines */
+#define DC_CAL 0x1
+#define WBD_CAL 0x2
+#define TEMP_CAL 0x4
+#define CAPTRIM_CAL 0x8
+
+#define KROSUS_PLL_LOCKED 0x800
+#define KROSUS 0x2
+
+/* Use those defines to identify SOC version */
+#define SOC 0x02
+#define SOC_7090_P1G_11R1 0x82
+#define SOC_7090_P1G_21R1 0x8a
+#define SOC_8090_P1G_11R1 0x86
+#define SOC_8090_P1G_21R1 0x8e
+
+/* else use thos ones to check */
+#define P1A_B 0x0
+#define P1C 0x1
+#define P1D_E_F 0x3
+#define P1G 0x7
+#define P1G_21R2 0xf
+
+#define MP001 0x1 /* Single 9090/8096 */
+#define MP005 0x4 /* Single Sband */
+#define MP008 0x6 /* Dual diversity VHF-UHF-LBAND */
+#define MP009 0x7 /* Dual diversity 29098 CBAND-UHF-LBAND-SBAND */
+
#define pgm_read_word(w) (*w)
struct dc_calibration;
@@ -84,7 +113,7 @@ struct dib0090_tuning {
u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
u8 switch_trim;
u8 lna_tune;
- u8 lna_bias;
+ u16 lna_bias;
u16 v2i;
u16 mix;
u16 load;
@@ -99,13 +128,19 @@ struct dib0090_pll {
u8 topresc;
};
+struct dib0090_identity {
+ u8 version;
+ u8 product;
+ u8 p1g;
+ u8 in_soc;
+};
+
struct dib0090_state {
struct i2c_adapter *i2c;
struct dvb_frontend *fe;
const struct dib0090_config *config;
u8 current_band;
- u16 revision;
enum frontend_tune_state tune_state;
u32 current_rf;
@@ -143,7 +178,26 @@ struct dib0090_state {
u8 tuner_is_tuned;
u8 agc_freeze;
- u8 reset;
+ struct dib0090_identity identity;
+
+ u32 rf_request;
+ u8 current_standard;
+
+ u8 calibrate;
+ u32 rest;
+ u16 bias;
+ s16 temperature;
+
+ u8 wbd_calibration_gain;
+ const struct dib0090_wbd_slope *current_wbd_table;
+ u16 wbdmux;
+};
+
+struct dib0090_fw_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe;
+ struct dib0090_identity identity;
+ const struct dib0090_config *config;
};
static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
@@ -171,6 +225,28 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
return 0;
}
+static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
+{
+ u8 b[2];
+ struct i2c_msg msg = {.addr = reg, .flags = I2C_M_RD, .buf = b, .len = 2 };
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "DiB0090 I2C read failed\n");
+ return 0;
+ }
+ return (b[0] << 8) | b[1];
+}
+
+static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
+{
+ u8 b[2] = { val >> 8, val & 0xff };
+ struct i2c_msg msg = {.addr = reg, .flags = 0, .buf = b, .len = 2 };
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "DiB0090 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0)
#define ADC_TARGET -220
#define GAIN_ALPHA 5
@@ -183,89 +259,327 @@ static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b,
} while (--c);
}
-static u16 dib0090_identify(struct dvb_frontend *fe)
+static int dib0090_identify(struct dvb_frontend *fe)
{
struct dib0090_state *state = fe->tuner_priv;
u16 v;
+ struct dib0090_identity *identity = &state->identity;
v = dib0090_read_reg(state, 0x1a);
-#ifdef FIRMWARE_FIREFLY
- /* pll is not locked locked */
- if (!(v & 0x800))
- dprintk("FE%d : Identification : pll is not yet locked", fe->id);
-#endif
+ identity->p1g = 0;
+ identity->in_soc = 0;
+
+ dprintk("Tuner identification (Version = 0x%04x)", v);
/* without PLL lock info */
- v &= 0x3ff;
- dprintk("P/V: %04x:", v);
+ v &= ~KROSUS_PLL_LOCKED;
- if ((v >> 8) & 0xf)
- dprintk("FE%d : Product ID = 0x%x : KROSUS", fe->id, (v >> 8) & 0xf);
- else
- return 0xff;
-
- v &= 0xff;
- if (((v >> 5) & 0x7) == 0x1)
- dprintk("FE%d : MP001 : 9090/8096", fe->id);
- else if (((v >> 5) & 0x7) == 0x4)
- dprintk("FE%d : MP005 : Single Sband", fe->id);
- else if (((v >> 5) & 0x7) == 0x6)
- dprintk("FE%d : MP008 : diversity VHF-UHF-LBAND", fe->id);
- else if (((v >> 5) & 0x7) == 0x7)
- dprintk("FE%d : MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND", fe->id);
- else
- return 0xff;
-
- /* revision only */
- if ((v & 0x1f) == 0x3)
- dprintk("FE%d : P1-D/E/F detected", fe->id);
- else if ((v & 0x1f) == 0x1)
- dprintk("FE%d : P1C detected", fe->id);
- else if ((v & 0x1f) == 0x0) {
-#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
- dprintk("FE%d : P1-A/B detected: using previous driver - support will be removed soon", fe->id);
- dib0090_p1b_register(fe);
-#else
- dprintk("FE%d : P1-A/B detected: driver is deactivated - not available", fe->id);
- return 0xff;
-#endif
+ identity->version = v & 0xff;
+ identity->product = (v >> 8) & 0xf;
+
+ if (identity->product != KROSUS)
+ goto identification_error;
+
+ if ((identity->version & 0x3) == SOC) {
+ identity->in_soc = 1;
+ switch (identity->version) {
+ case SOC_8090_P1G_11R1:
+ dprintk("SOC 8090 P1-G11R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_8090_P1G_21R1:
+ dprintk("SOC 8090 P1-G21R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_7090_P1G_11R1:
+ dprintk("SOC 7090 P1-G11R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_7090_P1G_21R1:
+ dprintk("SOC 7090 P1-G21R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ default:
+ goto identification_error;
+ }
+ } else {
+ switch ((identity->version >> 5) & 0x7) {
+ case MP001:
+ dprintk("MP001 : 9090/8096");
+ break;
+ case MP005:
+ dprintk("MP005 : Single Sband");
+ break;
+ case MP008:
+ dprintk("MP008 : diversity VHF-UHF-LBAND");
+ break;
+ case MP009:
+ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND");
+ break;
+ default:
+ goto identification_error;
+ }
+
+ switch (identity->version & 0x1f) {
+ case P1G_21R2:
+ dprintk("P1G_21R2 detected");
+ identity->p1g = 1;
+ break;
+ case P1G:
+ dprintk("P1G detected");
+ identity->p1g = 1;
+ break;
+ case P1D_E_F:
+ dprintk("P1D/E/F detected");
+ break;
+ case P1C:
+ dprintk("P1C detected");
+ break;
+ case P1A_B:
+ dprintk("P1-A/B detected: driver is deactivated - not available");
+ goto identification_error;
+ break;
+ default:
+ goto identification_error;
+ }
}
- return v;
+ return 0;
+
+identification_error:
+ return -EIO;
+}
+
+static int dib0090_fw_identify(struct dvb_frontend *fe)
+{
+ struct dib0090_fw_state *state = fe->tuner_priv;
+ struct dib0090_identity *identity = &state->identity;
+
+ u16 v = dib0090_fw_read_reg(state, 0x1a);
+ identity->p1g = 0;
+ identity->in_soc = 0;
+
+ dprintk("FE: Tuner identification (Version = 0x%04x)", v);
+
+ /* without PLL lock info */
+ v &= ~KROSUS_PLL_LOCKED;
+
+ identity->version = v & 0xff;
+ identity->product = (v >> 8) & 0xf;
+
+ if (identity->product != KROSUS)
+ goto identification_error;
+
+ if ((identity->version & 0x3) == SOC) {
+ identity->in_soc = 1;
+ switch (identity->version) {
+ case SOC_8090_P1G_11R1:
+ dprintk("SOC 8090 P1-G11R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_8090_P1G_21R1:
+ dprintk("SOC 8090 P1-G21R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_7090_P1G_11R1:
+ dprintk("SOC 7090 P1-G11R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ case SOC_7090_P1G_21R1:
+ dprintk("SOC 7090 P1-G21R1 Has been detected");
+ identity->p1g = 1;
+ break;
+ default:
+ goto identification_error;
+ }
+ } else {
+ switch ((identity->version >> 5) & 0x7) {
+ case MP001:
+ dprintk("MP001 : 9090/8096");
+ break;
+ case MP005:
+ dprintk("MP005 : Single Sband");
+ break;
+ case MP008:
+ dprintk("MP008 : diversity VHF-UHF-LBAND");
+ break;
+ case MP009:
+ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND");
+ break;
+ default:
+ goto identification_error;
+ }
+
+ switch (identity->version & 0x1f) {
+ case P1G_21R2:
+ dprintk("P1G_21R2 detected");
+ identity->p1g = 1;
+ break;
+ case P1G:
+ dprintk("P1G detected");
+ identity->p1g = 1;
+ break;
+ case P1D_E_F:
+ dprintk("P1D/E/F detected");
+ break;
+ case P1C:
+ dprintk("P1C detected");
+ break;
+ case P1A_B:
+ dprintk("P1-A/B detected: driver is deactivated - not available");
+ goto identification_error;
+ break;
+ default:
+ goto identification_error;
+ }
+ }
+
+ return 0;
+
+identification_error:
+ return -EIO;;
}
static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
{
struct dib0090_state *state = fe->tuner_priv;
+ u16 PllCfg, i, v;
HARD_RESET(state);
- dib0090_write_reg(state, 0x24, EN_PLL);
+ dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL);
dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */
- /* adcClkOutRatio=8->7, release reset */
- dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
+ if (!cfg->in_soc) {
+ /* adcClkOutRatio=8->7, release reset */
+ dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
+ if (cfg->clkoutdrive != 0)
+ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
+ | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
+ else
+ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
+ | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
+ }
+
+ /* Read Pll current config * */
+ PllCfg = dib0090_read_reg(state, 0x21);
+
+ /** Reconfigure PLL if current setting is different from default setting **/
+ if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && (!cfg->in_soc)
+ && !cfg->io.pll_bypass) {
+
+ /* Set Bypass mode */
+ PllCfg |= (1 << 15);
+ dib0090_write_reg(state, 0x21, PllCfg);
+
+ /* Set Reset Pll */
+ PllCfg &= ~(1 << 13);
+ dib0090_write_reg(state, 0x21, PllCfg);
+
+ /*** Set new Pll configuration in bypass and reset state ***/
+ PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv);
+ dib0090_write_reg(state, 0x21, PllCfg);
+
+ /* Remove Reset Pll */
+ PllCfg |= (1 << 13);
+ dib0090_write_reg(state, 0x21, PllCfg);
+
+ /*** Wait for PLL lock ***/
+ i = 100;
+ do {
+ v = !!(dib0090_read_reg(state, 0x1a) & 0x800);
+ if (v)
+ break;
+ } while (--i);
+
+ if (i == 0) {
+ dprintk("Pll: Unable to lock Pll");
+ return;
+ }
+
+ /* Finally Remove Bypass mode */
+ PllCfg &= ~(1 << 15);
+ dib0090_write_reg(state, 0x21, PllCfg);
+ }
+
+ if (cfg->io.pll_bypass) {
+ PllCfg |= (cfg->io.pll_bypass << 15);
+ dib0090_write_reg(state, 0x21, PllCfg);
+ }
+}
+
+static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
+{
+ struct dib0090_fw_state *state = fe->tuner_priv;
+ u16 PllCfg;
+ u16 v;
+ int i;
+
+ dprintk("fw reset digital");
+ HARD_RESET(state);
+
+ dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL);
+ dib0090_fw_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */
+
+ dib0090_fw_write_reg(state, 0x20,
+ ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (cfg->data_tx_drv << 4) | cfg->ls_cfg_pad_drv);
+
+ v = (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 9) | (0 << 8) | (cfg->clkouttobamse << 4) | (0 << 2) | (0);
if (cfg->clkoutdrive != 0)
- dib0090_write_reg(state, 0x23,
- (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (cfg->clkoutdrive << 5) | (cfg->
- clkouttobamse
- << 4) | (0
- <<
- 2)
- | (0));
+ v |= cfg->clkoutdrive << 5;
else
- dib0090_write_reg(state, 0x23,
- (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (7 << 5) | (cfg->
- clkouttobamse << 4) | (0
- <<
- 2)
- | (0));
+ v |= 7 << 5;
+
+ v |= 2 << 10;
+ dib0090_fw_write_reg(state, 0x23, v);
+
+ /* Read Pll current config * */
+ PllCfg = dib0090_fw_read_reg(state, 0x21);
+
+ /** Reconfigure PLL if current setting is different from default setting **/
+ if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && !cfg->io.pll_bypass) {
- /* enable pll, de-activate reset, ratio: 2/1 = 60MHz */
- dib0090_write_reg(state, 0x21,
- (cfg->io.pll_bypass << 15) | (1 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv));
+ /* Set Bypass mode */
+ PllCfg |= (1 << 15);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+ /* Set Reset Pll */
+ PllCfg &= ~(1 << 13);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+
+ /*** Set new Pll configuration in bypass and reset state ***/
+ PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+
+ /* Remove Reset Pll */
+ PllCfg |= (1 << 13);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+
+ /*** Wait for PLL lock ***/
+ i = 100;
+ do {
+ v = !!(dib0090_fw_read_reg(state, 0x1a) & 0x800);
+ if (v)
+ break;
+ } while (--i);
+
+ if (i == 0) {
+ dprintk("Pll: Unable to lock Pll");
+ return -EIO;
+ }
+
+ /* Finally Remove Bypass mode */
+ PllCfg &= ~(1 << 15);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+ }
+
+ if (cfg->io.pll_bypass) {
+ PllCfg |= (cfg->io.pll_bypass << 15);
+ dib0090_fw_write_reg(state, 0x21, PllCfg);
+ }
+
+ return dib0090_fw_identify(fe);
}
static int dib0090_wakeup(struct dvb_frontend *fe)
@@ -273,6 +587,9 @@ static int dib0090_wakeup(struct dvb_frontend *fe)
struct dib0090_state *state = fe->tuner_priv;
if (state->config->sleep)
state->config->sleep(fe, 0);
+
+ /* enable dataTX in case we have been restarted in the wrong moment */
+ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14));
return 0;
}
@@ -292,8 +609,75 @@ void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
else
dib0090_write_reg(state, 0x04, 1);
}
+
EXPORT_SYMBOL(dib0090_dcc_freq);
+static const u16 bb_ramp_pwm_normal_socs[] = {
+ 550, /* max BB gain in 10th of dB */
+ (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
+ 440,
+ (4 << 9) | 0, /* BB_RAMP3 = 26dB */
+ (0 << 9) | 208, /* BB_RAMP4 */
+ (4 << 9) | 208, /* BB_RAMP5 = 29dB */
+ (0 << 9) | 440, /* BB_RAMP6 */
+};
+
+static const u16 rf_ramp_pwm_cband_7090[] = {
+ 280, /* max RF gain in 10th of dB */
+ 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 504, /* ramp_max = maximum X used on the ramp */
+ (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */
+ (0 << 10) | 504, /* RF_RAMP6, LNA 1 */
+ (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */
+ (0 << 10) | 364, /* RF_RAMP8, LNA 2 */
+ (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */
+ (0 << 10) | 228, /* GAIN_4_2, LNA 3 */
+ (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */
+ (0 << 10) | 109, /* RF_RAMP4, LNA 4 */
+};
+
+static const u16 rf_ramp_pwm_cband_8090[] = {
+ 345, /* max RF gain in 10th of dB */
+ 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 1000, /* ramp_max = maximum X used on the ramp */
+ (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */
+ (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */
+ (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */
+ (0 << 10) | 772, /* RF_RAMP6, LNA 2 */
+ (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */
+ (0 << 10) | 496, /* RF_RAMP8, LNA 3 */
+ (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */
+ (0 << 10) | 200, /* GAIN_4_2, LNA 4 */
+};
+
+static const u16 rf_ramp_pwm_uhf_7090[] = {
+ 407, /* max RF gain in 10th of dB */
+ 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 529, /* ramp_max = maximum X used on the ramp */
+ (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
+ (0 << 10) | 176, /* RF_RAMP4, LNA 1 */
+ (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */
+ (0 << 10) | 529, /* RF_RAMP6, LNA 2 */
+ (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */
+ (0 << 10) | 400, /* RF_RAMP8, LNA 3 */
+ (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */
+ (0 << 10) | 316, /* GAIN_4_2, LNA 4 */
+};
+
+static const u16 rf_ramp_pwm_uhf_8090[] = {
+ 388, /* max RF gain in 10th of dB */
+ 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 1008, /* ramp_max = maximum X used on the ramp */
+ (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
+ (0 << 10) | 369, /* RF_RAMP4, LNA 1 */
+ (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */
+ (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */
+ (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */
+ (0 << 10) | 809, /* RF_RAMP8, LNA 3 */
+ (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */
+ (0 << 10) | 659, /* GAIN_4_2, LNA 4 */
+};
+
static const u16 rf_ramp_pwm_cband[] = {
0, /* max RF gain in 10th of dB */
0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
@@ -326,6 +710,16 @@ static const u16 rf_ramp_uhf[] = {
0, 0, 127, /* CBAND : 0.0 dB */
};
+static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */
+{
+ 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */
+ 84, 314, 127, /* LNA1 */
+ 80, 230, 255, /* LNA2 */
+ 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */
+ 70, 70, 127, /* LNA4 */
+ 0, 0, 127, /* CBAND */
+};
+
static const u16 rf_ramp_cband[] = {
332, /* max RF gain in 10th of dB */
132, 252, 127, /* LNA1, dB */
@@ -380,8 +774,8 @@ static const u16 bb_ramp_pwm_normal[] = {
};
struct slope {
- int16_t range;
- int16_t slope;
+ s16 range;
+ s16 slope;
};
static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val)
{
@@ -597,19 +991,39 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
#endif
#ifdef CONFIG_BAND_CBAND
if (state->current_band == BAND_CBAND) {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ if (state->identity.in_soc) {
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
+ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090);
+ else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1)
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090);
+ } else {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ }
} else
#endif
#ifdef CONFIG_BAND_VHF
if (state->current_band == BAND_VHF) {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ if (state->identity.in_soc) {
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
+ } else {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ }
} else
#endif
{
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ if (state->identity.in_soc) {
+ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090);
+ else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1)
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
+ } else {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ }
}
if (state->rf_ramp[0] != 0)
@@ -617,11 +1031,21 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
else
dib0090_write_reg(state, 0x32, (0 << 11));
+ dib0090_write_reg(state, 0x04, 0x01);
dib0090_write_reg(state, 0x39, (1 << 10));
}
}
+
EXPORT_SYMBOL(dib0090_pwm_gain_reset);
+static u32 dib0090_get_slow_adc_val(struct dib0090_state *state)
+{
+ u16 adc_val = dib0090_read_reg(state, 0x1d);
+ if (state->identity.in_soc)
+ adc_val >>= 2;
+ return adc_val;
+}
+
int dib0090_gain_control(struct dvb_frontend *fe)
{
struct dib0090_state *state = fe->tuner_priv;
@@ -643,18 +1067,21 @@ int dib0090_gain_control(struct dvb_frontend *fe)
} else
#endif
#ifdef CONFIG_BAND_VHF
- if (state->current_band == BAND_VHF) {
+ if (state->current_band == BAND_VHF && !state->identity.p1g) {
dib0090_set_rframp(state, rf_ramp_vhf);
dib0090_set_bbramp(state, bb_ramp_boost);
} else
#endif
#ifdef CONFIG_BAND_CBAND
- if (state->current_band == BAND_CBAND) {
+ if (state->current_band == BAND_CBAND && !state->identity.p1g) {
dib0090_set_rframp(state, rf_ramp_cband);
dib0090_set_bbramp(state, bb_ramp_boost);
} else
#endif
- {
+ if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) {
+ dib0090_set_rframp(state, rf_ramp_cband_broadmatching);
+ dib0090_set_bbramp(state, bb_ramp_boost);
+ } else {
dib0090_set_rframp(state, rf_ramp_uhf);
dib0090_set_bbramp(state, bb_ramp_boost);
}
@@ -669,17 +1096,25 @@ int dib0090_gain_control(struct dvb_frontend *fe)
*tune_state = CT_AGC_STEP_0;
} else if (!state->agc_freeze) {
- s16 wbd;
+ s16 wbd = 0, i, cnt;
int adc;
- wbd_val = dib0090_read_reg(state, 0x1d);
+ wbd_val = dib0090_get_slow_adc_val(state);
- /* read and calc the wbd power */
- wbd = dib0090_wbd_to_db(state, wbd_val);
+ if (*tune_state == CT_AGC_STEP_0)
+ cnt = 5;
+ else
+ cnt = 1;
+
+ for (i = 0; i < cnt; i++) {
+ wbd_val = dib0090_get_slow_adc_val(state);
+ wbd += dib0090_wbd_to_db(state, wbd_val);
+ }
+ wbd /= cnt;
wbd_error = state->wbd_target - wbd;
if (*tune_state == CT_AGC_STEP_0) {
- if (wbd_error < 0 && state->rf_gain_limit > 0) {
+ if (wbd_error < 0 && state->rf_gain_limit > 0 && !state->identity.p1g) {
#ifdef CONFIG_BAND_CBAND
/* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */
u8 ltg2 = (state->rf_lt_def >> 10) & 0x7;
@@ -700,39 +1135,39 @@ int dib0090_gain_control(struct dvb_frontend *fe)
adc_error = (s16) (((s32) ADC_TARGET) - adc);
#ifdef CONFIG_STANDARD_DAB
if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB)
- adc_error += 130;
+ adc_error -= 10;
#endif
#ifdef CONFIG_STANDARD_DVBT
if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT &&
- (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16))
+ (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16))
adc_error += 60;
#endif
#ifdef CONFIG_SYS_ISDBT
if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count >
- 0)
- &&
- ((state->fe->dtv_property_cache.layer[0].modulation ==
- QAM_64)
- || (state->fe->dtv_property_cache.layer[0].
- modulation == QAM_16)))
- ||
- ((state->fe->dtv_property_cache.layer[1].segment_count >
- 0)
- &&
- ((state->fe->dtv_property_cache.layer[1].modulation ==
- QAM_64)
- || (state->fe->dtv_property_cache.layer[1].
- modulation == QAM_16)))
- ||
- ((state->fe->dtv_property_cache.layer[2].segment_count >
- 0)
- &&
- ((state->fe->dtv_property_cache.layer[2].modulation ==
- QAM_64)
- || (state->fe->dtv_property_cache.layer[2].
- modulation == QAM_16)))
- )
- )
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[0].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.
+ layer[0].modulation == QAM_16)))
+ ||
+ ((state->fe->dtv_property_cache.layer[1].segment_count >
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[1].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.
+ layer[1].modulation == QAM_16)))
+ ||
+ ((state->fe->dtv_property_cache.layer[2].segment_count >
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[2].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.
+ layer[2].modulation == QAM_16)))
+ )
+ )
adc_error += 60;
#endif
@@ -760,9 +1195,9 @@ int dib0090_gain_control(struct dvb_frontend *fe)
}
#ifdef DEBUG_AGC
dprintk
- ("FE: %d, tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm",
- (u32) fe->id, (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val,
- (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA));
+ ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm",
+ (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val,
+ (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA));
#endif
}
@@ -771,6 +1206,7 @@ int dib0090_gain_control(struct dvb_frontend *fe)
dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly);
return ret;
}
+
EXPORT_SYMBOL(dib0090_gain_control);
void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt)
@@ -785,13 +1221,47 @@ void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 *
if (rflt)
*rflt = (state->rf_lt_def >> 10) & 0x7;
}
+
EXPORT_SYMBOL(dib0090_get_current_gain);
-u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner)
+u16 dib0090_get_wbd_offset(struct dvb_frontend *fe)
{
- struct dib0090_state *st = tuner->tuner_priv;
- return st->wbd_offset;
+ struct dib0090_state *state = fe->tuner_priv;
+ u32 f_MHz = state->fe->dtv_property_cache.frequency / 1000000;
+ s32 current_temp = state->temperature;
+ s32 wbd_thot, wbd_tcold;
+ const struct dib0090_wbd_slope *wbd = state->current_wbd_table;
+
+ while (f_MHz > wbd->max_freq)
+ wbd++;
+
+ dprintk("using wbd-table-entry with max freq %d", wbd->max_freq);
+
+ if (current_temp < 0)
+ current_temp = 0;
+ if (current_temp > 128)
+ current_temp = 128;
+
+ state->wbdmux &= ~(7 << 13);
+ if (wbd->wbd_gain != 0)
+ state->wbdmux |= (wbd->wbd_gain << 13);
+ else
+ state->wbdmux |= (4 << 13);
+
+ dib0090_write_reg(state, 0x10, state->wbdmux);
+
+ wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6);
+ wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6);
+
+ wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7;
+
+ state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold);
+ dprintk("wbd-target: %d dB", (u32) state->wbd_target);
+ dprintk("wbd offset applied is %d", wbd_tcold);
+
+ return state->wbd_offset + wbd_tcold;
}
+
EXPORT_SYMBOL(dib0090_get_wbd_offset);
static const u16 dib0090_defaults[] = {
@@ -801,7 +1271,7 @@ static const u16 dib0090_defaults[] = {
0x99a0,
0x6008,
0x0000,
- 0x8acb,
+ 0x8bcb,
0x0000,
0x0405,
0x0000,
@@ -829,8 +1299,6 @@ static const u16 dib0090_defaults[] = {
1, 0x39,
0x0000,
- 1, 0x1b,
- EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL,
2, 0x1e,
0x07FF,
0x0007,
@@ -844,50 +1312,125 @@ static const u16 dib0090_defaults[] = {
0
};
-static int dib0090_reset(struct dvb_frontend *fe)
-{
- struct dib0090_state *state = fe->tuner_priv;
- u16 l, r, *n;
+static const u16 dib0090_p1g_additionnal_defaults[] = {
+ 1, 0x05,
+ 0xabcd,
- dib0090_reset_digital(fe, state->config);
- state->revision = dib0090_identify(fe);
+ 1, 0x11,
+ 0x00b4,
- /* Revision definition */
- if (state->revision == 0xff)
- return -EINVAL;
-#ifdef EFUSE
- else if ((state->revision & 0x1f) >= 3) /* Update the efuse : Only available for KROSUS > P1C */
- dib0090_set_EFUSE(state);
-#endif
+ 1, 0x1c,
+ 0xfffd,
-#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
- if (!(state->revision & 0x1)) /* it is P1B - reset is already done */
- return 0;
-#endif
+ 1, 0x40,
+ 0x108,
+ 0
+};
+
+static void dib0090_set_default_config(struct dib0090_state *state, const u16 * n)
+{
+ u16 l, r;
- /* Upload the default values */
- n = (u16 *) dib0090_defaults;
l = pgm_read_word(n++);
while (l) {
r = pgm_read_word(n++);
do {
- /* DEBUG_TUNER */
- /* dprintk("%d, %d, %d", l, r, pgm_read_word(n)); */
dib0090_write_reg(state, r, pgm_read_word(n++));
r++;
} while (--l);
l = pgm_read_word(n++);
}
+}
+
+#define CAP_VALUE_MIN (u8) 9
+#define CAP_VALUE_MAX (u8) 40
+#define HR_MIN (u8) 25
+#define HR_MAX (u8) 40
+#define POLY_MIN (u8) 0
+#define POLY_MAX (u8) 8
+
+void dib0090_set_EFUSE(struct dib0090_state *state)
+{
+ u8 c, h, n;
+ u16 e2, e4;
+ u16 cal;
+
+ e2 = dib0090_read_reg(state, 0x26);
+ e4 = dib0090_read_reg(state, 0x28);
+
+ if ((state->identity.version == P1D_E_F) ||
+ (state->identity.version == P1G) || (e2 == 0xffff)) {
+
+ dib0090_write_reg(state, 0x22, 0x10);
+ cal = (dib0090_read_reg(state, 0x22) >> 6) & 0x3ff;
+
+ if ((cal < 670) || (cal == 1023))
+ cal = 850;
+ n = 165 - ((cal * 10)>>6) ;
+ e2 = e4 = (3<<12) | (34<<6) | (n);
+ }
+
+ if (e2 != e4)
+ e2 &= e4; /* Remove the redundancy */
+
+ if (e2 != 0xffff) {
+ c = e2 & 0x3f;
+ n = (e2 >> 12) & 0xf;
+ h = (e2 >> 6) & 0x3f;
+
+ if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN))
+ c = 32;
+ if ((h >= HR_MAX) || (h <= HR_MIN))
+ h = 34;
+ if ((n >= POLY_MAX) || (n <= POLY_MIN))
+ n = 3;
+
+ dib0090_write_reg(state, 0x13, (h << 10)) ;
+ e2 = (n<<11) | ((h>>2)<<6) | (c);
+ dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */
+ }
+}
+
+static int dib0090_reset(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+
+ dib0090_reset_digital(fe, state->config);
+ if (dib0090_identify(fe) < 0)
+ return -EIO;
+
+#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
+ if (!(state->identity.version & 0x1)) /* it is P1B - reset is already done */
+ return 0;
+#endif
+
+ if (!state->identity.in_soc) {
+ if ((dib0090_read_reg(state, 0x1a) >> 5) & 0x2)
+ dib0090_write_reg(state, 0x1b, (EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL));
+ else
+ dib0090_write_reg(state, 0x1b, (EN_DIGCLK | EN_PLL | EN_CRYSTAL));
+ }
+
+ dib0090_set_default_config(state, dib0090_defaults);
+
+ if (state->identity.in_soc)
+ dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */
+
+ if (state->identity.p1g)
+ dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults);
+
+ /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/
+ if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc))
+ dib0090_set_EFUSE(state);
/* Congigure in function of the crystal */
if (state->config->io.clock_khz >= 24000)
- l = 1;
+ dib0090_write_reg(state, 0x14, 1);
else
- l = 2;
- dib0090_write_reg(state, 0x14, l);
+ dib0090_write_reg(state, 0x14, 2);
dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1);
- state->reset = 3; /* enable iq-offset-calibration and wbd-calibration when tuning next time */
+ state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */
return 0;
}
@@ -927,11 +1470,11 @@ static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_st
}
struct dc_calibration {
- uint8_t addr;
- uint8_t offset;
- uint8_t pga:1;
- uint16_t bb1;
- uint8_t i:1;
+ u8 addr;
+ u8 offset;
+ u8 pga:1;
+ u16 bb1;
+ u8 i:1;
};
static const struct dc_calibration dc_table[] = {
@@ -944,6 +1487,17 @@ static const struct dc_calibration dc_table[] = {
{0},
};
+static const struct dc_calibration dc_p1g_table[] = {
+ /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */
+ /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */
+ {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1},
+ {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0},
+ /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */
+ {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1},
+ {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0},
+ {0},
+};
+
static void dib0090_set_trim(struct dib0090_state *state)
{
u16 *val;
@@ -962,41 +1516,45 @@ static void dib0090_set_trim(struct dib0090_state *state)
static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
{
int ret = 0;
+ u16 reg;
switch (*tune_state) {
-
case CT_TUNER_START:
- /* init */
- dprintk("Internal DC calibration");
-
- /* the LNA is off */
- dib0090_write_reg(state, 0x24, 0x02ed);
+ dprintk("Start DC offset calibration");
/* force vcm2 = 0.8V */
state->bb6 = 0;
state->bb7 = 0x040d;
+ /* the LNA AND LO are off */
+ reg = dib0090_read_reg(state, 0x24) & 0x0ffb; /* shutdown lna and lo */
+ dib0090_write_reg(state, 0x24, reg);
+
+ state->wbdmux = dib0090_read_reg(state, 0x10);
+ dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3);
+ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14));
+
state->dc = dc_table;
+ if (state->identity.p1g)
+ state->dc = dc_p1g_table;
*tune_state = CT_TUNER_STEP_0;
/* fall through */
case CT_TUNER_STEP_0:
+ dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q");
dib0090_write_reg(state, 0x01, state->dc->bb1);
dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7));
state->step = 0;
-
state->min_adc_diff = 1023;
-
*tune_state = CT_TUNER_STEP_1;
ret = 50;
break;
case CT_TUNER_STEP_1:
dib0090_set_trim(state);
-
*tune_state = CT_TUNER_STEP_2;
break;
@@ -1007,7 +1565,13 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
break;
case CT_TUNER_STEP_5: /* found an offset */
- dprintk("FE%d: IQC read=%d, current=%x", state->fe->id, (u32) state->adc_diff, state->step);
+ dprintk("adc_diff = %d, current step= %d", (u32) state->adc_diff, state->step);
+ if (state->step == 0 && state->adc_diff < 0) {
+ state->min_adc_diff = -1023;
+ dprintk("Change of sign of the minimum adc diff");
+ }
+
+ dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d", state->adc_diff, state->min_adc_diff, state->step);
/* first turn for this frequency */
if (state->step == 0) {
@@ -1017,20 +1581,21 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
state->step = 0x10;
}
- state->adc_diff = ABS(state->adc_diff);
-
- if (state->adc_diff < state->min_adc_diff && steps(state->step) < 15) { /* stop search when the delta to 0 is increasing */
+ /* Look for a change of Sign in the Adc_diff.min_adc_diff is used to STORE the setp N-1 */
+ if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) {
+ /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */
state->step++;
state->min_adc_diff = state->adc_diff;
*tune_state = CT_TUNER_STEP_1;
} else {
-
/* the minimum was what we have seen in the step before */
- state->step--;
- dib0090_set_trim(state);
+ if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) {
+ dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff);
+ state->step--;
+ }
- dprintk("FE%d: BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->fe->id, state->dc->addr, state->adc_diff,
- state->step);
+ dib0090_set_trim(state);
+ dprintk("BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->dc->addr, state->adc_diff, state->step);
state->dc++;
if (state->dc->addr == 0) /* done */
@@ -1045,7 +1610,7 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008);
dib0090_write_reg(state, 0x1f, 0x7);
*tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
- state->reset &= ~0x1;
+ state->calibrate &= ~DC_CAL;
default:
break;
}
@@ -1054,21 +1619,43 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
{
+ u8 wbd_gain;
+ const struct dib0090_wbd_slope *wbd = state->current_wbd_table;
+
switch (*tune_state) {
case CT_TUNER_START:
- /* WBD-mode=log, Bias=2, Gain=6, Testmode=1, en=1, WBDMUX=1 */
- dib0090_write_reg(state, 0x10, 0xdb09 | (1 << 10));
- dib0090_write_reg(state, 0x24, EN_UHF & 0x0fff);
+ while (state->current_rf / 1000 > wbd->max_freq)
+ wbd++;
+ if (wbd->wbd_gain != 0)
+ wbd_gain = wbd->wbd_gain;
+ else {
+ wbd_gain = 4;
+#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND)
+ if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND))
+ wbd_gain = 2;
+#endif
+ }
+
+ if (wbd_gain == state->wbd_calibration_gain) { /* the WBD calibration has already been done */
+ *tune_state = CT_TUNER_START;
+ state->calibrate &= ~WBD_CAL;
+ return 0;
+ }
+
+ dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3));
+ dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1)));
*tune_state = CT_TUNER_STEP_0;
+ state->wbd_calibration_gain = wbd_gain;
return 90; /* wait for the WBDMUX to switch and for the ADC to sample */
+
case CT_TUNER_STEP_0:
- state->wbd_offset = dib0090_read_reg(state, 0x1d);
+ state->wbd_offset = dib0090_get_slow_adc_val(state);
dprintk("WBD calibration offset = %d", state->wbd_offset);
-
*tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
- state->reset &= ~0x2;
+ state->calibrate &= ~WBD_CAL;
break;
+
default:
break;
}
@@ -1092,6 +1679,15 @@ static void dib0090_set_bandwidth(struct dib0090_state *state)
state->bb_1_def |= tmp;
dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */
+
+ dib0090_write_reg(state, 0x03, 0x6008); /* = 0x6008 : vcm3_trim = 1 ; filter2_gm1_trim = 8 ; filter2_cutoff_freq = 0 */
+ dib0090_write_reg(state, 0x04, 0x1); /* 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast */
+ if (state->identity.in_soc) {
+ dib0090_write_reg(state, 0x05, 0x9bcf); /* attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 1 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 15 */
+ } else {
+ dib0090_write_reg(state, 0x02, (5 << 11) | (8 << 6) | (22 & 0x3f)); /* 22 = cap_value */
+ dib0090_write_reg(state, 0x05, 0xabcd); /* = 0xabcd : attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 2 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 13 */
+ }
}
static const struct dib0090_pll dib0090_pll_table[] = {
@@ -1180,6 +1776,255 @@ static const struct dib0090_tuning dib0090_tuning_table[] = {
#endif
};
+static const struct dib0090_tuning dib0090_p1g_tuning_table[] = {
+#ifdef CONFIG_BAND_CBAND
+ {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB},
+#endif
+#ifdef CONFIG_BAND_VHF
+ {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+ {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+ {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
+ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
+#endif
+};
+
+static const struct dib0090_pll dib0090_p1g_pll_table[] = {
+#ifdef CONFIG_BAND_CBAND
+ {57000, 0, 11, 48, 6},
+ {70000, 1, 11, 48, 6},
+ {86000, 0, 10, 32, 4},
+ {105000, 1, 10, 32, 4},
+ {115000, 0, 9, 24, 6},
+ {140000, 1, 9, 24, 6},
+ {170000, 0, 8, 16, 4},
+#endif
+#ifdef CONFIG_BAND_VHF
+ {200000, 1, 8, 16, 4},
+ {230000, 0, 7, 12, 6},
+ {280000, 1, 7, 12, 6},
+ {340000, 0, 6, 8, 4},
+ {380000, 1, 6, 8, 4},
+ {455000, 0, 5, 6, 6},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {580000, 1, 5, 6, 6},
+ {680000, 0, 4, 4, 4},
+ {860000, 1, 4, 4, 4},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1800000, 1, 2, 2, 4},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2900000, 0, 1, 1, 6},
+#endif
+};
+
+static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = {
+#ifdef CONFIG_BAND_CBAND
+ {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB},
+ {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB},
+ {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
+ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
+#endif
+};
+
+static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = {
+#ifdef CONFIG_BAND_CBAND
+ {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB},
+ {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB},
+ {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB},
+ {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB},
+#endif
+};
+
+static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tune_state *tune_state)
+{
+ int ret = 0;
+ u16 lo4 = 0xe900;
+
+ s16 adc_target;
+ u16 adc;
+ s8 step_sign;
+ u8 force_soft_search = 0;
+
+ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
+ force_soft_search = 1;
+
+ if (*tune_state == CT_TUNER_START) {
+ dprintk("Start Captrim search : %s", (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO");
+ dib0090_write_reg(state, 0x10, 0x2B1);
+ dib0090_write_reg(state, 0x1e, 0x0032);
+
+ if (!state->tuner_is_tuned) {
+ /* prepare a complete captrim */
+ if (!state->identity.p1g || force_soft_search)
+ state->step = state->captrim = state->fcaptrim = 64;
+
+ state->current_rf = state->rf_request;
+ } else { /* we are already tuned to this frequency - the configuration is correct */
+ if (!state->identity.p1g || force_soft_search) {
+ /* do a minimal captrim even if the frequency has not changed */
+ state->step = 4;
+ state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f;
+ }
+ }
+ state->adc_diff = 3000;
+ *tune_state = CT_TUNER_STEP_0;
+
+ } else if (*tune_state == CT_TUNER_STEP_0) {
+ if (state->identity.p1g && !force_soft_search) {
+ u8 ratio = 31;
+
+ dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1);
+ dib0090_read_reg(state, 0x40);
+ ret = 50;
+ } else {
+ state->step /= 2;
+ dib0090_write_reg(state, 0x18, lo4 | state->captrim);
+
+ if (state->identity.in_soc)
+ ret = 25;
+ }
+ *tune_state = CT_TUNER_STEP_1;
+
+ } else if (*tune_state == CT_TUNER_STEP_1) {
+ if (state->identity.p1g && !force_soft_search) {
+ dib0090_write_reg(state, 0x40, 0x18c | (0 << 1) | 0);
+ dib0090_read_reg(state, 0x40);
+
+ state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F;
+ dprintk("***Final Captrim= 0x%x", state->fcaptrim);
+ *tune_state = CT_TUNER_STEP_3;
+
+ } else {
+ /* MERGE for all krosus before P1G */
+ adc = dib0090_get_slow_adc_val(state);
+ dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024);
+
+ if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */
+ adc_target = 200;
+ } else
+ adc_target = 400;
+
+ if (adc >= adc_target) {
+ adc -= adc_target;
+ step_sign = -1;
+ } else {
+ adc = adc_target - adc;
+ step_sign = 1;
+ }
+
+ if (adc < state->adc_diff) {
+ dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
+ state->adc_diff = adc;
+ state->fcaptrim = state->captrim;
+ }
+
+ state->captrim += step_sign * state->step;
+ if (state->step >= 1)
+ *tune_state = CT_TUNER_STEP_0;
+ else
+ *tune_state = CT_TUNER_STEP_2;
+
+ ret = 25;
+ }
+ } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */
+ /*write the final cptrim config */
+ dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim);
+
+ *tune_state = CT_TUNER_STEP_3;
+
+ } else if (*tune_state == CT_TUNER_STEP_3) {
+ state->calibrate &= ~CAPTRIM_CAL;
+ *tune_state = CT_TUNER_STEP_0;
+ }
+
+ return ret;
+}
+
+static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tune_state *tune_state)
+{
+ int ret = 15;
+ s16 val;
+
+ switch (*tune_state) {
+ case CT_TUNER_START:
+ state->wbdmux = dib0090_read_reg(state, 0x10);
+ dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3));
+
+ state->bias = dib0090_read_reg(state, 0x13);
+ dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8));
+
+ *tune_state = CT_TUNER_STEP_0;
+ /* wait for the WBDMUX to switch and for the ADC to sample */
+ break;
+
+ case CT_TUNER_STEP_0:
+ state->adc_diff = dib0090_get_slow_adc_val(state);
+ dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8));
+ *tune_state = CT_TUNER_STEP_1;
+ break;
+
+ case CT_TUNER_STEP_1:
+ val = dib0090_get_slow_adc_val(state);
+ state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55;
+
+ dprintk("temperature: %d C", state->temperature - 30);
+
+ *tune_state = CT_TUNER_STEP_2;
+ break;
+
+ case CT_TUNER_STEP_2:
+ dib0090_write_reg(state, 0x13, state->bias);
+ dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */
+
+ *tune_state = CT_TUNER_START;
+ state->calibrate &= ~TEMP_CAL;
+ if (state->config->analog_output == 0)
+ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14));
+
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */
static int dib0090_tune(struct dvb_frontend *fe)
{
@@ -1188,87 +2033,131 @@ static int dib0090_tune(struct dvb_frontend *fe)
const struct dib0090_pll *pll = state->current_pll_table_index;
enum frontend_tune_state *tune_state = &state->tune_state;
- u32 rf;
- u16 lo4 = 0xe900, lo5, lo6, Den;
+ u16 lo5, lo6, Den, tmp;
u32 FBDiv, Rest, FREF, VCOF_kHz = 0;
- u16 tmp, adc;
- int8_t step_sign;
int ret = 10; /* 1ms is the default delay most of the time */
u8 c, i;
- state->current_band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
- rf = fe->dtv_property_cache.frequency / 1000 + (state->current_band ==
- BAND_UHF ? state->config->freq_offset_khz_uhf : state->config->freq_offset_khz_vhf);
- /* in any case we first need to do a reset if needed */
- if (state->reset & 0x1)
- return dib0090_dc_offset_calibration(state, tune_state);
- else if (state->reset & 0x2)
- return dib0090_wbd_calibration(state, tune_state);
-
- /************************* VCO ***************************/
+ /************************* VCO ***************************/
/* Default values for FG */
/* from these are needed : */
/* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */
-#ifdef CONFIG_SYS_ISDBT
- if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
- rf += 850;
-#endif
+ /* in any case we first need to do a calibration if needed */
+ if (*tune_state == CT_TUNER_START) {
+ /* deactivate DataTX before some calibrations */
+ if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL))
+ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14));
+ else
+ /* Activate DataTX in case a calibration has been done before */
+ if (state->config->analog_output == 0)
+ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14));
+ }
- if (state->current_rf != rf) {
- state->tuner_is_tuned = 0;
+ if (state->calibrate & DC_CAL)
+ return dib0090_dc_offset_calibration(state, tune_state);
+ else if (state->calibrate & WBD_CAL) {
+ if (state->current_rf == 0)
+ state->current_rf = state->fe->dtv_property_cache.frequency / 1000;
+ return dib0090_wbd_calibration(state, tune_state);
+ } else if (state->calibrate & TEMP_CAL)
+ return dib0090_get_temperature(state, tune_state);
+ else if (state->calibrate & CAPTRIM_CAL)
+ return dib0090_captrim_search(state, tune_state);
- tune = dib0090_tuning_table;
+ if (*tune_state == CT_TUNER_START) {
+ /* if soc and AGC pwm control, disengage mux to be able to R/W access to 0x01 register to set the right filter (cutoff_freq_select) during the tune sequence, otherwise, SOC SERPAR error when accessing to 0x01 */
+ if (state->config->use_pwm_agc && state->identity.in_soc) {
+ tmp = dib0090_read_reg(state, 0x39);
+ if ((tmp >> 10) & 0x1)
+ dib0090_write_reg(state, 0x39, tmp & ~(1 << 10));
+ }
- tmp = (state->revision >> 5) & 0x7;
- if (tmp == 0x4 || tmp == 0x7) {
- /* CBAND tuner version for VHF */
- if (state->current_band == BAND_FM || state->current_band == BAND_VHF) {
- /* Force CBAND */
- state->current_band = BAND_CBAND;
- tune = dib0090_tuning_table_fm_vhf_on_cband;
+ state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000);
+ state->rf_request =
+ state->fe->dtv_property_cache.frequency / 1000 + (state->current_band ==
+ BAND_UHF ? state->config->freq_offset_khz_uhf : state->config->
+ freq_offset_khz_vhf);
+
+ /* in ISDB-T 1seg we shift tuning frequency */
+ if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1
+ && state->fe->dtv_property_cache.isdbt_partial_reception == 0)) {
+ const struct dib0090_low_if_offset_table *LUT_offset = state->config->low_if;
+ u8 found_offset = 0;
+ u32 margin_khz = 100;
+
+ if (LUT_offset != NULL) {
+ while (LUT_offset->RF_freq != 0xffff) {
+ if (((state->rf_request > (LUT_offset->RF_freq - margin_khz))
+ && (state->rf_request < (LUT_offset->RF_freq + margin_khz)))
+ && LUT_offset->std == state->fe->dtv_property_cache.delivery_system) {
+ state->rf_request += LUT_offset->offset_khz;
+ found_offset = 1;
+ break;
+ }
+ LUT_offset++;
+ }
}
+
+ if (found_offset == 0)
+ state->rf_request += 400;
}
+ if (state->current_rf != state->rf_request || (state->current_standard != state->fe->dtv_property_cache.delivery_system)) {
+ state->tuner_is_tuned = 0;
+ state->current_rf = 0;
+ state->current_standard = 0;
- pll = dib0090_pll_table;
- /* Look for the interval */
- while (rf > tune->max_freq)
- tune++;
- while (rf > pll->max_freq)
- pll++;
- state->current_tune_table_index = tune;
- state->current_pll_table_index = pll;
- }
+ tune = dib0090_tuning_table;
+ if (state->identity.p1g)
+ tune = dib0090_p1g_tuning_table;
- if (*tune_state == CT_TUNER_START) {
+ tmp = (state->identity.version >> 5) & 0x7;
- if (state->tuner_is_tuned == 0)
- state->current_rf = 0;
+ if (state->identity.in_soc) {
+ if (state->config->force_cband_input) { /* Use the CBAND input for all band */
+ if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF
+ || state->current_band & BAND_UHF) {
+ state->current_band = BAND_CBAND;
+ tune = dib0090_tuning_table_cband_7090;
+ }
+ } else { /* Use the CBAND input for all band under UHF */
+ if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF) {
+ state->current_band = BAND_CBAND;
+ tune = dib0090_tuning_table_cband_7090;
+ }
+ }
+ } else
+ if (tmp == 0x4 || tmp == 0x7) {
+ /* CBAND tuner version for VHF */
+ if (state->current_band == BAND_FM || state->current_band == BAND_CBAND || state->current_band == BAND_VHF) {
+ state->current_band = BAND_CBAND; /* Force CBAND */
+
+ tune = dib0090_tuning_table_fm_vhf_on_cband;
+ if (state->identity.p1g)
+ tune = dib0090_p1g_tuning_table_fm_vhf_on_cband;
+ }
+ }
- if (state->current_rf != rf) {
+ pll = dib0090_pll_table;
+ if (state->identity.p1g)
+ pll = dib0090_p1g_pll_table;
- dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim));
+ /* Look for the interval */
+ while (state->rf_request > tune->max_freq)
+ tune++;
+ while (state->rf_request > pll->max_freq)
+ pll++;
- /* external loop filter, otherwise:
- * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4;
- * lo6 = 0x0e34 */
- if (pll->vco_band)
- lo5 = 0x049e;
- else if (state->config->analog_output)
- lo5 = 0x041d;
- else
- lo5 = 0x041c;
-
- lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */
+ state->current_tune_table_index = tune;
+ state->current_pll_table_index = pll;
- if (!state->config->io.pll_int_loop_filt)
- lo6 = 0xff28;
- else
- lo6 = (state->config->io.pll_int_loop_filt << 3);
+ dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim));
- VCOF_kHz = (pll->hfdiv * rf) * 2;
+ VCOF_kHz = (pll->hfdiv * state->rf_request) * 2;
FREF = state->config->io.clock_khz;
+ if (state->config->fref_clock_ratio != 0)
+ FREF /= state->config->fref_clock_ratio;
FBDiv = (VCOF_kHz / pll->topresc / FREF);
Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF;
@@ -1283,144 +2172,132 @@ static int dib0090_tune(struct dvb_frontend *fe)
} else if (Rest > (FREF - 2 * LPF))
Rest = FREF - 2 * LPF;
Rest = (Rest * 6528) / (FREF / 10);
+ state->rest = Rest;
- Den = 1;
+ /* external loop filter, otherwise:
+ * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4;
+ * lo6 = 0x0e34 */
+
+ if (Rest == 0) {
+ if (pll->vco_band)
+ lo5 = 0x049f;
+ else
+ lo5 = 0x041f;
+ } else {
+ if (pll->vco_band)
+ lo5 = 0x049e;
+ else if (state->config->analog_output)
+ lo5 = 0x041d;
+ else
+ lo5 = 0x041c;
+ }
+
+ if (state->identity.p1g) { /* Bias is done automatically in P1G */
+ if (state->identity.in_soc) {
+ if (state->identity.version == SOC_8090_P1G_11R1)
+ lo5 = 0x46f;
+ else
+ lo5 = 0x42f;
+ } else
+ lo5 = 0x42c;
+ }
+
+ lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */
- dprintk(" ***** ******* Rest value = %d", Rest);
+ if (!state->config->io.pll_int_loop_filt) {
+ if (state->identity.in_soc)
+ lo6 = 0xff98;
+ else if (state->identity.p1g || (Rest == 0))
+ lo6 = 0xfff8;
+ else
+ lo6 = 0xff28;
+ } else
+ lo6 = (state->config->io.pll_int_loop_filt << 3);
+
+ Den = 1;
if (Rest > 0) {
if (state->config->analog_output)
lo6 |= (1 << 2) | 2;
- else
- lo6 |= (1 << 2) | 1;
+ else {
+ if (state->identity.in_soc)
+ lo6 |= (1 << 2) | 2;
+ else
+ lo6 |= (1 << 2) | 2;
+ }
Den = 255;
}
-#ifdef CONFIG_BAND_SBAND
- if (state->current_band == BAND_SBAND)
- lo6 &= 0xfffb;
-#endif
-
dib0090_write_reg(state, 0x15, (u16) FBDiv);
-
- dib0090_write_reg(state, 0x16, (Den << 8) | 1);
-
+ if (state->config->fref_clock_ratio != 0)
+ dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio);
+ else
+ dib0090_write_reg(state, 0x16, (Den << 8) | 1);
dib0090_write_reg(state, 0x17, (u16) Rest);
-
dib0090_write_reg(state, 0x19, lo5);
-
dib0090_write_reg(state, 0x1c, lo6);
lo6 = tune->tuner_enable;
if (state->config->analog_output)
lo6 = (lo6 & 0xff9f) | 0x2;
- dib0090_write_reg(state, 0x24, lo6 | EN_LO
-#ifdef CONFIG_DIB0090_USE_PWM_AGC
- | state->config->use_pwm_agc * EN_CRYSTAL
-#endif
- );
-
- state->current_rf = rf;
-
- /* prepare a complete captrim */
- state->step = state->captrim = state->fcaptrim = 64;
-
- } else { /* we are already tuned to this frequency - the configuration is correct */
+ dib0090_write_reg(state, 0x24, lo6 | EN_LO | state->config->use_pwm_agc * EN_CRYSTAL);
- /* do a minimal captrim even if the frequency has not changed */
- state->step = 4;
- state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f;
}
- state->adc_diff = 3000;
-
- dib0090_write_reg(state, 0x10, 0x2B1);
- dib0090_write_reg(state, 0x1e, 0x0032);
+ state->current_rf = state->rf_request;
+ state->current_standard = state->fe->dtv_property_cache.delivery_system;
ret = 20;
- *tune_state = CT_TUNER_STEP_1;
- } else if (*tune_state == CT_TUNER_STEP_0) {
- /* nothing */
- } else if (*tune_state == CT_TUNER_STEP_1) {
- state->step /= 2;
- dib0090_write_reg(state, 0x18, lo4 | state->captrim);
- *tune_state = CT_TUNER_STEP_2;
- } else if (*tune_state == CT_TUNER_STEP_2) {
+ state->calibrate = CAPTRIM_CAL; /* captrim serach now */
+ }
- adc = dib0090_read_reg(state, 0x1d);
- dprintk("FE %d CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) fe->id, (u32) state->captrim, (u32) adc,
- (u32) (adc) * (u32) 1800 / (u32) 1024);
+ else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */
+ const struct dib0090_wbd_slope *wbd = state->current_wbd_table;
- if (adc >= 400) {
- adc -= 400;
- step_sign = -1;
- } else {
- adc = 400 - adc;
- step_sign = 1;
- }
+ while (state->current_rf / 1000 > wbd->max_freq)
+ wbd++;
- if (adc < state->adc_diff) {
- dprintk("FE %d CAPTRIM=%d is closer to target (%d/%d)", (u32) fe->id, (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
- state->adc_diff = adc;
- state->fcaptrim = state->captrim;
-
- }
+ dib0090_write_reg(state, 0x1e, 0x07ff);
+ dprintk("Final Captrim: %d", (u32) state->fcaptrim);
+ dprintk("HFDIV code: %d", (u32) pll->hfdiv_code);
+ dprintk("VCO = %d", (u32) pll->vco_band);
+ dprintk("VCOF in kHz: %d ((%d*%d) << 1))", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request);
+ dprintk("REFDIV: %d, FREF: %d", (u32) 1, (u32) state->config->io.clock_khz);
+ dprintk("FBDIV: %d, Rest: %d", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
+ dprintk("Num: %d, Den: %d, SD: %d", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8),
+ (u32) dib0090_read_reg(state, 0x1c) & 0x3);
- state->captrim += step_sign * state->step;
- if (state->step >= 1)
- *tune_state = CT_TUNER_STEP_1;
- else
- *tune_state = CT_TUNER_STEP_3;
+#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */
+ c = 4;
+ i = 3;
- ret = 15;
- } else if (*tune_state == CT_TUNER_STEP_3) {
- /*write the final cptrim config */
- dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim);
+ if (wbd->wbd_gain != 0)
+ c = wbd->wbd_gain;
-#ifdef CONFIG_TUNER_DIB0090_CAPTRIM_MEMORY
- state->memory[state->memory_index].cap = state->fcaptrim;
-#endif
+ state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1));
+ dib0090_write_reg(state, 0x10, state->wbdmux);
- *tune_state = CT_TUNER_STEP_4;
- } else if (*tune_state == CT_TUNER_STEP_4) {
- dib0090_write_reg(state, 0x1e, 0x07ff);
-
- dprintk("FE %d Final Captrim: %d", (u32) fe->id, (u32) state->fcaptrim);
- dprintk("FE %d HFDIV code: %d", (u32) fe->id, (u32) pll->hfdiv_code);
- dprintk("FE %d VCO = %d", (u32) fe->id, (u32) pll->vco_band);
- dprintk("FE %d VCOF in kHz: %d ((%d*%d) << 1))", (u32) fe->id, (u32) ((pll->hfdiv * rf) * 2), (u32) pll->hfdiv, (u32) rf);
- dprintk("FE %d REFDIV: %d, FREF: %d", (u32) fe->id, (u32) 1, (u32) state->config->io.clock_khz);
- dprintk("FE %d FBDIV: %d, Rest: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
- dprintk("FE %d Num: %d, Den: %d, SD: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x17),
- (u32) (dib0090_read_reg(state, 0x16) >> 8), (u32) dib0090_read_reg(state, 0x1c) & 0x3);
+ if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) {
+ dprintk("P1G : The cable band is selected and lna_tune = %d", tune->lna_tune);
+ dib0090_write_reg(state, 0x09, tune->lna_bias);
+ dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim));
+ } else
+ dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | tune->lna_bias);
- c = 4;
- i = 3;
-#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND)
- if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) {
- c = 2;
- i = 2;
- }
-#endif
- dib0090_write_reg(state, 0x10, (c << 13) | (i << 11) | (WBD
-#ifdef CONFIG_DIB0090_USE_PWM_AGC
- | (state->config->use_pwm_agc << 1)
-#endif
- ));
- dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | (tune->lna_bias << 0));
dib0090_write_reg(state, 0x0c, tune->v2i);
dib0090_write_reg(state, 0x0d, tune->mix);
dib0090_write_reg(state, 0x0e, tune->load);
+ *tune_state = CT_TUNER_STEP_1;
- *tune_state = CT_TUNER_STEP_5;
- } else if (*tune_state == CT_TUNER_STEP_5) {
-
+ } else if (*tune_state == CT_TUNER_STEP_1) {
/* initialize the lt gain register */
state->rf_lt_def = 0x7c00;
- dib0090_write_reg(state, 0x0f, state->rf_lt_def);
dib0090_set_bandwidth(state);
state->tuner_is_tuned = 1;
+
+ state->calibrate |= WBD_CAL;
+ state->calibrate |= TEMP_CAL;
*tune_state = CT_TUNER_STOP;
} else
ret = FE_CALLBACK_TIME_NEVER;
@@ -1440,6 +2317,7 @@ enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe)
return state->tune_state;
}
+
EXPORT_SYMBOL(dib0090_get_tune_state);
int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
@@ -1449,6 +2327,7 @@ int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tun
state->tune_state = tune_state;
return 0;
}
+
EXPORT_SYMBOL(dib0090_set_tune_state);
static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency)
@@ -1462,7 +2341,7 @@ static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency)
static int dib0090_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
{
struct dib0090_state *state = fe->tuner_priv;
- uint32_t ret;
+ u32 ret;
state->tune_state = CT_TUNER_START;
@@ -1492,6 +2371,29 @@ static const struct dvb_tuner_ops dib0090_ops = {
.get_frequency = dib0090_get_frequency,
};
+static const struct dvb_tuner_ops dib0090_fw_ops = {
+ .info = {
+ .name = "DiBcom DiB0090",
+ .frequency_min = 45000000,
+ .frequency_max = 860000000,
+ .frequency_step = 1000,
+ },
+ .release = dib0090_release,
+
+ .init = NULL,
+ .sleep = NULL,
+ .set_params = NULL,
+ .get_frequency = NULL,
+};
+
+static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = {
+ {470, 0, 250, 0, 100, 4},
+ {860, 51, 866, 21, 375, 4},
+ {1700, 0, 800, 0, 850, 4},
+ {2900, 0, 250, 0, 100, 6},
+ {0xFFFF, 0, 0, 0, 0, 0},
+};
+
struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config)
{
struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL);
@@ -1503,6 +2405,11 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
st->fe = fe;
fe->tuner_priv = st;
+ if (config->wbd == NULL)
+ st->current_wbd_table = dib0090_wbd_table_default;
+ else
+ st->current_wbd_table = config->wbd;
+
if (dib0090_reset(fe) != 0)
goto free_mem;
@@ -1515,8 +2422,34 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
fe->tuner_priv = NULL;
return NULL;
}
+
EXPORT_SYMBOL(dib0090_register);
+struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config)
+{
+ struct dib0090_fw_state *st = kzalloc(sizeof(struct dib0090_fw_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ st->config = config;
+ st->i2c = i2c;
+ st->fe = fe;
+ fe->tuner_priv = st;
+
+ if (dib0090_fw_reset_digital(fe, st->config) != 0)
+ goto free_mem;
+
+ dprintk("DiB0090 FW: successfully identified");
+ memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops));
+
+ return fe;
+free_mem:
+ kfree(st);
+ fe->tuner_priv = NULL;
+ return NULL;
+}
+EXPORT_SYMBOL(dib0090_fw_register);
+
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
MODULE_AUTHOR("Olivier Grenie <olivier.grenie@dibcom.fr>");
MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner");
diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb/frontends/dib0090.h
index aa7711e88776..13d85244ec16 100644
--- a/drivers/media/dvb/frontends/dib0090.h
+++ b/drivers/media/dvb/frontends/dib0090.h
@@ -27,6 +27,21 @@ struct dib0090_io_config {
u16 pll_int_loop_filt;
};
+struct dib0090_wbd_slope {
+ u16 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u16 slope_cold;
+ u16 offset_cold;
+ u16 slope_hot;
+ u16 offset_hot;
+ u8 wbd_gain;
+};
+
+struct dib0090_low_if_offset_table {
+ int std;
+ u32 RF_freq;
+ s32 offset_khz;
+};
+
struct dib0090_config {
struct dib0090_io_config io;
int (*reset) (struct dvb_frontend *, int);
@@ -47,10 +62,20 @@ struct dib0090_config {
u16 wbd_cband_offset;
u8 use_pwm_agc;
u8 clkoutdrive;
+
+ u8 ls_cfg_pad_drv;
+ u8 data_tx_drv;
+
+ u8 in_soc;
+ const struct dib0090_low_if_offset_table *low_if;
+ u8 fref_clock_ratio;
+ u16 force_cband_input;
+ struct dib0090_wbd_slope *wbd;
};
#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE))
extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
+extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast);
extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe);
extern u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner);
@@ -65,6 +90,12 @@ static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, str
return NULL;
}
+static inline struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 6aa02cb80733..900af60b9d36 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -26,24 +26,29 @@ MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (defau
#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0)
+struct i2c_device {
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+};
+
struct dib7000p_state {
struct dvb_frontend demod;
- struct dib7000p_config cfg;
+ struct dib7000p_config cfg;
u8 i2c_addr;
- struct i2c_adapter *i2c_adap;
+ struct i2c_adapter *i2c_adap;
struct dibx000_i2c_master i2c_master;
u16 wbd_ref;
- u8 current_band;
+ u8 current_band;
u32 current_bandwidth;
struct dibx000_agc_config *current_agc;
u32 timf;
- u8 div_force_off : 1;
- u8 div_state : 1;
+ u8 div_force_off:1;
+ u8 div_state:1;
u16 div_sync_wait;
u8 agc_state;
@@ -51,7 +56,13 @@ struct dib7000p_state {
u16 gpio_dir;
u16 gpio_val;
- u8 sfn_workaround_active :1;
+ u8 sfn_workaround_active:1;
+
+#define SOC7090 0x7090
+ u16 version;
+
+ u16 tuner_enable;
+ struct i2c_adapter dib7090_tuner_adap;
};
enum dib7000p_power_mode {
@@ -60,17 +71,20 @@ enum dib7000p_power_mode {
DIB7000P_POWER_INTERFACE_ONLY,
};
+static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode);
+static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff);
+
static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
{
u8 wb[2] = { reg >> 8, reg & 0xff };
u8 rb[2];
struct i2c_msg msg[2] = {
- { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
- { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ {.addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2},
+ {.addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2},
};
if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
- dprintk("i2c read error on %d",reg);
+ dprintk("i2c read error on %d", reg);
return (rb[0] << 8) | rb[1];
}
@@ -86,7 +100,8 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
};
return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
}
-static void dib7000p_write_tab(struct dib7000p_state *state, u16 *buf)
+
+static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf)
{
u16 l = 0, r, *n;
n = buf;
@@ -104,54 +119,54 @@ static void dib7000p_write_tab(struct dib7000p_state *state, u16 *buf)
static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
{
- int ret = 0;
+ int ret = 0;
u16 outreg, fifo_threshold, smo_mode;
outreg = 0;
fifo_threshold = 1792;
smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
- dprintk( "setting output mode for demod %p to %d",
- &state->demod, mode);
+ dprintk("setting output mode for demod %p to %d", &state->demod, mode);
switch (mode) {
- case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
- outreg = (1 << 10); /* 0x0400 */
- break;
- case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
- outreg = (1 << 10) | (1 << 6); /* 0x0440 */
- break;
- case OUTMODE_MPEG2_SERIAL: // STBs with serial input
- outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */
- break;
- case OUTMODE_DIVERSITY:
- if (state->cfg.hostbus_diversity)
- outreg = (1 << 10) | (4 << 6); /* 0x0500 */
- else
- outreg = (1 << 11);
- break;
- case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
- smo_mode |= (3 << 1);
- fifo_threshold = 512;
- outreg = (1 << 10) | (5 << 6);
- break;
- case OUTMODE_ANALOG_ADC:
- outreg = (1 << 10) | (3 << 6);
- break;
- case OUTMODE_HIGH_Z: // disable
- outreg = 0;
- break;
- default:
- dprintk( "Unhandled output_mode passed to be set for demod %p",&state->demod);
- break;
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK:
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL:
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity)
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ else
+ outreg = (1 << 11);
+ break;
+ case OUTMODE_MPEG2_FIFO:
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_ANALOG_ADC:
+ outreg = (1 << 10) | (3 << 6);
+ break;
+ case OUTMODE_HIGH_Z:
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p", &state->demod);
+ break;
}
if (state->cfg.output_mpeg2_in_188_bytes)
- smo_mode |= (1 << 5) ;
+ smo_mode |= (1 << 5);
- ret |= dib7000p_write_word(state, 235, smo_mode);
- ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */
- ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */
+ ret |= dib7000p_write_word(state, 235, smo_mode);
+ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */
+ if (state->version != SOC7090)
+ ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */
return ret;
}
@@ -161,13 +176,13 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff)
struct dib7000p_state *state = demod->demodulator_priv;
if (state->div_force_off) {
- dprintk( "diversity combination deactivated - forced by COFDM parameters");
+ dprintk("diversity combination deactivated - forced by COFDM parameters");
onoff = 0;
dib7000p_write_word(state, 207, 0);
} else
dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
- state->div_state = (u8)onoff;
+ state->div_state = (u8) onoff;
if (onoff) {
dib7000p_write_word(state, 204, 6);
@@ -184,37 +199,48 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff)
static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode)
{
/* by default everything is powered off */
- u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003,
- reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff);
+ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff);
/* now, depending on the requested mode, we power on */
switch (mode) {
/* power up everything in the demod */
- case DIB7000P_POWER_ALL:
- reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff;
- break;
-
- case DIB7000P_POWER_ANALOG_ADC:
- /* dem, cfg, iqc, sad, agc */
- reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9));
- /* nud */
- reg_776 &= ~((1 << 0));
- /* Dout */
+ case DIB7000P_POWER_ALL:
+ reg_774 = 0x0000;
+ reg_775 = 0x0000;
+ reg_776 = 0x0;
+ reg_899 = 0x0;
+ if (state->version == SOC7090)
+ reg_1280 &= 0x001f;
+ else
+ reg_1280 &= 0x01ff;
+ break;
+
+ case DIB7000P_POWER_ANALOG_ADC:
+ /* dem, cfg, iqc, sad, agc */
+ reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9));
+ /* nud */
+ reg_776 &= ~((1 << 0));
+ /* Dout */
+ if (state->version != SOC7090)
reg_1280 &= ~((1 << 11));
- /* fall through wanted to enable the interfaces */
+ reg_1280 &= ~(1 << 6);
+ /* fall through wanted to enable the interfaces */
/* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
- case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ if (state->version == SOC7090)
+ reg_1280 &= ~((1 << 7) | (1 << 5));
+ else
reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
- break;
+ break;
/* TODO following stuff is just converted from the dib7000-driver - check when is used what */
}
- dib7000p_write_word(state, 774, reg_774);
- dib7000p_write_word(state, 775, reg_775);
- dib7000p_write_word(state, 776, reg_776);
- dib7000p_write_word(state, 899, reg_899);
+ dib7000p_write_word(state, 774, reg_774);
+ dib7000p_write_word(state, 775, reg_775);
+ dib7000p_write_word(state, 776, reg_776);
+ dib7000p_write_word(state, 899, reg_899);
dib7000p_write_word(state, 1280, reg_1280);
return 0;
@@ -222,40 +248,57 @@ static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_p
static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no)
{
- u16 reg_908 = dib7000p_read_word(state, 908),
- reg_909 = dib7000p_read_word(state, 909);
+ u16 reg_908 = dib7000p_read_word(state, 908), reg_909 = dib7000p_read_word(state, 909);
+ u16 reg;
switch (no) {
- case DIBX000_SLOW_ADC_ON:
+ case DIBX000_SLOW_ADC_ON:
+ if (state->version == SOC7090) {
+ reg = dib7000p_read_word(state, 1925);
+
+ dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */
+
+ reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */
+ msleep(200);
+ dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */
+
+ reg = dib7000p_read_word(state, 72) & ~((0x3 << 14) | (0x3 << 12));
+ dib7000p_write_word(state, 72, reg | (1 << 14) | (3 << 12) | 524); /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; (Vin2 = Vcm) */
+ } else {
reg_909 |= (1 << 1) | (1 << 0);
dib7000p_write_word(state, 909, reg_909);
reg_909 &= ~(1 << 1);
- break;
+ }
+ break;
- case DIBX000_SLOW_ADC_OFF:
- reg_909 |= (1 << 1) | (1 << 0);
- break;
+ case DIBX000_SLOW_ADC_OFF:
+ if (state->version == SOC7090) {
+ reg = dib7000p_read_word(state, 1925);
+ dib7000p_write_word(state, 1925, (reg & ~(1 << 2)) | (1 << 4)); /* reset_sladc = 1 en_slowAdc = 0 */
+ } else
+ reg_909 |= (1 << 1) | (1 << 0);
+ break;
- case DIBX000_ADC_ON:
- reg_908 &= 0x0fff;
- reg_909 &= 0x0003;
- break;
+ case DIBX000_ADC_ON:
+ reg_908 &= 0x0fff;
+ reg_909 &= 0x0003;
+ break;
- case DIBX000_ADC_OFF: // leave the VBG voltage on
- reg_908 |= (1 << 14) | (1 << 13) | (1 << 12);
- reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
- break;
+ case DIBX000_ADC_OFF:
+ reg_908 |= (1 << 14) | (1 << 13) | (1 << 12);
+ reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+ break;
- case DIBX000_VBG_ENABLE:
- reg_908 &= ~(1 << 15);
- break;
+ case DIBX000_VBG_ENABLE:
+ reg_908 &= ~(1 << 15);
+ break;
- case DIBX000_VBG_DISABLE:
- reg_908 |= (1 << 15);
- break;
+ case DIBX000_VBG_DISABLE:
+ reg_908 |= (1 << 15);
+ break;
- default:
- break;
+ default:
+ break;
}
// dprintk( "908: %x, 909: %x\n", reg_908, reg_909);
@@ -275,17 +318,17 @@ static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw)
state->current_bandwidth = bw;
if (state->timf == 0) {
- dprintk( "using default timf");
+ dprintk("using default timf");
timf = state->cfg.bw->timf;
} else {
- dprintk( "using updated timf");
+ dprintk("using updated timf");
timf = state->timf;
}
timf = timf * (bw / 50) / 160;
dib7000p_write_word(state, 23, (u16) ((timf >> 16) & 0xffff));
- dib7000p_write_word(state, 24, (u16) ((timf ) & 0xffff));
+ dib7000p_write_word(state, 24, (u16) ((timf) & 0xffff));
return 0;
}
@@ -293,9 +336,12 @@ static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw)
static int dib7000p_sad_calib(struct dib7000p_state *state)
{
/* internal */
-// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
dib7000p_write_word(state, 73, (0 << 1) | (0 << 0));
- dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096
+
+ if (state->version == SOC7090)
+ dib7000p_write_word(state, 74, 2048);
+ else
+ dib7000p_write_word(state, 74, 776);
/* do the calibration */
dib7000p_write_word(state, 73, (1 << 0));
@@ -314,37 +360,91 @@ int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value)
state->wbd_ref = value;
return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value);
}
-
EXPORT_SYMBOL(dib7000p_set_wbd_ref);
+
static void dib7000p_reset_pll(struct dib7000p_state *state)
{
struct dibx000_bandwidth_config *bw = &state->cfg.bw[0];
u16 clk_cfg0;
- /* force PLL bypass */
- clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) |
- (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) |
- (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0);
+ if (state->version == SOC7090) {
+ dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv));
+
+ while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1)
+ ;
- dib7000p_write_word(state, 900, clk_cfg0);
+ dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15));
+ } else {
+ /* force PLL bypass */
+ clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) |
+ (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0);
+
+ dib7000p_write_word(state, 900, clk_cfg0);
- /* P_pll_cfg */
- dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset);
- clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff);
- dib7000p_write_word(state, 900, clk_cfg0);
+ /* P_pll_cfg */
+ dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset);
+ clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff);
+ dib7000p_write_word(state, 900, clk_cfg0);
+ }
- dib7000p_write_word(state, 18, (u16) (((bw->internal*1000) >> 16) & 0xffff));
- dib7000p_write_word(state, 19, (u16) ( (bw->internal*1000 ) & 0xffff));
- dib7000p_write_word(state, 21, (u16) ( (bw->ifreq >> 16) & 0xffff));
- dib7000p_write_word(state, 22, (u16) ( (bw->ifreq ) & 0xffff));
+ dib7000p_write_word(state, 18, (u16) (((bw->internal * 1000) >> 16) & 0xffff));
+ dib7000p_write_word(state, 19, (u16) ((bw->internal * 1000) & 0xffff));
+ dib7000p_write_word(state, 21, (u16) ((bw->ifreq >> 16) & 0xffff));
+ dib7000p_write_word(state, 22, (u16) ((bw->ifreq) & 0xffff));
dib7000p_write_word(state, 72, bw->sad_cfg);
}
+static u32 dib7000p_get_internal_freq(struct dib7000p_state *state)
+{
+ u32 internal = (u32) dib7000p_read_word(state, 18) << 16;
+ internal |= (u32) dib7000p_read_word(state, 19);
+ internal /= 1000;
+
+ return internal;
+}
+
+int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856);
+ u8 loopdiv, prediv;
+ u32 internal, xtal;
+
+ /* get back old values */
+ prediv = reg_1856 & 0x3f;
+ loopdiv = (reg_1856 >> 6) & 0x3f;
+
+ if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) {
+ dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio);
+ reg_1856 &= 0xf000;
+ reg_1857 = dib7000p_read_word(state, 1857);
+ dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15));
+
+ dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f));
+
+ /* write new system clk into P_sec_len */
+ internal = dib7000p_get_internal_freq(state);
+ xtal = (internal / loopdiv) * prediv;
+ internal = 1000 * (xtal / bw->pll_prediv) * bw->pll_ratio; /* new internal */
+ dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff));
+ dib7000p_write_word(state, 19, (u16) (internal & 0xffff));
+
+ dib7000p_write_word(state, 1857, reg_1857 | (1 << 15));
+
+ while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1)
+ dprintk("Waiting for PLL to lock");
+
+ return 0;
+ }
+ return -EIO;
+}
+EXPORT_SYMBOL(dib7000p_update_pll);
+
static int dib7000p_reset_gpio(struct dib7000p_state *st)
{
/* reset the GPIOs */
- dprintk( "gpio dir: %x: val: %x, pwm_pos: %x",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos);
+ dprintk("gpio dir: %x: val: %x, pwm_pos: %x", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos);
dib7000p_write_word(st, 1029, st->gpio_dir);
dib7000p_write_word(st, 1030, st->gpio_val);
@@ -360,13 +460,13 @@ static int dib7000p_reset_gpio(struct dib7000p_state *st)
static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val)
{
st->gpio_dir = dib7000p_read_word(st, 1029);
- st->gpio_dir &= ~(1 << num); /* reset the direction bit */
- st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */
+ st->gpio_dir &= ~(1 << num); /* reset the direction bit */
+ st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */
dib7000p_write_word(st, 1029, st->gpio_dir);
st->gpio_val = dib7000p_read_word(st, 1030);
- st->gpio_val &= ~(1 << num); /* reset the direction bit */
- st->gpio_val |= (val & 0x01) << num; /* set the new value */
+ st->gpio_val &= ~(1 << num); /* reset the direction bit */
+ st->gpio_val |= (val & 0x01) << num; /* set the new value */
dib7000p_write_word(st, 1030, st->gpio_val);
return 0;
@@ -377,96 +477,94 @@ int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val)
struct dib7000p_state *state = demod->demodulator_priv;
return dib7000p_cfg_gpio(state, num, dir, val);
}
-
EXPORT_SYMBOL(dib7000p_set_gpio);
-static u16 dib7000p_defaults[] =
-{
+static u16 dib7000p_defaults[] = {
// auto search configuration
3, 2,
- 0x0004,
- 0x1000,
- 0x0814, /* Equal Lock */
+ 0x0004,
+ 0x1000,
+ 0x0814, /* Equal Lock */
12, 6,
- 0x001b,
- 0x7740,
- 0x005b,
- 0x8d80,
- 0x01c9,
- 0xc380,
- 0x0000,
- 0x0080,
- 0x0000,
- 0x0090,
- 0x0001,
- 0xd4c0,
+ 0x001b,
+ 0x7740,
+ 0x005b,
+ 0x8d80,
+ 0x01c9,
+ 0xc380,
+ 0x0000,
+ 0x0080,
+ 0x0000,
+ 0x0090,
+ 0x0001,
+ 0xd4c0,
1, 26,
- 0x6680, // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26
+ 0x6680,
/* set ADC level to -16 */
11, 79,
- (1 << 13) - 825 - 117,
- (1 << 13) - 837 - 117,
- (1 << 13) - 811 - 117,
- (1 << 13) - 766 - 117,
- (1 << 13) - 737 - 117,
- (1 << 13) - 693 - 117,
- (1 << 13) - 648 - 117,
- (1 << 13) - 619 - 117,
- (1 << 13) - 575 - 117,
- (1 << 13) - 531 - 117,
- (1 << 13) - 501 - 117,
+ (1 << 13) - 825 - 117,
+ (1 << 13) - 837 - 117,
+ (1 << 13) - 811 - 117,
+ (1 << 13) - 766 - 117,
+ (1 << 13) - 737 - 117,
+ (1 << 13) - 693 - 117,
+ (1 << 13) - 648 - 117,
+ (1 << 13) - 619 - 117,
+ (1 << 13) - 575 - 117,
+ (1 << 13) - 531 - 117,
+ (1 << 13) - 501 - 117,
1, 142,
- 0x0410, // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16
+ 0x0410,
/* disable power smoothing */
8, 145,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
1, 154,
- 1 << 13, // P_fft_freq_dir=1, P_fft_nb_to_cut=0
+ 1 << 13,
1, 168,
- 0x0ccd, // P_pha3_thres, default 0x3000
-
-// 1, 169,
-// 0x0010, // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010
+ 0x0ccd,
1, 183,
- 0x200f, // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005
+ 0x200f,
+
+ 1, 212,
+ 0x169,
5, 187,
- 0x023d, // P_adp_regul_cnt=573, default: 410
- 0x00a4, // P_adp_noise_cnt=
- 0x00a4, // P_adp_regul_ext
- 0x7ff0, // P_adp_noise_ext
- 0x3ccc, // P_adp_fil
+ 0x023d,
+ 0x00a4,
+ 0x00a4,
+ 0x7ff0,
+ 0x3ccc,
1, 198,
- 0x800, // P_equal_thres_wgn
+ 0x800,
1, 222,
- 0x0010, // P_fec_ber_rs_len=2
+ 0x0010,
1, 235,
- 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ 0x0062,
2, 901,
- 0x0006, // P_clk_cfg1
- (3 << 10) | (1 << 6), // P_divclksel=3 P_divbitsel=1
+ 0x0006,
+ (3 << 10) | (1 << 6),
1, 905,
- 0x2c8e, // Tuner IO bank: max drive (14mA) + divout pads max drive
+ 0x2c8e,
0,
};
@@ -475,51 +573,64 @@ static int dib7000p_demod_reset(struct dib7000p_state *state)
{
dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ if (state->version == SOC7090)
+ dibx000_reset_i2c_master(&state->i2c_master);
+
dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE);
/* restart all parts */
- dib7000p_write_word(state, 770, 0xffff);
- dib7000p_write_word(state, 771, 0xffff);
- dib7000p_write_word(state, 772, 0x001f);
- dib7000p_write_word(state, 898, 0x0003);
- /* except i2c, sdio, gpio - control interfaces */
- dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) );
-
- dib7000p_write_word(state, 770, 0);
- dib7000p_write_word(state, 771, 0);
- dib7000p_write_word(state, 772, 0);
- dib7000p_write_word(state, 898, 0);
+ dib7000p_write_word(state, 770, 0xffff);
+ dib7000p_write_word(state, 771, 0xffff);
+ dib7000p_write_word(state, 772, 0x001f);
+ dib7000p_write_word(state, 898, 0x0003);
+ dib7000p_write_word(state, 1280, 0x001f - ((1 << 4) | (1 << 3)));
+
+ dib7000p_write_word(state, 770, 0);
+ dib7000p_write_word(state, 771, 0);
+ dib7000p_write_word(state, 772, 0);
+ dib7000p_write_word(state, 898, 0);
dib7000p_write_word(state, 1280, 0);
/* default */
dib7000p_reset_pll(state);
if (dib7000p_reset_gpio(state) != 0)
- dprintk( "GPIO reset was not successful.");
-
- if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
- dprintk( "OUTPUT_MODE could not be reset.");
+ dprintk("GPIO reset was not successful.");
- /* unforce divstr regardless whether i2c enumeration was done or not */
- dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) );
+ if (state->version == SOC7090) {
+ dib7000p_write_word(state, 899, 0);
- dib7000p_set_bandwidth(state, 8000);
+ /* impulse noise */
+ dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */
+ dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */
+ dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */
+ dib7000p_write_word(state, 273, (1<<6) | 30);
+ }
+ if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ dprintk("OUTPUT_MODE could not be reset.");
dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
dib7000p_sad_calib(state);
dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_OFF);
- // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ...
- if(state->cfg.tuner_is_baseband)
- dib7000p_write_word(state, 36,0x0755);
- else
- dib7000p_write_word(state, 36,0x1f55);
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1));
+
+ dib7000p_set_bandwidth(state, 8000);
+
+ if (state->version == SOC7090) {
+ dib7000p_write_word(state, 36, 0x5755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */
+ } else {
+ if (state->cfg.tuner_is_baseband)
+ dib7000p_write_word(state, 36, 0x0755);
+ else
+ dib7000p_write_word(state, 36, 0x1f55);
+ }
dib7000p_write_tab(state, dib7000p_defaults);
dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
-
return 0;
}
@@ -527,9 +638,9 @@ static void dib7000p_pll_clk_cfg(struct dib7000p_state *state)
{
u16 tmp = 0;
tmp = dib7000p_read_word(state, 903);
- dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll
+ dib7000p_write_word(state, 903, (tmp | 0x1));
tmp = dib7000p_read_word(state, 900);
- dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock
+ dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6));
}
static void dib7000p_restart_agc(struct dib7000p_state *state)
@@ -543,11 +654,9 @@ static int dib7000p_update_lna(struct dib7000p_state *state)
{
u16 dyn_gain;
- // when there is no LNA to program return immediatly
if (state->cfg.update_lna) {
- // read dyn_gain here (because it is demod-dependent and not fe)
dyn_gain = dib7000p_read_word(state, 394);
- if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+ if (state->cfg.update_lna(&state->demod, dyn_gain)) {
dib7000p_restart_agc(state);
return 1;
}
@@ -571,24 +680,24 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band)
}
if (agc == NULL) {
- dprintk( "no valid AGC configuration found for band 0x%02x",band);
+ dprintk("no valid AGC configuration found for band 0x%02x", band);
return -EINVAL;
}
state->current_agc = agc;
/* AGC */
- dib7000p_write_word(state, 75 , agc->setup );
- dib7000p_write_word(state, 76 , agc->inv_gain );
- dib7000p_write_word(state, 77 , agc->time_stabiliz );
+ dib7000p_write_word(state, 75, agc->setup);
+ dib7000p_write_word(state, 76, agc->inv_gain);
+ dib7000p_write_word(state, 77, agc->time_stabiliz);
dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock);
// Demod AGC loop configuration
dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp);
- dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp);
+ dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp);
/* AGC continued */
- dprintk( "WBD: ref: %d, sel: %d, active: %d, alpha: %d",
+ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d",
state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
if (state->wbd_ref != 0)
@@ -598,101 +707,135 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band)
dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8));
- dib7000p_write_word(state, 107, agc->agc1_max);
- dib7000p_write_word(state, 108, agc->agc1_min);
- dib7000p_write_word(state, 109, agc->agc2_max);
- dib7000p_write_word(state, 110, agc->agc2_min);
- dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
- dib7000p_write_word(state, 112, agc->agc1_pt3);
+ dib7000p_write_word(state, 107, agc->agc1_max);
+ dib7000p_write_word(state, 108, agc->agc1_min);
+ dib7000p_write_word(state, 109, agc->agc2_max);
+ dib7000p_write_word(state, 110, agc->agc2_min);
+ dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
+ dib7000p_write_word(state, 112, agc->agc1_pt3);
dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
- dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
return 0;
}
+static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
+{
+ u32 internal = dib7000p_get_internal_freq(state);
+ s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */
+ u32 abs_offset_khz = ABS(offset_khz);
+ u32 dds = state->cfg.bw->ifreq & 0x1ffffff;
+ u8 invert = !!(state->cfg.bw->ifreq & (1 << 25));
+
+ dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d", offset_khz, internal, invert);
+
+ if (offset_khz < 0)
+ unit_khz_dds_val *= -1;
+
+ /* IF tuner */
+ if (invert)
+ dds -= (abs_offset_khz * unit_khz_dds_val); /* /100 because of /100 on the unit_khz_dds_val line calc for better accuracy */
+ else
+ dds += (abs_offset_khz * unit_khz_dds_val);
+
+ if (abs_offset_khz <= (internal / 2)) { /* Max dds offset is the half of the demod freq */
+ dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9)));
+ dib7000p_write_word(state, 22, (u16) (dds & 0xffff));
+ }
+}
+
static int dib7000p_agc_startup(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch)
{
struct dib7000p_state *state = demod->demodulator_priv;
int ret = -1;
u8 *agc_state = &state->agc_state;
u8 agc_split;
+ u16 reg;
+ u32 upd_demod_gain_period = 0x1000;
switch (state->agc_state) {
- case 0:
- // set power-up level: interf+analog+AGC
- dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ case 0:
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ if (state->version == SOC7090) {
+ reg = dib7000p_read_word(state, 0x79b) & 0xff00;
+ dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */
+ dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF));
+
+ /* enable adc i & q */
+ reg = dib7000p_read_word(state, 0x780);
+ dib7000p_write_word(state, 0x780, (reg | (0x3)) & (~(1 << 7)));
+ } else {
dib7000p_set_adc_state(state, DIBX000_ADC_ON);
dib7000p_pll_clk_cfg(state);
+ }
- if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency/1000)) != 0)
- return -1;
-
- ret = 7;
- (*agc_state)++;
- break;
+ if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0)
+ return -1;
- case 1:
- // AGC initialization
- if (state->cfg.agc_control)
- state->cfg.agc_control(&state->demod, 1);
-
- dib7000p_write_word(state, 78, 32768);
- if (!state->current_agc->perform_agc_softsplit) {
- /* we are using the wbd - so slow AGC startup */
- /* force 0 split on WBD and restart AGC */
- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8));
- (*agc_state)++;
- ret = 5;
- } else {
- /* default AGC startup */
- (*agc_state) = 4;
- /* wait AGC rough lock time */
- ret = 7;
- }
+ dib7000p_set_dds(state, 0);
+ ret = 7;
+ (*agc_state)++;
+ break;
- dib7000p_restart_agc(state);
- break;
+ case 1:
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 1);
- case 2: /* fast split search path after 5sec */
- dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */
- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */
+ dib7000p_write_word(state, 78, 32768);
+ if (!state->current_agc->perform_agc_softsplit) {
+ /* we are using the wbd - so slow AGC startup */
+ /* force 0 split on WBD and restart AGC */
+ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8));
(*agc_state)++;
- ret = 14;
- break;
+ ret = 5;
+ } else {
+ /* default AGC startup */
+ (*agc_state) = 4;
+ /* wait AGC rough lock time */
+ ret = 7;
+ }
- case 3: /* split search ended */
- agc_split = (u8)dib7000p_read_word(state, 396); /* store the split value for the next time */
- dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */
+ dib7000p_restart_agc(state);
+ break;
- dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */
- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */
+ case 2: /* fast split search path after 5sec */
+ dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */
+ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */
+ (*agc_state)++;
+ ret = 14;
+ break;
- dib7000p_restart_agc(state);
+ case 3: /* split search ended */
+ agc_split = (u8) dib7000p_read_word(state, 396); /* store the split value for the next time */
+ dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */
- dprintk( "SPLIT %p: %hd", demod, agc_split);
+ dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */
+ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */
- (*agc_state)++;
- ret = 5;
- break;
+ dib7000p_restart_agc(state);
- case 4: /* LNA startup */
- // wait AGC accurate lock time
- ret = 7;
+ dprintk("SPLIT %p: %hd", demod, agc_split);
- if (dib7000p_update_lna(state))
- // wait only AGC rough lock time
- ret = 5;
- else // nothing was done, go to the next state
- (*agc_state)++;
- break;
+ (*agc_state)++;
+ ret = 5;
+ break;
- case 5:
- if (state->cfg.agc_control)
- state->cfg.agc_control(&state->demod, 0);
+ case 4: /* LNA startup */
+ ret = 7;
+
+ if (dib7000p_update_lna(state))
+ ret = 5;
+ else
(*agc_state)++;
- break;
- default:
- break;
+ break;
+
+ case 5:
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 0);
+ (*agc_state)++;
+ break;
+ default:
+ break;
}
return ret;
}
@@ -703,45 +846,89 @@ static void dib7000p_update_timf(struct dib7000p_state *state)
state->timf = timf * 160 / (state->current_bandwidth / 50);
dib7000p_write_word(state, 23, (u16) (timf >> 16));
dib7000p_write_word(state, 24, (u16) (timf & 0xffff));
- dprintk( "updated timf_frequency: %d (default: %d)",state->timf, state->cfg.bw->timf);
+ dprintk("updated timf_frequency: %d (default: %d)", state->timf, state->cfg.bw->timf);
+
+}
+u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ switch (op) {
+ case DEMOD_TIMF_SET:
+ state->timf = timf;
+ break;
+ case DEMOD_TIMF_UPDATE:
+ dib7000p_update_timf(state);
+ break;
+ case DEMOD_TIMF_GET:
+ break;
+ }
+ dib7000p_set_bandwidth(state, state->current_bandwidth);
+ return state->timf;
}
+EXPORT_SYMBOL(dib7000p_ctrl_timf);
static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_frontend_parameters *ch, u8 seq)
{
u16 value, est[4];
- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
+ dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
/* nfft, guard, qam, alpha */
value = 0;
switch (ch->u.ofdm.transmission_mode) {
- case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
- case TRANSMISSION_MODE_4K: value |= (2 << 7); break;
- default:
- case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
+ case TRANSMISSION_MODE_2K:
+ value |= (0 << 7);
+ break;
+ case TRANSMISSION_MODE_4K:
+ value |= (2 << 7);
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ value |= (1 << 7);
+ break;
}
switch (ch->u.ofdm.guard_interval) {
- case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
- case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
- case GUARD_INTERVAL_1_4: value |= (3 << 5); break;
- default:
- case GUARD_INTERVAL_1_8: value |= (2 << 5); break;
+ case GUARD_INTERVAL_1_32:
+ value |= (0 << 5);
+ break;
+ case GUARD_INTERVAL_1_16:
+ value |= (1 << 5);
+ break;
+ case GUARD_INTERVAL_1_4:
+ value |= (3 << 5);
+ break;
+ default:
+ case GUARD_INTERVAL_1_8:
+ value |= (2 << 5);
+ break;
}
switch (ch->u.ofdm.constellation) {
- case QPSK: value |= (0 << 3); break;
- case QAM_16: value |= (1 << 3); break;
- default:
- case QAM_64: value |= (2 << 3); break;
+ case QPSK:
+ value |= (0 << 3);
+ break;
+ case QAM_16:
+ value |= (1 << 3);
+ break;
+ default:
+ case QAM_64:
+ value |= (2 << 3);
+ break;
}
switch (HIERARCHY_1) {
- case HIERARCHY_2: value |= 2; break;
- case HIERARCHY_4: value |= 4; break;
- default:
- case HIERARCHY_1: value |= 1; break;
+ case HIERARCHY_2:
+ value |= 2;
+ break;
+ case HIERARCHY_4:
+ value |= 4;
+ break;
+ default:
+ case HIERARCHY_1:
+ value |= 1;
+ break;
}
dib7000p_write_word(state, 0, value);
- dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */
+ dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */
/* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */
value = 0;
@@ -752,39 +939,63 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte
if (1 == 1)
value |= 1;
switch ((ch->u.ofdm.hierarchy_information == 0 || 1 == 1) ? ch->u.ofdm.code_rate_HP : ch->u.ofdm.code_rate_LP) {
- case FEC_2_3: value |= (2 << 1); break;
- case FEC_3_4: value |= (3 << 1); break;
- case FEC_5_6: value |= (5 << 1); break;
- case FEC_7_8: value |= (7 << 1); break;
- default:
- case FEC_1_2: value |= (1 << 1); break;
+ case FEC_2_3:
+ value |= (2 << 1);
+ break;
+ case FEC_3_4:
+ value |= (3 << 1);
+ break;
+ case FEC_5_6:
+ value |= (5 << 1);
+ break;
+ case FEC_7_8:
+ value |= (7 << 1);
+ break;
+ default:
+ case FEC_1_2:
+ value |= (1 << 1);
+ break;
}
dib7000p_write_word(state, 208, value);
/* offset loop parameters */
- dib7000p_write_word(state, 26, 0x6680); // timf(6xxx)
- dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3)
- dib7000p_write_word(state, 29, 0x1273); // isi
- dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5)
+ dib7000p_write_word(state, 26, 0x6680);
+ dib7000p_write_word(state, 32, 0x0003);
+ dib7000p_write_word(state, 29, 0x1273);
+ dib7000p_write_word(state, 33, 0x0005);
/* P_dvsy_sync_wait */
switch (ch->u.ofdm.transmission_mode) {
- case TRANSMISSION_MODE_8K: value = 256; break;
- case TRANSMISSION_MODE_4K: value = 128; break;
- case TRANSMISSION_MODE_2K:
- default: value = 64; break;
+ case TRANSMISSION_MODE_8K:
+ value = 256;
+ break;
+ case TRANSMISSION_MODE_4K:
+ value = 128;
+ break;
+ case TRANSMISSION_MODE_2K:
+ default:
+ value = 64;
+ break;
}
switch (ch->u.ofdm.guard_interval) {
- case GUARD_INTERVAL_1_16: value *= 2; break;
- case GUARD_INTERVAL_1_8: value *= 4; break;
- case GUARD_INTERVAL_1_4: value *= 8; break;
- default:
- case GUARD_INTERVAL_1_32: value *= 1; break;
+ case GUARD_INTERVAL_1_16:
+ value *= 2;
+ break;
+ case GUARD_INTERVAL_1_8:
+ value *= 4;
+ break;
+ case GUARD_INTERVAL_1_4:
+ value *= 8;
+ break;
+ default:
+ case GUARD_INTERVAL_1_32:
+ value *= 1;
+ break;
}
if (state->cfg.diversity_delay == 0)
- state->div_sync_wait = (value * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo
+ state->div_sync_wait = (value * 3) / 2 + 48;
else
- state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for one DVSY-fifo
+ state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay;
/* deactive the possibility of diversity reception if extended interleaver */
state->div_force_off = !1 && ch->u.ofdm.transmission_mode != TRANSMISSION_MODE_8K;
@@ -792,24 +1003,24 @@ static void dib7000p_set_channel(struct dib7000p_state *state, struct dvb_fronte
/* channel estimation fine configuration */
switch (ch->u.ofdm.constellation) {
- case QAM_64:
- est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
- est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
- est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
- break;
- case QAM_16:
- est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
- est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
- est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
- break;
- default:
- est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
- est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
- est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
- est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
- break;
+ case QAM_64:
+ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
+ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
+ break;
+ case QAM_16:
+ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
+ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
+ break;
+ default:
+ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
+ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
+ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
+ break;
}
for (value = 0; value < 4; value++)
dib7000p_write_word(state, 187 + value, est[value]);
@@ -820,14 +1031,15 @@ static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dvb_fron
struct dib7000p_state *state = demod->demodulator_priv;
struct dvb_frontend_parameters schan;
u32 value, factor;
+ u32 internal = dib7000p_get_internal_freq(state);
schan = *ch;
schan.u.ofdm.constellation = QAM_64;
- schan.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
- schan.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
- schan.u.ofdm.code_rate_HP = FEC_2_3;
- schan.u.ofdm.code_rate_LP = FEC_3_4;
- schan.u.ofdm.hierarchy_information = 0;
+ schan.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ schan.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ schan.u.ofdm.code_rate_HP = FEC_2_3;
+ schan.u.ofdm.code_rate_LP = FEC_3_4;
+ schan.u.ofdm.hierarchy_information = 0;
dib7000p_set_channel(state, &schan, 7);
@@ -837,16 +1049,15 @@ static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dvb_fron
else
factor = 6;
- // always use the setting for 8MHz here lock_time for 7,6 MHz are longer
- value = 30 * state->cfg.bw->internal * factor;
- dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time
- dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time
- value = 100 * state->cfg.bw->internal * factor;
- dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time
- dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time
- value = 500 * state->cfg.bw->internal * factor;
- dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
- dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time
+ value = 30 * internal * factor;
+ dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff));
+ dib7000p_write_word(state, 7, (u16) (value & 0xffff));
+ value = 100 * internal * factor;
+ dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff));
+ dib7000p_write_word(state, 9, (u16) (value & 0xffff));
+ value = 500 * internal * factor;
+ dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff));
+ dib7000p_write_word(state, 11, (u16) (value & 0xffff));
value = dib7000p_read_word(state, 0);
dib7000p_write_word(state, 0, (u16) ((1 << 9) | value));
@@ -861,101 +1072,101 @@ static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod)
struct dib7000p_state *state = demod->demodulator_priv;
u16 irq_pending = dib7000p_read_word(state, 1284);
- if (irq_pending & 0x1) // failed
+ if (irq_pending & 0x1)
return 1;
- if (irq_pending & 0x2) // succeeded
+ if (irq_pending & 0x2)
return 2;
- return 0; // still pending
+ return 0;
}
static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw)
{
- static s16 notch[]={16143, 14402, 12238, 9713, 6902, 3888, 759, -2392};
- static u8 sine [] ={0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22,
- 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51,
- 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80,
- 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105,
- 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126,
- 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146,
- 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165,
- 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182,
- 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
- 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212,
- 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224,
- 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235,
- 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243,
- 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249,
- 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254,
- 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255};
+ static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 };
+ static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22,
+ 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51,
+ 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80,
+ 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105,
+ 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126,
+ 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146,
+ 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165,
+ 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224,
+ 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235,
+ 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243,
+ 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249,
+ 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254,
+ 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255
+ };
u32 xtal = state->cfg.bw->xtal_hz / 1000;
int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz;
int k;
- int coef_re[8],coef_im[8];
+ int coef_re[8], coef_im[8];
int bw_khz = bw;
u32 pha;
- dprintk( "relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal);
-
+ dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal);
- if (f_rel < -bw_khz/2 || f_rel > bw_khz/2)
+ if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2)
return;
bw_khz /= 100;
- dib7000p_write_word(state, 142 ,0x0610);
+ dib7000p_write_word(state, 142, 0x0610);
for (k = 0; k < 8; k++) {
- pha = ((f_rel * (k+1) * 112 * 80/bw_khz) /1000) & 0x3ff;
+ pha = ((f_rel * (k + 1) * 112 * 80 / bw_khz) / 1000) & 0x3ff;
- if (pha==0) {
+ if (pha == 0) {
coef_re[k] = 256;
coef_im[k] = 0;
- } else if(pha < 256) {
- coef_re[k] = sine[256-(pha&0xff)];
- coef_im[k] = sine[pha&0xff];
+ } else if (pha < 256) {
+ coef_re[k] = sine[256 - (pha & 0xff)];
+ coef_im[k] = sine[pha & 0xff];
} else if (pha == 256) {
coef_re[k] = 0;
coef_im[k] = 256;
} else if (pha < 512) {
- coef_re[k] = -sine[pha&0xff];
- coef_im[k] = sine[256 - (pha&0xff)];
+ coef_re[k] = -sine[pha & 0xff];
+ coef_im[k] = sine[256 - (pha & 0xff)];
} else if (pha == 512) {
coef_re[k] = -256;
coef_im[k] = 0;
} else if (pha < 768) {
- coef_re[k] = -sine[256-(pha&0xff)];
- coef_im[k] = -sine[pha&0xff];
+ coef_re[k] = -sine[256 - (pha & 0xff)];
+ coef_im[k] = -sine[pha & 0xff];
} else if (pha == 768) {
coef_re[k] = 0;
coef_im[k] = -256;
} else {
- coef_re[k] = sine[pha&0xff];
- coef_im[k] = -sine[256 - (pha&0xff)];
+ coef_re[k] = sine[pha & 0xff];
+ coef_im[k] = -sine[256 - (pha & 0xff)];
}
coef_re[k] *= notch[k];
- coef_re[k] += (1<<14);
- if (coef_re[k] >= (1<<24))
- coef_re[k] = (1<<24) - 1;
- coef_re[k] /= (1<<15);
+ coef_re[k] += (1 << 14);
+ if (coef_re[k] >= (1 << 24))
+ coef_re[k] = (1 << 24) - 1;
+ coef_re[k] /= (1 << 15);
coef_im[k] *= notch[k];
- coef_im[k] += (1<<14);
- if (coef_im[k] >= (1<<24))
- coef_im[k] = (1<<24)-1;
- coef_im[k] /= (1<<15);
+ coef_im[k] += (1 << 14);
+ if (coef_im[k] >= (1 << 24))
+ coef_im[k] = (1 << 24) - 1;
+ coef_im[k] /= (1 << 15);
- dprintk( "PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]);
+ dprintk("PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]);
dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff));
dib7000p_write_word(state, 144, coef_im[k] & 0x3ff);
dib7000p_write_word(state, 143, (1 << 14) | (k << 10) | (coef_re[k] & 0x3ff));
}
- dib7000p_write_word(state,143 ,0);
+ dib7000p_write_word(state, 143, 0);
}
static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch)
@@ -976,11 +1187,11 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet
/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3);
if (state->sfn_workaround_active) {
- dprintk( "SFN workaround is active");
+ dprintk("SFN workaround is active");
tmp |= (1 << 9);
- dib7000p_write_word(state, 166, 0x4000); // P_pha3_force_pha_shift
+ dib7000p_write_word(state, 166, 0x4000);
} else {
- dib7000p_write_word(state, 166, 0x0000); // P_pha3_force_pha_shift
+ dib7000p_write_word(state, 166, 0x0000);
}
dib7000p_write_word(state, 29, tmp);
@@ -993,51 +1204,72 @@ static int dib7000p_tune(struct dvb_frontend *demod, struct dvb_frontend_paramet
/* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
tmp = (6 << 8) | 0x80;
switch (ch->u.ofdm.transmission_mode) {
- case TRANSMISSION_MODE_2K: tmp |= (7 << 12); break;
- case TRANSMISSION_MODE_4K: tmp |= (8 << 12); break;
- default:
- case TRANSMISSION_MODE_8K: tmp |= (9 << 12); break;
+ case TRANSMISSION_MODE_2K:
+ tmp |= (2 << 12);
+ break;
+ case TRANSMISSION_MODE_4K:
+ tmp |= (3 << 12);
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ tmp |= (4 << 12);
+ break;
}
- dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */
+ dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */
/* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
tmp = (0 << 4);
switch (ch->u.ofdm.transmission_mode) {
- case TRANSMISSION_MODE_2K: tmp |= 0x6; break;
- case TRANSMISSION_MODE_4K: tmp |= 0x7; break;
- default:
- case TRANSMISSION_MODE_8K: tmp |= 0x8; break;
+ case TRANSMISSION_MODE_2K:
+ tmp |= 0x6;
+ break;
+ case TRANSMISSION_MODE_4K:
+ tmp |= 0x7;
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ tmp |= 0x8;
+ break;
}
- dib7000p_write_word(state, 32, tmp);
+ dib7000p_write_word(state, 32, tmp);
/* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
tmp = (0 << 4);
switch (ch->u.ofdm.transmission_mode) {
- case TRANSMISSION_MODE_2K: tmp |= 0x6; break;
- case TRANSMISSION_MODE_4K: tmp |= 0x7; break;
- default:
- case TRANSMISSION_MODE_8K: tmp |= 0x8; break;
+ case TRANSMISSION_MODE_2K:
+ tmp |= 0x6;
+ break;
+ case TRANSMISSION_MODE_4K:
+ tmp |= 0x7;
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ tmp |= 0x8;
+ break;
}
- dib7000p_write_word(state, 33, tmp);
+ dib7000p_write_word(state, 33, tmp);
- tmp = dib7000p_read_word(state,509);
+ tmp = dib7000p_read_word(state, 509);
if (!((tmp >> 6) & 0x1)) {
/* restart the fec */
- tmp = dib7000p_read_word(state,771);
+ tmp = dib7000p_read_word(state, 771);
dib7000p_write_word(state, 771, tmp | (1 << 1));
dib7000p_write_word(state, 771, tmp);
- msleep(10);
- tmp = dib7000p_read_word(state,509);
+ msleep(40);
+ tmp = dib7000p_read_word(state, 509);
}
-
// we achieved a lock - it's time to update the osc freq
- if ((tmp >> 6) & 0x1)
+ if ((tmp >> 6) & 0x1) {
dib7000p_update_timf(state);
+ /* P_timf_alpha += 2 */
+ tmp = dib7000p_read_word(state, 26);
+ dib7000p_write_word(state, 26, (tmp & ~(0xf << 12)) | ((((tmp >> 12) & 0xf) + 5) << 12));
+ }
if (state->cfg.spur_protect)
- dib7000p_spur_protect(state, ch->frequency/1000, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
+ dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
+ dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
return 0;
}
@@ -1046,63 +1278,82 @@ static int dib7000p_wakeup(struct dvb_frontend *demod)
struct dib7000p_state *state = demod->demodulator_priv;
dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+ if (state->version == SOC7090)
+ dib7000p_sad_calib(state);
return 0;
}
static int dib7000p_sleep(struct dvb_frontend *demod)
{
struct dib7000p_state *state = demod->demodulator_priv;
+ if (state->version == SOC7090)
+ return dib7090_set_output_mode(demod, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
}
static int dib7000p_identify(struct dib7000p_state *st)
{
u16 value;
- dprintk( "checking demod on I2C address: %d (%x)",
- st->i2c_addr, st->i2c_addr);
+ dprintk("checking demod on I2C address: %d (%x)", st->i2c_addr, st->i2c_addr);
if ((value = dib7000p_read_word(st, 768)) != 0x01b3) {
- dprintk( "wrong Vendor ID (read=0x%x)",value);
+ dprintk("wrong Vendor ID (read=0x%x)", value);
return -EREMOTEIO;
}
if ((value = dib7000p_read_word(st, 769)) != 0x4000) {
- dprintk( "wrong Device ID (%x)",value);
+ dprintk("wrong Device ID (%x)", value);
return -EREMOTEIO;
}
return 0;
}
-
-static int dib7000p_get_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
+static int dib7000p_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dib7000p_state *state = fe->demodulator_priv;
- u16 tps = dib7000p_read_word(state,463);
+ u16 tps = dib7000p_read_word(state, 463);
fep->inversion = INVERSION_AUTO;
fep->u.ofdm.bandwidth = BANDWIDTH_TO_INDEX(state->current_bandwidth);
switch ((tps >> 8) & 0x3) {
- case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
- case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
- /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+ case 0:
+ fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
}
switch (tps & 0x3) {
- case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
- case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
- case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
- case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ case 0:
+ fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
}
switch ((tps >> 14) & 0x3) {
- case 0: fep->u.ofdm.constellation = QPSK; break;
- case 1: fep->u.ofdm.constellation = QAM_16; break;
- case 2:
- default: fep->u.ofdm.constellation = QAM_64; break;
+ case 0:
+ fep->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ fep->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ default:
+ fep->u.ofdm.constellation = QAM_64;
+ break;
}
/* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
@@ -1110,22 +1361,42 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe,
fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
switch ((tps >> 5) & 0x7) {
- case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
- case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
- case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
- case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
- case 7:
- default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+ case 1:
+ fep->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 2:
+ fep->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 3:
+ fep->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 5:
+ fep->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 7:
+ default:
+ fep->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
}
switch ((tps >> 2) & 0x7) {
- case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
- case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
- case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
- case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
- case 7:
- default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ case 1:
+ fep->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 2:
+ fep->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 3:
+ fep->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 5:
+ fep->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 7:
+ default:
+ fep->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
}
/* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */
@@ -1133,15 +1404,18 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe,
return 0;
}
-static int dib7000p_set_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
+static int dib7000p_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dib7000p_state *state = fe->demodulator_priv;
int time, ret;
- dib7000p_set_output_mode(state, OUTMODE_HIGH_Z);
+ if (state->version == SOC7090) {
+ dib7090_set_diversity_in(fe, 0);
+ dib7090_set_output_mode(fe, OUTMODE_HIGH_Z);
+ } else
+ dib7000p_set_output_mode(state, OUTMODE_HIGH_Z);
- /* maybe the parameter has been changed */
+ /* maybe the parameter has been changed */
state->sfn_workaround_active = buggy_sfn_workaround;
if (fe->ops.tuner_ops.set_params)
@@ -1156,9 +1430,7 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
} while (time != -1);
if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
- fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
- fep->u.ofdm.constellation == QAM_AUTO ||
- fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) {
int i = 800, found;
dib7000p_autosearch_start(fe, fep);
@@ -1167,9 +1439,9 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
found = dib7000p_autosearch_is_irq(fe);
} while (found == 0 && i--);
- dprintk("autosearch returns: %d",found);
+ dprintk("autosearch returns: %d", found);
if (found == 0 || found == 1)
- return 0; // no channel found
+ return 0;
dib7000p_get_frontend(fe, fep);
}
@@ -1177,11 +1449,15 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
ret = dib7000p_tune(fe, fep);
/* make this a config parameter */
- dib7000p_set_output_mode(state, state->cfg.output_mode);
- return ret;
+ if (state->version == SOC7090)
+ dib7090_set_output_mode(fe, state->cfg.output_mode);
+ else
+ dib7000p_set_output_mode(state, state->cfg.output_mode);
+
+ return ret;
}
-static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 lock = dib7000p_read_word(state, 509);
@@ -1196,27 +1472,27 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
*stat |= FE_HAS_VITERBI;
if (lock & 0x0010)
*stat |= FE_HAS_SYNC;
- if ((lock & 0x0038) == 0x38)
+ if ((lock & 0x0038) == 0x38)
*stat |= FE_HAS_LOCK;
return 0;
}
-static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber)
+static int dib7000p_read_ber(struct dvb_frontend *fe, u32 * ber)
{
struct dib7000p_state *state = fe->demodulator_priv;
*ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501);
return 0;
}
-static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
{
struct dib7000p_state *state = fe->demodulator_priv;
*unc = dib7000p_read_word(state, 506);
return 0;
}
-static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 val = dib7000p_read_word(state, 394);
@@ -1224,7 +1500,7 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
return 0;
}
-static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
+static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 val;
@@ -1240,19 +1516,17 @@ static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
noise_exp -= 0x40;
signal_mant = (val >> 6) & 0xFF;
- signal_exp = (val & 0x3F);
+ signal_exp = (val & 0x3F);
if ((signal_exp & 0x20) != 0)
signal_exp -= 0x40;
if (signal_mant != 0)
- result = intlog10(2) * 10 * signal_exp + 10 *
- intlog10(signal_mant);
+ result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant);
else
result = intlog10(2) * 10 * signal_exp - 100;
if (noise_mant != 0)
- result -= intlog10(2) * 10 * noise_exp + 10 *
- intlog10(noise_mant);
+ result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant);
else
result -= intlog10(2) * 10 * noise_exp - 100;
@@ -1260,7 +1534,7 @@ static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
return 0;
}
-static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 1000;
return 0;
@@ -1270,6 +1544,7 @@ static void dib7000p_release(struct dvb_frontend *demod)
{
struct dib7000p_state *st = demod->demodulator_priv;
dibx000_exit_i2c_master(&st->i2c_master);
+ i2c_del_adapter(&st->dib7090_tuner_adap);
kfree(st);
}
@@ -1277,8 +1552,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
{
u8 tx[2], rx[2];
struct i2c_msg msg[2] = {
- { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 },
- { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 },
+ {.addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2},
+ {.addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2},
};
tx[0] = 0x03;
@@ -1303,7 +1578,7 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
}
EXPORT_SYMBOL(dib7000pc_detection);
-struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
{
struct dib7000p_state *st = demod->demodulator_priv;
return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
@@ -1312,19 +1587,19 @@ EXPORT_SYMBOL(dib7000p_get_i2c_master);
int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
- struct dib7000p_state *state = fe->demodulator_priv;
- u16 val = dib7000p_read_word(state, 235) & 0xffef;
- val |= (onoff & 0x1) << 4;
- dprintk("PID filter enabled %d", onoff);
- return dib7000p_write_word(state, 235, val);
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val = dib7000p_read_word(state, 235) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+ dprintk("PID filter enabled %d", onoff);
+ return dib7000p_write_word(state, 235, val);
}
EXPORT_SYMBOL(dib7000p_pid_filter_ctrl);
int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
- struct dib7000p_state *state = fe->demodulator_priv;
- dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
- return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
+ struct dib7000p_state *state = fe->demodulator_priv;
+ dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
}
EXPORT_SYMBOL(dib7000p_pid_filter);
@@ -1340,16 +1615,19 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
dpst->i2c_adap = i2c;
- for (k = no_of_demods-1; k >= 0; k--) {
+ for (k = no_of_demods - 1; k >= 0; k--) {
dpst->cfg = cfg[k];
/* designated i2c address */
- new_addr = (0x40 + k) << 1;
+ if (cfg[k].default_i2c_addr != 0)
+ new_addr = cfg[k].default_i2c_addr + (k << 1);
+ else
+ new_addr = (0x40 + k) << 1;
dpst->i2c_addr = new_addr;
- dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
+ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
if (dib7000p_identify(dpst) != 0) {
dpst->i2c_addr = default_addr;
- dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
+ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
if (dib7000p_identify(dpst) != 0) {
dprintk("DiB7000P #%d: not identified\n", k);
kfree(dpst);
@@ -1368,7 +1646,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
for (k = 0; k < no_of_demods; k++) {
dpst->cfg = cfg[k];
- dpst->i2c_addr = (0x40 + k) << 1;
+ if (cfg[k].default_i2c_addr != 0)
+ dpst->i2c_addr = (cfg[k].default_i2c_addr + k) << 1;
+ else
+ dpst->i2c_addr = (0x40 + k) << 1;
// unforce divstr
dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2);
@@ -1382,8 +1663,613 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
}
EXPORT_SYMBOL(dib7000p_i2c_enumeration);
+static const s32 lut_1000ln_mant[] = {
+ 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600
+};
+
+static s32 dib7000p_get_adc_power(struct dvb_frontend *fe)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u32 tmp_val = 0, exp = 0, mant = 0;
+ s32 pow_i;
+ u16 buf[2];
+ u8 ix = 0;
+
+ buf[0] = dib7000p_read_word(state, 0x184);
+ buf[1] = dib7000p_read_word(state, 0x185);
+ pow_i = (buf[0] << 16) | buf[1];
+ dprintk("raw pow_i = %d", pow_i);
+
+ tmp_val = pow_i;
+ while (tmp_val >>= 1)
+ exp++;
+
+ mant = (pow_i * 1000 / (1 << exp));
+ dprintk(" mant = %d exp = %d", mant / 1000, exp);
+
+ ix = (u8) ((mant - 1000) / 100); /* index of the LUT */
+ dprintk(" ix = %d", ix);
+
+ pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908);
+ pow_i = (pow_i << 8) / 1000;
+ dprintk(" pow_i = %d", pow_i);
+
+ return pow_i;
+}
+
+static int map_addr_to_serpar_number(struct i2c_msg *msg)
+{
+ if ((msg->buf[0] <= 15))
+ msg->buf[0] -= 1;
+ else if (msg->buf[0] == 17)
+ msg->buf[0] = 15;
+ else if (msg->buf[0] == 16)
+ msg->buf[0] = 17;
+ else if (msg->buf[0] == 19)
+ msg->buf[0] = 16;
+ else if (msg->buf[0] >= 21 && msg->buf[0] <= 25)
+ msg->buf[0] -= 3;
+ else if (msg->buf[0] == 28)
+ msg->buf[0] = 23;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
+ u8 n_overflow = 1;
+ u16 i = 1000;
+ u16 serpar_num = msg[0].buf[0];
+
+ while (n_overflow == 1 && i) {
+ n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1;
+ i--;
+ if (i == 0)
+ dprintk("Tuner ITF: write busy (overflow)");
+ }
+ dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f));
+ dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]);
+
+ return num;
+}
+
+static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
+ u8 n_overflow = 1, n_empty = 1;
+ u16 i = 1000;
+ u16 serpar_num = msg[0].buf[0];
+ u16 read_word;
+
+ while (n_overflow == 1 && i) {
+ n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1;
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: read busy (overflow)");
+ }
+ dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f));
+
+ i = 1000;
+ while (n_empty == 1 && i) {
+ n_empty = dib7000p_read_word(state, 1984) & 0x1;
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: read busy (empty)");
+ }
+ read_word = dib7000p_read_word(state, 1987);
+ msg[1].buf[0] = (read_word >> 8) & 0xff;
+ msg[1].buf[1] = (read_word) & 0xff;
+
+ return num;
+}
+
+static int w7090p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ if (map_addr_to_serpar_number(&msg[0]) == 0) { /* else = Tuner regs to ignore : DIG_CFG, CTRL_RF_LT, PLL_CFG, PWM1_REG, ADCCLK, DIG_CFG_3; SLEEP_EN... */
+ if (num == 1) { /* write */
+ return w7090p_tuner_write_serpar(i2c_adap, msg, 1);
+ } else { /* read */
+ return w7090p_tuner_read_serpar(i2c_adap, msg, 2);
+ }
+ }
+ return num;
+}
+
+int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num, u16 apb_address)
+{
+ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
+ u16 word;
+
+ if (num == 1) { /* write */
+ dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2])));
+ } else {
+ word = dib7000p_read_word(state, apb_address);
+ msg[1].buf[0] = (word >> 8) & 0xff;
+ msg[1].buf[1] = (word) & 0xff;
+ }
+
+ return num;
+}
+
+static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
+
+ u16 apb_address = 0, word;
+ int i = 0;
+ switch (msg[0].buf[0]) {
+ case 0x12:
+ apb_address = 1920;
+ break;
+ case 0x14:
+ apb_address = 1921;
+ break;
+ case 0x24:
+ apb_address = 1922;
+ break;
+ case 0x1a:
+ apb_address = 1923;
+ break;
+ case 0x22:
+ apb_address = 1924;
+ break;
+ case 0x33:
+ apb_address = 1926;
+ break;
+ case 0x34:
+ apb_address = 1927;
+ break;
+ case 0x35:
+ apb_address = 1928;
+ break;
+ case 0x36:
+ apb_address = 1929;
+ break;
+ case 0x37:
+ apb_address = 1930;
+ break;
+ case 0x38:
+ apb_address = 1931;
+ break;
+ case 0x39:
+ apb_address = 1932;
+ break;
+ case 0x2a:
+ apb_address = 1935;
+ break;
+ case 0x2b:
+ apb_address = 1936;
+ break;
+ case 0x2c:
+ apb_address = 1937;
+ break;
+ case 0x2d:
+ apb_address = 1938;
+ break;
+ case 0x2e:
+ apb_address = 1939;
+ break;
+ case 0x2f:
+ apb_address = 1940;
+ break;
+ case 0x30:
+ apb_address = 1941;
+ break;
+ case 0x31:
+ apb_address = 1942;
+ break;
+ case 0x32:
+ apb_address = 1943;
+ break;
+ case 0x3e:
+ apb_address = 1944;
+ break;
+ case 0x3f:
+ apb_address = 1945;
+ break;
+ case 0x40:
+ apb_address = 1948;
+ break;
+ case 0x25:
+ apb_address = 914;
+ break;
+ case 0x26:
+ apb_address = 915;
+ break;
+ case 0x27:
+ apb_address = 916;
+ break;
+ case 0x28:
+ apb_address = 917;
+ break;
+ case 0x1d:
+ i = ((dib7000p_read_word(state, 72) >> 12) & 0x3);
+ word = dib7000p_read_word(state, 384 + i);
+ msg[1].buf[0] = (word >> 8) & 0xff;
+ msg[1].buf[1] = (word) & 0xff;
+ return num;
+ case 0x1f:
+ if (num == 1) { /* write */
+ word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]);
+ word &= 0x3;
+ word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12);
+ dib7000p_write_word(state, 72, word); /* Set the proper input */
+ return num;
+ }
+ }
+
+ if (apb_address != 0) /* R/W acces via APB */
+ return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address);
+ else /* R/W access via SERPAR */
+ return w7090p_tuner_rw_serpar(i2c_adap, msg, num);
+
+ return 0;
+}
+
+static u32 dib7000p_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dib7090_tuner_xfer_algo = {
+ .master_xfer = dib7090_tuner_xfer,
+ .functionality = dib7000p_i2c_func,
+};
+
+struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe)
+{
+ struct dib7000p_state *st = fe->demodulator_priv;
+ return &st->dib7090_tuner_adap;
+}
+EXPORT_SYMBOL(dib7090_get_i2c_tuner);
+
+static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive)
+{
+ u16 reg;
+
+ /* drive host bus 2, 3, 4 */
+ reg = dib7000p_read_word(state, 1798) & ~((0x7) | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive << 12) | (drive << 6) | drive;
+ dib7000p_write_word(state, 1798, reg);
+
+ /* drive host bus 5,6 */
+ reg = dib7000p_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8));
+ reg |= (drive << 8) | (drive << 2);
+ dib7000p_write_word(state, 1799, reg);
+
+ /* drive host bus 7, 8, 9 */
+ reg = dib7000p_read_word(state, 1800) & ~((0x7) | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive << 12) | (drive << 6) | drive;
+ dib7000p_write_word(state, 1800, reg);
+
+ /* drive host bus 10, 11 */
+ reg = dib7000p_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8));
+ reg |= (drive << 8) | (drive << 2);
+ dib7000p_write_word(state, 1801, reg);
+
+ /* drive host bus 12, 13, 14 */
+ reg = dib7000p_read_word(state, 1802) & ~((0x7) | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive << 12) | (drive << 6) | drive;
+ dib7000p_write_word(state, 1802, reg);
+
+ return 0;
+}
+
+static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 syncSize)
+{
+ u32 quantif = 3;
+ u32 nom = (insertExtSynchro * P_Kin + syncSize);
+ u32 denom = P_Kout;
+ u32 syncFreq = ((nom << quantif) / denom);
+
+ if ((syncFreq & ((1 << quantif) - 1)) != 0)
+ syncFreq = (syncFreq >> quantif) + 1;
+ else
+ syncFreq = (syncFreq >> quantif);
+
+ if (syncFreq != 0)
+ syncFreq = syncFreq - 1;
+
+ return syncFreq;
+}
+
+static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize)
+{
+ u8 index_buf;
+ u16 rx_copy_buf[22];
+
+ dprintk("Configure DibStream Tx");
+ for (index_buf = 0; index_buf < 22; index_buf++)
+ rx_copy_buf[index_buf] = dib7000p_read_word(state, 1536+index_buf);
+
+ dib7000p_write_word(state, 1615, 1);
+ dib7000p_write_word(state, 1603, P_Kin);
+ dib7000p_write_word(state, 1605, P_Kout);
+ dib7000p_write_word(state, 1606, insertExtSynchro);
+ dib7000p_write_word(state, 1608, synchroMode);
+ dib7000p_write_word(state, 1609, (syncWord >> 16) & 0xffff);
+ dib7000p_write_word(state, 1610, syncWord & 0xffff);
+ dib7000p_write_word(state, 1612, syncSize);
+ dib7000p_write_word(state, 1615, 0);
+
+ for (index_buf = 0; index_buf < 22; index_buf++)
+ dib7000p_write_word(state, 1536+index_buf, rx_copy_buf[index_buf]);
+
+ return 0;
+}
+
+static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, u32 syncWord, u32 syncSize,
+ u32 dataOutRate)
+{
+ u32 syncFreq;
+
+ dprintk("Configure DibStream Rx");
+ if ((P_Kin != 0) && (P_Kout != 0)) {
+ syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize);
+ dib7000p_write_word(state, 1542, syncFreq);
+ }
+ dib7000p_write_word(state, 1554, 1);
+ dib7000p_write_word(state, 1536, P_Kin);
+ dib7000p_write_word(state, 1537, P_Kout);
+ dib7000p_write_word(state, 1539, synchroMode);
+ dib7000p_write_word(state, 1540, (syncWord >> 16) & 0xffff);
+ dib7000p_write_word(state, 1541, syncWord & 0xffff);
+ dib7000p_write_word(state, 1543, syncSize);
+ dib7000p_write_word(state, 1544, dataOutRate);
+ dib7000p_write_word(state, 1554, 0);
+
+ return 0;
+}
+
+static int dib7090_enDivOnHostBus(struct dib7000p_state *state)
+{
+ u16 reg;
+
+ dprintk("Enable Diversity on host bus");
+ reg = (1 << 8) | (1 << 5);
+ dib7000p_write_word(state, 1288, reg);
+
+ return dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0);
+}
+
+static int dib7090_enAdcOnHostBus(struct dib7000p_state *state)
+{
+ u16 reg;
+
+ dprintk("Enable ADC on host bus");
+ reg = (1 << 7) | (1 << 5);
+ dib7000p_write_word(state, 1288, reg);
+
+ return dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0);
+}
+
+static int dib7090_enMpegOnHostBus(struct dib7000p_state *state)
+{
+ u16 reg;
+
+ dprintk("Enable Mpeg on host bus");
+ reg = (1 << 9) | (1 << 5);
+ dib7000p_write_word(state, 1288, reg);
+
+ return dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0);
+}
+
+static int dib7090_enMpegInput(struct dib7000p_state *state)
+{
+ dprintk("Enable Mpeg input");
+ return dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); /*outputRate = 8 */
+}
+
+static int dib7090_enMpegMux(struct dib7000p_state *state, u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2)
+{
+ u16 reg = (1 << 7) | ((pulseWidth & 0x1f) << 2) | ((enSerialMode & 0x1) << 1) | (enSerialClkDiv2 & 0x1);
+
+ dprintk("Enable Mpeg mux");
+ dib7000p_write_word(state, 1287, reg);
+
+ reg &= ~(1 << 7);
+ dib7000p_write_word(state, 1287, reg);
+
+ reg = (1 << 4);
+ dib7000p_write_word(state, 1288, reg);
+
+ return 0;
+}
+
+static int dib7090_disableMpegMux(struct dib7000p_state *state)
+{
+ u16 reg;
+
+ dprintk("Disable Mpeg mux");
+ dib7000p_write_word(state, 1288, 0);
+
+ reg = dib7000p_read_word(state, 1287);
+ reg &= ~(1 << 7);
+ dib7000p_write_word(state, 1287, reg);
+
+ return 0;
+}
+
+static int dib7090_set_input_mode(struct dvb_frontend *fe, int mode)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+
+ switch (mode) {
+ case INPUT_MODE_DIVERSITY:
+ dprintk("Enable diversity INPUT");
+ dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0);
+ break;
+ case INPUT_MODE_MPEG:
+ dprintk("Enable Mpeg INPUT");
+ dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); /*outputRate = 8 */
+ break;
+ case INPUT_MODE_OFF:
+ default:
+ dprintk("Disable INPUT");
+ dib7090_cfg_DibRx(state, 0, 0, 0, 0, 0, 0, 0);
+ break;
+ }
+ return 0;
+}
+
+static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
+{
+ switch (onoff) {
+ case 0: /* only use the internal way - not the diversity input */
+ dib7090_set_input_mode(fe, INPUT_MODE_MPEG);
+ break;
+ case 1: /* both ways */
+ case 2: /* only the diversity input */
+ dib7090_set_input_mode(fe, INPUT_MODE_DIVERSITY);
+ break;
+ }
+
+ return 0;
+}
+
+static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+
+ u16 outreg, smo_mode, fifo_threshold;
+ u8 prefer_mpeg_mux_use = 1;
+ int ret = 0;
+
+ dib7090_host_bus_drive(state, 1);
+
+ fifo_threshold = 1792;
+ smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
+ outreg = dib7000p_read_word(state, 1286) & ~((1 << 10) | (0x7 << 6) | (1 << 1));
+
+ switch (mode) {
+ case OUTMODE_HIGH_Z:
+ outreg = 0;
+ break;
+
+ case OUTMODE_MPEG2_SERIAL:
+ if (prefer_mpeg_mux_use) {
+ dprintk("Sip 7090P setting output mode TS_SERIAL using Mpeg Mux");
+ dib7090_enMpegOnHostBus(state);
+ dib7090_enMpegInput(state);
+ if (state->cfg.enMpegOutput == 1)
+ dib7090_enMpegMux(state, 3, 1, 1);
+
+ } else { /* Use Smooth block */
+ dprintk("Sip 7090P setting output mode TS_SERIAL using Smooth bloc");
+ dib7090_disableMpegMux(state);
+ dib7000p_write_word(state, 1288, (1 << 6));
+ outreg |= (2 << 6) | (0 << 1);
+ }
+ break;
+
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ if (prefer_mpeg_mux_use) {
+ dprintk("Sip 7090P setting output mode TS_PARALLEL_GATED using Mpeg Mux");
+ dib7090_enMpegOnHostBus(state);
+ dib7090_enMpegInput(state);
+ if (state->cfg.enMpegOutput == 1)
+ dib7090_enMpegMux(state, 2, 0, 0);
+ } else { /* Use Smooth block */
+ dprintk("Sip 7090P setting output mode TS_PARALLEL_GATED using Smooth block");
+ dib7090_disableMpegMux(state);
+ dib7000p_write_word(state, 1288, (1 << 6));
+ outreg |= (0 << 6);
+ }
+ break;
+
+ case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */
+ dprintk("Sip 7090P setting output mode TS_PARALLEL_CONT using Smooth block");
+ dib7090_disableMpegMux(state);
+ dib7000p_write_word(state, 1288, (1 << 6));
+ outreg |= (1 << 6);
+ break;
+
+ case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */
+ dprintk("Sip 7090P setting output mode TS_FIFO using Smooth block");
+ dib7090_disableMpegMux(state);
+ dib7000p_write_word(state, 1288, (1 << 6));
+ outreg |= (5 << 6);
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ break;
+
+ case OUTMODE_DIVERSITY:
+ dprintk("Sip 7090P setting output mode MODE_DIVERSITY");
+ dib7090_disableMpegMux(state);
+ dib7090_enDivOnHostBus(state);
+ break;
+
+ case OUTMODE_ANALOG_ADC:
+ dprintk("Sip 7090P setting output mode MODE_ANALOG_ADC");
+ dib7090_enAdcOnHostBus(state);
+ break;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5);
+
+ ret |= dib7000p_write_word(state, 235, smo_mode);
+ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */
+ ret |= dib7000p_write_word(state, 1286, outreg | (1 << 10)); /* allways set Dout active = 1 !!! */
+
+ return ret;
+}
+
+int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 en_cur_state;
+
+ dprintk("sleep dib7090: %d", onoff);
+
+ en_cur_state = dib7000p_read_word(state, 1922);
+
+ if (en_cur_state > 0xff)
+ state->tuner_enable = en_cur_state;
+
+ if (onoff)
+ en_cur_state &= 0x00ff;
+ else {
+ if (state->tuner_enable != 0)
+ en_cur_state = state->tuner_enable;
+ }
+
+ dib7000p_write_word(state, 1922, en_cur_state);
+
+ return 0;
+}
+EXPORT_SYMBOL(dib7090_tuner_sleep);
+
+int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart)
+{
+ dprintk("AGC restart callback: %d", restart);
+ return 0;
+}
+EXPORT_SYMBOL(dib7090_agc_restart);
+
+int dib7090_get_adc_power(struct dvb_frontend *fe)
+{
+ return dib7000p_get_adc_power(fe);
+}
+EXPORT_SYMBOL(dib7090_get_adc_power);
+
+int dib7090_slave_reset(struct dvb_frontend *fe)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 reg;
+
+ reg = dib7000p_read_word(state, 1794);
+ dib7000p_write_word(state, 1794, reg | (4 << 12));
+
+ dib7000p_write_word(state, 1032, 0xffff);
+ return 0;
+}
+EXPORT_SYMBOL(dib7090_slave_reset);
+
static struct dvb_frontend_ops dib7000p_ops;
-struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
+struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
{
struct dvb_frontend *demod;
struct dib7000p_state *st;
@@ -1400,28 +2286,41 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
/* Ensure the output mode remains at the previous default if it's
* not specifically set by the caller.
*/
- if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) &&
- (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
+ if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
st->cfg.output_mode = OUTMODE_MPEG2_FIFO;
- demod = &st->demod;
+ demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
- dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
+ dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
if (dib7000p_identify(st) != 0)
goto error;
+ st->version = dib7000p_read_word(st, 897);
+
/* FIXME: make sure the dev.parent field is initialized, or else
- request_firmware() will hit an OOPS (this should be moved somewhere
- more common) */
- st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
+ request_firmware() will hit an OOPS (this should be moved somewhere
+ more common) */
dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
+ /* init 7090 tuner adapter */
+ strncpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", sizeof(st->dib7090_tuner_adap.name));
+ st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo;
+ st->dib7090_tuner_adap.algo_data = NULL;
+ st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent;
+ i2c_set_adapdata(&st->dib7090_tuner_adap, st);
+ i2c_add_adapter(&st->dib7090_tuner_adap);
+
dib7000p_demod_reset(st);
+ if (st->version == SOC7090) {
+ dib7090_set_output_mode(demod, st->cfg.output_mode);
+ dib7090_set_diversity_in(demod, 0);
+ }
+
return demod;
error:
@@ -1432,37 +2331,35 @@ EXPORT_SYMBOL(dib7000p_attach);
static struct dvb_frontend_ops dib7000p_ops = {
.info = {
- .name = "DiBcom 7000PC",
- .type = FE_OFDM,
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
- .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 = dib7000p_release,
-
- .init = dib7000p_wakeup,
- .sleep = dib7000p_sleep,
-
- .set_frontend = dib7000p_set_frontend,
- .get_tune_settings = dib7000p_fe_get_tune_settings,
- .get_frontend = dib7000p_get_frontend,
-
- .read_status = dib7000p_read_status,
- .read_ber = dib7000p_read_ber,
+ .name = "DiBcom 7000PC",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .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 = dib7000p_release,
+
+ .init = dib7000p_wakeup,
+ .sleep = dib7000p_sleep,
+
+ .set_frontend = dib7000p_set_frontend,
+ .get_tune_settings = dib7000p_fe_get_tune_settings,
+ .get_frontend = dib7000p_get_frontend,
+
+ .read_status = dib7000p_read_status,
+ .read_ber = dib7000p_read_ber,
.read_signal_strength = dib7000p_read_signal_strength,
- .read_snr = dib7000p_read_snr,
- .read_ucblocks = dib7000p_read_unc_blocks,
+ .read_snr = dib7000p_read_snr,
+ .read_ucblocks = dib7000p_read_unc_blocks,
};
+MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
index da17345bf5bd..0179f9474bac 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -33,59 +33,54 @@ struct dib7000p_config {
int (*agc_control) (struct dvb_frontend *, u8 before);
u8 output_mode;
- u8 disable_sample_and_hold : 1;
+ u8 disable_sample_and_hold:1;
- u8 enable_current_mirror : 1;
- u8 diversity_delay;
+ u8 enable_current_mirror:1;
+ u16 diversity_delay;
+ u8 default_i2c_addr;
+ u8 enMpegOutput:1;
};
#define DEFAULT_DIB7000P_I2C_ADDRESS 18
#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \
- defined(MODULE))
-extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- struct dib7000p_config *cfg);
-extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *,
- enum dibx000_i2c_interface,
- int);
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
- int no_of_demods, u8 default_addr,
- struct dib7000p_config cfg[]);
+ defined(MODULE))
+extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
+extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]);
extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff);
+extern int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw);
+extern u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf);
+extern int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart);
+extern int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff);
+extern int dib7090_get_adc_power(struct dvb_frontend *fe);
+extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe);
+extern int dib7090_slave_reset(struct dvb_frontend *fe);
#else
-static inline
-struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
- struct dib7000p_config *cfg)
+static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
-static inline
-struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
- enum dibx000_i2c_interface i,
- int x)
+static inline struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
-static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
- int no_of_demods, u8 default_addr,
- struct dib7000p_config cfg[])
+static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-static inline int dib7000p_set_gpio(struct dvb_frontend *fe,
- u8 num, u8 dir, u8 val)
+static inline int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
@@ -102,16 +97,59 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap)
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
+
static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -ENODEV;
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
}
static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff)
{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -ENODEV;
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib7090_get_adc_power(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline int dib7090_slave_reset(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
}
#endif
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index df17b91b3250..c1c3e26906e2 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -22,6 +22,7 @@
#define LAYER_C 3
#define FE_CALLBACK_TIME_NEVER 0xffffffff
+#define MAX_NUMBER_OF_FRONTENDS 6
static int debug;
module_param(debug, int, 0644);
@@ -37,7 +38,6 @@ struct i2c_device {
};
struct dib8000_state {
- struct dvb_frontend fe;
struct dib8000_config cfg;
struct i2c_device i2c;
@@ -68,6 +68,8 @@ struct dib8000_state {
u8 isdbt_cfg_loaded;
enum frontend_tune_state tune_state;
u32 status;
+
+ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS];
};
enum dib8000_power_mode {
@@ -122,111 +124,111 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
return dib8000_i2c_write16(&state->i2c, reg, val);
}
-static const int16_t coeff_2k_sb_1seg_dqpsk[8] = {
+static const s16 coeff_2k_sb_1seg_dqpsk[8] = {
(769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c,
- (920 << 5) | 0x09
+ (920 << 5) | 0x09
};
-static const int16_t coeff_2k_sb_1seg[8] = {
+static const s16 coeff_2k_sb_1seg[8] = {
(692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f
};
-static const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = {
(832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11,
- (-931 << 5) | 0x0f
+ (-931 << 5) | 0x0f
};
-static const int16_t coeff_2k_sb_3seg_0dqpsk[8] = {
+static const s16 coeff_2k_sb_3seg_0dqpsk[8] = {
(622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e,
- (982 << 5) | 0x0c
+ (982 << 5) | 0x0c
};
-static const int16_t coeff_2k_sb_3seg_1dqpsk[8] = {
+static const s16 coeff_2k_sb_3seg_1dqpsk[8] = {
(699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12,
- (-720 << 5) | 0x0d
+ (-720 << 5) | 0x0d
};
-static const int16_t coeff_2k_sb_3seg[8] = {
+static const s16 coeff_2k_sb_3seg[8] = {
(664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e,
- (-610 << 5) | 0x0a
+ (-610 << 5) | 0x0a
};
-static const int16_t coeff_4k_sb_1seg_dqpsk[8] = {
+static const s16 coeff_4k_sb_1seg_dqpsk[8] = {
(-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f,
- (-922 << 5) | 0x0d
+ (-922 << 5) | 0x0d
};
-static const int16_t coeff_4k_sb_1seg[8] = {
+static const s16 coeff_4k_sb_1seg[8] = {
(638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d,
- (-655 << 5) | 0x0a
+ (-655 << 5) | 0x0a
};
-static const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = {
(-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14,
- (-958 << 5) | 0x13
+ (-958 << 5) | 0x13
};
-static const int16_t coeff_4k_sb_3seg_0dqpsk[8] = {
+static const s16 coeff_4k_sb_3seg_0dqpsk[8] = {
(-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12,
- (-568 << 5) | 0x0f
+ (-568 << 5) | 0x0f
};
-static const int16_t coeff_4k_sb_3seg_1dqpsk[8] = {
+static const s16 coeff_4k_sb_3seg_1dqpsk[8] = {
(-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14,
- (-848 << 5) | 0x13
+ (-848 << 5) | 0x13
};
-static const int16_t coeff_4k_sb_3seg[8] = {
+static const s16 coeff_4k_sb_3seg[8] = {
(612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12,
- (-869 << 5) | 0x13
+ (-869 << 5) | 0x13
};
-static const int16_t coeff_8k_sb_1seg_dqpsk[8] = {
+static const s16 coeff_8k_sb_1seg_dqpsk[8] = {
(-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13,
- (-598 << 5) | 0x10
+ (-598 << 5) | 0x10
};
-static const int16_t coeff_8k_sb_1seg[8] = {
+static const s16 coeff_8k_sb_1seg[8] = {
(673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f,
- (585 << 5) | 0x0f
+ (585 << 5) | 0x0f
};
-static const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = {
(863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18,
- (0 << 5) | 0x14
+ (0 << 5) | 0x14
};
-static const int16_t coeff_8k_sb_3seg_0dqpsk[8] = {
+static const s16 coeff_8k_sb_3seg_0dqpsk[8] = {
(-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15,
- (-877 << 5) | 0x15
+ (-877 << 5) | 0x15
};
-static const int16_t coeff_8k_sb_3seg_1dqpsk[8] = {
+static const s16 coeff_8k_sb_3seg_1dqpsk[8] = {
(-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18,
- (-921 << 5) | 0x14
+ (-921 << 5) | 0x14
};
-static const int16_t coeff_8k_sb_3seg[8] = {
+static const s16 coeff_8k_sb_3seg[8] = {
(514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15,
- (690 << 5) | 0x14
+ (690 << 5) | 0x14
};
-static const int16_t ana_fe_coeff_3seg[24] = {
+static const s16 ana_fe_coeff_3seg[24] = {
81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017
};
-static const int16_t ana_fe_coeff_1seg[24] = {
+static const s16 ana_fe_coeff_1seg[24] = {
249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003
};
-static const int16_t ana_fe_coeff_13seg[24] = {
+static const s16 ana_fe_coeff_13seg[24] = {
396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1
};
static u16 fft_to_mode(struct dib8000_state *state)
{
u16 mode;
- switch (state->fe.dtv_property_cache.transmission_mode) {
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_2K:
mode = 1;
break;
@@ -249,16 +251,18 @@ static void dib8000_set_acquisition_mode(struct dib8000_state *state)
dprintk("acquisition mode activated");
dib8000_write_word(state, 298, nud);
}
-
-static int dib8000_set_output_mode(struct dib8000_state *state, int mode)
+static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode)
{
+ struct dib8000_state *state = fe->demodulator_priv;
+
u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */
outreg = 0;
fifo_threshold = 1792;
smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1);
- dprintk("-I- Setting output mode for demod %p to %d", &state->fe, mode);
+ dprintk("-I- Setting output mode for demod %p to %d",
+ &state->fe[0], mode);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
@@ -292,7 +296,8 @@ static int dib8000_set_output_mode(struct dib8000_state *state, int mode)
break;
default:
- dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe);
+ dprintk("Unhandled output_mode passed to be set for demod %p",
+ &state->fe[0]);
return -EINVAL;
}
@@ -342,7 +347,8 @@ static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_pow
{
/* by default everything is going to be powered off */
u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff,
- reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00;
+ reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3,
+ reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00;
/* now, depending on the requested mode, we power on */
switch (mode) {
@@ -411,8 +417,9 @@ static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_s
return ret;
}
-static int dib8000_set_bandwidth(struct dib8000_state *state, u32 bw)
+static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw)
{
+ struct dib8000_state *state = fe->demodulator_priv;
u32 timf;
if (bw == 0)
@@ -478,7 +485,8 @@ static void dib8000_reset_pll(struct dib8000_state *state)
// clk_cfg1
clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) |
- (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) | (pll->pll_range << 1) | (pll->pll_reset << 0);
+ (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | (1 << 3) |
+ (pll->pll_range << 1) | (pll->pll_reset << 0);
dib8000_write_word(state, 902, clk_cfg1);
clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3);
@@ -488,11 +496,12 @@ static void dib8000_reset_pll(struct dib8000_state *state)
/* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */
if (state->cfg.pll->ADClkSrc == 0)
- dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1));
+ dib8000_write_word(state, 904, (0 << 15) | (0 << 12) | (0 << 10) |
+ (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1));
else if (state->cfg.refclksel != 0)
- dib8000_write_word(state, 904,
- (0 << 15) | (1 << 12) | ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) | (pll->
- ADClkSrc << 7) | (0 << 1));
+ dib8000_write_word(state, 904, (0 << 15) | (1 << 12) |
+ ((state->cfg.refclksel & 0x3) << 10) | (pll->modulo << 8) |
+ (pll->ADClkSrc << 7) | (0 << 1));
else
dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | (3 << 10) | (pll->modulo << 8) | (pll->ADClkSrc << 7) | (0 << 1));
@@ -560,7 +569,7 @@ static const u16 dib8000_defaults[] = {
0xd4c0,
/*1, 32,
- 0x6680 // P_corm_thres Lock algorithms configuration */
+ 0x6680 // P_corm_thres Lock algorithms configuration */
11, 80, /* set ADC level to -16 */
(1 << 13) - 825 - 117,
@@ -623,14 +632,14 @@ static const u16 dib8000_defaults[] = {
1, 285,
0x0020, //p_fec_
1, 299,
- 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */
1, 338,
(1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1
- (1 << 10) | // P_ctrl_pre_freq_mode_sat=1
- (0 << 9) | // P_ctrl_pre_freq_inh=0
- (3 << 5) | // P_ctrl_pre_freq_step=3
- (1 << 0), // P_pre_freq_win_len=1
+ (1 << 10) |
+ (0 << 9) | /* P_ctrl_pre_freq_inh=0 */
+ (3 << 5) | /* P_ctrl_pre_freq_step=3 */
+ (1 << 0), /* P_pre_freq_win_len=1 */
1, 903,
(0 << 4) | 2, // P_divclksel=0 P_divbitsel=2 (was clk=3,bit=1 for MPW)
@@ -717,7 +726,7 @@ static int dib8000_reset(struct dvb_frontend *fe)
if (dib8000_reset_gpio(state) != 0)
dprintk("GPIO reset was not successful.");
- if (dib8000_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ if (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0)
dprintk("OUTPUT_MODE could not be resetted.");
state->current_agc = NULL;
@@ -752,7 +761,7 @@ static int dib8000_reset(struct dvb_frontend *fe)
/* unforce divstr regardless whether i2c enumeration was done or not */
dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1));
- dib8000_set_bandwidth(state, 6000);
+ dib8000_set_bandwidth(fe, 6000);
dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON);
dib8000_sad_calib(state);
@@ -778,7 +787,7 @@ static int dib8000_update_lna(struct dib8000_state *state)
// read dyn_gain here (because it is demod-dependent and not tuner)
dyn_gain = dib8000_read_word(state, 390);
- if (state->cfg.update_lna(&state->fe, dyn_gain)) { // LNA has changed
+ if (state->cfg.update_lna(state->fe[0], dyn_gain)) {
dib8000_restart_agc(state);
return 1;
}
@@ -865,7 +874,8 @@ static int dib8000_agc_soft_split(struct dib8000_state *state)
split_offset = state->current_agc->split.max;
else
split_offset = state->current_agc->split.max *
- (agc - state->current_agc->split.min_thres) / (state->current_agc->split.max_thres - state->current_agc->split.min_thres);
+ (agc - state->current_agc->split.min_thres) /
+ (state->current_agc->split.max_thres - state->current_agc->split.min_thres);
dprintk("AGC split_offset: %d", split_offset);
@@ -900,7 +910,7 @@ static int dib8000_agc_startup(struct dvb_frontend *fe)
case CT_AGC_STEP_0:
//AGC initialization
if (state->cfg.agc_control)
- state->cfg.agc_control(&state->fe, 1);
+ state->cfg.agc_control(fe, 1);
dib8000_restart_agc(state);
@@ -924,7 +934,7 @@ static int dib8000_agc_startup(struct dvb_frontend *fe)
dib8000_agc_soft_split(state);
if (state->cfg.agc_control)
- state->cfg.agc_control(&state->fe, 0);
+ state->cfg.agc_control(fe, 0);
*tune_state = CT_AGC_STOP;
break;
@@ -936,29 +946,28 @@ static int dib8000_agc_startup(struct dvb_frontend *fe)
}
-static const int32_t lut_1000ln_mant[] =
+static const s32 lut_1000ln_mant[] =
{
908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600
};
-int32_t dib8000_get_adc_power(struct dvb_frontend *fe, uint8_t mode)
+s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode)
{
- struct dib8000_state *state = fe->demodulator_priv;
- uint32_t ix = 0, tmp_val = 0, exp = 0, mant = 0;
- int32_t val;
-
- val = dib8000_read32(state, 384);
- /* mode = 1 : ln_agcpower calc using mant-exp conversion and mantis look up table */
- if (mode) {
- tmp_val = val;
- while (tmp_val >>= 1)
- exp++;
- mant = (val * 1000 / (1<<exp));
- ix = (uint8_t)((mant-1000)/100); /* index of the LUT */
- val = (lut_1000ln_mant[ix] + 693*(exp-20) - 6908); /* 1000 * ln(adcpower_real) ; 693 = 1000ln(2) ; 6908 = 1000*ln(1000) ; 20 comes from adc_real = adc_pow_int / 2**20 */
- val = (val*256)/1000;
- }
- return val;
+ struct dib8000_state *state = fe->demodulator_priv;
+ u32 ix = 0, tmp_val = 0, exp = 0, mant = 0;
+ s32 val;
+
+ val = dib8000_read32(state, 384);
+ if (mode) {
+ tmp_val = val;
+ while (tmp_val >>= 1)
+ exp++;
+ mant = (val * 1000 / (1<<exp));
+ ix = (u8)((mant-1000)/100); /* index of the LUT */
+ val = (lut_1000ln_mant[ix] + 693*(exp-20) - 6908);
+ val = (val*256)/1000;
+ }
+ return val;
}
EXPORT_SYMBOL(dib8000_get_adc_power);
@@ -1002,22 +1011,23 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60);
i = dib8000_read_word(state, 26) & 1; // P_dds_invspec
- dib8000_write_word(state, 26, state->fe.dtv_property_cache.inversion ^ i);
+ dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i);
- if (state->fe.dtv_property_cache.isdbt_sb_mode) {
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) {
//compute new dds_freq for the seg and adjust prbs
int seg_offset =
- state->fe.dtv_property_cache.isdbt_sb_segment_idx - (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) -
- (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2);
+ state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx -
+ (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) -
+ (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2);
int clk = state->cfg.pll->internal;
u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26)
int dds_offset = seg_offset * segtodds;
int new_dds, sub_channel;
- if ((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0) // if even
+ if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
dds_offset -= (int)(segtodds / 2);
if (state->cfg.pll->ifreq == 0) {
- if ((state->fe.dtv_property_cache.inversion ^ i) == 0) {
+ if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) {
dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1);
new_dds = dds_offset;
} else
@@ -1027,35 +1037,35 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
// - the segment of center frequency with an odd total number of segments
// - the segment to the left of center frequency with an even total number of segments
// - the segment to the right of center frequency with an even total number of segments
- if ((state->fe.dtv_property_cache.delivery_system == SYS_ISDBT) && (state->fe.dtv_property_cache.isdbt_sb_mode == 1)
- &&
- (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2)
- && (state->fe.dtv_property_cache.isdbt_sb_segment_idx ==
- ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
- || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
- && (state->fe.dtv_property_cache.isdbt_sb_segment_idx == (state->fe.dtv_property_cache.isdbt_sb_segment_count / 2)))
- || (((state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
- && (state->fe.dtv_property_cache.isdbt_sb_segment_idx ==
- ((state->fe.dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
- )) {
+ if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT)
+ && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)
+ && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2)
+ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx ==
+ ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
+ || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
+ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2)))
+ || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
+ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx ==
+ ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
+ )) {
new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26)
}
} else {
- if ((state->fe.dtv_property_cache.inversion ^ i) == 0)
+ if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0)
new_dds = state->cfg.pll->ifreq - dds_offset;
else
new_dds = state->cfg.pll->ifreq + dds_offset;
}
dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff));
dib8000_write_word(state, 28, (u16) (new_dds & 0xffff));
- if (state->fe.dtv_property_cache.isdbt_sb_segment_count % 2) // if odd
- sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3;
- else // if even
- sub_channel = ((state->fe.dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3;
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2)
+ sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3;
+ else
+ sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3;
sub_channel -= 6;
- if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K
- || state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) {
+ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K
+ || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) {
dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1
dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1
} else {
@@ -1063,7 +1073,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0
}
- switch (state->fe.dtv_property_cache.transmission_mode) {
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_2K:
switch (sub_channel) {
case -6:
@@ -1209,7 +1219,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
}
break;
}
- } else { // if not state->fe.dtv_property_cache.isdbt_sb_mode
+ } else {
dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff));
dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff));
dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003));
@@ -1218,7 +1228,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 10, (seq << 4));
// dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000);
- switch (state->fe.dtv_property_cache.guard_interval) {
+ switch (state->fe[0]->dtv_property_cache.guard_interval) {
case GUARD_INTERVAL_1_32:
guard = 0;
break;
@@ -1238,7 +1248,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
max_constellation = DQPSK;
for (i = 0; i < 3; i++) {
- switch (state->fe.dtv_property_cache.layer[i].modulation) {
+ switch (state->fe[0]->dtv_property_cache.layer[i].modulation) {
case DQPSK:
constellation = 0;
break;
@@ -1254,7 +1264,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
break;
}
- switch (state->fe.dtv_property_cache.layer[i].fec) {
+ switch (state->fe[0]->dtv_property_cache.layer[i].fec) {
case FEC_1_2:
crate = 1;
break;
@@ -1273,26 +1283,26 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
break;
}
- if ((state->fe.dtv_property_cache.layer[i].interleaving > 0) &&
- ((state->fe.dtv_property_cache.layer[i].interleaving <= 3) ||
- (state->fe.dtv_property_cache.layer[i].interleaving == 4 && state->fe.dtv_property_cache.isdbt_sb_mode == 1))
- )
- timeI = state->fe.dtv_property_cache.layer[i].interleaving;
+ if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) &&
+ ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) ||
+ (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1))
+ )
+ timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving;
else
timeI = 0;
- dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe.dtv_property_cache.layer[i].segment_count & 0xf) << 6) |
- (crate << 3) | timeI);
- if (state->fe.dtv_property_cache.layer[i].segment_count > 0) {
+ dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) |
+ (crate << 3) | timeI);
+ if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) {
switch (max_constellation) {
case DQPSK:
case QPSK:
- if (state->fe.dtv_property_cache.layer[i].modulation == QAM_16 ||
- state->fe.dtv_property_cache.layer[i].modulation == QAM_64)
- max_constellation = state->fe.dtv_property_cache.layer[i].modulation;
+ if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 ||
+ state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64)
+ max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation;
break;
case QAM_16:
- if (state->fe.dtv_property_cache.layer[i].modulation == QAM_64)
- max_constellation = state->fe.dtv_property_cache.layer[i].modulation;
+ if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64)
+ max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation;
break;
}
}
@@ -1303,34 +1313,34 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
//dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/
dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) |
- ((state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe.dtv_property_cache.
+ ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache.
isdbt_sb_mode & 1) << 4));
- dprintk("mode = %d ; guard = %d", mode, state->fe.dtv_property_cache.guard_interval);
+ dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval);
/* signal optimization parameter */
- if (state->fe.dtv_property_cache.isdbt_partial_reception) {
- seg_diff_mask = (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0];
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) {
+ seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0];
for (i = 1; i < 3; i++)
nbseg_diff +=
- (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count;
+ (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count;
for (i = 0; i < nbseg_diff; i++)
seg_diff_mask |= 1 << permu_seg[i + 1];
} else {
for (i = 0; i < 3; i++)
nbseg_diff +=
- (state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * state->fe.dtv_property_cache.layer[i].segment_count;
+ (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count;
for (i = 0; i < nbseg_diff; i++)
seg_diff_mask |= 1 << permu_seg[i];
}
dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask);
state->differential_constellation = (seg_diff_mask != 0);
- dib8000_set_diversity_in(&state->fe, state->diversity_onoff);
+ dib8000_set_diversity_in(state->fe[0], state->diversity_onoff);
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1)
seg_mask13 = 0x00E0;
else // 1-segment
seg_mask13 = 0x0040;
@@ -1340,7 +1350,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
// WRITE: Mode & Diff mask
dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask);
- if ((seg_diff_mask) || (state->fe.dtv_property_cache.isdbt_sb_mode))
+ if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode))
dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200);
else
dib8000_write_word(state, 268, (2 << 9) | 39); //init value
@@ -1351,26 +1361,25 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 353, seg_mask13); // ADDR 353
-/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */
- // dib8000_write_word(state, 351, (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5 );
+/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */
// ---- SMALL ----
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) {
- switch (state->fe.dtv_property_cache.transmission_mode) {
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_2K:
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) {
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK)
ncoeff = coeff_2k_sb_1seg_dqpsk;
else // QPSK or QAM
ncoeff = coeff_2k_sb_1seg;
} else { // 3-segments
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) {
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK)
ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk;
else // QPSK or QAM on external segments
ncoeff = coeff_2k_sb_3seg_0dqpsk;
} else { // QPSK or QAM on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK)
ncoeff = coeff_2k_sb_3seg_1dqpsk;
else // QPSK or QAM on external segments
ncoeff = coeff_2k_sb_3seg;
@@ -1379,20 +1388,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
break;
case TRANSMISSION_MODE_4K:
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) {
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK)
ncoeff = coeff_4k_sb_1seg_dqpsk;
else // QPSK or QAM
ncoeff = coeff_4k_sb_1seg;
} else { // 3-segments
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) {
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) {
ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk;
} else { // QPSK or QAM on external segments
ncoeff = coeff_4k_sb_3seg_0dqpsk;
}
} else { // QPSK or QAM on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) {
ncoeff = coeff_4k_sb_3seg_1dqpsk;
} else // QPSK or QAM on external segments
ncoeff = coeff_4k_sb_3seg;
@@ -1403,20 +1412,20 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
case TRANSMISSION_MODE_AUTO:
case TRANSMISSION_MODE_8K:
default:
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // 1-seg
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) // DQPSK
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) {
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK)
ncoeff = coeff_8k_sb_1seg_dqpsk;
else // QPSK or QAM
ncoeff = coeff_8k_sb_1seg;
} else { // 3-segments
- if (state->fe.dtv_property_cache.layer[0].modulation == DQPSK) { // DQPSK on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) {
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) {
ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk;
} else { // QPSK or QAM on external segments
ncoeff = coeff_8k_sb_3seg_0dqpsk;
}
} else { // QPSK or QAM on central segment
- if (state->fe.dtv_property_cache.layer[1].modulation == DQPSK) { // DQPSK on external segments
+ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) {
ncoeff = coeff_8k_sb_3seg_1dqpsk;
} else // QPSK or QAM on external segments
ncoeff = coeff_8k_sb_3seg;
@@ -1430,22 +1439,22 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
// P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5
dib8000_write_word(state, 351,
- (state->fe.dtv_property_cache.isdbt_sb_mode << 9) | (state->fe.dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5);
+ (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5);
// ---- COFF ----
// Carloff, the most robust
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // Sound Broadcasting mode - use both TMCC and AC pilots
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
// P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64
// P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1
dib8000_write_word(state, 187,
- (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe.dtv_property_cache.isdbt_partial_reception & 1) << 2)
- | 0x3);
+ (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2)
+ | 0x3);
-/* // P_small_coef_ext_enable = 1 */
-/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */
+/* // P_small_coef_ext_enable = 1 */
+/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) {
// P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1)
if (mode == 3)
@@ -1469,10 +1478,10 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 186, 80);
} else { // Sound Broadcasting mode 3 seg
// P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15
- /* if (mode == 3) */
- /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */
- /* else */
- /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */
+ /* if (mode == 3) */
+ /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */
+ /* else */
+ /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */
dib8000_write_word(state, 180, 0x1fcf | (1 << 14));
// P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1,
@@ -1509,7 +1518,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0));
}
// ---- FFT ----
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 && state->fe.dtv_property_cache.isdbt_partial_reception == 0) // 1-seg
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0)
dib8000_write_word(state, 178, 64); // P_fft_powrange=64
else
dib8000_write_word(state, 178, 32); // P_fft_powrange=32
@@ -1518,12 +1527,12 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
* 6bits; p_coff_thres_lock 6bits (for coff lock if needed)
*/
/* if ( ( nbseg_diff>0)&&(nbseg_diff<13))
- dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */
+ dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */
dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */
dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */
dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */
- if ((!state->fe.dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0))
+ if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0))
dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */
else
dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */
@@ -1538,8 +1547,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */
/* offset loop parameters */
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) {
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0)
/* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */
dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40);
@@ -1551,8 +1560,8 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
/* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */
dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80);
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) {
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0)
/* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */
dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode));
@@ -1564,7 +1573,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode));
/* P_dvsy_sync_wait - reuse mode */
- switch (state->fe.dtv_property_cache.transmission_mode) {
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_8K:
mode = 256;
break;
@@ -1624,15 +1633,15 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
}
// ---- ANA_FE ----
- if (state->fe.dtv_property_cache.isdbt_sb_mode) {
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 1) // 3-segments
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1)
ana_fe = ana_fe_coeff_3seg;
else // 1-segment
ana_fe = ana_fe_coeff_1seg;
} else
ana_fe = ana_fe_coeff_13seg;
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0)
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0)
for (mode = 0; mode < 24; mode++)
dib8000_write_word(state, 117 + mode, ana_fe[mode]);
@@ -1648,11 +1657,11 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
// "P_cspu_left_edge" not used => do not care
// "P_cspu_right_edge" not used => do not care
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) { // ISDB-Tsb
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1
dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0 // 1-segment
- && state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0
+ && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) {
//dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0
dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15
}
@@ -1664,7 +1673,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
// ---- TMCC ----
for (i = 0; i < 3; i++)
tmcc_pow +=
- (((state->fe.dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe.dtv_property_cache.layer[i].segment_count);
+ (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count);
// Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9);
// Threshold is set at 1/4 of max power.
tmcc_pow *= (1 << (9 - 2));
@@ -1678,7 +1687,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
if (state->isdbt_cfg_loaded == 0)
dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1)
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)
state->isdbt_cfg_loaded = 0;
else
state->isdbt_cfg_loaded = 1;
@@ -1693,38 +1702,38 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe)
int slist = 0;
- state->fe.dtv_property_cache.inversion = 0;
- if (!state->fe.dtv_property_cache.isdbt_sb_mode)
- state->fe.dtv_property_cache.layer[0].segment_count = 13;
- state->fe.dtv_property_cache.layer[0].modulation = QAM_64;
- state->fe.dtv_property_cache.layer[0].fec = FEC_2_3;
- state->fe.dtv_property_cache.layer[0].interleaving = 0;
+ state->fe[0]->dtv_property_cache.inversion = 0;
+ if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode)
+ state->fe[0]->dtv_property_cache.layer[0].segment_count = 13;
+ state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64;
+ state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3;
+ state->fe[0]->dtv_property_cache.layer[0].interleaving = 0;
//choose the right list, in sb, always do everything
- if (state->fe.dtv_property_cache.isdbt_sb_mode) {
- state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
- state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) {
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
slist = 7;
dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13));
} else {
- if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) {
- if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) {
+ if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) {
+ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) {
slist = 7;
dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2
} else
slist = 3;
} else {
- if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) {
+ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) {
slist = 2;
dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1
} else
slist = 0;
}
- if (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO)
- state->fe.dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
- if (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO)
- state->fe.dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
+ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO)
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
+ if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO)
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
dprintk("using list for autosearch : %d", slist);
dib8000_set_channel(state, (unsigned char)slist, 1);
@@ -1786,7 +1795,7 @@ static int dib8000_tune(struct dvb_frontend *fe)
if (state == NULL)
return -EINVAL;
- dib8000_set_bandwidth(state, state->fe.dtv_property_cache.bandwidth_hz / 1000);
+ dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000);
dib8000_set_channel(state, 0, 0);
// restart demod
@@ -1799,17 +1808,16 @@ static int dib8000_tune(struct dvb_frontend *fe)
// never achieved a lock before - wait for timfreq to update
if (state->timf == 0) {
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) {
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) // Sound Broadcasting mode 1 seg
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0)
msleep(300);
else // Sound Broadcasting mode 3 seg
msleep(500);
} else // 13 seg
msleep(200);
}
- //dump_reg(state);
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1) {
- if (state->fe.dtv_property_cache.isdbt_partial_reception == 0) { // Sound Broadcasting mode 1 seg
+ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) {
+ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) {
/* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */
dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40);
@@ -1854,26 +1862,38 @@ static int dib8000_tune(struct dvb_frontend *fe)
static int dib8000_wakeup(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ int ret;
dib8000_set_power_mode(state, DIB8000M_POWER_ALL);
dib8000_set_adc_state(state, DIBX000_ADC_ON);
if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
dprintk("could not start Slow ADC");
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
static int dib8000_sleep(struct dvb_frontend *fe)
{
- struct dib8000_state *st = fe->demodulator_priv;
- if (1) {
- dib8000_set_output_mode(st, OUTMODE_HIGH_Z);
- dib8000_set_power_mode(st, DIB8000M_POWER_INTERFACE_ONLY);
- return dib8000_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(st, DIBX000_ADC_OFF);
- } else {
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ int ret;
- return 0;
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
+ if (ret < 0)
+ return ret;
}
+
+ dib8000_set_output_mode(fe, OUTMODE_HIGH_Z);
+ dib8000_set_power_mode(state, DIB8000M_POWER_INTERFACE_ONLY);
+ return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF);
}
enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe)
@@ -1891,16 +1911,40 @@ int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tun
}
EXPORT_SYMBOL(dib8000_set_tune_state);
-
-
-
static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dib8000_state *state = fe->demodulator_priv;
u16 i, val = 0;
+ fe_status_t stat;
+ u8 index_frontend, sub_index_frontend;
fe->dtv_property_cache.bandwidth_hz = 6000000;
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
+ if (stat&FE_HAS_SYNC) {
+ dprintk("TMCC lock on the slave%i", index_frontend);
+ /* synchronize the cache with the other frontends */
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep);
+ for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) {
+ if (sub_index_frontend != index_frontend) {
+ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode;
+ state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion;
+ state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode;
+ state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval;
+ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception;
+ for (i = 0; i < 3; i++) {
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+
fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1;
val = dib8000_read_word(state, 570);
@@ -1992,112 +2036,200 @@ static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
break;
}
}
+
+ /* synchronize the cache with the other frontends */
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode;
+ state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = fe->dtv_property_cache.isdbt_partial_reception;
+ for (i = 0; i < 3; i++) {
+ state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = fe->dtv_property_cache.layer[i].segment_count;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = fe->dtv_property_cache.layer[i].interleaving;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].fec = fe->dtv_property_cache.layer[i].fec;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = fe->dtv_property_cache.layer[i].modulation;
+ }
+ }
return 0;
}
static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dib8000_state *state = fe->demodulator_priv;
+ u8 nbr_pending, exit_condition, index_frontend;
+ s8 index_frontend_success = -1;
int time, ret;
+ int time_slave = FE_CALLBACK_TIME_NEVER;
- fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+ if (state->fe[0]->dtv_property_cache.frequency == 0) {
+ dprintk("dib8000: must at least specify frequency ");
+ return 0;
+ }
- dib8000_set_output_mode(state, OUTMODE_HIGH_Z);
+ if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) {
+ dprintk("dib8000: no bandwidth specified, set to default ");
+ state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000;
+ }
+
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ /* synchronization of the cache */
+ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT;
+ memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties));
+
+ dib8000_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z);
+ if (state->fe[index_frontend]->ops.tuner_ops.set_params)
+ state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend], fep);
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe, fep);
+ dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START);
+ }
/* start up the AGC */
- state->tune_state = CT_AGC_START;
do {
- time = dib8000_agc_startup(fe);
+ time = dib8000_agc_startup(state->fe[0]);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ time_slave = dib8000_agc_startup(state->fe[index_frontend]);
+ if (time == FE_CALLBACK_TIME_NEVER)
+ time = time_slave;
+ else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time))
+ time = time_slave;
+ }
if (time != FE_CALLBACK_TIME_NEVER)
msleep(time / 10);
else
break;
- } while (state->tune_state != CT_AGC_STOP);
-
- if (state->fe.dtv_property_cache.frequency == 0) {
- dprintk("dib8000: must at least specify frequency ");
- return 0;
- }
-
- if (state->fe.dtv_property_cache.bandwidth_hz == 0) {
- dprintk("dib8000: no bandwidth specified, set to default ");
- state->fe.dtv_property_cache.bandwidth_hz = 6000000;
- }
+ exit_condition = 1;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) {
+ exit_condition = 0;
+ break;
+ }
+ }
+ } while (exit_condition == 0);
+
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
+
+ if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) ||
+ (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) ||
+ (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) ||
+ (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) ||
+ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) &&
+ (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) &&
+ (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) &&
+ ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) ||
+ (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) ||
+ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) &&
+ (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) &&
+ (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) &&
+ ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) ||
+ (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) ||
+ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) &&
+ (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) &&
+ (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) &&
+ ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) ||
+ (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) ||
+ (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) ||
+ ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) &&
+ ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) ||
+ ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) &&
+ ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) {
+ int i = 80000;
+ u8 found = 0;
+ u8 tune_failed = 0;
+
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000);
+ dib8000_autosearch_start(state->fe[index_frontend]);
+ }
- state->tune_state = CT_DEMOD_START;
-
- if ((state->fe.dtv_property_cache.delivery_system != SYS_ISDBT) ||
- (state->fe.dtv_property_cache.inversion == INVERSION_AUTO) ||
- (state->fe.dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) ||
- (state->fe.dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) ||
- (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) &&
- (state->fe.dtv_property_cache.layer[0].segment_count != 0xff) &&
- (state->fe.dtv_property_cache.layer[0].segment_count != 0) &&
- ((state->fe.dtv_property_cache.layer[0].modulation == QAM_AUTO) ||
- (state->fe.dtv_property_cache.layer[0].fec == FEC_AUTO))) ||
- (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) &&
- (state->fe.dtv_property_cache.layer[1].segment_count != 0xff) &&
- (state->fe.dtv_property_cache.layer[1].segment_count != 0) &&
- ((state->fe.dtv_property_cache.layer[1].modulation == QAM_AUTO) ||
- (state->fe.dtv_property_cache.layer[1].fec == FEC_AUTO))) ||
- (((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) &&
- (state->fe.dtv_property_cache.layer[2].segment_count != 0xff) &&
- (state->fe.dtv_property_cache.layer[2].segment_count != 0) &&
- ((state->fe.dtv_property_cache.layer[2].modulation == QAM_AUTO) ||
- (state->fe.dtv_property_cache.layer[2].fec == FEC_AUTO))) ||
- (((state->fe.dtv_property_cache.layer[0].segment_count == 0) ||
- ((state->fe.dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) &&
- ((state->fe.dtv_property_cache.layer[1].segment_count == 0) ||
- ((state->fe.dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) &&
- ((state->fe.dtv_property_cache.layer[2].segment_count == 0) || ((state->fe.dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) {
- int i = 800, found;
-
- dib8000_set_bandwidth(state, fe->dtv_property_cache.bandwidth_hz / 1000);
- dib8000_autosearch_start(fe);
do {
- msleep(10);
- found = dib8000_autosearch_irq(fe);
- } while (found == 0 && i--);
+ msleep(20);
+ nbr_pending = 0;
+ exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (((tune_failed >> index_frontend) & 0x1) == 0) {
+ found = dib8000_autosearch_irq(state->fe[index_frontend]);
+ switch (found) {
+ case 0: /* tune pending */
+ nbr_pending++;
+ break;
+ case 2:
+ dprintk("autosearch succeed on the frontend%i", index_frontend);
+ exit_condition = 2;
+ index_frontend_success = index_frontend;
+ break;
+ default:
+ dprintk("unhandled autosearch result");
+ case 1:
+ dprintk("autosearch failed for the frontend%i", index_frontend);
+ break;
+ }
+ }
+ }
- dprintk("Frequency %d Hz, autosearch returns: %d", fep->frequency, found);
+ /* if all tune are done and no success, exit: tune failed */
+ if ((nbr_pending == 0) && (exit_condition == 0))
+ exit_condition = 1;
+ } while ((exit_condition == 0) && i--);
- if (found == 0 || found == 1)
- return 0; // no channel found
+ if (exit_condition == 1) { /* tune failed */
+ dprintk("tune failed");
+ return 0;
+ }
+
+ dprintk("tune success on frontend%i", index_frontend_success);
dib8000_get_frontend(fe, fep);
}
- ret = dib8000_tune(fe);
+ for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ ret = dib8000_tune(state->fe[index_frontend]);
+
+ /* set output mode and diversity input */
+ dib8000_set_output_mode(state->fe[0], state->cfg.output_mode);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ dib8000_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY);
+ dib8000_set_diversity_in(state->fe[index_frontend-1], 1);
+ }
- /* make this a config parameter */
- dib8000_set_output_mode(state, state->cfg.output_mode);
+ /* turn off the diversity of the last chip */
+ dib8000_set_diversity_in(state->fe[index_frontend-1], 0);
return ret;
}
+static u16 dib8000_read_lock(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ return dib8000_read_word(state, 568);
+}
+
static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
{
struct dib8000_state *state = fe->demodulator_priv;
- u16 lock = dib8000_read_word(state, 568);
+ u16 lock_slave = 0, lock = dib8000_read_word(state, 568);
+ u8 index_frontend;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ lock_slave |= dib8000_read_lock(state->fe[index_frontend]);
*stat = 0;
- if ((lock >> 13) & 1)
+ if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1))
*stat |= FE_HAS_SIGNAL;
- if ((lock >> 8) & 1) /* Equal */
+ if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */
*stat |= FE_HAS_CARRIER;
- if (((lock >> 1) & 0xf) == 0xf) /* TMCC_SYNC */
+ if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */
*stat |= FE_HAS_SYNC;
- if (((lock >> 12) & 1) && ((lock >> 5) & 7)) /* FEC MPEG */
+ if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */
*stat |= FE_HAS_LOCK;
- if ((lock >> 12) & 1) {
+ if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) {
lock = dib8000_read_word(state, 554); /* Viterbi Layer A */
if (lock & 0x01)
*stat |= FE_HAS_VITERBI;
@@ -2131,44 +2263,120 @@ static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
{
struct dib8000_state *state = fe->demodulator_priv;
- u16 val = dib8000_read_word(state, 390);
- *strength = 65535 - val;
+ u8 index_frontend;
+ u16 val;
+
+ *strength = 0;
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
+ }
+
+ val = 65535 - dib8000_read_word(state, 390);
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
return 0;
}
-static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
+static u32 dib8000_get_snr(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
+ u32 n, s, exp;
u16 val;
- s32 signal_mant, signal_exp, noise_mant, noise_exp;
- u32 result = 0;
val = dib8000_read_word(state, 542);
- noise_mant = (val >> 6) & 0xff;
- noise_exp = (val & 0x3f);
+ n = (val >> 6) & 0xff;
+ exp = (val & 0x3f);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ n <<= exp+16;
val = dib8000_read_word(state, 543);
- signal_mant = (val >> 6) & 0xff;
- signal_exp = (val & 0x3f);
+ s = (val >> 6) & 0xff;
+ exp = (val & 0x3f);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ s <<= exp+16;
+
+ if (n > 0) {
+ u32 t = (s/n) << 16;
+ return t + ((s << 16) - n*t) / n;
+ }
+ return 0xffffffff;
+}
- if ((noise_exp & 0x20) != 0)
- noise_exp -= 0x40;
- if ((signal_exp & 0x20) != 0)
- signal_exp -= 0x40;
+static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u32 snr_master;
- if (signal_mant != 0)
- result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant);
- else
- result = intlog10(2) * 10 * signal_exp - 100;
- if (noise_mant != 0)
- result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant);
+ snr_master = dib8000_get_snr(fe);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ snr_master += dib8000_get_snr(state->fe[index_frontend]);
+
+ if (snr_master != 0) {
+ snr_master = 10*intlog10(snr_master>>16);
+ *snr = snr_master / ((1 << 24) / 10);
+ }
else
- result -= intlog10(2) * 10 * noise_exp - 100;
+ *snr = 0;
- *snr = result / ((1 << 24) / 10);
return 0;
}
+int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend = 1;
+
+ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
+ index_frontend++;
+ if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
+ dprintk("set slave fe %p to index %i", fe_slave, index_frontend);
+ state->fe[index_frontend] = fe_slave;
+ return 0;
+ }
+
+ dprintk("too many slave frontend");
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(dib8000_set_slave_frontend);
+
+int dib8000_remove_slave_frontend(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend = 1;
+
+ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
+ index_frontend++;
+ if (index_frontend != 1) {
+ dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1);
+ state->fe[index_frontend] = NULL;
+ return 0;
+ }
+
+ dprintk("no frontend to be removed");
+ return -ENODEV;
+}
+EXPORT_SYMBOL(dib8000_remove_slave_frontend);
+
+struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ if (slave_index >= MAX_NUMBER_OF_FRONTENDS)
+ return NULL;
+ return state->fe[slave_index];
+}
+EXPORT_SYMBOL(dib8000_get_slave_frontend);
+
+
int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr)
{
int k = 0;
@@ -2227,7 +2435,13 @@ static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_fron
static void dib8000_release(struct dvb_frontend *fe)
{
struct dib8000_state *st = fe->demodulator_priv;
+ u8 index_frontend;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
+ dvb_frontend_detach(st->fe[index_frontend]);
+
dibx000_exit_i2c_master(&st->i2c_master);
+ kfree(st->fe[0]);
kfree(st);
}
@@ -2242,19 +2456,19 @@ EXPORT_SYMBOL(dib8000_get_i2c_master);
int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
struct dib8000_state *st = fe->demodulator_priv;
- u16 val = dib8000_read_word(st, 299) & 0xffef;
- val |= (onoff & 0x1) << 4;
+ u16 val = dib8000_read_word(st, 299) & 0xffef;
+ val |= (onoff & 0x1) << 4;
- dprintk("pid filter enabled %d", onoff);
- return dib8000_write_word(st, 299, val);
+ dprintk("pid filter enabled %d", onoff);
+ return dib8000_write_word(st, 299, val);
}
EXPORT_SYMBOL(dib8000_pid_filter_ctrl);
int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib8000_state *st = fe->demodulator_priv;
- dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
- return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0);
+ dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0);
}
EXPORT_SYMBOL(dib8000_pid_filter);
@@ -2298,6 +2512,9 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s
state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL);
if (state == NULL)
return NULL;
+ fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ goto error;
memcpy(&state->cfg, cfg, sizeof(struct dib8000_config));
state->i2c.adap = i2c_adap;
@@ -2311,9 +2528,9 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s
if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
state->cfg.output_mode = OUTMODE_MPEG2_FIFO;
- fe = &state->fe;
+ state->fe[0] = fe;
fe->demodulator_priv = state;
- memcpy(&state->fe.ops, &dib8000_ops, sizeof(struct dvb_frontend_ops));
+ memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops));
state->timf_default = cfg->pll->timf;
diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h
index e0a9ded11df4..617f9eba3a09 100644
--- a/drivers/media/dvb/frontends/dib8000.h
+++ b/drivers/media/dvb/frontends/dib8000.h
@@ -50,6 +50,9 @@ extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_st
extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe);
extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe);
extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode);
+extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave);
+extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe);
+extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index);
#else
static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
{
@@ -111,6 +114,23 @@ static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode)
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return 0;
}
+static inline int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+int dib8000_remove_slave_frontend(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
#endif
#endif
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
new file mode 100644
index 000000000000..91518761a2da
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -0,0 +1,2351 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family.
+ *
+ * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; 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/kernel.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+#include "dvb_math.h"
+#include "dvb_frontend.h"
+
+#include "dib9000.h"
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0)
+#define MAX_NUMBER_OF_FRONTENDS 6
+
+struct i2c_device {
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+};
+
+/* lock */
+#define DIB_LOCK struct mutex
+#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0)
+#define DibReleaseLock(lock) mutex_unlock(lock)
+#define DibInitLock(lock) mutex_init(lock)
+#define DibFreeLock(lock)
+
+struct dib9000_state {
+ struct i2c_device i2c;
+
+ struct dibx000_i2c_master i2c_master;
+ struct i2c_adapter tuner_adap;
+ struct i2c_adapter component_bus;
+
+ u16 revision;
+ u8 reg_offs;
+
+ enum frontend_tune_state tune_state;
+ u32 status;
+ struct dvb_frontend_parametersContext channel_status;
+
+ u8 fe_id;
+
+#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB9000_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ union { /* common for all chips */
+ struct {
+ u8 mobile_mode:1;
+ } host;
+
+ struct {
+ struct dib9000_fe_memory_map {
+ u16 addr;
+ u16 size;
+ } fe_mm[18];
+ u8 memcmd;
+
+ DIB_LOCK mbx_if_lock; /* to protect read/write operations */
+ DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */
+
+ DIB_LOCK mem_lock; /* to protect the memory accesses */
+ DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */
+
+#define MBX_MAX_WORDS (256 - 200 - 2)
+#define DIB9000_MSG_CACHE_SIZE 2
+ u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS];
+ u8 fw_is_running;
+ } risc;
+ } platform;
+
+ union { /* common for all platforms */
+ struct {
+ struct dib9000_config cfg;
+ } d9;
+ } chip;
+
+ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS];
+ u16 component_bus_speed;
+};
+
+u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0
+};
+
+enum dib9000_power_mode {
+ DIB9000_POWER_ALL = 0,
+
+ DIB9000_POWER_NO,
+ DIB9000_POWER_INTERF_ANALOG_AGC,
+ DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
+ DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD,
+ DIB9000_POWER_INTERFACE_ONLY,
+};
+
+enum dib9000_out_messages {
+ OUT_MSG_HBM_ACK,
+ OUT_MSG_HOST_BUF_FAIL,
+ OUT_MSG_REQ_VERSION,
+ OUT_MSG_BRIDGE_I2C_W,
+ OUT_MSG_BRIDGE_I2C_R,
+ OUT_MSG_BRIDGE_APB_W,
+ OUT_MSG_BRIDGE_APB_R,
+ OUT_MSG_SCAN_CHANNEL,
+ OUT_MSG_MONIT_DEMOD,
+ OUT_MSG_CONF_GPIO,
+ OUT_MSG_DEBUG_HELP,
+ OUT_MSG_SUBBAND_SEL,
+ OUT_MSG_ENABLE_TIME_SLICE,
+ OUT_MSG_FE_FW_DL,
+ OUT_MSG_FE_CHANNEL_SEARCH,
+ OUT_MSG_FE_CHANNEL_TUNE,
+ OUT_MSG_FE_SLEEP,
+ OUT_MSG_FE_SYNC,
+ OUT_MSG_CTL_MONIT,
+
+ OUT_MSG_CONF_SVC,
+ OUT_MSG_SET_HBM,
+ OUT_MSG_INIT_DEMOD,
+ OUT_MSG_ENABLE_DIVERSITY,
+ OUT_MSG_SET_OUTPUT_MODE,
+ OUT_MSG_SET_PRIORITARY_CHANNEL,
+ OUT_MSG_ACK_FRG,
+ OUT_MSG_INIT_PMU,
+};
+
+enum dib9000_in_messages {
+ IN_MSG_DATA,
+ IN_MSG_FRAME_INFO,
+ IN_MSG_CTL_MONIT,
+ IN_MSG_ACK_FREE_ITEM,
+ IN_MSG_DEBUG_BUF,
+ IN_MSG_MPE_MONITOR,
+ IN_MSG_RAWTS_MONITOR,
+ IN_MSG_END_BRIDGE_I2C_RW,
+ IN_MSG_END_BRIDGE_APB_RW,
+ IN_MSG_VERSION,
+ IN_MSG_END_OF_SCAN,
+ IN_MSG_MONIT_DEMOD,
+ IN_MSG_ERROR,
+ IN_MSG_FE_FW_DL_DONE,
+ IN_MSG_EVENT,
+ IN_MSG_ACK_CHANGE_SVC,
+ IN_MSG_HBM_PROF,
+};
+
+/* memory_access requests */
+#define FE_MM_W_CHANNEL 0
+#define FE_MM_W_FE_INFO 1
+#define FE_MM_RW_SYNC 2
+
+#define FE_SYNC_CHANNEL 1
+#define FE_SYNC_W_GENERIC_MONIT 2
+#define FE_SYNC_COMPONENT_ACCESS 3
+
+#define FE_MM_R_CHANNEL_SEARCH_STATE 3
+#define FE_MM_R_CHANNEL_UNION_CONTEXT 4
+#define FE_MM_R_FE_INFO 5
+#define FE_MM_R_FE_MONITOR 6
+
+#define FE_MM_W_CHANNEL_HEAD 7
+#define FE_MM_W_CHANNEL_UNION 8
+#define FE_MM_W_CHANNEL_CONTEXT 9
+#define FE_MM_R_CHANNEL_UNION 10
+#define FE_MM_R_CHANNEL_CONTEXT 11
+#define FE_MM_R_CHANNEL_TUNE_STATE 12
+
+#define FE_MM_R_GENERIC_MONITORING_SIZE 13
+#define FE_MM_W_GENERIC_MONITORING 14
+#define FE_MM_R_GENERIC_MONITORING 15
+
+#define FE_MM_W_COMPONENT_ACCESS 16
+#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17
+static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len);
+static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len);
+
+static u16 to_fw_output_mode(u16 mode)
+{
+ switch (mode) {
+ case OUTMODE_HIGH_Z:
+ return 0;
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ return 4;
+ case OUTMODE_MPEG2_PAR_CONT_CLK:
+ return 8;
+ case OUTMODE_MPEG2_SERIAL:
+ return 16;
+ case OUTMODE_DIVERSITY:
+ return 128;
+ case OUTMODE_MPEG2_FIFO:
+ return 2;
+ case OUTMODE_ANALOG_ADC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute)
+{
+ u32 chunk_size = 126;
+ u32 l;
+ int ret;
+ u8 wb[2] = { reg >> 8, reg & 0xff };
+ struct i2c_msg msg[2] = {
+ {.addr = state->i2c.i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2},
+ {.addr = state->i2c.i2c_addr >> 1, .flags = I2C_M_RD, .buf = b, .len = len},
+ };
+
+ if (state->platform.risc.fw_is_running && (reg < 1024))
+ return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len);
+
+ if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
+ wb[0] |= (1 << 5);
+ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+ wb[0] |= (1 << 4);
+
+ do {
+ l = len < chunk_size ? len : chunk_size;
+ msg[1].len = l;
+ msg[1].buf = b;
+ ret = i2c_transfer(state->i2c.i2c_adap, msg, 2) != 2 ? -EREMOTEIO : 0;
+ if (ret != 0) {
+ dprintk("i2c read error on %d", reg);
+ return -EREMOTEIO;
+ }
+
+ b += l;
+ len -= l;
+
+ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
+ reg += l / 2;
+ } while ((ret == 0) && len);
+
+ return 0;
+}
+
+static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg)
+{
+ u8 b[2];
+ u8 wb[2] = { reg >> 8, reg & 0xff };
+ struct i2c_msg msg[2] = {
+ {.addr = i2c->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2},
+ {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD, .buf = b, .len = 2},
+ };
+
+ if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) {
+ dprintk("read register %x error", reg);
+ return 0;
+ }
+
+ return (b[0] << 8) | b[1];
+}
+
+static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg)
+{
+ u8 b[2];
+ if (dib9000_read16_attr(state, reg, b, 2, 0) != 0)
+ return 0;
+ return (b[0] << 8 | b[1]);
+}
+
+static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute)
+{
+ u8 b[2];
+ if (dib9000_read16_attr(state, reg, b, 2, attribute) != 0)
+ return 0;
+ return (b[0] << 8 | b[1]);
+}
+
+#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+
+static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute)
+{
+ u8 b[255];
+ u32 chunk_size = 126;
+ u32 l;
+ int ret;
+
+ struct i2c_msg msg = {
+ .addr = state->i2c.i2c_addr >> 1, .flags = 0, .buf = b, .len = len + 2
+ };
+
+ if (state->platform.risc.fw_is_running && (reg < 1024)) {
+ if (dib9000_risc_apb_access_write
+ (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0)
+ return -EINVAL;
+ return 0;
+ }
+
+ b[0] = (reg >> 8) & 0xff;
+ b[1] = (reg) & 0xff;
+
+ if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
+ b[0] |= (1 << 5);
+ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+ b[0] |= (1 << 4);
+
+ do {
+ l = len < chunk_size ? len : chunk_size;
+ msg.len = l + 2;
+ memcpy(&b[2], buf, l);
+
+ ret = i2c_transfer(state->i2c.i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+
+ buf += l;
+ len -= l;
+
+ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
+ reg += l / 2;
+ } while ((ret == 0) && len);
+
+ return ret;
+}
+
+static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
+{
+ u8 b[4] = { (reg >> 8) & 0xff, reg & 0xff, (val >> 8) & 0xff, val & 0xff };
+ struct i2c_msg msg = {
+ .addr = i2c->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+
+ return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val)
+{
+ u8 b[2] = { val >> 8, val & 0xff };
+ return dib9000_write16_attr(state, reg, b, 2, 0);
+}
+
+static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute)
+{
+ u8 b[2] = { val >> 8, val & 0xff };
+ return dib9000_write16_attr(state, reg, b, 2, attribute);
+}
+
+#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0)
+#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+#define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute))
+
+#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0)
+#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0)
+
+#define MAC_IRQ (1 << 1)
+#define IRQ_POL_MSK (1 << 4)
+
+#define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+#define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+
+static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading)
+{
+ u8 b[14] = { 0 };
+
+/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */
+/* b[0] = 0 << 7; */
+ b[1] = 1;
+
+/* b[2] = 0; */
+/* b[3] = 0; */
+ b[4] = (u8) (addr >> 8);
+ b[5] = (u8) (addr & 0xff);
+
+/* b[10] = 0; */
+/* b[11] = 0; */
+ b[12] = (u8) (addr >> 8);
+ b[13] = (u8) (addr & 0xff);
+
+ addr += len;
+/* b[6] = 0; */
+/* b[7] = 0; */
+ b[8] = (u8) (addr >> 8);
+ b[9] = (u8) (addr & 0xff);
+
+ dib9000_write(state, 1056, b, 14);
+ if (reading)
+ dib9000_write_word(state, 1056, (1 << 15) | 1);
+ state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */
+}
+
+static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd)
+{
+ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f];
+ /* decide whether we need to "refresh" the memory controller */
+ if (state->platform.risc.memcmd == cmd && /* same command */
+ !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */
+ return;
+ dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80);
+ state->platform.risc.memcmd = cmd;
+}
+
+static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len)
+{
+ if (!state->platform.risc.fw_is_running)
+ return -EIO;
+
+ DibAcquireLock(&state->platform.risc.mem_lock);
+ dib9000_risc_mem_setup(state, cmd | 0x80);
+ dib9000_risc_mem_read_chunks(state, b, len);
+ DibReleaseLock(&state->platform.risc.mem_lock);
+ return 0;
+}
+
+static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b)
+{
+ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd];
+ if (!state->platform.risc.fw_is_running)
+ return -EIO;
+
+ DibAcquireLock(&state->platform.risc.mem_lock);
+ dib9000_risc_mem_setup(state, cmd);
+ dib9000_risc_mem_write_chunks(state, b, m->size);
+ DibReleaseLock(&state->platform.risc.mem_lock);
+ return 0;
+}
+
+static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len)
+{
+ u16 offs;
+
+ if (risc_id == 1)
+ offs = 16;
+ else
+ offs = 0;
+
+ /* config crtl reg */
+ dib9000_write_word(state, 1024 + offs, 0x000f);
+ dib9000_write_word(state, 1025 + offs, 0);
+ dib9000_write_word(state, 1031 + offs, key);
+
+ dprintk("going to download %dB of microcode", len);
+ if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) {
+ dprintk("error while downloading microcode for RISC %c", 'A' + risc_id);
+ return -EIO;
+ }
+
+ dprintk("Microcode for RISC %c loaded", 'A' + risc_id);
+
+ return 0;
+}
+
+static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id)
+{
+ u16 mbox_offs;
+ u16 reset_reg;
+ u16 tries = 1000;
+
+ if (risc_id == 1)
+ mbox_offs = 16;
+ else
+ mbox_offs = 0;
+
+ /* Reset mailbox */
+ dib9000_write_word(state, 1027 + mbox_offs, 0x8000);
+
+ /* Read reset status */
+ do {
+ reset_reg = dib9000_read_word(state, 1027 + mbox_offs);
+ msleep(100);
+ } while ((reset_reg & 0x8000) && --tries);
+
+ if (reset_reg & 0x8000) {
+ dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id);
+ return -EIO;
+ }
+ dprintk("MBX: initialized");
+ return 0;
+}
+
+#define MAX_MAILBOX_TRY 100
+static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr)
+{
+ u8 *d, b[2];
+ u16 tmp;
+ u16 size;
+ u32 i;
+ int ret = 0;
+
+ if (!state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ tmp = MAX_MAILBOX_TRY;
+ do {
+ size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
+ if ((size + len + 1) > MBX_MAX_WORDS && --tmp) {
+ dprintk("MBX: RISC mbx full, retrying");
+ msleep(100);
+ } else
+ break;
+ } while (1);
+
+ /*dprintk( "MBX: size: %d", size); */
+
+ if (tmp == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+#ifdef DUMP_MSG
+ dprintk("--> %02x %d ", id, len + 1);
+ for (i = 0; i < len; i++)
+ dprintk("%04x ", data[i]);
+ dprintk("\n");
+#endif
+
+ /* byte-order conversion - works on big (where it is not necessary) or little endian */
+ d = (u8 *) data;
+ for (i = 0; i < len; i++) {
+ tmp = data[i];
+ *d++ = tmp >> 8;
+ *d++ = tmp & 0xff;
+ }
+
+ /* write msg */
+ b[0] = id;
+ b[1] = len + 1;
+ if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* update register nb_mes_in_RX */
+ ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr);
+
+out:
+ DibReleaseLock(&state->platform.risc.mbx_if_lock);
+
+ return ret;
+}
+
+static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr)
+{
+#ifdef DUMP_MSG
+ u16 *d = data;
+#endif
+
+ u16 tmp, i;
+ u8 size;
+ u8 mc_base;
+
+ if (!state->platform.risc.fw_is_running)
+ return 0;
+
+ DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ if (risc_id == 1)
+ mc_base = 16;
+ else
+ mc_base = 0;
+
+ /* Length and type in the first word */
+ *data = dib9000_read_word_attr(state, 1029 + mc_base, attr);
+
+ size = *data & 0xff;
+ if (size <= MBX_MAX_WORDS) {
+ data++;
+ size--; /* Initial word already read */
+
+ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr);
+
+ /* to word conversion */
+ for (i = 0; i < size; i++) {
+ tmp = *data;
+ *data = (tmp >> 8) | (tmp << 8);
+ data++;
+ }
+
+#ifdef DUMP_MSG
+ dprintk("<-- ");
+ for (i = 0; i < size + 1; i++)
+ dprintk("%04x ", d[i]);
+ dprintk("\n");
+#endif
+ } else {
+ dprintk("MBX: message is too big for message cache (%d), flushing message", size);
+ size--; /* Initial word already read */
+ while (size--)
+ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr);
+ }
+ /* Update register nb_mes_in_TX */
+ dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr);
+
+ DibReleaseLock(&state->platform.risc.mbx_if_lock);
+
+ return size + 1;
+}
+
+static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size)
+{
+ u32 ts = data[1] << 16 | data[0];
+ char *b = (char *)&data[2];
+
+ b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */
+ if (*b == '~') {
+ b++;
+ dprintk(b);
+ } else
+ dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : "<emtpy>");
+ return 1;
+}
+
+static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr)
+{
+ int i;
+ u8 size;
+ u16 *block;
+ /* find a free slot */
+ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
+ block = state->platform.risc.message_cache[i];
+ if (*block == 0) {
+ size = dib9000_mbx_read(state, block, 1, attr);
+
+/* dprintk( "MBX: fetched %04x message to cache", *block); */
+
+ switch (*block >> 8) {
+ case IN_MSG_DEBUG_BUF:
+ dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */
+ *block = 0; /* free the block */
+ break;
+#if 0
+ case IN_MSG_DATA: /* FE-TRACE */
+ dib9000_risc_data_process(state, block + 1, size);
+ *block = 0;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return 1;
+ }
+ }
+ dprintk("MBX: no free cache-slot found for new message...");
+ return -1;
+}
+
+static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr)
+{
+ if (risc_id == 0)
+ return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */
+ else
+ return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */
+}
+
+static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
+{
+ int ret = 0;
+ u16 tmp;
+
+ if (!state->platform.risc.fw_is_running)
+ return -1;
+
+ DibAcquireLock(&state->platform.risc.mbx_lock);
+
+ if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
+ ret = dib9000_mbx_fetch_to_cache(state, attr);
+
+ tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */
+/* if (tmp) */
+/* dprintk( "cleared IRQ: %x", tmp); */
+ DibReleaseLock(&state->platform.risc.mbx_lock);
+
+ return ret;
+}
+
+static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr)
+{
+ u8 i;
+ u16 *block;
+ u16 timeout = 30;
+
+ *msg = 0;
+ do {
+ /* dib9000_mbx_get_from_cache(); */
+ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
+ block = state->platform.risc.message_cache[i];
+ if ((*block >> 8) == id) {
+ *size = (*block & 0xff) - 1;
+ memcpy(msg, block + 1, (*size) * 2);
+ *block = 0; /* free the block */
+ i = 0; /* signal that we found a message */
+ break;
+ }
+ }
+
+ if (i == 0)
+ break;
+
+ if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */
+ return -1;
+
+ } while (--timeout);
+
+ if (timeout == 0) {
+ dprintk("waiting for message %d timed out", id);
+ return -1;
+ }
+
+ return i == 0;
+}
+
+static int dib9000_risc_check_version(struct dib9000_state *state)
+{
+ u8 r[4];
+ u8 size;
+ u16 fw_version = 0;
+
+ if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0)
+ return -EIO;
+
+ fw_version = (r[0] << 8) | r[1];
+ dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]);
+
+ if ((fw_version >> 10) != 7)
+ return -EINVAL;
+
+ switch (fw_version & 0x3ff) {
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ break;
+ default:
+ dprintk("RISC: invalid firmware version");
+ return -EINVAL;
+ }
+
+ dprintk("RISC: valid firmware version");
+ return 0;
+}
+
+static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB)
+{
+ /* Reconfig pool mac ram */
+ dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */
+ dib9000_write_word(state, 1226, 0x05);
+
+ /* Toggles IP crypto to Host APB interface. */
+ dib9000_write_word(state, 1542, 1);
+
+ /* Set jump and no jump in the dma box */
+ dib9000_write_word(state, 1074, 0);
+ dib9000_write_word(state, 1075, 0);
+
+ /* Set MAC as APB Master. */
+ dib9000_write_word(state, 1237, 0);
+
+ /* Reset the RISCs */
+ if (codeA != NULL)
+ dib9000_write_word(state, 1024, 2);
+ else
+ dib9000_write_word(state, 1024, 15);
+ if (codeB != NULL)
+ dib9000_write_word(state, 1040, 2);
+
+ if (codeA != NULL)
+ dib9000_firmware_download(state, 0, 0x1234, codeA, lenA);
+ if (codeB != NULL)
+ dib9000_firmware_download(state, 1, 0x1234, codeB, lenB);
+
+ /* Run the RISCs */
+ if (codeA != NULL)
+ dib9000_write_word(state, 1024, 0);
+ if (codeB != NULL)
+ dib9000_write_word(state, 1040, 0);
+
+ if (codeA != NULL)
+ if (dib9000_mbx_host_init(state, 0) != 0)
+ return -EIO;
+ if (codeB != NULL)
+ if (dib9000_mbx_host_init(state, 1) != 0)
+ return -EIO;
+
+ msleep(100);
+ state->platform.risc.fw_is_running = 1;
+
+ if (dib9000_risc_check_version(state) != 0)
+ return -EINVAL;
+
+ state->platform.risc.memcmd = 0xff;
+ return 0;
+}
+
+static u16 dib9000_identify(struct i2c_device *client)
+{
+ u16 value;
+
+ value = dib9000_i2c_read16(client, 896);
+ if (value != 0x01b3) {
+ dprintk("wrong Vendor ID (0x%x)", value);
+ return 0;
+ }
+
+ value = dib9000_i2c_read16(client, 897);
+ if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) {
+ dprintk("wrong Device ID (0x%x)", value);
+ return 0;
+ }
+
+ /* protect this driver to be used with 7000PC */
+ if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) {
+ dprintk("this driver does not work with DiB7000PC");
+ return 0;
+ }
+
+ switch (value) {
+ case 0x4000:
+ dprintk("found DiB7000MA/PA/MB/PB");
+ break;
+ case 0x4001:
+ dprintk("found DiB7000HC");
+ break;
+ case 0x4002:
+ dprintk("found DiB7000MC");
+ break;
+ case 0x4003:
+ dprintk("found DiB9000A");
+ break;
+ case 0x4004:
+ dprintk("found DiB9000H");
+ break;
+ case 0x4005:
+ dprintk("found DiB9000M");
+ break;
+ }
+
+ return value;
+}
+
+static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode)
+{
+ /* by default everything is going to be powered off */
+ u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906;
+ u8 offset;
+
+ if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005)
+ offset = 1;
+ else
+ offset = 0;
+
+ reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB9000_POWER_ALL:
+ reg_903 = 0x0000;
+ reg_904 = 0x0000;
+ reg_905 = 0x0000;
+ reg_906 = 0x0000;
+ break;
+
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
+ case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
+ break;
+
+ case DIB9000_POWER_INTERF_ANALOG_AGC:
+ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
+ reg_903 = 0x0000;
+ reg_904 = 0x801f;
+ reg_905 = 0x0000;
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD:
+ reg_903 = 0x0000;
+ reg_904 = 0x8000;
+ reg_905 = 0x010b;
+ reg_906 &= ~((1 << 0));
+ break;
+ default:
+ case DIB9000_POWER_NO:
+ break;
+ }
+
+ /* always power down unused parts */
+ if (!state->platform.host.mobile_mode)
+ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
+
+ /* P_sdio_select_clk = 0 on MC and after */
+ if (state->revision != 0x4000)
+ reg_906 <<= 1;
+
+ dib9000_write_word(state, 903 + offset, reg_903);
+ dib9000_write_word(state, 904 + offset, reg_904);
+ dib9000_write_word(state, 905 + offset, reg_905);
+ dib9000_write_word(state, 906 + offset, reg_906);
+}
+
+static int dib9000_fw_reset(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ dib9000_write_word(state, 1817, 0x0003);
+
+ dib9000_write_word(state, 1227, 1);
+ dib9000_write_word(state, 1227, 0);
+
+ switch ((state->revision = dib9000_identify(&state->i2c))) {
+ case 0x4003:
+ case 0x4004:
+ case 0x4005:
+ state->reg_offs = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* reset the i2c-master to use the host interface */
+ dibx000_reset_i2c_master(&state->i2c_master);
+
+ dib9000_set_power_mode(state, DIB9000_POWER_ALL);
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1));
+ dib9000_write_word(state, 1796, 0);
+ dib9000_write_word(state, 1805, 0x805);
+
+ /* restart all parts */
+ dib9000_write_word(state, 898, 0xffff);
+ dib9000_write_word(state, 899, 0xffff);
+ dib9000_write_word(state, 900, 0x0001);
+ dib9000_write_word(state, 901, 0xff19);
+ dib9000_write_word(state, 902, 0x003c);
+
+ dib9000_write_word(state, 898, 0);
+ dib9000_write_word(state, 899, 0);
+ dib9000_write_word(state, 900, 0);
+ dib9000_write_word(state, 901, 0);
+ dib9000_write_word(state, 902, 0);
+
+ dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives);
+
+ dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len)
+{
+ u16 mb[10];
+ u8 i, s;
+
+ if (address >= 1024 || !state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ /* dprintk( "APB access thru rd fw %d %x", address, attribute); */
+
+ mb[0] = (u16) address;
+ mb[1] = len / 2;
+ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute);
+ switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) {
+ case 1:
+ s--;
+ for (i = 0; i < s; i++) {
+ b[i * 2] = (mb[i + 1] >> 8) & 0xff;
+ b[i * 2 + 1] = (mb[i + 1]) & 0xff;
+ }
+ return 0;
+ default:
+ return -EIO;
+ }
+ return -EIO;
+}
+
+static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len)
+{
+ u16 mb[10];
+ u8 s, i;
+
+ if (address >= 1024 || !state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ /* dprintk( "APB access thru wr fw %d %x", address, attribute); */
+
+ mb[0] = (unsigned short)address;
+ for (i = 0; i < len && i < 20; i += 2)
+ mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]);
+
+ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute);
+ return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL;
+}
+
+static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i)
+{
+ u8 index_loop = 10;
+
+ if (!state->platform.risc.fw_is_running)
+ return 0;
+ dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i);
+ do {
+ dib9000_risc_mem_read(state, FE_MM_RW_SYNC, &i, 1);
+ } while (i && index_loop--);
+
+ if (index_loop > 0)
+ return 0;
+ return -EIO;
+}
+
+static int dib9000_fw_init(struct dib9000_state *state)
+{
+ struct dibGPIOFunction *f;
+ u16 b[40] = { 0 };
+ u8 i;
+ u8 size;
+
+ if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0)
+ return -EIO;
+
+ /* initialize the firmware */
+ for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) {
+ f = &state->chip.d9.cfg.gpio_function[i];
+ if (f->mask) {
+ switch (f->function) {
+ case BOARD_GPIO_FUNCTION_COMPONENT_ON:
+ b[0] = (u16) f->mask;
+ b[1] = (u16) f->direction;
+ b[2] = (u16) f->value;
+ break;
+ case BOARD_GPIO_FUNCTION_COMPONENT_OFF:
+ b[3] = (u16) f->mask;
+ b[4] = (u16) f->direction;
+ b[5] = (u16) f->value;
+ break;
+ }
+ }
+ }
+ if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0)
+ return -EIO;
+
+ /* subband */
+ b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */
+ for (i = 0; i < state->chip.d9.cfg.subband.size; i++) {
+ b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz;
+ b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask;
+ b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction;
+ b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value;
+ }
+ b[1 + i * 4] = 0; /* fe_id */
+ if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0)
+ return -EIO;
+
+ /* 0 - id, 1 - no_of_frontends */
+ b[0] = (0 << 8) | 1;
+ /* 0 = i2c-address demod, 0 = tuner */
+ b[1] = (0 << 8) | (0);
+ b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff);
+ b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff);
+ b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff);
+ b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff);
+ b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff);
+ b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff);
+ b[29] = state->chip.d9.cfg.if_drives;
+ if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0)
+ return -EIO;
+
+ if (size > ARRAY_SIZE(b)) {
+ dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size,
+ (int)ARRAY_SIZE(b));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ state->platform.risc.fe_mm[i / 2].addr = b[i + 0];
+ state->platform.risc.fe_mm[i / 2].size = b[i + 1];
+ }
+
+ return 0;
+}
+
+static void dib9000_fw_set_channel_head(struct dib9000_state *state, struct dvb_frontend_parameters *ch)
+{
+ u8 b[9];
+ u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000;
+ if (state->fe_id % 2)
+ freq += 101;
+
+ b[0] = (u8) ((freq >> 0) & 0xff);
+ b[1] = (u8) ((freq >> 8) & 0xff);
+ b[2] = (u8) ((freq >> 16) & 0xff);
+ b[3] = (u8) ((freq >> 24) & 0xff);
+ b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff);
+ b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff);
+ b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff);
+ b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff);
+ b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */
+ if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT)
+ b[8] |= 1;
+ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b);
+}
+
+static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_parameters *channel)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ struct dibDVBTChannel {
+ s8 spectrum_inversion;
+
+ s8 nfft;
+ s8 guard;
+ s8 constellation;
+
+ s8 hrch;
+ s8 alpha;
+ s8 code_rate_hp;
+ s8 code_rate_lp;
+ s8 select_hp;
+
+ s8 intlv_native;
+ };
+ struct dibDVBTChannel ch;
+ int ret = 0;
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ goto error;
+ ret = -EIO;
+ }
+
+ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, (u8 *) &ch, sizeof(struct dibDVBTChannel));
+
+ switch (ch.spectrum_inversion & 0x7) {
+ case 1:
+ state->fe[0]->dtv_property_cache.inversion = INVERSION_ON;
+ break;
+ case 0:
+ state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO;
+ break;
+ }
+ switch (ch.nfft) {
+ case 0:
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 2:
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case 1:
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
+ break;
+ }
+ switch (ch.guard) {
+ case 0:
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
+ break;
+ }
+ switch (ch.constellation) {
+ case 2:
+ state->fe[0]->dtv_property_cache.modulation = QAM_64;
+ break;
+ case 1:
+ state->fe[0]->dtv_property_cache.modulation = QAM_16;
+ break;
+ case 0:
+ state->fe[0]->dtv_property_cache.modulation = QPSK;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.modulation = QAM_AUTO;
+ break;
+ }
+ switch (ch.hrch) {
+ case 0:
+ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO;
+ break;
+ }
+ switch (ch.code_rate_hp) {
+ case 1:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2;
+ break;
+ case 2:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3;
+ break;
+ case 3:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4;
+ break;
+ case 5:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6;
+ break;
+ case 7:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO;
+ break;
+ }
+ switch (ch.code_rate_lp) {
+ case 1:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2;
+ break;
+ case 2:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3;
+ break;
+ case 3:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4;
+ break;
+ case 5:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6;
+ break;
+ case 7:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8;
+ break;
+ default:
+ case -1:
+ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO;
+ break;
+ }
+
+error:
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ return ret;
+}
+
+static int dib9000_fw_set_channel_union(struct dvb_frontend *fe, struct dvb_frontend_parameters *channel)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ struct dibDVBTChannel {
+ s8 spectrum_inversion;
+
+ s8 nfft;
+ s8 guard;
+ s8 constellation;
+
+ s8 hrch;
+ s8 alpha;
+ s8 code_rate_hp;
+ s8 code_rate_lp;
+ s8 select_hp;
+
+ s8 intlv_native;
+ };
+ struct dibDVBTChannel ch;
+
+ switch (state->fe[0]->dtv_property_cache.inversion) {
+ case INVERSION_ON:
+ ch.spectrum_inversion = 1;
+ break;
+ case INVERSION_OFF:
+ ch.spectrum_inversion = 0;
+ break;
+ default:
+ case INVERSION_AUTO:
+ ch.spectrum_inversion = -1;
+ break;
+ }
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ ch.nfft = 0;
+ break;
+ case TRANSMISSION_MODE_4K:
+ ch.nfft = 2;
+ break;
+ case TRANSMISSION_MODE_8K:
+ ch.nfft = 1;
+ break;
+ default:
+ case TRANSMISSION_MODE_AUTO:
+ ch.nfft = 1;
+ break;
+ }
+ switch (state->fe[0]->dtv_property_cache.guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ ch.guard = 0;
+ break;
+ case GUARD_INTERVAL_1_16:
+ ch.guard = 1;
+ break;
+ case GUARD_INTERVAL_1_8:
+ ch.guard = 2;
+ break;
+ case GUARD_INTERVAL_1_4:
+ ch.guard = 3;
+ break;
+ default:
+ case GUARD_INTERVAL_AUTO:
+ ch.guard = -1;
+ break;
+ }
+ switch (state->fe[0]->dtv_property_cache.modulation) {
+ case QAM_64:
+ ch.constellation = 2;
+ break;
+ case QAM_16:
+ ch.constellation = 1;
+ break;
+ case QPSK:
+ ch.constellation = 0;
+ break;
+ default:
+ case QAM_AUTO:
+ ch.constellation = -1;
+ break;
+ }
+ switch (state->fe[0]->dtv_property_cache.hierarchy) {
+ case HIERARCHY_NONE:
+ ch.hrch = 0;
+ break;
+ case HIERARCHY_1:
+ case HIERARCHY_2:
+ case HIERARCHY_4:
+ ch.hrch = 1;
+ break;
+ default:
+ case HIERARCHY_AUTO:
+ ch.hrch = -1;
+ break;
+ }
+ ch.alpha = 1;
+ switch (state->fe[0]->dtv_property_cache.code_rate_HP) {
+ case FEC_1_2:
+ ch.code_rate_hp = 1;
+ break;
+ case FEC_2_3:
+ ch.code_rate_hp = 2;
+ break;
+ case FEC_3_4:
+ ch.code_rate_hp = 3;
+ break;
+ case FEC_5_6:
+ ch.code_rate_hp = 5;
+ break;
+ case FEC_7_8:
+ ch.code_rate_hp = 7;
+ break;
+ default:
+ case FEC_AUTO:
+ ch.code_rate_hp = -1;
+ break;
+ }
+ switch (state->fe[0]->dtv_property_cache.code_rate_LP) {
+ case FEC_1_2:
+ ch.code_rate_lp = 1;
+ break;
+ case FEC_2_3:
+ ch.code_rate_lp = 2;
+ break;
+ case FEC_3_4:
+ ch.code_rate_lp = 3;
+ break;
+ case FEC_5_6:
+ ch.code_rate_lp = 5;
+ break;
+ case FEC_7_8:
+ ch.code_rate_lp = 7;
+ break;
+ default:
+ case FEC_AUTO:
+ ch.code_rate_lp = -1;
+ break;
+ }
+ ch.select_hp = 1;
+ ch.intlv_native = 1;
+
+ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch);
+
+ return 0;
+}
+
+static int dib9000_fw_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN;
+ s8 i;
+
+ switch (state->tune_state) {
+ case CT_DEMOD_START:
+ dib9000_fw_set_channel_head(state, ch);
+
+ /* write the channel context - a channel is initialized to 0, so it is OK */
+ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info);
+ dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info);
+
+ if (search)
+ dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0);
+ else {
+ dib9000_fw_set_channel_union(fe, ch);
+ dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0);
+ }
+ state->tune_state = CT_DEMOD_STEP_1;
+ break;
+ case CT_DEMOD_STEP_1:
+ if (search)
+ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, (u8 *) &i, 1);
+ else
+ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, (u8 *) &i, 1);
+ switch (i) { /* something happened */
+ case 0:
+ break;
+ case -2: /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */
+ if (search)
+ state->status = FE_STATUS_DEMOD_SUCCESS;
+ else {
+ state->tune_state = CT_DEMOD_STOP;
+ state->status = FE_STATUS_LOCKED;
+ }
+ break;
+ default:
+ state->status = FE_STATUS_TUNE_FAILED;
+ state->tune_state = CT_DEMOD_STOP;
+ break;
+ }
+ break;
+ default:
+ ret = FE_CALLBACK_TIME_NEVER;
+ break;
+ }
+
+ return ret;
+}
+
+static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 mode = (u16) onoff;
+ return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1);
+}
+
+static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 outreg, smo_mode;
+
+ dprintk("setting output mode for demod %p to %d", fe, mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK:
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL:
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */
+ break;
+ case OUTMODE_DIVERSITY:
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ break;
+ case OUTMODE_MPEG2_FIFO:
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z:
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]);
+ return -EINVAL;
+ }
+
+ dib9000_write_word(state, 1795, outreg);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ case OUTMODE_MPEG2_PAR_CONT_CLK:
+ case OUTMODE_MPEG2_SERIAL:
+ case OUTMODE_MPEG2_FIFO:
+ smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1);
+ if (state->chip.d9.cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5);
+ dib9000_write_word(state, 295, smo_mode);
+ break;
+ }
+
+ outreg = to_fw_output_mode(mode);
+ return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1);
+}
+
+static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
+ u16 i, len, t, index_msg;
+
+ for (index_msg = 0; index_msg < num; index_msg++) {
+ if (msg[index_msg].flags & I2C_M_RD) { /* read */
+ len = msg[index_msg].len;
+ if (len > 16)
+ len = 16;
+
+ if (dib9000_read_word(state, 790) != 0)
+ dprintk("TunerITF: read busy");
+
+ dib9000_write_word(state, 784, (u16) (msg[index_msg].addr));
+ dib9000_write_word(state, 787, (len / 2) - 1);
+ dib9000_write_word(state, 786, 1); /* start read */
+
+ i = 1000;
+ while (dib9000_read_word(state, 790) != (len / 2) && i)
+ i--;
+
+ if (i == 0)
+ dprintk("TunerITF: read failed");
+
+ for (i = 0; i < len; i += 2) {
+ t = dib9000_read_word(state, 785);
+ msg[index_msg].buf[i] = (t >> 8) & 0xff;
+ msg[index_msg].buf[i + 1] = (t) & 0xff;
+ }
+ if (dib9000_read_word(state, 790) != 0)
+ dprintk("TunerITF: read more data than expected");
+ } else {
+ i = 1000;
+ while (dib9000_read_word(state, 789) && i)
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: write busy");
+
+ len = msg[index_msg].len;
+ if (len > 16)
+ len = 16;
+
+ for (i = 0; i < len; i += 2)
+ dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]);
+ dib9000_write_word(state, 784, (u16) msg[index_msg].addr);
+ dib9000_write_word(state, 787, (len / 2) - 1);
+ dib9000_write_word(state, 786, 0); /* start write */
+
+ i = 1000;
+ while (dib9000_read_word(state, 791) > 0 && i)
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: write failed");
+ }
+ }
+ return num;
+}
+
+int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ state->component_bus_speed = speed;
+ return 0;
+}
+EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed);
+
+static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
+ u8 type = 0; /* I2C */
+ u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4;
+ u16 scl = state->component_bus_speed; /* SCL frequency */
+ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER];
+ u8 p[13] = { 0 };
+
+ p[0] = type;
+ p[1] = port;
+ p[2] = msg[0].addr << 1;
+
+ p[3] = (u8) scl & 0xff; /* scl */
+ p[4] = (u8) (scl >> 8);
+
+ p[7] = 0;
+ p[8] = 0;
+
+ p[9] = (u8) (msg[0].len);
+ p[10] = (u8) (msg[0].len >> 8);
+ if ((num > 1) && (msg[1].flags & I2C_M_RD)) {
+ p[11] = (u8) (msg[1].len);
+ p[12] = (u8) (msg[1].len >> 8);
+ } else {
+ p[11] = 0;
+ p[12] = 0;
+ }
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+
+ dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
+
+ { /* write-part */
+ dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0);
+ dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len);
+ }
+
+ /* do the transaction */
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ return 0;
+ }
+
+ /* read back any possible result */
+ if ((num > 1) && (msg[1].flags & I2C_M_RD))
+ dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len);
+
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+
+ return num;
+}
+
+static u32 dib9000_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dib9000_tuner_algo = {
+ .master_xfer = dib9000_tuner_xfer,
+ .functionality = dib9000_i2c_func,
+};
+
+static struct i2c_algorithm dib9000_component_bus_algo = {
+ .master_xfer = dib9000_fw_component_bus_xfer,
+ .functionality = dib9000_i2c_func,
+};
+
+struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe)
+{
+ struct dib9000_state *st = fe->demodulator_priv;
+ return &st->tuner_adap;
+}
+EXPORT_SYMBOL(dib9000_get_tuner_interface);
+
+struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe)
+{
+ struct dib9000_state *st = fe->demodulator_priv;
+ return &st->component_bus;
+}
+EXPORT_SYMBOL(dib9000_get_component_bus_interface);
+
+struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib9000_state *st = fe->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib9000_get_i2c_master);
+
+int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c)
+{
+ struct dib9000_state *st = fe->demodulator_priv;
+
+ st->i2c.i2c_adap = i2c;
+ return 0;
+}
+EXPORT_SYMBOL(dib9000_set_i2c_adapter);
+
+static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val)
+{
+ st->gpio_dir = dib9000_read_word(st, 773);
+ st->gpio_dir &= ~(1 << num); /* reset the direction bit */
+ st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */
+ dib9000_write_word(st, 773, st->gpio_dir);
+
+ st->gpio_val = dib9000_read_word(st, 774);
+ st->gpio_val &= ~(1 << num); /* reset the direction bit */
+ st->gpio_val |= (val & 0x01) << num; /* set the new value */
+ dib9000_write_word(st, 774, st->gpio_val);
+
+ dprintk("gpio dir: %04x: gpio val: %04x", st->gpio_dir, st->gpio_val);
+
+ return 0;
+}
+
+int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ return dib9000_cfg_gpio(state, num, dir, val);
+}
+EXPORT_SYMBOL(dib9000_set_gpio);
+
+int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 val = dib9000_read_word(state, 294 + 1) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+
+ dprintk("PID filter enabled %d", onoff);
+ return dib9000_write_word(state, 294 + 1, val);
+}
+EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl);
+
+int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib9000_fw_pid_filter);
+
+int dib9000_firmware_post_pll_init(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ return dib9000_fw_init(state);
+}
+EXPORT_SYMBOL(dib9000_firmware_post_pll_init);
+
+static void dib9000_release(struct dvb_frontend *demod)
+{
+ struct dib9000_state *st = demod->demodulator_priv;
+ u8 index_frontend;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
+ dvb_frontend_detach(st->fe[index_frontend]);
+
+ DibFreeLock(&state->platform.risc.mbx_if_lock);
+ DibFreeLock(&state->platform.risc.mbx_lock);
+ DibFreeLock(&state->platform.risc.mem_lock);
+ DibFreeLock(&state->platform.risc.mem_mbx_lock);
+ dibx000_exit_i2c_master(&st->i2c_master);
+
+ i2c_del_adapter(&st->tuner_adap);
+ i2c_del_adapter(&st->component_bus);
+ kfree(st->fe[0]);
+ kfree(st);
+}
+
+static int dib9000_wakeup(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int dib9000_sleep(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ int ret;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
+ if (ret < 0)
+ return ret;
+ }
+ return dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
+}
+
+static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend, sub_index_frontend;
+ fe_status_t stat;
+ int ret;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
+ if (stat & FE_HAS_SYNC) {
+ dprintk("TPS lock on the slave%i", index_frontend);
+
+ /* synchronize the cache with the other frontends */
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], fep);
+ for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL);
+ sub_index_frontend++) {
+ if (sub_index_frontend != index_frontend) {
+ state->fe[sub_index_frontend]->dtv_property_cache.modulation =
+ state->fe[index_frontend]->dtv_property_cache.modulation;
+ state->fe[sub_index_frontend]->dtv_property_cache.inversion =
+ state->fe[index_frontend]->dtv_property_cache.inversion;
+ state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode =
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode;
+ state->fe[sub_index_frontend]->dtv_property_cache.guard_interval =
+ state->fe[index_frontend]->dtv_property_cache.guard_interval;
+ state->fe[sub_index_frontend]->dtv_property_cache.hierarchy =
+ state->fe[index_frontend]->dtv_property_cache.hierarchy;
+ state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP =
+ state->fe[index_frontend]->dtv_property_cache.code_rate_HP;
+ state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP =
+ state->fe[index_frontend]->dtv_property_cache.code_rate_LP;
+ state->fe[sub_index_frontend]->dtv_property_cache.rolloff =
+ state->fe[index_frontend]->dtv_property_cache.rolloff;
+ }
+ }
+ return 0;
+ }
+ }
+
+ /* get the channel from master chip */
+ ret = dib9000_fw_get_channel(fe, fep);
+ if (ret != 0)
+ return ret;
+
+ /* synchronize the cache with the other frontends */
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation;
+ state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP;
+ state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff;
+ }
+
+ return 0;
+}
+
+static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ state->tune_state = tune_state;
+ if (tune_state == CT_DEMOD_START)
+ state->status = FE_STATUS_TUNE_PENDING;
+
+ return 0;
+}
+
+static u32 dib9000_get_status(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ return state->status;
+}
+
+static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext));
+ return 0;
+}
+
+static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ int sleep_time, sleep_time_slave;
+ u32 frontend_status;
+ u8 nbr_pending, exit_condition, index_frontend, index_frontend_success;
+ struct dvb_frontend_parametersContext channel_status;
+
+ /* check that the correct parameters are set */
+ if (state->fe[0]->dtv_property_cache.frequency == 0) {
+ dprintk("dib9000: must specify frequency ");
+ return 0;
+ }
+
+ if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) {
+ dprintk("dib9000: must specify bandwidth ");
+ return 0;
+ }
+ fe->dtv_property_cache.delivery_system = SYS_DVBT;
+
+ /* set the master status */
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || fep->u.ofdm.constellation == QAM_AUTO || fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ /* no channel specified, autosearch the channel */
+ state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN;
+ } else
+ state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
+
+ /* set mode and status for the different frontends */
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ dib9000_fw_set_diversity_in(state->fe[index_frontend], 1);
+
+ /* synchronization of the cache */
+ memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties));
+
+ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT;
+ dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z);
+
+ dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status);
+ dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
+ }
+
+ /* actual tune */
+ exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */
+ index_frontend_success = 0;
+ do {
+ sleep_time = dib9000_fw_tune(state->fe[0], NULL);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL);
+ if (sleep_time == FE_CALLBACK_TIME_NEVER)
+ sleep_time = sleep_time_slave;
+ else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
+ sleep_time = sleep_time_slave;
+ }
+ if (sleep_time != FE_CALLBACK_TIME_NEVER)
+ msleep(sleep_time / 10);
+ else
+ break;
+
+ nbr_pending = 0;
+ exit_condition = 0;
+ index_frontend_success = 0;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ frontend_status = -dib9000_get_status(state->fe[index_frontend]);
+ if (frontend_status > -FE_STATUS_TUNE_PENDING) {
+ exit_condition = 2; /* tune success */
+ index_frontend_success = index_frontend;
+ break;
+ }
+ if (frontend_status == -FE_STATUS_TUNE_PENDING)
+ nbr_pending++; /* some frontends are still tuning */
+ }
+ if ((exit_condition != 2) && (nbr_pending == 0))
+ exit_condition = 1; /* if all tune are done and no success, exit: tune failed */
+
+ } while (exit_condition == 0);
+
+ /* check the tune result */
+ if (exit_condition == 1) { /* tune failed */
+ dprintk("tune failed");
+ return 0;
+ }
+
+ dprintk("tune success on frontend%i", index_frontend_success);
+
+ /* synchronize all the channel cache */
+ dib9000_get_frontend(state->fe[0], fep);
+
+ /* retune the other frontends with the found channel */
+ channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ /* only retune the frontends which was not tuned success */
+ if (index_frontend != index_frontend_success) {
+ dib9000_set_channel_status(state->fe[index_frontend], &channel_status);
+ dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
+ }
+ }
+ do {
+ sleep_time = FE_CALLBACK_TIME_NEVER;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (index_frontend != index_frontend_success) {
+ sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend], NULL);
+ if (sleep_time == FE_CALLBACK_TIME_NEVER)
+ sleep_time = sleep_time_slave;
+ else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
+ sleep_time = sleep_time_slave;
+ }
+ }
+ if (sleep_time != FE_CALLBACK_TIME_NEVER)
+ msleep(sleep_time / 10);
+ else
+ break;
+
+ nbr_pending = 0;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (index_frontend != index_frontend_success) {
+ frontend_status = -dib9000_get_status(state->fe[index_frontend]);
+ if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING))
+ nbr_pending++; /* some frontends are still tuning */
+ }
+ }
+ } while (nbr_pending != 0);
+
+ /* set the output mode */
+ dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY);
+
+ /* turn off the diversity for the last frontend */
+ dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0);
+
+ return 0;
+}
+
+static u16 dib9000_read_lock(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ return dib9000_read_word(state, 535);
+}
+
+static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u16 lock = 0, lock_slave = 0;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
+
+ lock = dib9000_read_word(state, 535);
+
+ *stat = 0;
+
+ if ((lock & 0x8000) || (lock_slave & 0x8000))
+ *stat |= FE_HAS_SIGNAL;
+ if ((lock & 0x3000) || (lock_slave & 0x3000))
+ *stat |= FE_HAS_CARRIER;
+ if ((lock & 0x0100) || (lock_slave & 0x0100))
+ *stat |= FE_HAS_VITERBI;
+ if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38))
+ *stat |= FE_HAS_SYNC;
+ if ((lock & 0x0008) || (lock_slave & 0x0008))
+ *stat |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 c[16];
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
+ return -EIO;
+ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c));
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+
+ *ber = c[10] << 16 | c[11];
+ return 0;
+}
+
+static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u16 c[16];
+ u16 val;
+
+ *strength = 0;
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
+ }
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
+ return -EIO;
+ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c));
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+
+ val = 65535 - c[4];
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
+ return 0;
+}
+
+static u32 dib9000_get_snr(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 c[16];
+ u32 n, s, exp;
+ u16 val;
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
+ return -EIO;
+ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c));
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+
+ val = c[7];
+ n = (val >> 4) & 0xff;
+ exp = ((val & 0xf) << 2);
+ val = c[8];
+ exp += ((val >> 14) & 0x3);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ n <<= exp + 16;
+
+ s = (val >> 6) & 0xFF;
+ exp = (val & 0x3F);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ s <<= exp + 16;
+
+ if (n > 0) {
+ u32 t = (s / n) << 16;
+ return t + ((s << 16) - n * t) / n;
+ }
+ return 0xffffffff;
+}
+
+static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u32 snr_master;
+
+ snr_master = dib9000_get_snr(fe);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ snr_master += dib9000_get_snr(state->fe[index_frontend]);
+
+ if ((snr_master >> 16) != 0) {
+ snr_master = 10 * intlog10(snr_master >> 16);
+ *snr = snr_master / ((1 << 24) / 10);
+ } else
+ *snr = 0;
+
+ return 0;
+}
+
+static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u16 c[16];
+
+ DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
+ return -EIO;
+ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, sizeof(c));
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+
+ *unc = c[12];
+ return 0;
+}
+
+int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr)
+{
+ int k = 0;
+ u8 new_addr = 0;
+ struct i2c_device client = {.i2c_adap = i2c };
+
+ client.i2c_addr = default_addr + 16;
+ dib9000_i2c_write16(&client, 1796, 0x0);
+
+ for (k = no_of_demods - 1; k >= 0; k--) {
+ /* designated i2c address */
+ new_addr = first_addr + (k << 1);
+ client.i2c_addr = default_addr;
+
+ dib9000_i2c_write16(&client, 1817, 3);
+ dib9000_i2c_write16(&client, 1796, 0);
+ dib9000_i2c_write16(&client, 1227, 1);
+ dib9000_i2c_write16(&client, 1227, 0);
+
+ client.i2c_addr = new_addr;
+ dib9000_i2c_write16(&client, 1817, 3);
+ dib9000_i2c_write16(&client, 1796, 0);
+ dib9000_i2c_write16(&client, 1227, 1);
+ dib9000_i2c_write16(&client, 1227, 0);
+
+ if (dib9000_identify(&client) == 0) {
+ client.i2c_addr = default_addr;
+ if (dib9000_identify(&client) == 0) {
+ dprintk("DiB9000 #%d: not identified", k);
+ return -EIO;
+ }
+ }
+
+ dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6));
+ dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ new_addr = first_addr | (k << 1);
+ client.i2c_addr = new_addr;
+
+ dib9000_i2c_write16(&client, 1794, (new_addr << 2));
+ dib9000_i2c_write16(&client, 1795, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dib9000_i2c_enumeration);
+
+int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend = 1;
+
+ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
+ index_frontend++;
+ if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
+ dprintk("set slave fe %p to index %i", fe_slave, index_frontend);
+ state->fe[index_frontend] = fe_slave;
+ return 0;
+ }
+
+ dprintk("too many slave frontend");
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(dib9000_set_slave_frontend);
+
+int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+ u8 index_frontend = 1;
+
+ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
+ index_frontend++;
+ if (index_frontend != 1) {
+ dprintk("remove slave fe %p (index %i)", state->fe[index_frontend - 1], index_frontend - 1);
+ state->fe[index_frontend] = NULL;
+ return 0;
+ }
+
+ dprintk("no frontend to be removed");
+ return -ENODEV;
+}
+EXPORT_SYMBOL(dib9000_remove_slave_frontend);
+
+struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ if (slave_index >= MAX_NUMBER_OF_FRONTENDS)
+ return NULL;
+ return state->fe[slave_index];
+}
+EXPORT_SYMBOL(dib9000_get_slave_frontend);
+
+static struct dvb_frontend_ops dib9000_ops;
+struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg)
+{
+ struct dvb_frontend *fe;
+ struct dib9000_state *st;
+ st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+ fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ return NULL;
+
+ memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config));
+ st->i2c.i2c_adap = i2c_adap;
+ st->i2c.i2c_addr = i2c_addr;
+
+ st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS;
+ st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES;
+ st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS;
+
+ DibInitLock(&st->platform.risc.mbx_if_lock);
+ DibInitLock(&st->platform.risc.mbx_lock);
+ DibInitLock(&st->platform.risc.mem_lock);
+ DibInitLock(&st->platform.risc.mem_mbx_lock);
+
+ st->fe[0] = fe;
+ fe->demodulator_priv = st;
+ memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops));
+
+ /* Ensure the output mode remains at the previous default if it's
+ * not specifically set by the caller.
+ */
+ if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
+ st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO;
+
+ if (dib9000_identify(&st->i2c) == 0)
+ goto error;
+
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr);
+
+ st->tuner_adap.dev.parent = i2c_adap->dev.parent;
+ strncpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", sizeof(st->tuner_adap.name));
+ st->tuner_adap.algo = &dib9000_tuner_algo;
+ st->tuner_adap.algo_data = NULL;
+ i2c_set_adapdata(&st->tuner_adap, st);
+ if (i2c_add_adapter(&st->tuner_adap) < 0)
+ goto error;
+
+ st->component_bus.dev.parent = i2c_adap->dev.parent;
+ strncpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", sizeof(st->component_bus.name));
+ st->component_bus.algo = &dib9000_component_bus_algo;
+ st->component_bus.algo_data = NULL;
+ st->component_bus_speed = 340;
+ i2c_set_adapdata(&st->component_bus, st);
+ if (i2c_add_adapter(&st->component_bus) < 0)
+ goto component_bus_add_error;
+
+ dib9000_fw_reset(fe);
+
+ return fe;
+
+component_bus_add_error:
+ i2c_del_adapter(&st->tuner_adap);
+error:
+ kfree(st);
+ return NULL;
+}
+EXPORT_SYMBOL(dib9000_attach);
+
+static struct dvb_frontend_ops dib9000_ops = {
+ .info = {
+ .name = "DiBcom 9000",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .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 = dib9000_release,
+
+ .init = dib9000_wakeup,
+ .sleep = dib9000_sleep,
+
+ .set_frontend = dib9000_set_frontend,
+ .get_tune_settings = dib9000_fe_get_tune_settings,
+ .get_frontend = dib9000_get_frontend,
+
+ .read_status = dib9000_read_status,
+ .read_ber = dib9000_read_ber,
+ .read_signal_strength = dib9000_read_signal_strength,
+ .read_snr = dib9000_read_snr,
+ .read_ucblocks = dib9000_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb/frontends/dib9000.h
new file mode 100644
index 000000000000..b5781a48034c
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib9000.h
@@ -0,0 +1,131 @@
+#ifndef DIB9000_H
+#define DIB9000_H
+
+#include "dibx000_common.h"
+
+struct dib9000_config {
+ u8 dvbt_mode;
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ struct dibx000_bandwidth_config *bw;
+
+ u16 if_drives;
+
+ u32 timing_frequency;
+ u32 xtal_clock_khz;
+ u32 vcxo_timer;
+ u32 demod_clock_khz;
+
+ const u8 *microcode_B_fe_buffer;
+ u32 microcode_B_fe_size;
+
+ struct dibGPIOFunction gpio_function[2];
+ struct dibSubbandSelection subband;
+
+ u8 output_mode;
+};
+
+#define DEFAULT_DIB9000_I2C_ADDRESS 18
+
+#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg);
+extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr);
+extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe);
+extern struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating);
+extern int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val);
+extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff);
+extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff);
+extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe);
+extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave);
+extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe);
+extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index);
+extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe);
+extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c);
+extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed);
+#else
+static inline struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib9000_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib9000_firmware_post_pll_init(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index 2311c0a3406c..f6938f97feb4 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -17,9 +17,145 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
struct i2c_msg msg = {
.addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4
};
+
return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
}
+static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
+{
+ u8 wb[2] = { reg >> 8, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ {.addr = mst->i2c_addr, .flags = 0, .buf = wb, .len = 2},
+ {.addr = mst->i2c_addr, .flags = I2C_M_RD, .buf = rb, .len = 2},
+ };
+
+ if (i2c_transfer(mst->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d", reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst)
+{
+ int i = 100;
+ u16 status;
+
+ while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0)
+ ;
+
+ /* i2c timed out */
+ if (i == 0)
+ return -EREMOTEIO;
+
+ /* no acknowledge */
+ if ((status & 0x0080) == 0)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_msg *msg, u8 stop)
+{
+ u16 data;
+ u16 da;
+ u16 i;
+ u16 txlen = msg->len, len;
+ const u8 *b = msg->buf;
+
+ while (txlen) {
+ dibx000_read_word(mst, mst->base_reg + 2);
+
+ len = txlen > 8 ? 8 : txlen;
+ for (i = 0; i < len; i += 2) {
+ data = *b++ << 8;
+ if (i+1 < len)
+ data |= *b++;
+ dibx000_write_word(mst, mst->base_reg, data);
+ }
+ da = (((u8) (msg->addr)) << 9) |
+ (1 << 8) |
+ (1 << 7) |
+ (0 << 6) |
+ (0 << 5) |
+ ((len & 0x7) << 2) |
+ (0 << 1) |
+ (0 << 0);
+
+ if (txlen == msg->len)
+ da |= 1 << 5; /* start */
+
+ if (txlen-len == 0 && stop)
+ da |= 1 << 6; /* stop */
+
+ dibx000_write_word(mst, mst->base_reg+1, da);
+
+ if (dibx000_is_i2c_done(mst) != 0)
+ return -EREMOTEIO;
+ txlen -= len;
+ }
+
+ return 0;
+}
+
+static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_msg *msg)
+{
+ u16 da;
+ u8 *b = msg->buf;
+ u16 rxlen = msg->len, len;
+
+ while (rxlen) {
+ len = rxlen > 8 ? 8 : rxlen;
+ da = (((u8) (msg->addr)) << 9) |
+ (1 << 8) |
+ (1 << 7) |
+ (0 << 6) |
+ (0 << 5) |
+ ((len & 0x7) << 2) |
+ (1 << 1) |
+ (0 << 0);
+
+ if (rxlen == msg->len)
+ da |= 1 << 5; /* start */
+
+ if (rxlen-len == 0)
+ da |= 1 << 6; /* stop */
+ dibx000_write_word(mst, mst->base_reg+1, da);
+
+ if (dibx000_is_i2c_done(mst) != 0)
+ return -EREMOTEIO;
+
+ rxlen -= len;
+
+ while (len) {
+ da = dibx000_read_word(mst, mst->base_reg);
+ *b++ = (da >> 8) & 0xff;
+ len--;
+ if (len >= 1) {
+ *b++ = da & 0xff;
+ len--;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed)
+{
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+
+ if (mst->device_rev < DIB7000MC && speed < 235)
+ speed = 235;
+ return dibx000_write_word(mst, mst->base_reg + 3, (u16)(60000 / speed));
+
+}
+EXPORT_SYMBOL(dibx000_i2c_set_speed);
+
+static u32 dibx000_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst,
enum dibx000_i2c_interface intf)
@@ -32,6 +168,60 @@ static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst,
return 0;
}
+static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int msg_index;
+ int ret = 0;
+
+ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2);
+ for (msg_index = 0; msg_index < num; msg_index++) {
+ if (msg[msg_index].flags & I2C_M_RD) {
+ ret = dibx000_master_i2c_read(mst, &msg[msg_index]);
+ if (ret != 0)
+ return 0;
+ } else {
+ ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1);
+ if (ret != 0)
+ return 0;
+ }
+ }
+
+ return num;
+}
+
+static int dibx000_i2c_master_xfer_gpio34(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ int msg_index;
+ int ret = 0;
+
+ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_3_4);
+ for (msg_index = 0; msg_index < num; msg_index++) {
+ if (msg[msg_index].flags & I2C_M_RD) {
+ ret = dibx000_master_i2c_read(mst, &msg[msg_index]);
+ if (ret != 0)
+ return 0;
+ } else {
+ ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1);
+ if (ret != 0)
+ return 0;
+ }
+ }
+
+ return num;
+}
+
+static struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = {
+ .master_xfer = dibx000_i2c_master_xfer_gpio12,
+ .functionality = dibx000_i2c_func,
+};
+
+static struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = {
+ .master_xfer = dibx000_i2c_master_xfer_gpio34,
+ .functionality = dibx000_i2c_func,
+};
+
static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4],
u8 addr, int onoff)
{
@@ -54,11 +244,37 @@ static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4],
return 0;
}
-static u32 dibx000_i2c_func(struct i2c_adapter *adapter)
+static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
{
- return I2C_FUNC_I2C;
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ struct i2c_msg m[2 + num];
+ u8 tx_open[4], tx_close[4];
+
+ memset(m, 0, sizeof(struct i2c_msg) * (2 + num));
+
+ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7);
+
+ dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1);
+ m[0].addr = mst->i2c_addr;
+ m[0].buf = tx_open;
+ m[0].len = 4;
+
+ memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
+
+ dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0);
+ m[num + 1].addr = mst->i2c_addr;
+ m[num + 1].buf = tx_close;
+ m[num + 1].len = 4;
+
+ return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO;
}
+static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = {
+ .master_xfer = dibx000_i2c_gated_gpio67_xfer,
+ .functionality = dibx000_i2c_func,
+};
+
static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
@@ -91,8 +307,8 @@ static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
};
struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst,
- enum dibx000_i2c_interface intf,
- int gating)
+ enum dibx000_i2c_interface intf,
+ int gating)
{
struct i2c_adapter *i2c = NULL;
@@ -101,6 +317,18 @@ struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst,
if (gating)
i2c = &mst->gated_tuner_i2c_adap;
break;
+ case DIBX000_I2C_INTERFACE_GPIO_1_2:
+ if (!gating)
+ i2c = &mst->master_i2c_adap_gpio12;
+ break;
+ case DIBX000_I2C_INTERFACE_GPIO_3_4:
+ if (!gating)
+ i2c = &mst->master_i2c_adap_gpio34;
+ break;
+ case DIBX000_I2C_INTERFACE_GPIO_6_7:
+ if (gating)
+ i2c = &mst->master_i2c_adap_gpio67;
+ break;
default:
printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
break;
@@ -126,8 +354,8 @@ void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst)
EXPORT_SYMBOL(dibx000_reset_i2c_master);
static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
- struct i2c_algorithm *algo, const char *name,
- struct dibx000_i2c_master *mst)
+ struct i2c_algorithm *algo, const char *name,
+ struct dibx000_i2c_master *mst)
{
strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
i2c_adap->algo = algo;
@@ -139,7 +367,7 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
}
int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
- struct i2c_adapter *i2c_adap, u8 i2c_addr)
+ struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
u8 tx[4];
struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 };
@@ -153,11 +381,33 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
else
mst->base_reg = 768;
+ mst->gated_tuner_i2c_adap.dev.parent = mst->i2c_adap->dev.parent;
+ if (i2c_adapter_init
+ (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo,
+ "DiBX000 tuner I2C bus", mst) != 0)
+ printk(KERN_ERR
+ "DiBX000: could not initialize the tuner i2c_adapter\n");
+
+ mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent;
+ if (i2c_adapter_init
+ (&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo,
+ "DiBX000 master GPIO12 I2C bus", mst) != 0)
+ printk(KERN_ERR
+ "DiBX000: could not initialize the master i2c_adapter\n");
+
+ mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent;
+ if (i2c_adapter_init
+ (&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo,
+ "DiBX000 master GPIO34 I2C bus", mst) != 0)
+ printk(KERN_ERR
+ "DiBX000: could not initialize the master i2c_adapter\n");
+
+ mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent;
if (i2c_adapter_init
- (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo,
- "DiBX000 tuner I2C bus", mst) != 0)
+ (&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo,
+ "DiBX000 master GPIO67 I2C bus", mst) != 0)
printk(KERN_ERR
- "DiBX000: could not initialize the tuner i2c_adapter\n");
+ "DiBX000: could not initialize the master i2c_adapter\n");
/* initialize the i2c-master by closing the gate */
dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
@@ -170,16 +420,19 @@ EXPORT_SYMBOL(dibx000_init_i2c_master);
void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
{
i2c_del_adapter(&mst->gated_tuner_i2c_adap);
+ i2c_del_adapter(&mst->master_i2c_adap_gpio12);
+ i2c_del_adapter(&mst->master_i2c_adap_gpio34);
+ i2c_del_adapter(&mst->master_i2c_adap_gpio67);
}
EXPORT_SYMBOL(dibx000_exit_i2c_master);
u32 systime(void)
{
- struct timespec t;
+ struct timespec t;
- t = current_kernel_time();
- return (t.tv_sec * 10000) + (t.tv_nsec / 100000);
+ t = current_kernel_time();
+ return (t.tv_sec * 10000) + (t.tv_nsec / 100000);
}
EXPORT_SYMBOL(systime);
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index 4f5d141a308d..977d343369aa 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -4,7 +4,8 @@
enum dibx000_i2c_interface {
DIBX000_I2C_INTERFACE_TUNER = 0,
DIBX000_I2C_INTERFACE_GPIO_1_2 = 1,
- DIBX000_I2C_INTERFACE_GPIO_3_4 = 2
+ DIBX000_I2C_INTERFACE_GPIO_3_4 = 2,
+ DIBX000_I2C_INTERFACE_GPIO_6_7 = 3
};
struct dibx000_i2c_master {
@@ -17,8 +18,11 @@ struct dibx000_i2c_master {
enum dibx000_i2c_interface selected_interface;
-// struct i2c_adapter tuner_i2c_adap;
+/* struct i2c_adapter tuner_i2c_adap; */
struct i2c_adapter gated_tuner_i2c_adap;
+ struct i2c_adapter master_i2c_adap_gpio12;
+ struct i2c_adapter master_i2c_adap_gpio34;
+ struct i2c_adapter master_i2c_adap_gpio67;
struct i2c_adapter *i2c_adap;
u8 i2c_addr;
@@ -27,14 +31,15 @@ struct dibx000_i2c_master {
};
extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst,
- u16 device_rev, struct i2c_adapter *i2c_adap,
- u8 i2c_addr);
+ u16 device_rev, struct i2c_adapter *i2c_adap,
+ u8 i2c_addr);
extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master
- *mst,
- enum dibx000_i2c_interface
- intf, int gating);
+ *mst,
+ enum dibx000_i2c_interface
+ intf, int gating);
extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst);
+extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed);
extern u32 systime(void);
@@ -42,7 +47,7 @@ extern u32 systime(void);
#define BAND_UHF 0x02
#define BAND_VHF 0x04
#define BAND_SBAND 0x08
-#define BAND_FM 0x10
+#define BAND_FM 0x10
#define BAND_CBAND 0x20
#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \
@@ -135,9 +140,9 @@ enum dibx000_adc_states {
DIBX000_VBG_DISABLE,
};
-#define BANDWIDTH_TO_KHZ(v) ( (v) == BANDWIDTH_8_MHZ ? 8000 : \
- (v) == BANDWIDTH_7_MHZ ? 7000 : \
- (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 )
+#define BANDWIDTH_TO_KHZ(v) ((v) == BANDWIDTH_8_MHZ ? 8000 : \
+ (v) == BANDWIDTH_7_MHZ ? 7000 : \
+ (v) == BANDWIDTH_6_MHZ ? 6000 : 8000)
#define BANDWIDTH_TO_INDEX(v) ( \
(v) == 8000 ? BANDWIDTH_8_MHZ : \
@@ -153,53 +158,57 @@ enum dibx000_adc_states {
#define OUTMODE_MPEG2_FIFO 5
#define OUTMODE_ANALOG_ADC 6
+#define INPUT_MODE_OFF 0x11
+#define INPUT_MODE_DIVERSITY 0x12
+#define INPUT_MODE_MPEG 0x13
+
enum frontend_tune_state {
- CT_TUNER_START = 10,
- CT_TUNER_STEP_0,
- CT_TUNER_STEP_1,
- CT_TUNER_STEP_2,
- CT_TUNER_STEP_3,
- CT_TUNER_STEP_4,
- CT_TUNER_STEP_5,
- CT_TUNER_STEP_6,
- CT_TUNER_STEP_7,
- CT_TUNER_STOP,
-
- CT_AGC_START = 20,
- CT_AGC_STEP_0,
- CT_AGC_STEP_1,
- CT_AGC_STEP_2,
- CT_AGC_STEP_3,
- CT_AGC_STEP_4,
- CT_AGC_STOP,
+ CT_TUNER_START = 10,
+ CT_TUNER_STEP_0,
+ CT_TUNER_STEP_1,
+ CT_TUNER_STEP_2,
+ CT_TUNER_STEP_3,
+ CT_TUNER_STEP_4,
+ CT_TUNER_STEP_5,
+ CT_TUNER_STEP_6,
+ CT_TUNER_STEP_7,
+ CT_TUNER_STOP,
+
+ CT_AGC_START = 20,
+ CT_AGC_STEP_0,
+ CT_AGC_STEP_1,
+ CT_AGC_STEP_2,
+ CT_AGC_STEP_3,
+ CT_AGC_STEP_4,
+ CT_AGC_STOP,
CT_DEMOD_START = 30,
- CT_DEMOD_STEP_1,
- CT_DEMOD_STEP_2,
- CT_DEMOD_STEP_3,
- CT_DEMOD_STEP_4,
- CT_DEMOD_STEP_5,
- CT_DEMOD_STEP_6,
- CT_DEMOD_STEP_7,
- CT_DEMOD_STEP_8,
- CT_DEMOD_STEP_9,
- CT_DEMOD_STEP_10,
- CT_DEMOD_SEARCH_NEXT = 41,
- CT_DEMOD_STEP_LOCKED,
- CT_DEMOD_STOP,
-
- CT_DONE = 100,
- CT_SHUTDOWN,
+ CT_DEMOD_STEP_1,
+ CT_DEMOD_STEP_2,
+ CT_DEMOD_STEP_3,
+ CT_DEMOD_STEP_4,
+ CT_DEMOD_STEP_5,
+ CT_DEMOD_STEP_6,
+ CT_DEMOD_STEP_7,
+ CT_DEMOD_STEP_8,
+ CT_DEMOD_STEP_9,
+ CT_DEMOD_STEP_10,
+ CT_DEMOD_SEARCH_NEXT = 41,
+ CT_DEMOD_STEP_LOCKED,
+ CT_DEMOD_STOP,
+
+ CT_DONE = 100,
+ CT_SHUTDOWN,
};
struct dvb_frontend_parametersContext {
#define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01
#define CHANNEL_STATUS_PARAMETERS_SET 0x02
- u8 status;
- u32 tune_time_estimation[2];
- s32 tps_available;
- u16 tps[9];
+ u8 status;
+ u32 tune_time_estimation[2];
+ s32 tps_available;
+ u16 tps[9];
};
#define FE_STATUS_TUNE_FAILED 0
@@ -216,4 +225,49 @@ struct dvb_frontend_parametersContext {
#define ABS(x) ((x < 0) ? (-x) : (x))
+#define DATA_BUS_ACCESS_MODE_8BIT 0x01
+#define DATA_BUS_ACCESS_MODE_16BIT 0x02
+#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10
+
+struct dibGPIOFunction {
+#define BOARD_GPIO_COMPONENT_BUS_ADAPTER 1
+#define BOARD_GPIO_COMPONENT_DEMOD 2
+ u8 component;
+
+#define BOARD_GPIO_FUNCTION_BOARD_ON 1
+#define BOARD_GPIO_FUNCTION_BOARD_OFF 2
+#define BOARD_GPIO_FUNCTION_COMPONENT_ON 3
+#define BOARD_GPIO_FUNCTION_COMPONENT_OFF 4
+#define BOARD_GPIO_FUNCTION_SUBBAND_PWM 5
+#define BOARD_GPIO_FUNCTION_SUBBAND_GPIO 6
+ u8 function;
+
+/* mask, direction and value are used specify which GPIO to change GPIO0
+ * is LSB and possible GPIO31 is MSB. The same bit-position as in the
+ * mask is used for the direction and the value. Direction == 1 is OUT,
+ * 0 == IN. For direction "OUT" value is either 1 or 0, for direction IN
+ * value has no meaning.
+ *
+ * In case of BOARD_GPIO_FUNCTION_PWM mask is giving the GPIO to be
+ * used to do the PWM. Direction gives the PWModulator to be used.
+ * Value gives the PWM value in device-dependent scale.
+ */
+ u32 mask;
+ u32 direction;
+ u32 value;
+};
+
+#define MAX_NB_SUBBANDS 8
+struct dibSubbandSelection {
+ u8 size; /* Actual number of subbands. */
+ struct {
+ u16 f_mhz;
+ struct dibGPIOFunction gpio;
+ } subband[MAX_NB_SUBBANDS];
+};
+
+#define DEMOD_TIMF_SET 0x00
+#define DEMOD_TIMF_GET 0x01
+#define DEMOD_TIMF_UPDATE 0x02
+
#endif
diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c
index fc61d9230db8..90bf573308b0 100644
--- a/drivers/media/dvb/frontends/ds3000.c
+++ b/drivers/media/dvb/frontends/ds3000.c
@@ -229,31 +229,11 @@ static u8 ds3000_dvbs2_init_tab[] = {
0xb8, 0x00,
};
-/* DS3000 doesn't need some parameters as input and auto-detects them */
-/* save input from the application of those parameters */
-struct ds3000_tuning {
- u32 frequency;
- u32 symbol_rate;
- fe_spectral_inversion_t inversion;
- enum fe_code_rate fec;
-
- /* input values */
- u8 inversion_val;
- fe_modulation_t delivery;
- u8 rolloff;
-};
-
struct ds3000_state {
struct i2c_adapter *i2c;
const struct ds3000_config *config;
-
struct dvb_frontend frontend;
-
- struct ds3000_tuning dcur;
- struct ds3000_tuning dnxt;
-
u8 skip_fw_load;
-
/* previous uncorrected block counter for DVB-S2 */
u16 prevUCBS2;
};
@@ -305,7 +285,7 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg,
struct i2c_msg msg;
u8 *buf;
- buf = kmalloc(3, GFP_KERNEL);
+ buf = kmalloc(33, GFP_KERNEL);
if (buf == NULL) {
printk(KERN_ERR "Unable to kmalloc\n");
ret = -ENOMEM;
@@ -317,10 +297,10 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg,
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.buf = buf;
- msg.len = 3;
+ msg.len = 33;
- for (i = 0; i < len; i += 2) {
- memcpy(buf + 1, data + i, 2);
+ for (i = 0; i < len; i += 32) {
+ memcpy(buf + 1, data + i, 32);
dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len);
@@ -401,45 +381,6 @@ static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg)
return b1[0];
}
-static int ds3000_set_inversion(struct ds3000_state *state,
- fe_spectral_inversion_t inversion)
-{
- dprintk("%s(%d)\n", __func__, inversion);
-
- switch (inversion) {
- case INVERSION_OFF:
- case INVERSION_ON:
- case INVERSION_AUTO:
- break;
- default:
- return -EINVAL;
- }
-
- state->dnxt.inversion = inversion;
-
- return 0;
-}
-
-static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate)
-{
- int ret = 0;
-
- dprintk("%s()\n", __func__);
-
- dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate);
-
- /* check if symbol rate is within limits */
- if ((state->dnxt.symbol_rate >
- state->frontend.ops.info.symbol_rate_max) ||
- (state->dnxt.symbol_rate <
- state->frontend.ops.info.symbol_rate_min))
- ret = -EOPNOTSUPP;
-
- state->dnxt.symbol_rate = rate;
-
- return ret;
-}
-
static int ds3000_load_firmware(struct dvb_frontend *fe,
const struct firmware *fw);
@@ -509,23 +450,31 @@ static int ds3000_load_firmware(struct dvb_frontend *fe,
return 0;
}
-static void ds3000_dump_registers(struct dvb_frontend *fe)
+static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
struct ds3000_state *state = fe->demodulator_priv;
- int x, y, reg = 0, val;
-
- for (y = 0; y < 16; y++) {
- dprintk("%s: %02x: ", __func__, y);
- for (x = 0; x < 16; x++) {
- reg = (y << 4) + x;
- val = ds3000_readreg(state, reg);
- if (x != 15)
- dprintk("%02x ", val);
- else
- dprintk("%02x\n", val);
- }
+ u8 data;
+
+ dprintk("%s(%d)\n", __func__, voltage);
+
+ data = ds3000_readreg(state, 0xa2);
+ data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+ switch (voltage) {
+ case SEC_VOLTAGE_18:
+ data &= ~0x03;
+ break;
+ case SEC_VOLTAGE_13:
+ data &= ~0x03;
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
}
- dprintk("%s: -- DS3000 DUMP DONE --\n", __func__);
+
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
}
static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status)
@@ -562,16 +511,6 @@ static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status)
return 0;
}
-#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK)
-static int ds3000_is_tuned(struct dvb_frontend *fe)
-{
- fe_status_t tunerstat;
-
- ds3000_read_status(fe, &tunerstat);
-
- return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED);
-}
-
/* read DS3000 BER value */
static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
{
@@ -792,13 +731,6 @@ static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
return 0;
}
-/* Overwrite the current tuning params, we are about to tune */
-static void ds3000_clone_params(struct dvb_frontend *fe)
-{
- struct ds3000_state *state = fe->demodulator_priv;
- memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
-}
-
static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
{
struct ds3000_state *state = fe->demodulator_priv;
@@ -1016,287 +948,298 @@ static int ds3000_get_property(struct dvb_frontend *fe,
return 0;
}
-static int ds3000_tune(struct dvb_frontend *fe,
+static int ds3000_set_carrier_offset(struct dvb_frontend *fe,
+ s32 carrier_offset_khz)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ s32 tmp;
+
+ tmp = carrier_offset_khz;
+ tmp *= 65536;
+ tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE);
+
+ if (tmp < 0)
+ tmp += 65536;
+
+ ds3000_writereg(state, 0x5f, tmp >> 8);
+ ds3000_writereg(state, 0x5e, tmp & 0xff);
+
+ return 0;
+}
+
+static int ds3000_set_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
struct ds3000_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret = 0, retune, i;
- u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf;
+ int i;
+ fe_status_t status;
+ u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4;
+ s32 offset_khz;
u16 value, ndiv;
u32 f3db;
dprintk("%s() ", __func__);
- /* Load the firmware if required */
- ret = ds3000_firmware_ondemand(fe);
- if (ret != 0) {
- printk(KERN_ERR "%s: Unable initialise the firmware\n",
- __func__);
- return ret;
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+ /* Tune */
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x07, 0x02);
+ ds3000_tuner_writereg(state, 0x10, 0x00);
+ ds3000_tuner_writereg(state, 0x60, 0x79);
+ ds3000_tuner_writereg(state, 0x08, 0x01);
+ ds3000_tuner_writereg(state, 0x00, 0x01);
+ div4 = 0;
+
+ /* calculate and set freq divider */
+ if (p->frequency < 1146000) {
+ ds3000_tuner_writereg(state, 0x10, 0x11);
+ div4 = 1;
+ ndiv = ((p->frequency * (6 + 8) * 4) +
+ (DS3000_XTAL_FREQ / 2)) /
+ DS3000_XTAL_FREQ - 1024;
+ } else {
+ ds3000_tuner_writereg(state, 0x10, 0x01);
+ ndiv = ((p->frequency * (6 + 8) * 2) +
+ (DS3000_XTAL_FREQ / 2)) /
+ DS3000_XTAL_FREQ - 1024;
}
- state->dnxt.delivery = c->modulation;
- state->dnxt.frequency = c->frequency;
- state->dnxt.rolloff = 2; /* fixme */
- state->dnxt.fec = c->fec_inner;
+ ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8);
+ ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff);
+
+ /* set pll */
+ ds3000_tuner_writereg(state, 0x03, 0x06);
+ ds3000_tuner_writereg(state, 0x51, 0x0f);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x10);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x51, 0x17);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x08);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ value = ds3000_tuner_readreg(state, 0x3d);
+ value &= 0x0f;
+ if ((value > 4) && (value < 15)) {
+ value -= 3;
+ if (value < 4)
+ value = 4;
+ value = ((value << 3) | 0x01) & 0x79;
+ }
- ret = ds3000_set_inversion(state, p->inversion);
- if (ret != 0)
- return ret;
+ ds3000_tuner_writereg(state, 0x60, value);
+ ds3000_tuner_writereg(state, 0x51, 0x17);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x08);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+
+ /* set low-pass filter period */
+ ds3000_tuner_writereg(state, 0x04, 0x2e);
+ ds3000_tuner_writereg(state, 0x51, 0x1b);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x04);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000;
+ if ((c->symbol_rate / 1000) < 5000)
+ f3db += 3000;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ /* set low-pass filter baseband */
+ value = ds3000_tuner_readreg(state, 0x26);
+ mlpf = 0x2e * 207 / ((value << 1) + 151);
+ mlpf_max = mlpf * 135 / 100;
+ mlpf_min = mlpf * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ /* rounded to the closest integer */
+ nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2))
+ / (2766 * DS3000_XTAL_FREQ);
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ /* rounded to the closest integer */
+ mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+ (1000 * f3db / 2)) / (1000 * f3db);
+
+ if (mlpf_new < mlpf_min) {
+ nlpf++;
+ mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+ (1000 * f3db / 2)) / (1000 * f3db);
+ }
- ret = ds3000_set_symbolrate(state, c->symbol_rate);
- if (ret != 0)
- return ret;
+ if (mlpf_new > mlpf_max)
+ mlpf_new = mlpf_max;
+
+ ds3000_tuner_writereg(state, 0x04, mlpf_new);
+ ds3000_tuner_writereg(state, 0x06, nlpf);
+ ds3000_tuner_writereg(state, 0x51, 0x1b);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x04);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x51, 0x1e);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x01);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(60);
+
+ offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ
+ / (6 + 8) / (div4 + 1) / 2 - p->frequency;
+
+ /* ds3000 global reset */
+ ds3000_writereg(state, 0x07, 0x80);
+ ds3000_writereg(state, 0x07, 0x00);
+ /* ds3000 build-in uC reset */
+ ds3000_writereg(state, 0xb2, 0x01);
+ /* ds3000 software reset */
+ ds3000_writereg(state, 0x00, 0x01);
- /* discard the 'current' tuning parameters and prepare to tune */
- ds3000_clone_params(fe);
-
- retune = 1; /* try 1 times */
- dprintk("%s: retune = %d\n", __func__, retune);
- dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
- dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
- dprintk("%s: FEC = %d \n", __func__,
- state->dcur.fec);
- dprintk("%s: Inversion = %d\n", __func__, state->dcur.inversion);
-
- do {
- /* Reset status register */
- status = 0;
- /* Tune */
- /* TS2020 init */
- ds3000_tuner_writereg(state, 0x42, 0x73);
- ds3000_tuner_writereg(state, 0x05, 0x01);
- ds3000_tuner_writereg(state, 0x62, 0xf5);
- /* unknown */
- ds3000_tuner_writereg(state, 0x07, 0x02);
- ds3000_tuner_writereg(state, 0x10, 0x00);
- ds3000_tuner_writereg(state, 0x60, 0x79);
- ds3000_tuner_writereg(state, 0x08, 0x01);
- ds3000_tuner_writereg(state, 0x00, 0x01);
- /* calculate and set freq divider */
- if (state->dcur.frequency < 1146000) {
- ds3000_tuner_writereg(state, 0x10, 0x11);
- ndiv = ((state->dcur.frequency * (6 + 8) * 4) +
- (DS3000_XTAL_FREQ / 2)) /
- DS3000_XTAL_FREQ - 1024;
- } else {
- ds3000_tuner_writereg(state, 0x10, 0x01);
- ndiv = ((state->dcur.frequency * (6 + 8) * 2) +
- (DS3000_XTAL_FREQ / 2)) /
- DS3000_XTAL_FREQ - 1024;
- }
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* initialise the demod in DVB-S mode */
+ for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs_init_tab[i],
+ ds3000_dvbs_init_tab[i + 1]);
+ value = ds3000_readreg(state, 0xfe);
+ value &= 0xc0;
+ value |= 0x1b;
+ ds3000_writereg(state, 0xfe, value);
+ break;
+ case SYS_DVBS2:
+ /* initialise the demod in DVB-S2 mode */
+ for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs2_init_tab[i],
+ ds3000_dvbs2_init_tab[i + 1]);
+ ds3000_writereg(state, 0xfe, 0x98);
+ break;
+ default:
+ return 1;
+ }
- ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8);
- ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff);
-
- /* set pll */
- ds3000_tuner_writereg(state, 0x03, 0x06);
- ds3000_tuner_writereg(state, 0x51, 0x0f);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x10);
- ds3000_tuner_writereg(state, 0x50, 0x00);
- msleep(5);
-
- /* unknown */
- ds3000_tuner_writereg(state, 0x51, 0x17);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x08);
- ds3000_tuner_writereg(state, 0x50, 0x00);
- msleep(5);
-
- value = ds3000_tuner_readreg(state, 0x3d);
- value &= 0x0f;
- if ((value > 4) && (value < 15)) {
- value -= 3;
- if (value < 4)
- value = 4;
- value = ((value << 3) | 0x01) & 0x79;
- }
+ /* enable 27MHz clock output */
+ ds3000_writereg(state, 0x29, 0x80);
+ /* enable ac coupling */
+ ds3000_writereg(state, 0x25, 0x8a);
+
+ /* enhance symbol rate performance */
+ if ((c->symbol_rate / 1000) <= 5000) {
+ value = 29777 / (c->symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x0d);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x10);
+ ds3000_writereg(state, 0xc7, 0x0e);
+ } else if ((c->symbol_rate / 1000) <= 10000) {
+ value = 92166 / (c->symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x07);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x09);
+ ds3000_writereg(state, 0xc7, 0x12);
+ } else if ((c->symbol_rate / 1000) <= 20000) {
+ value = 64516 / (c->symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0e);
+ ds3000_writereg(state, 0xc4, 0x07);
+ ds3000_writereg(state, 0xc7, 0x18);
+ } else {
+ value = 129032 / (c->symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0a);
+ ds3000_writereg(state, 0xc4, 0x05);
+ ds3000_writereg(state, 0xc7, 0x24);
+ }
- ds3000_tuner_writereg(state, 0x60, value);
- ds3000_tuner_writereg(state, 0x51, 0x17);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x08);
- ds3000_tuner_writereg(state, 0x50, 0x00);
-
- /* set low-pass filter period */
- ds3000_tuner_writereg(state, 0x04, 0x2e);
- ds3000_tuner_writereg(state, 0x51, 0x1b);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x04);
- ds3000_tuner_writereg(state, 0x50, 0x00);
- msleep(5);
-
- f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000;
- if ((state->dcur.symbol_rate / 1000) < 5000)
- f3db += 3000;
- if (f3db < 7000)
- f3db = 7000;
- if (f3db > 40000)
- f3db = 40000;
-
- /* set low-pass filter baseband */
- value = ds3000_tuner_readreg(state, 0x26);
- mlpf = 0x2e * 207 / ((value << 1) + 151);
- mlpf_max = mlpf * 135 / 100;
- mlpf_min = mlpf * 78 / 100;
- if (mlpf_max > 63)
- mlpf_max = 63;
-
- /* rounded to the closest integer */
- nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2))
- / (2766 * DS3000_XTAL_FREQ);
- if (nlpf > 23)
- nlpf = 23;
- if (nlpf < 1)
- nlpf = 1;
-
- /* rounded to the closest integer */
- mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
- (1000 * f3db / 2)) / (1000 * f3db);
+ /* normalized symbol rate rounded to the closest integer */
+ value = (((c->symbol_rate / 1000) << 16) +
+ (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
+ ds3000_writereg(state, 0x61, value & 0x00ff);
+ ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
- if (mlpf_new < mlpf_min) {
- nlpf++;
- mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
- (1000 * f3db / 2)) / (1000 * f3db);
- }
+ /* co-channel interference cancellation disabled */
+ ds3000_writereg(state, 0x56, 0x00);
+
+ /* equalizer disabled */
+ ds3000_writereg(state, 0x76, 0x00);
- if (mlpf_new > mlpf_max)
- mlpf_new = mlpf_max;
-
- ds3000_tuner_writereg(state, 0x04, mlpf_new);
- ds3000_tuner_writereg(state, 0x06, nlpf);
- ds3000_tuner_writereg(state, 0x51, 0x1b);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x04);
- ds3000_tuner_writereg(state, 0x50, 0x00);
- msleep(5);
-
- /* unknown */
- ds3000_tuner_writereg(state, 0x51, 0x1e);
- ds3000_tuner_writereg(state, 0x51, 0x1f);
- ds3000_tuner_writereg(state, 0x50, 0x01);
- ds3000_tuner_writereg(state, 0x50, 0x00);
- msleep(60);
-
- /* ds3000 global reset */
- ds3000_writereg(state, 0x07, 0x80);
- ds3000_writereg(state, 0x07, 0x00);
- /* ds3000 build-in uC reset */
- ds3000_writereg(state, 0xb2, 0x01);
- /* ds3000 software reset */
- ds3000_writereg(state, 0x00, 0x01);
+ /*ds3000_writereg(state, 0x08, 0x03);
+ ds3000_writereg(state, 0xfd, 0x22);
+ ds3000_writereg(state, 0x08, 0x07);
+ ds3000_writereg(state, 0xfd, 0x42);
+ ds3000_writereg(state, 0x08, 0x07);*/
+ if (state->config->ci_mode) {
switch (c->delivery_system) {
case SYS_DVBS:
- /* initialise the demod in DVB-S mode */
- for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
- ds3000_writereg(state,
- ds3000_dvbs_init_tab[i],
- ds3000_dvbs_init_tab[i + 1]);
- value = ds3000_readreg(state, 0xfe);
- value &= 0xc0;
- value |= 0x1b;
- ds3000_writereg(state, 0xfe, value);
- break;
+ default:
+ ds3000_writereg(state, 0xfd, 0x80);
+ break;
case SYS_DVBS2:
- /* initialise the demod in DVB-S2 mode */
- for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
- ds3000_writereg(state,
- ds3000_dvbs2_init_tab[i],
- ds3000_dvbs2_init_tab[i + 1]);
- ds3000_writereg(state, 0xfe, 0x54);
+ ds3000_writereg(state, 0xfd, 0x01);
break;
- default:
- return 1;
}
+ }
- /* enable 27MHz clock output */
- ds3000_writereg(state, 0x29, 0x80);
- /* enable ac coupling */
- ds3000_writereg(state, 0x25, 0x8a);
-
- /* enhance symbol rate performance */
- if ((state->dcur.symbol_rate / 1000) <= 5000) {
- value = 29777 / (state->dcur.symbol_rate / 1000) + 1;
- if (value % 2 != 0)
- value++;
- ds3000_writereg(state, 0xc3, 0x0d);
- ds3000_writereg(state, 0xc8, value);
- ds3000_writereg(state, 0xc4, 0x10);
- ds3000_writereg(state, 0xc7, 0x0e);
- } else if ((state->dcur.symbol_rate / 1000) <= 10000) {
- value = 92166 / (state->dcur.symbol_rate / 1000) + 1;
- if (value % 2 != 0)
- value++;
- ds3000_writereg(state, 0xc3, 0x07);
- ds3000_writereg(state, 0xc8, value);
- ds3000_writereg(state, 0xc4, 0x09);
- ds3000_writereg(state, 0xc7, 0x12);
- } else if ((state->dcur.symbol_rate / 1000) <= 20000) {
- value = 64516 / (state->dcur.symbol_rate / 1000) + 1;
- ds3000_writereg(state, 0xc3, value);
- ds3000_writereg(state, 0xc8, 0x0e);
- ds3000_writereg(state, 0xc4, 0x07);
- ds3000_writereg(state, 0xc7, 0x18);
- } else {
- value = 129032 / (state->dcur.symbol_rate / 1000) + 1;
- ds3000_writereg(state, 0xc3, value);
- ds3000_writereg(state, 0xc8, 0x0a);
- ds3000_writereg(state, 0xc4, 0x05);
- ds3000_writereg(state, 0xc7, 0x24);
- }
+ /* ds3000 out of software reset */
+ ds3000_writereg(state, 0x00, 0x00);
+ /* start ds3000 build-in uC */
+ ds3000_writereg(state, 0xb2, 0x00);
- /* normalized symbol rate rounded to the closest integer */
- value = (((state->dcur.symbol_rate / 1000) << 16) +
- (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
- ds3000_writereg(state, 0x61, value & 0x00ff);
- ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
-
- /* co-channel interference cancellation disabled */
- ds3000_writereg(state, 0x56, 0x00);
-
- /* equalizer disabled */
- ds3000_writereg(state, 0x76, 0x00);
-
- /*ds3000_writereg(state, 0x08, 0x03);
- ds3000_writereg(state, 0xfd, 0x22);
- ds3000_writereg(state, 0x08, 0x07);
- ds3000_writereg(state, 0xfd, 0x42);
- ds3000_writereg(state, 0x08, 0x07);*/
-
- /* ds3000 out of software reset */
- ds3000_writereg(state, 0x00, 0x00);
- /* start ds3000 build-in uC */
- ds3000_writereg(state, 0xb2, 0x00);
-
- /* TODO: calculate and set carrier offset */
-
- /* wait before retrying */
- for (i = 0; i < 30 ; i++) {
- if (ds3000_is_tuned(fe)) {
- dprintk("%s: Tuned\n", __func__);
- ds3000_dump_registers(fe);
- goto tuned;
- }
- msleep(1);
- }
+ ds3000_set_carrier_offset(fe, offset_khz);
- dprintk("%s: Not tuned\n", __func__);
- ds3000_dump_registers(fe);
+ for (i = 0; i < 30 ; i++) {
+ ds3000_read_status(fe, &status);
+ if (status && FE_HAS_LOCK)
+ break;
- } while (--retune);
+ msleep(10);
+ }
-tuned:
- return ret;
+ return 0;
+}
+
+static int ds3000_tune(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ fe_status_t *status)
+{
+ if (p) {
+ int ret = ds3000_set_frontend(fe, p);
+ if (ret)
+ return ret;
+ }
+
+ *delay = HZ / 5;
+
+ return ds3000_read_status(fe, status);
}
static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
{
dprintk("%s()\n", __func__);
- return DVBFE_ALGO_SW;
+ return DVBFE_ALGO_HW;
}
/*
@@ -1306,7 +1249,25 @@ static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
*/
static int ds3000_initfe(struct dvb_frontend *fe)
{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int ret;
+
dprintk("%s()\n", __func__);
+ /* hard reset */
+ ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08));
+ msleep(1);
+
+ /* TS2020 init */
+ ds3000_tuner_writereg(state, 0x42, 0x73);
+ ds3000_tuner_writereg(state, 0x05, 0x01);
+ ds3000_tuner_writereg(state, 0x62, 0xf5);
+ /* Load the firmware if required */
+ ret = ds3000_firmware_ondemand(fe);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
+ return ret;
+ }
+
return 0;
}
@@ -1345,6 +1306,7 @@ static struct dvb_frontend_ops ds3000_ops = {
.read_signal_strength = ds3000_read_signal_strength,
.read_snr = ds3000_read_snr,
.read_ucblocks = ds3000_read_ucblocks,
+ .set_voltage = ds3000_set_voltage,
.set_tone = ds3000_set_tone,
.diseqc_send_master_cmd = ds3000_send_diseqc_msg,
.diseqc_send_burst = ds3000_diseqc_send_burst,
@@ -1352,7 +1314,8 @@ static struct dvb_frontend_ops ds3000_ops = {
.set_property = ds3000_set_property,
.get_property = ds3000_get_property,
- .set_frontend = ds3000_tune,
+ .set_frontend = ds3000_set_frontend,
+ .tune = ds3000_tune,
};
module_param(debug, int, 0644);
diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h
index 67f67038740a..1b736888ea37 100644
--- a/drivers/media/dvb/frontends/ds3000.h
+++ b/drivers/media/dvb/frontends/ds3000.h
@@ -27,6 +27,9 @@
struct ds3000_config {
/* the demodulator's i2c address */
u8 demod_address;
+ u8 ci_mode;
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
};
#if defined(CONFIG_DVB_DS3000) || \
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index 4d4d0bb5920a..62a65efdf8d6 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -64,6 +64,7 @@ struct dvb_pll_desc {
void (*set)(struct dvb_frontend *fe, u8 *buf,
const struct dvb_frontend_parameters *params);
u8 *initdata;
+ u8 *initdata2;
u8 *sleepdata;
int count;
struct {
@@ -321,26 +322,73 @@ static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
static void opera1_bw(struct dvb_frontend *fe, u8 *buf,
const struct dvb_frontend_parameters *params)
{
- if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
- buf[2] |= 0x08;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ u32 b_w = (params->u.qpsk.symbol_rate * 27) / 32000;
+ struct i2c_msg msg = {
+ .addr = priv->pll_i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 4
+ };
+ int result;
+ u8 lpf;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ printk(KERN_ERR "%s: i2c_transfer failed:%d",
+ __func__, result);
+
+ if (b_w <= 10000)
+ lpf = 0xc;
+ else if (b_w <= 12000)
+ lpf = 0x2;
+ else if (b_w <= 14000)
+ lpf = 0xa;
+ else if (b_w <= 16000)
+ lpf = 0x6;
+ else if (b_w <= 18000)
+ lpf = 0xe;
+ else if (b_w <= 20000)
+ lpf = 0x1;
+ else if (b_w <= 22000)
+ lpf = 0x9;
+ else if (b_w <= 24000)
+ lpf = 0x5;
+ else if (b_w <= 26000)
+ lpf = 0xd;
+ else if (b_w <= 28000)
+ lpf = 0x3;
+ else
+ lpf = 0xb;
+ buf[2] ^= 0x1c; /* Flip bits 3-5 */
+ /* Set lpf */
+ buf[2] |= ((lpf >> 2) & 0x3) << 3;
+ buf[3] |= (lpf & 0x3) << 2;
+
+ return;
}
static struct dvb_pll_desc dvb_pll_opera1 = {
.name = "Opera Tuner",
.min = 900000,
.max = 2250000,
+ .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 },
+ .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 },
.iffreq= 0,
.set = opera1_bw,
.count = 8,
.entries = {
- { 1064000, 500, 0xe5, 0xc6 },
- { 1169000, 500, 0xe5, 0xe6 },
- { 1299000, 500, 0xe5, 0x24 },
- { 1444000, 500, 0xe5, 0x44 },
- { 1606000, 500, 0xe5, 0x64 },
- { 1777000, 500, 0xe5, 0x84 },
- { 1941000, 500, 0xe5, 0xa4 },
- { 2250000, 500, 0xe5, 0xc4 },
+ { 1064000, 500, 0xf9, 0xc2 },
+ { 1169000, 500, 0xf9, 0xe2 },
+ { 1299000, 500, 0xf9, 0x20 },
+ { 1444000, 500, 0xf9, 0x40 },
+ { 1606000, 500, 0xf9, 0x60 },
+ { 1777000, 500, 0xf9, 0x80 },
+ { 1941000, 500, 0xf9, 0xa0 },
+ { 2250000, 500, 0xf9, 0xc0 },
}
};
@@ -648,8 +696,17 @@ static int dvb_pll_init(struct dvb_frontend *fe)
int result;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
return result;
+ if (priv->pll_desc->initdata2) {
+ msg.buf = priv->pll_desc->initdata2 + 1;
+ msg.len = priv->pll_desc->initdata2[0];
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ return result;
}
return 0;
}
diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c
new file mode 100644
index 000000000000..eecdf23642eb
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0367.c
@@ -0,0 +1,3441 @@
+/*
+ * stv0367.c
+ *
+ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC.
+ *
+ * Copyright (C) ST Microelectronics.
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#include "stv0367.h"
+#include "stv0367_regs.h"
+#include "stv0367_priv.h"
+
+static int stvdebug;
+module_param_named(debug, stvdebug, int, 0644);
+
+static int i2cdebug;
+module_param_named(i2c_debug, i2cdebug, int, 0644);
+
+#define dprintk(args...) \
+ do { \
+ if (stvdebug) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+ /* DVB-C */
+
+struct stv0367cab_state {
+ enum stv0367_cab_signal_type state;
+ u32 mclk;
+ u32 adc_clk;
+ s32 search_range;
+ s32 derot_offset;
+ /* results */
+ int locked; /* channel found */
+ u32 freq_khz; /* found frequency (in kHz) */
+ u32 symbol_rate; /* found symbol rate (in Bds) */
+ enum stv0367cab_mod modulation; /* modulation */
+ fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */
+};
+
+struct stv0367ter_state {
+ /* DVB-T */
+ enum stv0367_ter_signal_type state;
+ enum stv0367_ter_if_iq_mode if_iq_mode;
+ enum stv0367_ter_mode mode;/* mode 2K or 8K */
+ fe_guard_interval_t guard;
+ enum stv0367_ter_hierarchy hierarchy;
+ u32 frequency;
+ fe_spectral_inversion_t sense; /* current search spectrum */
+ u8 force; /* force mode/guard */
+ u8 bw; /* channel width 6, 7 or 8 in MHz */
+ u8 pBW; /* channel width used during previous lock */
+ u32 pBER;
+ u32 pPER;
+ u32 ucblocks;
+ s8 echo_pos; /* echo position */
+ u8 first_lock;
+ u8 unlock_counter;
+ u32 agc_val;
+};
+
+struct stv0367_state {
+ struct dvb_frontend fe;
+ struct i2c_adapter *i2c;
+ /* config settings */
+ const struct stv0367_config *config;
+ u8 chip_id;
+ /* DVB-C */
+ struct stv0367cab_state *cab_state;
+ /* DVB-T */
+ struct stv0367ter_state *ter_state;
+};
+
+struct st_register {
+ u16 addr;
+ u8 value;
+};
+
+/* values for STV4100 XTAL=30M int clk=53.125M*/
+static struct st_register def0367ter[STV0367TER_NBREGS] = {
+ {R367TER_ID, 0x60},
+ {R367TER_I2CRPT, 0xa0},
+ /* {R367TER_I2CRPT, 0x22},*/
+ {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */
+ {R367TER_IOCFG0, 0x40},
+ {R367TER_DAC0R, 0x00},
+ {R367TER_IOCFG1, 0x00},
+ {R367TER_DAC1R, 0x00},
+ {R367TER_IOCFG2, 0x62},
+ {R367TER_SDFR, 0x00},
+ {R367TER_STATUS, 0xf8},
+ {R367TER_AUX_CLK, 0x0a},
+ {R367TER_FREESYS1, 0x00},
+ {R367TER_FREESYS2, 0x00},
+ {R367TER_FREESYS3, 0x00},
+ {R367TER_GPIO_CFG, 0x55},
+ {R367TER_GPIO_CMD, 0x00},
+ {R367TER_AGC2MAX, 0xff},
+ {R367TER_AGC2MIN, 0x00},
+ {R367TER_AGC1MAX, 0xff},
+ {R367TER_AGC1MIN, 0x00},
+ {R367TER_AGCR, 0xbc},
+ {R367TER_AGC2TH, 0x00},
+ {R367TER_AGC12C, 0x00},
+ {R367TER_AGCCTRL1, 0x85},
+ {R367TER_AGCCTRL2, 0x1f},
+ {R367TER_AGC1VAL1, 0x00},
+ {R367TER_AGC1VAL2, 0x00},
+ {R367TER_AGC2VAL1, 0x6f},
+ {R367TER_AGC2VAL2, 0x05},
+ {R367TER_AGC2PGA, 0x00},
+ {R367TER_OVF_RATE1, 0x00},
+ {R367TER_OVF_RATE2, 0x00},
+ {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */
+ {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */
+ {R367TER_INC_DEROT1, 0x55},
+ {R367TER_INC_DEROT2, 0x55},
+ {R367TER_PPM_CPAMP_DIR, 0x2c},
+ {R367TER_PPM_CPAMP_INV, 0x00},
+ {R367TER_FREESTFE_1, 0x00},
+ {R367TER_FREESTFE_2, 0x1c},
+ {R367TER_DCOFFSET, 0x00},
+ {R367TER_EN_PROCESS, 0x05},
+ {R367TER_SDI_SMOOTHER, 0x80},
+ {R367TER_FE_LOOP_OPEN, 0x1c},
+ {R367TER_FREQOFF1, 0x00},
+ {R367TER_FREQOFF2, 0x00},
+ {R367TER_FREQOFF3, 0x00},
+ {R367TER_TIMOFF1, 0x00},
+ {R367TER_TIMOFF2, 0x00},
+ {R367TER_EPQ, 0x02},
+ {R367TER_EPQAUTO, 0x01},
+ {R367TER_SYR_UPDATE, 0xf5},
+ {R367TER_CHPFREE, 0x00},
+ {R367TER_PPM_STATE_MAC, 0x23},
+ {R367TER_INR_THRESHOLD, 0xff},
+ {R367TER_EPQ_TPS_ID_CELL, 0xf9},
+ {R367TER_EPQ_CFG, 0x00},
+ {R367TER_EPQ_STATUS, 0x01},
+ {R367TER_AUTORELOCK, 0x81},
+ {R367TER_BER_THR_VMSB, 0x00},
+ {R367TER_BER_THR_MSB, 0x00},
+ {R367TER_BER_THR_LSB, 0x00},
+ {R367TER_CCD, 0x83},
+ {R367TER_SPECTR_CFG, 0x00},
+ {R367TER_CHC_DUMMY, 0x18},
+ {R367TER_INC_CTL, 0x88},
+ {R367TER_INCTHRES_COR1, 0xb4},
+ {R367TER_INCTHRES_COR2, 0x96},
+ {R367TER_INCTHRES_DET1, 0x0e},
+ {R367TER_INCTHRES_DET2, 0x11},
+ {R367TER_IIR_CELLNB, 0x8d},
+ {R367TER_IIRCX_COEFF1_MSB, 0x00},
+ {R367TER_IIRCX_COEFF1_LSB, 0x00},
+ {R367TER_IIRCX_COEFF2_MSB, 0x09},
+ {R367TER_IIRCX_COEFF2_LSB, 0x18},
+ {R367TER_IIRCX_COEFF3_MSB, 0x14},
+ {R367TER_IIRCX_COEFF3_LSB, 0x9c},
+ {R367TER_IIRCX_COEFF4_MSB, 0x00},
+ {R367TER_IIRCX_COEFF4_LSB, 0x00},
+ {R367TER_IIRCX_COEFF5_MSB, 0x36},
+ {R367TER_IIRCX_COEFF5_LSB, 0x42},
+ {R367TER_FEPATH_CFG, 0x00},
+ {R367TER_PMC1_FUNC, 0x65},
+ {R367TER_PMC1_FOR, 0x00},
+ {R367TER_PMC2_FUNC, 0x00},
+ {R367TER_STATUS_ERR_DA, 0xe0},
+ {R367TER_DIG_AGC_R, 0xfe},
+ {R367TER_COMAGC_TARMSB, 0x0b},
+ {R367TER_COM_AGC_TAR_ENMODE, 0x41},
+ {R367TER_COM_AGC_CFG, 0x3e},
+ {R367TER_COM_AGC_GAIN1, 0x39},
+ {R367TER_AUT_AGC_TARGETMSB, 0x0b},
+ {R367TER_LOCK_DET_MSB, 0x01},
+ {R367TER_AGCTAR_LOCK_LSBS, 0x40},
+ {R367TER_AUT_GAIN_EN, 0xf4},
+ {R367TER_AUT_CFG, 0xf0},
+ {R367TER_LOCKN, 0x23},
+ {R367TER_INT_X_3, 0x00},
+ {R367TER_INT_X_2, 0x03},
+ {R367TER_INT_X_1, 0x8d},
+ {R367TER_INT_X_0, 0xa0},
+ {R367TER_MIN_ERRX_MSB, 0x00},
+ {R367TER_COR_CTL, 0x23},
+ {R367TER_COR_STAT, 0xf6},
+ {R367TER_COR_INTEN, 0x00},
+ {R367TER_COR_INTSTAT, 0x3f},
+ {R367TER_COR_MODEGUARD, 0x03},
+ {R367TER_AGC_CTL, 0x08},
+ {R367TER_AGC_MANUAL1, 0x00},
+ {R367TER_AGC_MANUAL2, 0x00},
+ {R367TER_AGC_TARG, 0x16},
+ {R367TER_AGC_GAIN1, 0x53},
+ {R367TER_AGC_GAIN2, 0x1d},
+ {R367TER_RESERVED_1, 0x00},
+ {R367TER_RESERVED_2, 0x00},
+ {R367TER_RESERVED_3, 0x00},
+ {R367TER_CAS_CTL, 0x44},
+ {R367TER_CAS_FREQ, 0xb3},
+ {R367TER_CAS_DAGCGAIN, 0x12},
+ {R367TER_SYR_CTL, 0x04},
+ {R367TER_SYR_STAT, 0x10},
+ {R367TER_SYR_NCO1, 0x00},
+ {R367TER_SYR_NCO2, 0x00},
+ {R367TER_SYR_OFFSET1, 0x00},
+ {R367TER_SYR_OFFSET2, 0x00},
+ {R367TER_FFT_CTL, 0x00},
+ {R367TER_SCR_CTL, 0x70},
+ {R367TER_PPM_CTL1, 0xf8},
+ {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */
+ {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */
+ {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */
+ {R367TER_TRL_TIME1, 0x1d},
+ {R367TER_TRL_TIME2, 0xfc},
+ {R367TER_CRL_CTL, 0x24},
+ {R367TER_CRL_FREQ1, 0xad},
+ {R367TER_CRL_FREQ2, 0x9d},
+ {R367TER_CRL_FREQ3, 0xff},
+ {R367TER_CHC_CTL, 0x01},
+ {R367TER_CHC_SNR, 0xf0},
+ {R367TER_BDI_CTL, 0x00},
+ {R367TER_DMP_CTL, 0x00},
+ {R367TER_TPS_RCVD1, 0x30},
+ {R367TER_TPS_RCVD2, 0x02},
+ {R367TER_TPS_RCVD3, 0x01},
+ {R367TER_TPS_RCVD4, 0x00},
+ {R367TER_TPS_ID_CELL1, 0x00},
+ {R367TER_TPS_ID_CELL2, 0x00},
+ {R367TER_TPS_RCVD5_SET1, 0x02},
+ {R367TER_TPS_SET2, 0x02},
+ {R367TER_TPS_SET3, 0x01},
+ {R367TER_TPS_CTL, 0x00},
+ {R367TER_CTL_FFTOSNUM, 0x34},
+ {R367TER_TESTSELECT, 0x09},
+ {R367TER_MSC_REV, 0x0a},
+ {R367TER_PIR_CTL, 0x00},
+ {R367TER_SNR_CARRIER1, 0xa1},
+ {R367TER_SNR_CARRIER2, 0x9a},
+ {R367TER_PPM_CPAMP, 0x2c},
+ {R367TER_TSM_AP0, 0x00},
+ {R367TER_TSM_AP1, 0x00},
+ {R367TER_TSM_AP2 , 0x00},
+ {R367TER_TSM_AP3, 0x00},
+ {R367TER_TSM_AP4, 0x00},
+ {R367TER_TSM_AP5, 0x00},
+ {R367TER_TSM_AP6, 0x00},
+ {R367TER_TSM_AP7, 0x00},
+ {R367TER_TSTRES, 0x00},
+ {R367TER_ANACTRL, 0x0D},/* PLL stoped, restart at init!!! */
+ {R367TER_TSTBUS, 0x00},
+ {R367TER_TSTRATE, 0x00},
+ {R367TER_CONSTMODE, 0x01},
+ {R367TER_CONSTCARR1, 0x00},
+ {R367TER_CONSTCARR2, 0x00},
+ {R367TER_ICONSTEL, 0x0a},
+ {R367TER_QCONSTEL, 0x15},
+ {R367TER_TSTBISTRES0, 0x00},
+ {R367TER_TSTBISTRES1, 0x00},
+ {R367TER_TSTBISTRES2, 0x28},
+ {R367TER_TSTBISTRES3, 0x00},
+ {R367TER_RF_AGC1, 0xff},
+ {R367TER_RF_AGC2, 0x83},
+ {R367TER_ANADIGCTRL, 0x19},
+ {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */
+ {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */
+ {R367TER_PLLSETUP, 0x18},
+ {R367TER_DUAL_AD12, 0x04},/* for xc5000; was 0x00 */
+ {R367TER_TSTBIST, 0x00},
+ {R367TER_PAD_COMP_CTRL, 0x00},
+ {R367TER_PAD_COMP_WR, 0x00},
+ {R367TER_PAD_COMP_RD, 0xe0},
+ {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00},
+ {R367TER_SYR_FLAG, 0x00},
+ {R367TER_CRL_TARGET1, 0x00},
+ {R367TER_CRL_TARGET2, 0x00},
+ {R367TER_CRL_TARGET3, 0x00},
+ {R367TER_CRL_TARGET4, 0x00},
+ {R367TER_CRL_FLAG, 0x00},
+ {R367TER_TRL_TARGET1, 0x00},
+ {R367TER_TRL_TARGET2, 0x00},
+ {R367TER_TRL_CHC, 0x00},
+ {R367TER_CHC_SNR_TARG, 0x00},
+ {R367TER_TOP_TRACK, 0x00},
+ {R367TER_TRACKER_FREE1, 0x00},
+ {R367TER_ERROR_CRL1, 0x00},
+ {R367TER_ERROR_CRL2, 0x00},
+ {R367TER_ERROR_CRL3, 0x00},
+ {R367TER_ERROR_CRL4, 0x00},
+ {R367TER_DEC_NCO1, 0x2c},
+ {R367TER_DEC_NCO2, 0x0f},
+ {R367TER_DEC_NCO3, 0x20},
+ {R367TER_SNR, 0xf1},
+ {R367TER_SYR_FFTADJ1, 0x00},
+ {R367TER_SYR_FFTADJ2, 0x00},
+ {R367TER_SYR_CHCADJ1, 0x00},
+ {R367TER_SYR_CHCADJ2, 0x00},
+ {R367TER_SYR_OFF, 0x00},
+ {R367TER_PPM_OFFSET1, 0x00},
+ {R367TER_PPM_OFFSET2, 0x03},
+ {R367TER_TRACKER_FREE2, 0x00},
+ {R367TER_DEBG_LT10, 0x00},
+ {R367TER_DEBG_LT11, 0x00},
+ {R367TER_DEBG_LT12, 0x00},
+ {R367TER_DEBG_LT13, 0x00},
+ {R367TER_DEBG_LT14, 0x00},
+ {R367TER_DEBG_LT15, 0x00},
+ {R367TER_DEBG_LT16, 0x00},
+ {R367TER_DEBG_LT17, 0x00},
+ {R367TER_DEBG_LT18, 0x00},
+ {R367TER_DEBG_LT19, 0x00},
+ {R367TER_DEBG_LT1A, 0x00},
+ {R367TER_DEBG_LT1B, 0x00},
+ {R367TER_DEBG_LT1C, 0x00},
+ {R367TER_DEBG_LT1D, 0x00},
+ {R367TER_DEBG_LT1E, 0x00},
+ {R367TER_DEBG_LT1F, 0x00},
+ {R367TER_RCCFGH, 0x00},
+ {R367TER_RCCFGM, 0x00},
+ {R367TER_RCCFGL, 0x00},
+ {R367TER_RCINSDELH, 0x00},
+ {R367TER_RCINSDELM, 0x00},
+ {R367TER_RCINSDELL, 0x00},
+ {R367TER_RCSTATUS, 0x00},
+ {R367TER_RCSPEED, 0x6f},
+ {R367TER_RCDEBUGM, 0xe7},
+ {R367TER_RCDEBUGL, 0x9b},
+ {R367TER_RCOBSCFG, 0x00},
+ {R367TER_RCOBSM, 0x00},
+ {R367TER_RCOBSL, 0x00},
+ {R367TER_RCFECSPY, 0x00},
+ {R367TER_RCFSPYCFG, 0x00},
+ {R367TER_RCFSPYDATA, 0x00},
+ {R367TER_RCFSPYOUT, 0x00},
+ {R367TER_RCFSTATUS, 0x00},
+ {R367TER_RCFGOODPACK, 0x00},
+ {R367TER_RCFPACKCNT, 0x00},
+ {R367TER_RCFSPYMISC, 0x00},
+ {R367TER_RCFBERCPT4, 0x00},
+ {R367TER_RCFBERCPT3, 0x00},
+ {R367TER_RCFBERCPT2, 0x00},
+ {R367TER_RCFBERCPT1, 0x00},
+ {R367TER_RCFBERCPT0, 0x00},
+ {R367TER_RCFBERERR2, 0x00},
+ {R367TER_RCFBERERR1, 0x00},
+ {R367TER_RCFBERERR0, 0x00},
+ {R367TER_RCFSTATESM, 0x00},
+ {R367TER_RCFSTATESL, 0x00},
+ {R367TER_RCFSPYBER, 0x00},
+ {R367TER_RCFSPYDISTM, 0x00},
+ {R367TER_RCFSPYDISTL, 0x00},
+ {R367TER_RCFSPYOBS7, 0x00},
+ {R367TER_RCFSPYOBS6, 0x00},
+ {R367TER_RCFSPYOBS5, 0x00},
+ {R367TER_RCFSPYOBS4, 0x00},
+ {R367TER_RCFSPYOBS3, 0x00},
+ {R367TER_RCFSPYOBS2, 0x00},
+ {R367TER_RCFSPYOBS1, 0x00},
+ {R367TER_RCFSPYOBS0, 0x00},
+ {R367TER_TSGENERAL, 0x00},
+ {R367TER_RC1SPEED, 0x6f},
+ {R367TER_TSGSTATUS, 0x18},
+ {R367TER_FECM, 0x01},
+ {R367TER_VTH12, 0xff},
+ {R367TER_VTH23, 0xa1},
+ {R367TER_VTH34, 0x64},
+ {R367TER_VTH56, 0x40},
+ {R367TER_VTH67, 0x00},
+ {R367TER_VTH78, 0x2c},
+ {R367TER_VITCURPUN, 0x12},
+ {R367TER_VERROR, 0x01},
+ {R367TER_PRVIT, 0x3f},
+ {R367TER_VAVSRVIT, 0x00},
+ {R367TER_VSTATUSVIT, 0xbd},
+ {R367TER_VTHINUSE, 0xa1},
+ {R367TER_KDIV12, 0x20},
+ {R367TER_KDIV23, 0x40},
+ {R367TER_KDIV34, 0x20},
+ {R367TER_KDIV56, 0x30},
+ {R367TER_KDIV67, 0x00},
+ {R367TER_KDIV78, 0x30},
+ {R367TER_SIGPOWER, 0x54},
+ {R367TER_DEMAPVIT, 0x40},
+ {R367TER_VITSCALE, 0x00},
+ {R367TER_FFEC1PRG, 0x00},
+ {R367TER_FVITCURPUN, 0x12},
+ {R367TER_FVERROR, 0x01},
+ {R367TER_FVSTATUSVIT, 0xbd},
+ {R367TER_DEBUG_LT1, 0x00},
+ {R367TER_DEBUG_LT2, 0x00},
+ {R367TER_DEBUG_LT3, 0x00},
+ {R367TER_TSTSFMET, 0x00},
+ {R367TER_SELOUT, 0x00},
+ {R367TER_TSYNC, 0x00},
+ {R367TER_TSTERR, 0x00},
+ {R367TER_TSFSYNC, 0x00},
+ {R367TER_TSTSFERR, 0x00},
+ {R367TER_TSTTSSF1, 0x01},
+ {R367TER_TSTTSSF2, 0x1f},
+ {R367TER_TSTTSSF3, 0x00},
+ {R367TER_TSTTS1, 0x00},
+ {R367TER_TSTTS2, 0x1f},
+ {R367TER_TSTTS3, 0x01},
+ {R367TER_TSTTS4, 0x00},
+ {R367TER_TSTTSRC, 0x00},
+ {R367TER_TSTTSRS, 0x00},
+ {R367TER_TSSTATEM, 0xb0},
+ {R367TER_TSSTATEL, 0x40},
+ {R367TER_TSCFGH, 0xC0},
+ {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */
+ {R367TER_TSCFGL, 0x20},
+ {R367TER_TSSYNC, 0x00},
+ {R367TER_TSINSDELH, 0x00},
+ {R367TER_TSINSDELM, 0x00},
+ {R367TER_TSINSDELL, 0x00},
+ {R367TER_TSDIVN, 0x03},
+ {R367TER_TSDIVPM, 0x00},
+ {R367TER_TSDIVPL, 0x00},
+ {R367TER_TSDIVQM, 0x00},
+ {R367TER_TSDIVQL, 0x00},
+ {R367TER_TSDILSTKM, 0x00},
+ {R367TER_TSDILSTKL, 0x00},
+ {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */
+ {R367TER_TSSTATUS, 0x81},
+ {R367TER_TSSTATUS2, 0x6a},
+ {R367TER_TSBITRATEM, 0x0f},
+ {R367TER_TSBITRATEL, 0xc6},
+ {R367TER_TSPACKLENM, 0x00},
+ {R367TER_TSPACKLENL, 0xfc},
+ {R367TER_TSBLOCLENM, 0x0a},
+ {R367TER_TSBLOCLENL, 0x80},
+ {R367TER_TSDLYH, 0x90},
+ {R367TER_TSDLYM, 0x68},
+ {R367TER_TSDLYL, 0x01},
+ {R367TER_TSNPDAV, 0x00},
+ {R367TER_TSBUFSTATH, 0x00},
+ {R367TER_TSBUFSTATM, 0x00},
+ {R367TER_TSBUFSTATL, 0x00},
+ {R367TER_TSDEBUGM, 0xcf},
+ {R367TER_TSDEBUGL, 0x1e},
+ {R367TER_TSDLYSETH, 0x00},
+ {R367TER_TSDLYSETM, 0x68},
+ {R367TER_TSDLYSETL, 0x00},
+ {R367TER_TSOBSCFG, 0x00},
+ {R367TER_TSOBSM, 0x47},
+ {R367TER_TSOBSL, 0x1f},
+ {R367TER_ERRCTRL1, 0x95},
+ {R367TER_ERRCNT1H, 0x80},
+ {R367TER_ERRCNT1M, 0x00},
+ {R367TER_ERRCNT1L, 0x00},
+ {R367TER_ERRCTRL2, 0x95},
+ {R367TER_ERRCNT2H, 0x00},
+ {R367TER_ERRCNT2M, 0x00},
+ {R367TER_ERRCNT2L, 0x00},
+ {R367TER_FECSPY, 0x88},
+ {R367TER_FSPYCFG, 0x2c},
+ {R367TER_FSPYDATA, 0x3a},
+ {R367TER_FSPYOUT, 0x06},
+ {R367TER_FSTATUS, 0x61},
+ {R367TER_FGOODPACK, 0xff},
+ {R367TER_FPACKCNT, 0xff},
+ {R367TER_FSPYMISC, 0x66},
+ {R367TER_FBERCPT4, 0x00},
+ {R367TER_FBERCPT3, 0x00},
+ {R367TER_FBERCPT2, 0x36},
+ {R367TER_FBERCPT1, 0x36},
+ {R367TER_FBERCPT0, 0x14},
+ {R367TER_FBERERR2, 0x00},
+ {R367TER_FBERERR1, 0x03},
+ {R367TER_FBERERR0, 0x28},
+ {R367TER_FSTATESM, 0x00},
+ {R367TER_FSTATESL, 0x02},
+ {R367TER_FSPYBER, 0x00},
+ {R367TER_FSPYDISTM, 0x01},
+ {R367TER_FSPYDISTL, 0x9f},
+ {R367TER_FSPYOBS7, 0xc9},
+ {R367TER_FSPYOBS6, 0x99},
+ {R367TER_FSPYOBS5, 0x08},
+ {R367TER_FSPYOBS4, 0xec},
+ {R367TER_FSPYOBS3, 0x01},
+ {R367TER_FSPYOBS2, 0x0f},
+ {R367TER_FSPYOBS1, 0xf5},
+ {R367TER_FSPYOBS0, 0x08},
+ {R367TER_SFDEMAP, 0x40},
+ {R367TER_SFERROR, 0x00},
+ {R367TER_SFAVSR, 0x30},
+ {R367TER_SFECSTATUS, 0xcc},
+ {R367TER_SFKDIV12, 0x20},
+ {R367TER_SFKDIV23, 0x40},
+ {R367TER_SFKDIV34, 0x20},
+ {R367TER_SFKDIV56, 0x20},
+ {R367TER_SFKDIV67, 0x00},
+ {R367TER_SFKDIV78, 0x20},
+ {R367TER_SFDILSTKM, 0x00},
+ {R367TER_SFDILSTKL, 0x00},
+ {R367TER_SFSTATUS, 0xb5},
+ {R367TER_SFDLYH, 0x90},
+ {R367TER_SFDLYM, 0x60},
+ {R367TER_SFDLYL, 0x01},
+ {R367TER_SFDLYSETH, 0xc0},
+ {R367TER_SFDLYSETM, 0x60},
+ {R367TER_SFDLYSETL, 0x00},
+ {R367TER_SFOBSCFG, 0x00},
+ {R367TER_SFOBSM, 0x47},
+ {R367TER_SFOBSL, 0x05},
+ {R367TER_SFECINFO, 0x40},
+ {R367TER_SFERRCTRL, 0x74},
+ {R367TER_SFERRCNTH, 0x80},
+ {R367TER_SFERRCNTM , 0x00},
+ {R367TER_SFERRCNTL, 0x00},
+ {R367TER_SYMBRATEM, 0x2f},
+ {R367TER_SYMBRATEL, 0x50},
+ {R367TER_SYMBSTATUS, 0x7f},
+ {R367TER_SYMBCFG, 0x00},
+ {R367TER_SYMBFIFOM, 0xf4},
+ {R367TER_SYMBFIFOL, 0x0d},
+ {R367TER_SYMBOFFSM, 0xf0},
+ {R367TER_SYMBOFFSL, 0x2d},
+ {R367TER_DEBUG_LT4, 0x00},
+ {R367TER_DEBUG_LT5, 0x00},
+ {R367TER_DEBUG_LT6, 0x00},
+ {R367TER_DEBUG_LT7, 0x00},
+ {R367TER_DEBUG_LT8, 0x00},
+ {R367TER_DEBUG_LT9, 0x00},
+};
+
+#define RF_LOOKUP_TABLE_SIZE 31
+#define RF_LOOKUP_TABLE2_SIZE 16
+/* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/
+s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = {
+ {/*AGC1*/
+ 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 80, 83, 85, 88,
+ }, {/*RF(dbm)*/
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 46, 47,
+ 49, 50, 52, 53, 54, 55, 56,
+ }
+};
+/* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/
+s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = {
+ {/*AGC2*/
+ 28, 29, 31, 32, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45,
+ }, {/*RF(dbm)*/
+ 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ }
+};
+
+static struct st_register def0367cab[STV0367CAB_NBREGS] = {
+ {R367CAB_ID, 0x60},
+ {R367CAB_I2CRPT, 0xa0},
+ /*{R367CAB_I2CRPT, 0x22},*/
+ {R367CAB_TOPCTRL, 0x10},
+ {R367CAB_IOCFG0, 0x80},
+ {R367CAB_DAC0R, 0x00},
+ {R367CAB_IOCFG1, 0x00},
+ {R367CAB_DAC1R, 0x00},
+ {R367CAB_IOCFG2, 0x00},
+ {R367CAB_SDFR, 0x00},
+ {R367CAB_AUX_CLK, 0x00},
+ {R367CAB_FREESYS1, 0x00},
+ {R367CAB_FREESYS2, 0x00},
+ {R367CAB_FREESYS3, 0x00},
+ {R367CAB_GPIO_CFG, 0x55},
+ {R367CAB_GPIO_CMD, 0x01},
+ {R367CAB_TSTRES, 0x00},
+ {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/
+ {R367CAB_TSTBUS, 0x00},
+ {R367CAB_RF_AGC1, 0xea},
+ {R367CAB_RF_AGC2, 0x82},
+ {R367CAB_ANADIGCTRL, 0x0b},
+ {R367CAB_PLLMDIV, 0x01},
+ {R367CAB_PLLNDIV, 0x08},
+ {R367CAB_PLLSETUP, 0x18},
+ {R367CAB_DUAL_AD12, 0x04},
+ {R367CAB_TSTBIST, 0x00},
+ {R367CAB_CTRL_1, 0x00},
+ {R367CAB_CTRL_2, 0x03},
+ {R367CAB_IT_STATUS1, 0x2b},
+ {R367CAB_IT_STATUS2, 0x08},
+ {R367CAB_IT_EN1, 0x00},
+ {R367CAB_IT_EN2, 0x00},
+ {R367CAB_CTRL_STATUS, 0x04},
+ {R367CAB_TEST_CTL, 0x00},
+ {R367CAB_AGC_CTL, 0x73},
+ {R367CAB_AGC_IF_CFG, 0x50},
+ {R367CAB_AGC_RF_CFG, 0x00},
+ {R367CAB_AGC_PWM_CFG, 0x03},
+ {R367CAB_AGC_PWR_REF_L, 0x5a},
+ {R367CAB_AGC_PWR_REF_H, 0x00},
+ {R367CAB_AGC_RF_TH_L, 0xff},
+ {R367CAB_AGC_RF_TH_H, 0x07},
+ {R367CAB_AGC_IF_LTH_L, 0x00},
+ {R367CAB_AGC_IF_LTH_H, 0x08},
+ {R367CAB_AGC_IF_HTH_L, 0xff},
+ {R367CAB_AGC_IF_HTH_H, 0x07},
+ {R367CAB_AGC_PWR_RD_L, 0xa0},
+ {R367CAB_AGC_PWR_RD_M, 0xe9},
+ {R367CAB_AGC_PWR_RD_H, 0x03},
+ {R367CAB_AGC_PWM_IFCMD_L, 0xe4},
+ {R367CAB_AGC_PWM_IFCMD_H, 0x00},
+ {R367CAB_AGC_PWM_RFCMD_L, 0xff},
+ {R367CAB_AGC_PWM_RFCMD_H, 0x07},
+ {R367CAB_IQDEM_CFG, 0x01},
+ {R367CAB_MIX_NCO_LL, 0x22},
+ {R367CAB_MIX_NCO_HL, 0x96},
+ {R367CAB_MIX_NCO_HH, 0x55},
+ {R367CAB_SRC_NCO_LL, 0xff},
+ {R367CAB_SRC_NCO_LH, 0x0c},
+ {R367CAB_SRC_NCO_HL, 0xf5},
+ {R367CAB_SRC_NCO_HH, 0x20},
+ {R367CAB_IQDEM_GAIN_SRC_L, 0x06},
+ {R367CAB_IQDEM_GAIN_SRC_H, 0x01},
+ {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe},
+ {R367CAB_IQDEM_DCRM_CFG_LH, 0xff},
+ {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f},
+ {R367CAB_IQDEM_DCRM_CFG_HH, 0x00},
+ {R367CAB_IQDEM_ADJ_COEFF0, 0x34},
+ {R367CAB_IQDEM_ADJ_COEFF1, 0xae},
+ {R367CAB_IQDEM_ADJ_COEFF2, 0x46},
+ {R367CAB_IQDEM_ADJ_COEFF3, 0x77},
+ {R367CAB_IQDEM_ADJ_COEFF4, 0x96},
+ {R367CAB_IQDEM_ADJ_COEFF5, 0x69},
+ {R367CAB_IQDEM_ADJ_COEFF6, 0xc7},
+ {R367CAB_IQDEM_ADJ_COEFF7, 0x01},
+ {R367CAB_IQDEM_ADJ_EN, 0x04},
+ {R367CAB_IQDEM_ADJ_AGC_REF, 0x94},
+ {R367CAB_ALLPASSFILT1, 0xc9},
+ {R367CAB_ALLPASSFILT2, 0x2d},
+ {R367CAB_ALLPASSFILT3, 0xa3},
+ {R367CAB_ALLPASSFILT4, 0xfb},
+ {R367CAB_ALLPASSFILT5, 0xf6},
+ {R367CAB_ALLPASSFILT6, 0x45},
+ {R367CAB_ALLPASSFILT7, 0x6f},
+ {R367CAB_ALLPASSFILT8, 0x7e},
+ {R367CAB_ALLPASSFILT9, 0x05},
+ {R367CAB_ALLPASSFILT10, 0x0a},
+ {R367CAB_ALLPASSFILT11, 0x51},
+ {R367CAB_TRL_AGC_CFG, 0x20},
+ {R367CAB_TRL_LPF_CFG, 0x28},
+ {R367CAB_TRL_LPF_ACQ_GAIN, 0x44},
+ {R367CAB_TRL_LPF_TRK_GAIN, 0x22},
+ {R367CAB_TRL_LPF_OUT_GAIN, 0x03},
+ {R367CAB_TRL_LOCKDET_LTH, 0x04},
+ {R367CAB_TRL_LOCKDET_HTH, 0x11},
+ {R367CAB_TRL_LOCKDET_TRGVAL, 0x20},
+ {R367CAB_IQ_QAM, 0x01},
+ {R367CAB_FSM_STATE, 0xa0},
+ {R367CAB_FSM_CTL, 0x08},
+ {R367CAB_FSM_STS, 0x0c},
+ {R367CAB_FSM_SNR0_HTH, 0x00},
+ {R367CAB_FSM_SNR1_HTH, 0x00},
+ {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */
+ {R367CAB_FSM_SNR0_LTH, 0x00},
+ {R367CAB_FSM_SNR1_LTH, 0x00},
+ {R367CAB_FSM_EQA1_HTH, 0x00},
+ {R367CAB_FSM_TEMPO, 0x32},
+ {R367CAB_FSM_CONFIG, 0x03},
+ {R367CAB_EQU_I_TESTTAP_L, 0x11},
+ {R367CAB_EQU_I_TESTTAP_M, 0x00},
+ {R367CAB_EQU_I_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TESTAP_CFG, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_L, 0xff},
+ {R367CAB_EQU_Q_TESTTAP_M, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TAP_CTRL, 0x00},
+ {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11},
+ {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05},
+ {R367CAB_EQU_CTR_HIPOW_L, 0x00},
+ {R367CAB_EQU_CTR_HIPOW_H, 0x00},
+ {R367CAB_EQU_I_EQU_LO, 0xef},
+ {R367CAB_EQU_I_EQU_HI, 0x00},
+ {R367CAB_EQU_Q_EQU_LO, 0xee},
+ {R367CAB_EQU_Q_EQU_HI, 0x00},
+ {R367CAB_EQU_MAPPER, 0xc5},
+ {R367CAB_EQU_SWEEP_RATE, 0x80},
+ {R367CAB_EQU_SNR_LO, 0x64},
+ {R367CAB_EQU_SNR_HI, 0x03},
+ {R367CAB_EQU_GAMMA_LO, 0x00},
+ {R367CAB_EQU_GAMMA_HI, 0x00},
+ {R367CAB_EQU_ERR_GAIN, 0x36},
+ {R367CAB_EQU_RADIUS, 0xaa},
+ {R367CAB_EQU_FFE_MAINTAP, 0x00},
+ {R367CAB_EQU_FFE_LEAKAGE, 0x63},
+ {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf},
+ {R367CAB_EQU_GAIN_WIDE, 0x88},
+ {R367CAB_EQU_GAIN_NARROW, 0x41},
+ {R367CAB_EQU_CTR_LPF_GAIN, 0xd1},
+ {R367CAB_EQU_CRL_LPF_GAIN, 0xa7},
+ {R367CAB_EQU_GLOBAL_GAIN, 0x06},
+ {R367CAB_EQU_CRL_LD_SEN, 0x85},
+ {R367CAB_EQU_CRL_LD_VAL, 0xe2},
+ {R367CAB_EQU_CRL_TFR, 0x20},
+ {R367CAB_EQU_CRL_BISTH_LO, 0x00},
+ {R367CAB_EQU_CRL_BISTH_HI, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_LO, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_HI, 0x00},
+ {R367CAB_EQU_CRL_LIMITER, 0x40},
+ {R367CAB_EQU_MODULUS_MAP, 0x90},
+ {R367CAB_EQU_PNT_GAIN, 0xa7},
+ {R367CAB_FEC_AC_CTR_0, 0x16},
+ {R367CAB_FEC_AC_CTR_1, 0x0b},
+ {R367CAB_FEC_AC_CTR_2, 0x88},
+ {R367CAB_FEC_AC_CTR_3, 0x02},
+ {R367CAB_FEC_STATUS, 0x12},
+ {R367CAB_RS_COUNTER_0, 0x7d},
+ {R367CAB_RS_COUNTER_1, 0xd0},
+ {R367CAB_RS_COUNTER_2, 0x19},
+ {R367CAB_RS_COUNTER_3, 0x0b},
+ {R367CAB_RS_COUNTER_4, 0xa3},
+ {R367CAB_RS_COUNTER_5, 0x00},
+ {R367CAB_BERT_0, 0x01},
+ {R367CAB_BERT_1, 0x25},
+ {R367CAB_BERT_2, 0x41},
+ {R367CAB_BERT_3, 0x39},
+ {R367CAB_OUTFORMAT_0, 0xc2},
+ {R367CAB_OUTFORMAT_1, 0x22},
+ {R367CAB_SMOOTHER_2, 0x28},
+ {R367CAB_TSMF_CTRL_0, 0x01},
+ {R367CAB_TSMF_CTRL_1, 0xc6},
+ {R367CAB_TSMF_CTRL_3, 0x43},
+ {R367CAB_TS_ON_ID_0, 0x00},
+ {R367CAB_TS_ON_ID_1, 0x00},
+ {R367CAB_TS_ON_ID_2, 0x00},
+ {R367CAB_TS_ON_ID_3, 0x00},
+ {R367CAB_RE_STATUS_0, 0x00},
+ {R367CAB_RE_STATUS_1, 0x00},
+ {R367CAB_RE_STATUS_2, 0x00},
+ {R367CAB_RE_STATUS_3, 0x00},
+ {R367CAB_TS_STATUS_0, 0x00},
+ {R367CAB_TS_STATUS_1, 0x00},
+ {R367CAB_TS_STATUS_2, 0xa0},
+ {R367CAB_TS_STATUS_3, 0x00},
+ {R367CAB_T_O_ID_0, 0x00},
+ {R367CAB_T_O_ID_1, 0x00},
+ {R367CAB_T_O_ID_2, 0x00},
+ {R367CAB_T_O_ID_3, 0x00},
+};
+
+static
+int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len)
+{
+ u8 buf[len + 2];
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = len + 2
+ };
+ int ret;
+
+ buf[0] = MSB(reg);
+ buf[1] = LSB(reg);
+ memcpy(buf + 2, data, len);
+
+ if (i2cdebug)
+ printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1)
+ printk(KERN_ERR "%s: i2c write error!\n", __func__);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int stv0367_writereg(struct stv0367_state *state, u16 reg, u8 data)
+{
+ return stv0367_writeregs(state, reg, &data, 1);
+}
+
+static u8 stv0367_readreg(struct stv0367_state *state, u16 reg)
+{
+ u8 b0[] = { 0, 0 };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 2
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+ int ret;
+
+ b0[0] = MSB(reg);
+ b0[1] = LSB(reg);
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2)
+ printk(KERN_ERR "%s: i2c read error\n", __func__);
+
+ if (i2cdebug)
+ printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]);
+
+ return b1[0];
+}
+
+static void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
+{
+ u8 position = 0, i = 0;
+
+ (*mask) = label & 0xff;
+
+ while ((position == 0) && (i < 8)) {
+ position = ((*mask) >> i) & 0x01;
+ i++;
+ }
+
+ (*pos) = (i - 1);
+}
+
+static void stv0367_writebits(struct stv0367_state *state, u32 label, u8 val)
+{
+ u8 reg, mask, pos;
+
+ reg = stv0367_readreg(state, (label >> 16) & 0xffff);
+ extract_mask_pos(label, &mask, &pos);
+
+ val = mask & (val << pos);
+
+ reg = (reg & (~mask)) | val;
+ stv0367_writereg(state, (label >> 16) & 0xffff, reg);
+
+}
+
+static void stv0367_setbits(u8 *reg, u32 label, u8 val)
+{
+ u8 mask, pos;
+
+ extract_mask_pos(label, &mask, &pos);
+
+ val = mask & (val << pos);
+
+ (*reg) = ((*reg) & (~mask)) | val;
+}
+
+static u8 stv0367_readbits(struct stv0367_state *state, u32 label)
+{
+ u8 val = 0xff;
+ u8 mask, pos;
+
+ extract_mask_pos(label, &mask, &pos);
+
+ val = stv0367_readreg(state, label >> 16);
+ val = (val & mask) >> pos;
+
+ return val;
+}
+
+u8 stv0367_getbits(u8 reg, u32 label)
+{
+ u8 mask, pos;
+
+ extract_mask_pos(label, &mask, &pos);
+
+ return (reg & mask) >> pos;
+}
+
+static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u8 tmp = stv0367_readreg(state, R367TER_I2CRPT);
+
+ dprintk("%s:\n", __func__);
+
+ if (enable) {
+ stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 0);
+ stv0367_setbits(&tmp, F367TER_I2CT_ON, 1);
+ } else {
+ stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 1);
+ stv0367_setbits(&tmp, F367TER_I2CT_ON, 0);
+ }
+
+ stv0367_writereg(state, R367TER_I2CRPT, tmp);
+
+ return 0;
+}
+
+static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ u32 freq = 0;
+ u32 err = 0;
+
+ dprintk("%s:\n", __func__);
+
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_frequency) {
+ err = tuner_ops->get_frequency(fe, &freq);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ return err;
+ }
+
+ dprintk("%s: frequency=%d\n", __func__, freq);
+
+ } else
+ return -1;
+
+ return freq;
+}
+
+static u16 CellsCoeffs_8MHz_367cofdm[3][6][5] = {
+ {
+ {0x10EF, 0xE205, 0x10EF, 0xCE49, 0x6DA7}, /* CELL 1 COEFFS 27M*/
+ {0x2151, 0xc557, 0x2151, 0xc705, 0x6f93}, /* CELL 2 COEFFS */
+ {0x2503, 0xc000, 0x2503, 0xc375, 0x7194}, /* CELL 3 COEFFS */
+ {0x20E9, 0xca94, 0x20e9, 0xc153, 0x7194}, /* CELL 4 COEFFS */
+ {0x06EF, 0xF852, 0x06EF, 0xC057, 0x7207}, /* CELL 5 COEFFS */
+ {0x0000, 0x0ECC, 0x0ECC, 0x0000, 0x3647} /* CELL 6 COEFFS */
+ }, {
+ {0x10A0, 0xE2AF, 0x10A1, 0xCE76, 0x6D6D}, /* CELL 1 COEFFS 25M*/
+ {0x20DC, 0xC676, 0x20D9, 0xC80A, 0x6F29},
+ {0x2532, 0xC000, 0x251D, 0xC391, 0x706F},
+ {0x1F7A, 0xCD2B, 0x2032, 0xC15E, 0x711F},
+ {0x0698, 0xFA5E, 0x0568, 0xC059, 0x7193},
+ {0x0000, 0x0918, 0x149C, 0x0000, 0x3642} /* CELL 6 COEFFS */
+ }, {
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}
+ }
+};
+
+static u16 CellsCoeffs_7MHz_367cofdm[3][6][5] = {
+ {
+ {0x12CA, 0xDDAF, 0x12CA, 0xCCEB, 0x6FB1}, /* CELL 1 COEFFS 27M*/
+ {0x2329, 0xC000, 0x2329, 0xC6B0, 0x725F}, /* CELL 2 COEFFS */
+ {0x2394, 0xC000, 0x2394, 0xC2C7, 0x7410}, /* CELL 3 COEFFS */
+ {0x251C, 0xC000, 0x251C, 0xC103, 0x74D9}, /* CELL 4 COEFFS */
+ {0x0804, 0xF546, 0x0804, 0xC040, 0x7544}, /* CELL 5 COEFFS */
+ {0x0000, 0x0CD9, 0x0CD9, 0x0000, 0x370A} /* CELL 6 COEFFS */
+ }, {
+ {0x1285, 0xDE47, 0x1285, 0xCD17, 0x6F76}, /*25M*/
+ {0x234C, 0xC000, 0x2348, 0xC6DA, 0x7206},
+ {0x23B4, 0xC000, 0x23AC, 0xC2DB, 0x73B3},
+ {0x253D, 0xC000, 0x25B6, 0xC10B, 0x747F},
+ {0x0721, 0xF79C, 0x065F, 0xC041, 0x74EB},
+ {0x0000, 0x08FA, 0x1162, 0x0000, 0x36FF}
+ }, {
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}
+ }
+};
+
+static u16 CellsCoeffs_6MHz_367cofdm[3][6][5] = {
+ {
+ {0x1699, 0xD5B8, 0x1699, 0xCBC3, 0x713B}, /* CELL 1 COEFFS 27M*/
+ {0x2245, 0xC000, 0x2245, 0xC568, 0x74D5}, /* CELL 2 COEFFS */
+ {0x227F, 0xC000, 0x227F, 0xC1FC, 0x76C6}, /* CELL 3 COEFFS */
+ {0x235E, 0xC000, 0x235E, 0xC0A7, 0x778A}, /* CELL 4 COEFFS */
+ {0x0ECB, 0xEA0B, 0x0ECB, 0xC027, 0x77DD}, /* CELL 5 COEFFS */
+ {0x0000, 0x0B68, 0x0B68, 0x0000, 0xC89A}, /* CELL 6 COEFFS */
+ }, {
+ {0x1655, 0xD64E, 0x1658, 0xCBEF, 0x70FE}, /*25M*/
+ {0x225E, 0xC000, 0x2256, 0xC589, 0x7489},
+ {0x2293, 0xC000, 0x2295, 0xC209, 0x767E},
+ {0x2377, 0xC000, 0x23AA, 0xC0AB, 0x7746},
+ {0x0DC7, 0xEBC8, 0x0D07, 0xC027, 0x7799},
+ {0x0000, 0x0888, 0x0E9C, 0x0000, 0x3757}
+
+ }, {
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}
+ }
+};
+
+static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz)
+{
+ u32 mclk_Hz = 0; /* master clock frequency (Hz) */
+ u32 m, n, p;
+
+ dprintk("%s:\n", __func__);
+
+ if (stv0367_readbits(state, F367TER_BYPASS_PLLXN) == 0) {
+ n = (u32)stv0367_readbits(state, F367TER_PLL_NDIV);
+ if (n == 0)
+ n = n + 1;
+
+ m = (u32)stv0367_readbits(state, F367TER_PLL_MDIV);
+ if (m == 0)
+ m = m + 1;
+
+ p = (u32)stv0367_readbits(state, F367TER_PLL_PDIV);
+ if (p > 5)
+ p = 5;
+
+ mclk_Hz = ((ExtClk_Hz / 2) * n) / (m * (1 << p));
+
+ dprintk("N=%d M=%d P=%d mclk_Hz=%d ExtClk_Hz=%d\n",
+ n, m, p, mclk_Hz, ExtClk_Hz);
+ } else
+ mclk_Hz = ExtClk_Hz;
+
+ dprintk("%s: mclk_Hz=%d\n", __func__, mclk_Hz);
+
+ return mclk_Hz;
+}
+
+static int stv0367ter_filt_coeff_init(struct stv0367_state *state,
+ u16 CellsCoeffs[2][6][5], u32 DemodXtal)
+{
+ int i, j, k, freq;
+
+ dprintk("%s:\n", __func__);
+
+ freq = stv0367ter_get_mclk(state, DemodXtal);
+
+ if (freq == 53125000)
+ k = 1; /* equivalent to Xtal 25M on 362*/
+ else if (freq == 54000000)
+ k = 0; /* equivalent to Xtal 27M on 362*/
+ else if (freq == 52500000)
+ k = 2; /* equivalent to Xtal 30M on 362*/
+ else
+ return 0;
+
+ for (i = 1; i <= 6; i++) {
+ stv0367_writebits(state, F367TER_IIR_CELL_NB, i - 1);
+
+ for (j = 1; j <= 5; j++) {
+ stv0367_writereg(state,
+ (R367TER_IIRCX_COEFF1_MSB + 2 * (j - 1)),
+ MSB(CellsCoeffs[k][i-1][j-1]));
+ stv0367_writereg(state,
+ (R367TER_IIRCX_COEFF1_LSB + 2 * (j - 1)),
+ LSB(CellsCoeffs[k][i-1][j-1]));
+ }
+ }
+
+ return 1;
+
+}
+
+static void stv0367ter_agc_iir_lock_detect_set(struct stv0367_state *state)
+{
+ dprintk("%s:\n", __func__);
+
+ stv0367_writebits(state, F367TER_LOCK_DETECT_LSB, 0x00);
+
+ /* Lock detect 1 */
+ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x00);
+ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04);
+
+ /* Lock detect 2 */
+ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x01);
+ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04);
+
+ /* Lock detect 3 */
+ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x02);
+ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00);
+
+ /* Lock detect 4 */
+ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x03);
+ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00);
+
+}
+
+static int stv0367_iir_filt_init(struct stv0367_state *state, u8 Bandwidth,
+ u32 DemodXtalValue)
+{
+ dprintk("%s:\n", __func__);
+
+ stv0367_writebits(state, F367TER_NRST_IIR, 0);
+
+ switch (Bandwidth) {
+ case 6:
+ if (!stv0367ter_filt_coeff_init(state,
+ CellsCoeffs_6MHz_367cofdm,
+ DemodXtalValue))
+ return 0;
+ break;
+ case 7:
+ if (!stv0367ter_filt_coeff_init(state,
+ CellsCoeffs_7MHz_367cofdm,
+ DemodXtalValue))
+ return 0;
+ break;
+ case 8:
+ if (!stv0367ter_filt_coeff_init(state,
+ CellsCoeffs_8MHz_367cofdm,
+ DemodXtalValue))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ stv0367_writebits(state, F367TER_NRST_IIR, 1);
+
+ return 1;
+}
+
+static void stv0367ter_agc_iir_rst(struct stv0367_state *state)
+{
+
+ u8 com_n;
+
+ dprintk("%s:\n", __func__);
+
+ com_n = stv0367_readbits(state, F367TER_COM_N);
+
+ stv0367_writebits(state, F367TER_COM_N, 0x07);
+
+ stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x00);
+ stv0367_writebits(state, F367TER_COM_AGC_ON, 0x00);
+
+ stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x01);
+ stv0367_writebits(state, F367TER_COM_AGC_ON, 0x01);
+
+ stv0367_writebits(state, F367TER_COM_N, com_n);
+
+}
+
+static int stv0367ter_duration(s32 mode, int tempo1, int tempo2, int tempo3)
+{
+ int local_tempo = 0;
+ switch (mode) {
+ case 0:
+ local_tempo = tempo1;
+ break;
+ case 1:
+ local_tempo = tempo2;
+ break ;
+
+ case 2:
+ local_tempo = tempo3;
+ break;
+
+ default:
+ break;
+ }
+ /* msleep(local_tempo); */
+ return local_tempo;
+}
+
+static enum
+stv0367_ter_signal_type stv0367ter_check_syr(struct stv0367_state *state)
+{
+ int wd = 100;
+ unsigned short int SYR_var;
+ s32 SYRStatus;
+
+ dprintk("%s:\n", __func__);
+
+ SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK);
+
+ while ((!SYR_var) && (wd > 0)) {
+ usleep_range(2000, 3000);
+ wd -= 2;
+ SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK);
+ }
+
+ if (!SYR_var)
+ SYRStatus = FE_TER_NOSYMBOL;
+ else
+ SYRStatus = FE_TER_SYMBOLOK;
+
+ dprintk("stv0367ter_check_syr SYRStatus %s\n",
+ SYR_var == 0 ? "No Symbol" : "OK");
+
+ return SYRStatus;
+}
+
+static enum
+stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state,
+ s32 FFTmode)
+{
+
+ s32 CPAMPvalue = 0, CPAMPStatus, CPAMPMin;
+ int wd = 0;
+
+ dprintk("%s:\n", __func__);
+
+ switch (FFTmode) {
+ case 0: /*2k mode*/
+ CPAMPMin = 20;
+ wd = 10;
+ break;
+ case 1: /*8k mode*/
+ CPAMPMin = 80;
+ wd = 55;
+ break;
+ case 2: /*4k mode*/
+ CPAMPMin = 40;
+ wd = 30;
+ break;
+ default:
+ CPAMPMin = 0xffff; /*drives to NOCPAMP */
+ break;
+ }
+
+ dprintk("%s: CPAMPMin=%d wd=%d\n", __func__, CPAMPMin, wd);
+
+ CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT);
+ while ((CPAMPvalue < CPAMPMin) && (wd > 0)) {
+ usleep_range(1000, 2000);
+ wd -= 1;
+ CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT);
+ /*dprintk("CPAMPvalue= %d at wd=%d\n",CPAMPvalue,wd); */
+ }
+ dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd);
+ if (CPAMPvalue < CPAMPMin) {
+ CPAMPStatus = FE_TER_NOCPAMP;
+ printk(KERN_ERR "CPAMP failed\n");
+ } else {
+ printk(KERN_ERR "CPAMP OK !\n");
+ CPAMPStatus = FE_TER_CPAMPOK;
+ }
+
+ return CPAMPStatus;
+}
+
+enum
+stv0367_ter_signal_type stv0367ter_lock_algo(struct stv0367_state *state)
+{
+ enum stv0367_ter_signal_type ret_flag;
+ short int wd, tempo;
+ u8 try, u_var1 = 0, u_var2 = 0, u_var3 = 0, u_var4 = 0, mode, guard;
+ u8 tmp, tmp2;
+
+ dprintk("%s:\n", __func__);
+
+ if (state == NULL)
+ return FE_TER_SWNOK;
+
+ try = 0;
+ do {
+ ret_flag = FE_TER_LOCKOK;
+
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0);
+
+ if (state->config->if_iq_mode != 0)
+ stv0367_writebits(state, F367TER_COM_N, 0x07);
+
+ stv0367_writebits(state, F367TER_GUARD, 3);/* suggest 2k 1/4 */
+ stv0367_writebits(state, F367TER_MODE, 0);
+ stv0367_writebits(state, F367TER_SYR_TR_DIS, 0);
+ usleep_range(5000, 10000);
+
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1);
+
+
+ if (stv0367ter_check_syr(state) == FE_TER_NOSYMBOL)
+ return FE_TER_NOSYMBOL;
+ else { /*
+ if chip locked on wrong mode first try,
+ it must lock correctly second try */
+ mode = stv0367_readbits(state, F367TER_SYR_MODE);
+ if (stv0367ter_check_cpamp(state, mode) ==
+ FE_TER_NOCPAMP) {
+ if (try == 0)
+ ret_flag = FE_TER_NOCPAMP;
+
+ }
+ }
+
+ try++;
+ } while ((try < 10) && (ret_flag != FE_TER_LOCKOK));
+
+ tmp = stv0367_readreg(state, R367TER_SYR_STAT);
+ tmp2 = stv0367_readreg(state, R367TER_STATUS);
+ dprintk("state=0x%x\n", (int)state);
+ dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n",
+ mode, tmp, tmp2);
+
+ tmp = stv0367_readreg(state, R367TER_PRVIT);
+ tmp2 = stv0367_readreg(state, R367TER_I2CRPT);
+ dprintk("PRVIT=0x%x I2CRPT=0x%x\n", tmp, tmp2);
+
+ tmp = stv0367_readreg(state, R367TER_GAIN_SRC1);
+ dprintk("GAIN_SRC1=0x%x\n", tmp);
+
+ if ((mode != 0) && (mode != 1) && (mode != 2))
+ return FE_TER_SWNOK;
+
+ /*guard=stv0367_readbits(state,F367TER_SYR_GUARD); */
+
+ /*supress EPQ auto for SYR_GARD 1/16 or 1/32
+ and set channel predictor in automatic */
+#if 0
+ switch (guard) {
+
+ case 0:
+ case 1:
+ stv0367_writebits(state, F367TER_AUTO_LE_EN, 0);
+ stv0367_writereg(state, R367TER_CHC_CTL, 0x01);
+ break;
+ case 2:
+ case 3:
+ stv0367_writebits(state, F367TER_AUTO_LE_EN, 1);
+ stv0367_writereg(state, R367TER_CHC_CTL, 0x11);
+ break;
+
+ default:
+ return FE_TER_SWNOK;
+ }
+#endif
+
+ /*reset fec an reedsolo FOR 367 only*/
+ stv0367_writebits(state, F367TER_RST_SFEC, 1);
+ stv0367_writebits(state, F367TER_RST_REEDSOLO, 1);
+ usleep_range(1000, 2000);
+ stv0367_writebits(state, F367TER_RST_SFEC, 0);
+ stv0367_writebits(state, F367TER_RST_REEDSOLO, 0);
+
+ u_var1 = stv0367_readbits(state, F367TER_LK);
+ u_var2 = stv0367_readbits(state, F367TER_PRF);
+ u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK);
+ /* u_var4=stv0367_readbits(state,F367TER_TSFIFO_LINEOK); */
+
+ wd = stv0367ter_duration(mode, 125, 500, 250);
+ tempo = stv0367ter_duration(mode, 4, 16, 8);
+
+ /*while ( ((!u_var1)||(!u_var2)||(!u_var3)||(!u_var4)) && (wd>=0)) */
+ while (((!u_var1) || (!u_var2) || (!u_var3)) && (wd >= 0)) {
+ usleep_range(1000 * tempo, 1000 * (tempo + 1));
+ wd -= tempo;
+ u_var1 = stv0367_readbits(state, F367TER_LK);
+ u_var2 = stv0367_readbits(state, F367TER_PRF);
+ u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK);
+ /*u_var4=stv0367_readbits(state, F367TER_TSFIFO_LINEOK); */
+ }
+
+ if (!u_var1)
+ return FE_TER_NOLOCK;
+
+
+ if (!u_var2)
+ return FE_TER_NOPRFOUND;
+
+ if (!u_var3)
+ return FE_TER_NOTPS;
+
+ guard = stv0367_readbits(state, F367TER_SYR_GUARD);
+ stv0367_writereg(state, R367TER_CHC_CTL, 0x11);
+ switch (guard) {
+ case 0:
+ case 1:
+ stv0367_writebits(state, F367TER_AUTO_LE_EN, 0);
+ /*stv0367_writereg(state,R367TER_CHC_CTL, 0x1);*/
+ stv0367_writebits(state, F367TER_SYR_FILTER, 0);
+ break;
+ case 2:
+ case 3:
+ stv0367_writebits(state, F367TER_AUTO_LE_EN, 1);
+ /*stv0367_writereg(state,R367TER_CHC_CTL, 0x11);*/
+ stv0367_writebits(state, F367TER_SYR_FILTER, 1);
+ break;
+
+ default:
+ return FE_TER_SWNOK;
+ }
+
+ /* apply Sfec workaround if 8K 64QAM CR!=1/2*/
+ if ((stv0367_readbits(state, F367TER_TPS_CONST) == 2) &&
+ (mode == 1) &&
+ (stv0367_readbits(state, F367TER_TPS_HPCODE) != 0)) {
+ stv0367_writereg(state, R367TER_SFDLYSETH, 0xc0);
+ stv0367_writereg(state, R367TER_SFDLYSETM, 0x60);
+ stv0367_writereg(state, R367TER_SFDLYSETL, 0x0);
+ } else
+ stv0367_writereg(state, R367TER_SFDLYSETH, 0x0);
+
+ wd = stv0367ter_duration(mode, 125, 500, 250);
+ u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK);
+
+ while ((!u_var4) && (wd >= 0)) {
+ usleep_range(1000 * tempo, 1000 * (tempo + 1));
+ wd -= tempo;
+ u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK);
+ }
+
+ if (!u_var4)
+ return FE_TER_NOLOCK;
+
+ /* for 367 leave COM_N at 0x7 for IQ_mode*/
+ /*if(ter_state->if_iq_mode!=FE_TER_NORMAL_IF_TUNER) {
+ tempo=0;
+ while ((stv0367_readbits(state,F367TER_COM_USEGAINTRK)!=1) &&
+ (stv0367_readbits(state,F367TER_COM_AGCLOCK)!=1)&&(tempo<100)) {
+ ChipWaitOrAbort(state,1);
+ tempo+=1;
+ }
+
+ stv0367_writebits(state,F367TER_COM_N,0x17);
+ } */
+
+ stv0367_writebits(state, F367TER_SYR_TR_DIS, 1);
+
+ dprintk("FE_TER_LOCKOK !!!\n");
+
+ return FE_TER_LOCKOK;
+
+}
+
+static void stv0367ter_set_ts_mode(struct stv0367_state *state,
+ enum stv0367_ts_mode PathTS)
+{
+
+ dprintk("%s:\n", __func__);
+
+ if (state == NULL)
+ return;
+
+ stv0367_writebits(state, F367TER_TS_DIS, 0);
+ switch (PathTS) {
+ default:
+ /*for removing warning :default we can assume in parallel mode*/
+ case STV0367_PARALLEL_PUNCT_CLOCK:
+ stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 0);
+ stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 0);
+ break;
+ case STV0367_SERIAL_PUNCT_CLOCK:
+ stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 1);
+ stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 1);
+ break;
+ }
+}
+
+static void stv0367ter_set_clk_pol(struct stv0367_state *state,
+ enum stv0367_clk_pol clock)
+{
+
+ dprintk("%s:\n", __func__);
+
+ if (state == NULL)
+ return;
+
+ switch (clock) {
+ case STV0367_RISINGEDGE_CLOCK:
+ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 1);
+ break;
+ case STV0367_FALLINGEDGE_CLOCK:
+ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0);
+ break;
+ /*case FE_TER_CLOCK_POLARITY_DEFAULT:*/
+ default:
+ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0);
+ break;
+ }
+}
+
+#if 0
+static void stv0367ter_core_sw(struct stv0367_state *state)
+{
+
+ dprintk("%s:\n", __func__);
+
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0);
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1);
+ msleep(350);
+}
+#endif
+static int stv0367ter_standby(struct dvb_frontend *fe, u8 standby_on)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ dprintk("%s:\n", __func__);
+
+ if (standby_on) {
+ stv0367_writebits(state, F367TER_STDBY, 1);
+ stv0367_writebits(state, F367TER_STDBY_FEC, 1);
+ stv0367_writebits(state, F367TER_STDBY_CORE, 1);
+ } else {
+ stv0367_writebits(state, F367TER_STDBY, 0);
+ stv0367_writebits(state, F367TER_STDBY_FEC, 0);
+ stv0367_writebits(state, F367TER_STDBY_CORE, 0);
+ }
+
+ return 0;
+}
+
+static int stv0367ter_sleep(struct dvb_frontend *fe)
+{
+ return stv0367ter_standby(fe, 1);
+}
+
+int stv0367ter_init(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ int i;
+
+ dprintk("%s:\n", __func__);
+
+ ter_state->pBER = 0;
+
+ for (i = 0; i < STV0367TER_NBREGS; i++)
+ stv0367_writereg(state, def0367ter[i].addr,
+ def0367ter[i].value);
+
+ switch (state->config->xtal) {
+ /*set internal freq to 53.125MHz */
+ case 25000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0xa);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
+ stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
+ break;
+ default:
+ case 27000000:
+ dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n");
+ stv0367_writereg(state, R367TER_PLLMDIV, 0x1);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x8);
+ stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
+ break;
+ case 30000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0xc);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
+ stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
+ break;
+ }
+
+ stv0367_writereg(state, R367TER_I2CRPT, 0xa0);
+ stv0367_writereg(state, R367TER_ANACTRL, 0x00);
+
+ /*Set TS1 and TS2 to serial or parallel mode */
+ stv0367ter_set_ts_mode(state, state->config->ts_mode);
+ stv0367ter_set_clk_pol(state, state->config->clk_pol);
+
+ state->chip_id = stv0367_readreg(state, R367TER_ID);
+ ter_state->first_lock = 0;
+ ter_state->unlock_counter = 2;
+
+ return 0;
+}
+
+static int stv0367ter_algo(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ int offset = 0, tempo = 0;
+ u8 u_var;
+ u8 /*constell,*/ counter, tps_rcvd[2];
+ s8 step;
+ s32 timing_offset = 0;
+ u32 trl_nomrate = 0, InternalFreq = 0, temp = 0;
+
+ dprintk("%s:\n", __func__);
+
+ ter_state->frequency = param->frequency;
+ ter_state->force = FE_TER_FORCENONE
+ + stv0367_readbits(state, F367TER_FORCE) * 2;
+ ter_state->if_iq_mode = state->config->if_iq_mode;
+ switch (state->config->if_iq_mode) {
+ case FE_TER_NORMAL_IF_TUNER: /* Normal IF mode */
+ dprintk("ALGO: FE_TER_NORMAL_IF_TUNER selected\n");
+ stv0367_writebits(state, F367TER_TUNER_BB, 0);
+ stv0367_writebits(state, F367TER_LONGPATH_IF, 0);
+ stv0367_writebits(state, F367TER_DEMUX_SWAP, 0);
+ break;
+ case FE_TER_LONGPATH_IF_TUNER: /* Long IF mode */
+ dprintk("ALGO: FE_TER_LONGPATH_IF_TUNER selected\n");
+ stv0367_writebits(state, F367TER_TUNER_BB, 0);
+ stv0367_writebits(state, F367TER_LONGPATH_IF, 1);
+ stv0367_writebits(state, F367TER_DEMUX_SWAP, 1);
+ break;
+ case FE_TER_IQ_TUNER: /* IQ mode */
+ dprintk("ALGO: FE_TER_IQ_TUNER selected\n");
+ stv0367_writebits(state, F367TER_TUNER_BB, 1);
+ stv0367_writebits(state, F367TER_PPM_INVSEL, 0);
+ break;
+ default:
+ printk(KERN_ERR "ALGO: wrong TUNER type selected\n");
+ return -EINVAL;
+ }
+
+ usleep_range(5000, 7000);
+
+ switch (param->inversion) {
+ case INVERSION_AUTO:
+ default:
+ dprintk("%s: inversion AUTO\n", __func__);
+ if (ter_state->if_iq_mode == FE_TER_IQ_TUNER)
+ stv0367_writebits(state, F367TER_IQ_INVERT,
+ ter_state->sense);
+ else
+ stv0367_writebits(state, F367TER_INV_SPECTR,
+ ter_state->sense);
+
+ break;
+ case INVERSION_ON:
+ case INVERSION_OFF:
+ if (ter_state->if_iq_mode == FE_TER_IQ_TUNER)
+ stv0367_writebits(state, F367TER_IQ_INVERT,
+ param->inversion);
+ else
+ stv0367_writebits(state, F367TER_INV_SPECTR,
+ param->inversion);
+
+ break;
+ }
+
+ if ((ter_state->if_iq_mode != FE_TER_NORMAL_IF_TUNER) &&
+ (ter_state->pBW != ter_state->bw)) {
+ stv0367ter_agc_iir_lock_detect_set(state);
+
+ /*set fine agc target to 180 for LPIF or IQ mode*/
+ /* set Q_AGCTarget */
+ stv0367_writebits(state, F367TER_SEL_IQNTAR, 1);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB);
+ /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */
+
+ /* set Q_AGCTarget */
+ stv0367_writebits(state, F367TER_SEL_IQNTAR, 0);
+ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB);
+ /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */
+
+ if (!stv0367_iir_filt_init(state, ter_state->bw,
+ state->config->xtal))
+ return -EINVAL;
+ /*set IIR filter once for 6,7 or 8MHz BW*/
+ ter_state->pBW = ter_state->bw;
+
+ stv0367ter_agc_iir_rst(state);
+ }
+
+ if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO)
+ stv0367_writebits(state, F367TER_BDI_LPSEL, 0x01);
+ else
+ stv0367_writebits(state, F367TER_BDI_LPSEL, 0x00);
+
+ InternalFreq = stv0367ter_get_mclk(state, state->config->xtal) / 1000;
+ temp = (int)
+ ((((ter_state->bw * 64 * (1 << 15) * 100)
+ / (InternalFreq)) * 10) / 7);
+
+ stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, temp % 2);
+ temp = temp / 2;
+ stv0367_writebits(state, F367TER_TRL_NOMRATE_HI, temp / 256);
+ stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, temp % 256);
+
+ temp = stv0367_readbits(state, F367TER_TRL_NOMRATE_HI) * 512 +
+ stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 +
+ stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB);
+ temp = (int)(((1 << 17) * ter_state->bw * 1000) / (7 * (InternalFreq)));
+ stv0367_writebits(state, F367TER_GAIN_SRC_HI, temp / 256);
+ stv0367_writebits(state, F367TER_GAIN_SRC_LO, temp % 256);
+ temp = stv0367_readbits(state, F367TER_GAIN_SRC_HI) * 256 +
+ stv0367_readbits(state, F367TER_GAIN_SRC_LO);
+
+ temp = (int)
+ ((InternalFreq - state->config->if_khz) * (1 << 16)
+ / (InternalFreq));
+
+ dprintk("DEROT temp=0x%x\n", temp);
+ stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256);
+ stv0367_writebits(state, F367TER_INC_DEROT_LO, temp % 256);
+
+ ter_state->echo_pos = 0;
+ ter_state->ucblocks = 0; /* liplianin */
+ ter_state->pBER = 0; /* liplianin */
+ stv0367_writebits(state, F367TER_LONG_ECHO, ter_state->echo_pos);
+
+ if (stv0367ter_lock_algo(state) != FE_TER_LOCKOK)
+ return 0;
+
+ ter_state->state = FE_TER_LOCKOK;
+ /* update results */
+ tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2);
+ tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3);
+
+ ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE);
+ ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD);
+
+ ter_state->first_lock = 1; /* we know sense now :) */
+
+ ter_state->agc_val =
+ (stv0367_readbits(state, F367TER_AGC1_VAL_LO) << 16) +
+ (stv0367_readbits(state, F367TER_AGC1_VAL_HI) << 24) +
+ stv0367_readbits(state, F367TER_AGC2_VAL_LO) +
+ (stv0367_readbits(state, F367TER_AGC2_VAL_HI) << 8);
+
+ /* Carrier offset calculation */
+ stv0367_writebits(state, F367TER_FREEZE, 1);
+ offset = (stv0367_readbits(state, F367TER_CRL_FOFFSET_VHI) << 16) ;
+ offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_HI) << 8);
+ offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_LO));
+ stv0367_writebits(state, F367TER_FREEZE, 0);
+ if (offset > 8388607)
+ offset -= 16777216;
+
+ offset = offset * 2 / 16384;
+
+ if (ter_state->mode == FE_TER_MODE_2K)
+ offset = (offset * 4464) / 1000;/*** 1 FFT BIN=4.464khz***/
+ else if (ter_state->mode == FE_TER_MODE_4K)
+ offset = (offset * 223) / 100;/*** 1 FFT BIN=2.23khz***/
+ else if (ter_state->mode == FE_TER_MODE_8K)
+ offset = (offset * 111) / 100;/*** 1 FFT BIN=1.1khz***/
+
+ if (stv0367_readbits(state, F367TER_PPM_INVSEL) == 1) {
+ if ((stv0367_readbits(state, F367TER_INV_SPECTR) ==
+ (stv0367_readbits(state,
+ F367TER_STATUS_INV_SPECRUM) == 1)))
+ offset = offset * -1;
+ }
+
+ if (ter_state->bw == 6)
+ offset = (offset * 6) / 8;
+ else if (ter_state->bw == 7)
+ offset = (offset * 7) / 8;
+
+ ter_state->frequency += offset;
+
+ tempo = 10; /* exit even if timing_offset stays null */
+ while ((timing_offset == 0) && (tempo > 0)) {
+ usleep_range(10000, 20000); /*was 20ms */
+ /* fine tuning of timing offset if required */
+ timing_offset = stv0367_readbits(state, F367TER_TRL_TOFFSET_LO)
+ + 256 * stv0367_readbits(state,
+ F367TER_TRL_TOFFSET_HI);
+ if (timing_offset >= 32768)
+ timing_offset -= 65536;
+ trl_nomrate = (512 * stv0367_readbits(state,
+ F367TER_TRL_NOMRATE_HI)
+ + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2
+ + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB));
+
+ timing_offset = ((signed)(1000000 / trl_nomrate) *
+ timing_offset) / 2048;
+ tempo--;
+ }
+
+ if (timing_offset <= 0) {
+ timing_offset = (timing_offset - 11) / 22;
+ step = -1;
+ } else {
+ timing_offset = (timing_offset + 11) / 22;
+ step = 1;
+ }
+
+ for (counter = 0; counter < abs(timing_offset); counter++) {
+ trl_nomrate += step;
+ stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB,
+ trl_nomrate % 2);
+ stv0367_writebits(state, F367TER_TRL_NOMRATE_LO,
+ trl_nomrate / 2);
+ usleep_range(1000, 2000);
+ }
+
+ usleep_range(5000, 6000);
+ /* unlocks could happen in case of trl centring big step,
+ then a core off/on restarts demod */
+ u_var = stv0367_readbits(state, F367TER_LK);
+
+ if (!u_var) {
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0);
+ msleep(20);
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1);
+ }
+
+ return 0;
+}
+
+static int stv0367ter_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+
+ /*u8 trials[2]; */
+ s8 num_trials, index;
+ u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF };
+
+ stv0367ter_init(fe);
+
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.set_params(fe, param);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ switch (op->transmission_mode) {
+ default:
+ case TRANSMISSION_MODE_AUTO:
+ case TRANSMISSION_MODE_2K:
+ ter_state->mode = FE_TER_MODE_2K;
+ break;
+/* case TRANSMISSION_MODE_4K:
+ pLook.mode = FE_TER_MODE_4K;
+ break;*/
+ case TRANSMISSION_MODE_8K:
+ ter_state->mode = FE_TER_MODE_8K;
+ break;
+ }
+
+ switch (op->guard_interval) {
+ default:
+ case GUARD_INTERVAL_1_32:
+ case GUARD_INTERVAL_1_16:
+ case GUARD_INTERVAL_1_8:
+ case GUARD_INTERVAL_1_4:
+ ter_state->guard = op->guard_interval;
+ break;
+ case GUARD_INTERVAL_AUTO:
+ ter_state->guard = GUARD_INTERVAL_1_32;
+ break;
+ }
+
+ switch (op->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ ter_state->bw = FE_TER_CHAN_BW_6M;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ter_state->bw = FE_TER_CHAN_BW_7M;
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ ter_state->bw = FE_TER_CHAN_BW_8M;
+ }
+
+ ter_state->hierarchy = FE_TER_HIER_NONE;
+
+ switch (param->inversion) {
+ case INVERSION_OFF:
+ case INVERSION_ON:
+ num_trials = 1;
+ break;
+ default:
+ num_trials = 2;
+ if (ter_state->first_lock)
+ num_trials = 1;
+ break;
+ }
+
+ ter_state->state = FE_TER_NOLOCK;
+ index = 0;
+
+ while (((index) < num_trials) && (ter_state->state != FE_TER_LOCKOK)) {
+ if (!ter_state->first_lock) {
+ if (param->inversion == INVERSION_AUTO)
+ ter_state->sense = SenseTrials[index];
+
+ }
+ stv0367ter_algo(fe,/* &pLook, result,*/ param);
+
+ if ((ter_state->state == FE_TER_LOCKOK) &&
+ (param->inversion == INVERSION_AUTO) &&
+ (index == 1)) {
+ /* invert spectrum sense */
+ SenseTrials[index] = SenseTrials[0];
+ SenseTrials[(index + 1) % 2] = (SenseTrials[1] + 1) % 2;
+ }
+
+ index++;
+ }
+
+ return 0;
+}
+
+static int stv0367ter_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ u32 errs = 0;
+
+ /*wait for counting completion*/
+ if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) {
+ errs =
+ ((u32)stv0367_readbits(state, F367TER_ERR_CNT1)
+ * (1 << 16))
+ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI)
+ * (1 << 8))
+ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO));
+ ter_state->ucblocks = errs;
+ }
+
+ (*ucblocks) = ter_state->ucblocks;
+
+ return 0;
+}
+
+static int stv0367ter_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ int error = 0;
+ enum stv0367_ter_mode mode;
+ int constell = 0,/* snr = 0,*/ Data = 0;
+
+ param->frequency = stv0367_get_tuner_freq(fe);
+ if (param->frequency < 0)
+ param->frequency = c->frequency;
+
+ constell = stv0367_readbits(state, F367TER_TPS_CONST);
+ if (constell == 0)
+ op->constellation = QPSK;
+ else if (constell == 1)
+ op->constellation = QAM_16;
+ else
+ op->constellation = QAM_64;
+
+ param->inversion = stv0367_readbits(state, F367TER_INV_SPECTR);
+
+ /* Get the Hierarchical mode */
+ Data = stv0367_readbits(state, F367TER_TPS_HIERMODE);
+
+ switch (Data) {
+ case 0:
+ op->hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ op->hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ op->hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ op->hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ op->hierarchy_information = HIERARCHY_AUTO;
+ break; /* error */
+ }
+
+ /* Get the FEC Rate */
+ if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO)
+ Data = stv0367_readbits(state, F367TER_TPS_LPCODE);
+ else
+ Data = stv0367_readbits(state, F367TER_TPS_HPCODE);
+
+ switch (Data) {
+ case 0:
+ op->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ op->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ op->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ op->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ op->code_rate_HP = FEC_7_8;
+ break;
+ default:
+ op->code_rate_HP = FEC_AUTO;
+ break; /* error */
+ }
+
+ mode = stv0367_readbits(state, F367TER_SYR_MODE);
+
+ switch (mode) {
+ case FE_TER_MODE_2K:
+ op->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+/* case FE_TER_MODE_4K:
+ op->transmission_mode = TRANSMISSION_MODE_4K;
+ break;*/
+ case FE_TER_MODE_8K:
+ op->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ op->transmission_mode = TRANSMISSION_MODE_AUTO;
+ }
+
+ op->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD);
+
+ return error;
+}
+
+static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u32 snru32 = 0;
+ int cpt = 0;
+ u8 cut = stv0367_readbits(state, F367TER_IDENTIFICATIONREG);
+
+ while (cpt < 10) {
+ usleep_range(2000, 3000);
+ if (cut == 0x50) /*cut 1.0 cut 1.1*/
+ snru32 += stv0367_readbits(state, F367TER_CHCSNR) / 4;
+ else /*cu2.0*/
+ snru32 += 125 * stv0367_readbits(state, F367TER_CHCSNR);
+
+ cpt++;
+ }
+
+ snru32 /= 10;/*average on 10 values*/
+
+ *snr = snru32 / 1000;
+
+ return 0;
+}
+
+#if 0
+static int stv0367ter_status(struct dvb_frontend *fe)
+{
+
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ int locked = FALSE;
+
+ locked = (stv0367_readbits(state, F367TER_LK));
+ if (!locked)
+ ter_state->unlock_counter += 1;
+ else
+ ter_state->unlock_counter = 0;
+
+ if (ter_state->unlock_counter > 2) {
+ if (!stv0367_readbits(state, F367TER_TPS_LOCK) ||
+ (!stv0367_readbits(state, F367TER_LK))) {
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0);
+ usleep_range(2000, 3000);
+ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1);
+ msleep(350);
+ locked = (stv0367_readbits(state, F367TER_TPS_LOCK)) &&
+ (stv0367_readbits(state, F367TER_LK));
+ }
+
+ }
+
+ return locked;
+}
+#endif
+static int stv0367ter_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ dprintk("%s:\n", __func__);
+
+ *status = 0;
+
+ if (stv0367_readbits(state, F367TER_LK)) {
+ *status |= FE_HAS_LOCK;
+ dprintk("%s: stv0367 has locked\n", __func__);
+ }
+
+ return 0;
+}
+
+static int stv0367ter_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367ter_state *ter_state = state->ter_state;
+ u32 Errors = 0, tber = 0, temporary = 0;
+ int abc = 0, def = 0;
+
+
+ /*wait for counting completion*/
+ if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0)
+ Errors = ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT)
+ * (1 << 16))
+ + ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT_HI)
+ * (1 << 8))
+ + ((u32)stv0367_readbits(state,
+ F367TER_SFEC_ERR_CNT_LO));
+ /*measurement not completed, load previous value*/
+ else {
+ tber = ter_state->pBER;
+ return 0;
+ }
+
+ abc = stv0367_readbits(state, F367TER_SFEC_ERR_SOURCE);
+ def = stv0367_readbits(state, F367TER_SFEC_NUM_EVENT);
+
+ if (Errors == 0) {
+ tber = 0;
+ } else if (abc == 0x7) {
+ if (Errors <= 4) {
+ temporary = (Errors * 1000000000) / (8 * (1 << 14));
+ temporary = temporary;
+ } else if (Errors <= 42) {
+ temporary = (Errors * 100000000) / (8 * (1 << 14));
+ temporary = temporary * 10;
+ } else if (Errors <= 429) {
+ temporary = (Errors * 10000000) / (8 * (1 << 14));
+ temporary = temporary * 100;
+ } else if (Errors <= 4294) {
+ temporary = (Errors * 1000000) / (8 * (1 << 14));
+ temporary = temporary * 1000;
+ } else if (Errors <= 42949) {
+ temporary = (Errors * 100000) / (8 * (1 << 14));
+ temporary = temporary * 10000;
+ } else if (Errors <= 429496) {
+ temporary = (Errors * 10000) / (8 * (1 << 14));
+ temporary = temporary * 100000;
+ } else { /*if (Errors<4294967) 2^22 max error*/
+ temporary = (Errors * 1000) / (8 * (1 << 14));
+ temporary = temporary * 100000; /* still to *10 */
+ }
+
+ /* Byte error*/
+ if (def == 2)
+ /*tber=Errors/(8*(1 <<14));*/
+ tber = temporary;
+ else if (def == 3)
+ /*tber=Errors/(8*(1 <<16));*/
+ tber = temporary / 4;
+ else if (def == 4)
+ /*tber=Errors/(8*(1 <<18));*/
+ tber = temporary / 16;
+ else if (def == 5)
+ /*tber=Errors/(8*(1 <<20));*/
+ tber = temporary / 64;
+ else if (def == 6)
+ /*tber=Errors/(8*(1 <<22));*/
+ tber = temporary / 256;
+ else
+ /* should not pass here*/
+ tber = 0;
+
+ if ((Errors < 4294967) && (Errors > 429496))
+ tber *= 10;
+
+ }
+
+ /* save actual value */
+ ter_state->pBER = tber;
+
+ (*ber) = tber;
+
+ return 0;
+}
+#if 0
+static u32 stv0367ter_get_per(struct stv0367_state *state)
+{
+ struct stv0367ter_state *ter_state = state->ter_state;
+ u32 Errors = 0, Per = 0, temporary = 0;
+ int abc = 0, def = 0, cpt = 0;
+
+ while (((stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 1) &&
+ (cpt < 400)) || ((Errors == 0) && (cpt < 400))) {
+ usleep_range(1000, 2000);
+ Errors = ((u32)stv0367_readbits(state, F367TER_ERR_CNT1)
+ * (1 << 16))
+ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI)
+ * (1 << 8))
+ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO));
+ cpt++;
+ }
+ abc = stv0367_readbits(state, F367TER_ERR_SRC1);
+ def = stv0367_readbits(state, F367TER_NUM_EVT1);
+
+ if (Errors == 0)
+ Per = 0;
+ else if (abc == 0x9) {
+ if (Errors <= 4) {
+ temporary = (Errors * 1000000000) / (8 * (1 << 8));
+ temporary = temporary;
+ } else if (Errors <= 42) {
+ temporary = (Errors * 100000000) / (8 * (1 << 8));
+ temporary = temporary * 10;
+ } else if (Errors <= 429) {
+ temporary = (Errors * 10000000) / (8 * (1 << 8));
+ temporary = temporary * 100;
+ } else if (Errors <= 4294) {
+ temporary = (Errors * 1000000) / (8 * (1 << 8));
+ temporary = temporary * 1000;
+ } else if (Errors <= 42949) {
+ temporary = (Errors * 100000) / (8 * (1 << 8));
+ temporary = temporary * 10000;
+ } else { /*if(Errors<=429496) 2^16 errors max*/
+ temporary = (Errors * 10000) / (8 * (1 << 8));
+ temporary = temporary * 100000;
+ }
+
+ /* pkt error*/
+ if (def == 2)
+ /*Per=Errors/(1 << 8);*/
+ Per = temporary;
+ else if (def == 3)
+ /*Per=Errors/(1 << 10);*/
+ Per = temporary / 4;
+ else if (def == 4)
+ /*Per=Errors/(1 << 12);*/
+ Per = temporary / 16;
+ else if (def == 5)
+ /*Per=Errors/(1 << 14);*/
+ Per = temporary / 64;
+ else if (def == 6)
+ /*Per=Errors/(1 << 16);*/
+ Per = temporary / 256;
+ else
+ Per = 0;
+
+ }
+ /* save actual value */
+ ter_state->pPER = Per;
+
+ return Per;
+}
+#endif
+static int stv0367_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings
+ *fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 1000;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+
+ return 0;
+}
+
+static void stv0367_release(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ kfree(state->ter_state);
+ kfree(state->cab_state);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops stv0367ter_ops = {
+ .info = {
+ .name = "ST STV0367 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 47000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 15625,
+ .frequency_tolerance = 0,
+ .caps = 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_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER |
+ FE_CAN_INVERSION_AUTO |
+ FE_CAN_MUTE_TS
+ },
+ .release = stv0367_release,
+ .init = stv0367ter_init,
+ .sleep = stv0367ter_sleep,
+ .i2c_gate_ctrl = stv0367ter_gate_ctrl,
+ .set_frontend = stv0367ter_set_frontend,
+ .get_frontend = stv0367ter_get_frontend,
+ .get_tune_settings = stv0367_get_tune_settings,
+ .read_status = stv0367ter_read_status,
+ .read_ber = stv0367ter_read_ber,/* too slow */
+/* .read_signal_strength = stv0367_read_signal_strength,*/
+ .read_snr = stv0367ter_read_snr,
+ .read_ucblocks = stv0367ter_read_ucblocks,
+};
+
+struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stv0367_state *state = NULL;
+ struct stv0367ter_state *ter_state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL);
+ if (ter_state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ state->config = config;
+ state->ter_state = ter_state;
+ state->fe.ops = stv0367ter_ops;
+ state->fe.demodulator_priv = state;
+ state->chip_id = stv0367_readreg(state, 0xf000);
+
+ dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id);
+
+ /* check if the demod is there */
+ if ((state->chip_id != 0x50) && (state->chip_id != 0x60))
+ goto error;
+
+ return &state->fe;
+
+error:
+ kfree(ter_state);
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stv0367ter_attach);
+
+static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ dprintk("%s:\n", __func__);
+
+ stv0367_writebits(state, F367CAB_I2CT_ON, (enable > 0) ? 1 : 0);
+
+ return 0;
+}
+
+static u32 stv0367cab_get_mclk(struct dvb_frontend *fe, u32 ExtClk_Hz)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u32 mclk_Hz = 0;/* master clock frequency (Hz) */
+ u32 M, N, P;
+
+
+ if (stv0367_readbits(state, F367CAB_BYPASS_PLLXN) == 0) {
+ N = (u32)stv0367_readbits(state, F367CAB_PLL_NDIV);
+ if (N == 0)
+ N = N + 1;
+
+ M = (u32)stv0367_readbits(state, F367CAB_PLL_MDIV);
+ if (M == 0)
+ M = M + 1;
+
+ P = (u32)stv0367_readbits(state, F367CAB_PLL_PDIV);
+
+ if (P > 5)
+ P = 5;
+
+ mclk_Hz = ((ExtClk_Hz / 2) * N) / (M * (1 << P));
+ dprintk("stv0367cab_get_mclk BYPASS_PLLXN mclk_Hz=%d\n",
+ mclk_Hz);
+ } else
+ mclk_Hz = ExtClk_Hz;
+
+ dprintk("stv0367cab_get_mclk final mclk_Hz=%d\n", mclk_Hz);
+
+ return mclk_Hz;
+}
+
+static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz)
+{
+ u32 ADCClk_Hz = ExtClk_Hz;
+
+ ADCClk_Hz = stv0367cab_get_mclk(fe, ExtClk_Hz);
+
+ return ADCClk_Hz;
+}
+
+enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
+ u32 SymbolRate,
+ enum stv0367cab_mod QAMSize)
+{
+ /* Set QAM size */
+ stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize);
+
+ /* Set Registers settings specific to the QAM size */
+ switch (QAMSize) {
+ case FE_CAB_MOD_QAM4:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ break;
+ case FE_CAB_MOD_QAM16:
+ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x64);
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0x90);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40);
+ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x8a);
+ break;
+ case FE_CAB_MOD_QAM32:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x6e);
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xb7);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x9d);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f);
+ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7);
+ break;
+ case FE_CAB_MOD_QAM64:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82);
+ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a);
+ if (SymbolRate > 45000000) {
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5);
+ } else if (SymbolRate > 25000000) {
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6);
+ } else {
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7);
+ }
+ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40);
+ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x99);
+ break;
+ case FE_CAB_MOD_QAM128:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76);
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0x90);
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1);
+ if (SymbolRate > 45000000)
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7);
+ else if (SymbolRate > 25000000)
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6);
+ else
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97);
+
+ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x8e);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f);
+ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7);
+ break;
+ case FE_CAB_MOD_QAM256:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94);
+ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a);
+ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0);
+ if (SymbolRate > 45000000)
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ else if (SymbolRate > 25000000)
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
+ else
+ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1);
+
+ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x85);
+ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40);
+ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7);
+ break;
+ case FE_CAB_MOD_QAM512:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ break;
+ case FE_CAB_MOD_QAM1024:
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00);
+ break;
+ default:
+ break;
+ }
+
+ return QAMSize;
+}
+
+static u32 stv0367cab_set_derot_freq(struct stv0367_state *state,
+ u32 adc_hz, s32 derot_hz)
+{
+ u32 sampled_if = 0;
+ u32 adc_khz;
+
+ adc_khz = adc_hz / 1000;
+
+ dprintk("%s: adc_hz=%d derot_hz=%d\n", __func__, adc_hz, derot_hz);
+
+ if (adc_khz != 0) {
+ if (derot_hz < 1000000)
+ derot_hz = adc_hz / 4; /* ZIF operation */
+ if (derot_hz > adc_hz)
+ derot_hz = derot_hz - adc_hz;
+ sampled_if = (u32)derot_hz / 1000;
+ sampled_if *= 32768;
+ sampled_if /= adc_khz;
+ sampled_if *= 256;
+ }
+
+ if (sampled_if > 8388607)
+ sampled_if = 8388607;
+
+ dprintk("%s: sampled_if=0x%x\n", __func__, sampled_if);
+
+ stv0367_writereg(state, R367CAB_MIX_NCO_LL, sampled_if);
+ stv0367_writereg(state, R367CAB_MIX_NCO_HL, (sampled_if >> 8));
+ stv0367_writebits(state, F367CAB_MIX_NCO_INC_HH, (sampled_if >> 16));
+
+ return derot_hz;
+}
+
+static u32 stv0367cab_get_derot_freq(struct stv0367_state *state, u32 adc_hz)
+{
+ u32 sampled_if;
+
+ sampled_if = stv0367_readbits(state, F367CAB_MIX_NCO_INC_LL) +
+ (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HL) << 8) +
+ (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HH) << 16);
+
+ sampled_if /= 256;
+ sampled_if *= (adc_hz / 1000);
+ sampled_if += 1;
+ sampled_if /= 32768;
+
+ return sampled_if;
+}
+
+static u32 stv0367cab_set_srate(struct stv0367_state *state, u32 adc_hz,
+ u32 mclk_hz, u32 SymbolRate,
+ enum stv0367cab_mod QAMSize)
+{
+ u32 QamSizeCorr = 0;
+ u32 u32_tmp = 0, u32_tmp1 = 0;
+ u32 adp_khz;
+
+ dprintk("%s:\n", __func__);
+
+ /* Set Correction factor of SRC gain */
+ switch (QAMSize) {
+ case FE_CAB_MOD_QAM4:
+ QamSizeCorr = 1110;
+ break;
+ case FE_CAB_MOD_QAM16:
+ QamSizeCorr = 1032;
+ break;
+ case FE_CAB_MOD_QAM32:
+ QamSizeCorr = 954;
+ break;
+ case FE_CAB_MOD_QAM64:
+ QamSizeCorr = 983;
+ break;
+ case FE_CAB_MOD_QAM128:
+ QamSizeCorr = 957;
+ break;
+ case FE_CAB_MOD_QAM256:
+ QamSizeCorr = 948;
+ break;
+ case FE_CAB_MOD_QAM512:
+ QamSizeCorr = 0;
+ break;
+ case FE_CAB_MOD_QAM1024:
+ QamSizeCorr = 944;
+ break;
+ default:
+ break;
+ }
+
+ /* Transfer ratio calculation */
+ if (adc_hz != 0) {
+ u32_tmp = 256 * SymbolRate;
+ u32_tmp = u32_tmp / adc_hz;
+ }
+ stv0367_writereg(state, R367CAB_EQU_CRL_TFR, (u8)u32_tmp);
+
+ /* Symbol rate and SRC gain calculation */
+ adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */
+ if (adp_khz != 0) {
+ u32_tmp = SymbolRate;
+ u32_tmp1 = SymbolRate;
+
+ if (u32_tmp < 2097152) { /* 2097152 = 2^21 */
+ /* Symbol rate calculation */
+ u32_tmp *= 2048; /* 2048 = 2^11 */
+ u32_tmp = u32_tmp / adp_khz;
+ u32_tmp = u32_tmp * 16384; /* 16384 = 2^14 */
+ u32_tmp /= 125 ; /* 125 = 1000/2^3 */
+ u32_tmp = u32_tmp * 8; /* 8 = 2^3 */
+
+ /* SRC Gain Calculation */
+ u32_tmp1 *= 2048; /* *2*2^10 */
+ u32_tmp1 /= 439; /* *2/878 */
+ u32_tmp1 *= 256; /* *2^8 */
+ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */
+ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */
+ u32_tmp1 = u32_tmp1 / 10000000;
+
+ } else if (u32_tmp < 4194304) { /* 4194304 = 2**22 */
+ /* Symbol rate calculation */
+ u32_tmp *= 1024 ; /* 1024 = 2**10 */
+ u32_tmp = u32_tmp / adp_khz;
+ u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */
+ u32_tmp /= 125 ; /* 125 = 1000/2**3 */
+ u32_tmp = u32_tmp * 16; /* 16 = 2**4 */
+
+ /* SRC Gain Calculation */
+ u32_tmp1 *= 1024; /* *2*2^9 */
+ u32_tmp1 /= 439; /* *2/878 */
+ u32_tmp1 *= 256; /* *2^8 */
+ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz)*/
+ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */
+ u32_tmp1 = u32_tmp1 / 5000000;
+ } else if (u32_tmp < 8388607) { /* 8388607 = 2**23 */
+ /* Symbol rate calculation */
+ u32_tmp *= 512 ; /* 512 = 2**9 */
+ u32_tmp = u32_tmp / adp_khz;
+ u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */
+ u32_tmp /= 125 ; /* 125 = 1000/2**3 */
+ u32_tmp = u32_tmp * 32; /* 32 = 2**5 */
+
+ /* SRC Gain Calculation */
+ u32_tmp1 *= 512; /* *2*2^8 */
+ u32_tmp1 /= 439; /* *2/878 */
+ u32_tmp1 *= 256; /* *2^8 */
+ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */
+ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */
+ u32_tmp1 = u32_tmp1 / 2500000;
+ } else {
+ /* Symbol rate calculation */
+ u32_tmp *= 256 ; /* 256 = 2**8 */
+ u32_tmp = u32_tmp / adp_khz;
+ u32_tmp = u32_tmp * 16384; /* 16384 = 2**13 */
+ u32_tmp /= 125 ; /* 125 = 1000/2**3 */
+ u32_tmp = u32_tmp * 64; /* 64 = 2**6 */
+
+ /* SRC Gain Calculation */
+ u32_tmp1 *= 256; /* 2*2^7 */
+ u32_tmp1 /= 439; /* *2/878 */
+ u32_tmp1 *= 256; /* *2^8 */
+ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */
+ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */
+ u32_tmp1 = u32_tmp1 / 1250000;
+ }
+ }
+#if 0
+ /* Filters' coefficients are calculated and written
+ into registers only if the filters are enabled */
+ if (stv0367_readbits(state, F367CAB_ADJ_EN)) {
+ stv0367cab_SetIirAdjacentcoefficient(state, mclk_hz,
+ SymbolRate);
+ /* AllPass filter must be enabled
+ when the adjacents filter is used */
+ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 1);
+ stv0367cab_SetAllPasscoefficient(state, mclk_hz, SymbolRate);
+ } else
+ /* AllPass filter must be disabled
+ when the adjacents filter is not used */
+#endif
+ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0);
+
+ stv0367_writereg(state, R367CAB_SRC_NCO_LL, u32_tmp);
+ stv0367_writereg(state, R367CAB_SRC_NCO_LH, (u32_tmp >> 8));
+ stv0367_writereg(state, R367CAB_SRC_NCO_HL, (u32_tmp >> 16));
+ stv0367_writereg(state, R367CAB_SRC_NCO_HH, (u32_tmp >> 24));
+
+ stv0367_writereg(state, R367CAB_IQDEM_GAIN_SRC_L, u32_tmp1 & 0x00ff);
+ stv0367_writebits(state, F367CAB_GAIN_SRC_HI, (u32_tmp1 >> 8) & 0x00ff);
+
+ return SymbolRate ;
+}
+
+static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz)
+{
+ u32 regsym;
+ u32 adp_khz;
+
+ regsym = stv0367_readreg(state, R367CAB_SRC_NCO_LL) +
+ (stv0367_readreg(state, R367CAB_SRC_NCO_LH) << 8) +
+ (stv0367_readreg(state, R367CAB_SRC_NCO_HL) << 16) +
+ (stv0367_readreg(state, R367CAB_SRC_NCO_HH) << 24);
+
+ adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */
+
+ if (regsym < 134217728) { /* 134217728L = 2**27*/
+ regsym = regsym * 32; /* 32 = 2**5 */
+ regsym = regsym / 32768; /* 32768L = 2**15 */
+ regsym = adp_khz * regsym; /* AdpClk in kHz */
+ regsym = regsym / 128; /* 128 = 2**7 */
+ regsym *= 125 ; /* 125 = 1000/2**3 */
+ regsym /= 2048 ; /* 2048 = 2**11 */
+ } else if (regsym < 268435456) { /* 268435456L = 2**28 */
+ regsym = regsym * 16; /* 16 = 2**4 */
+ regsym = regsym / 32768; /* 32768L = 2**15 */
+ regsym = adp_khz * regsym; /* AdpClk in kHz */
+ regsym = regsym / 128; /* 128 = 2**7 */
+ regsym *= 125 ; /* 125 = 1000/2**3*/
+ regsym /= 1024 ; /* 256 = 2**10*/
+ } else if (regsym < 536870912) { /* 536870912L = 2**29*/
+ regsym = regsym * 8; /* 8 = 2**3 */
+ regsym = regsym / 32768; /* 32768L = 2**15 */
+ regsym = adp_khz * regsym; /* AdpClk in kHz */
+ regsym = regsym / 128; /* 128 = 2**7 */
+ regsym *= 125 ; /* 125 = 1000/2**3 */
+ regsym /= 512 ; /* 128 = 2**9 */
+ } else {
+ regsym = regsym * 4; /* 4 = 2**2 */
+ regsym = regsym / 32768; /* 32768L = 2**15 */
+ regsym = adp_khz * regsym; /* AdpClk in kHz */
+ regsym = regsym / 128; /* 128 = 2**7 */
+ regsym *= 125 ; /* 125 = 1000/2**3 */
+ regsym /= 256 ; /* 64 = 2**8 */
+ }
+
+ return regsym;
+}
+
+static int stv0367cab_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ dprintk("%s:\n", __func__);
+
+ *status = 0;
+
+ if (stv0367_readbits(state, F367CAB_QAMFEC_LOCK)) {
+ *status |= FE_HAS_LOCK;
+ dprintk("%s: stv0367 has locked\n", __func__);
+ }
+
+ return 0;
+}
+
+static int stv0367cab_standby(struct dvb_frontend *fe, u8 standby_on)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ dprintk("%s:\n", __func__);
+
+ if (standby_on) {
+ stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x03);
+ stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x01);
+ stv0367_writebits(state, F367CAB_STDBY, 1);
+ stv0367_writebits(state, F367CAB_STDBY_CORE, 1);
+ stv0367_writebits(state, F367CAB_EN_BUFFER_I, 0);
+ stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 0);
+ stv0367_writebits(state, F367CAB_POFFQ, 1);
+ stv0367_writebits(state, F367CAB_POFFI, 1);
+ } else {
+ stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x00);
+ stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x00);
+ stv0367_writebits(state, F367CAB_STDBY, 0);
+ stv0367_writebits(state, F367CAB_STDBY_CORE, 0);
+ stv0367_writebits(state, F367CAB_EN_BUFFER_I, 1);
+ stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 1);
+ stv0367_writebits(state, F367CAB_POFFQ, 0);
+ stv0367_writebits(state, F367CAB_POFFI, 0);
+ }
+
+ return 0;
+}
+
+static int stv0367cab_sleep(struct dvb_frontend *fe)
+{
+ return stv0367cab_standby(fe, 1);
+}
+
+int stv0367cab_init(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367cab_state *cab_state = state->cab_state;
+ int i;
+
+ dprintk("%s:\n", __func__);
+
+ for (i = 0; i < STV0367CAB_NBREGS; i++)
+ stv0367_writereg(state, def0367cab[i].addr,
+ def0367cab[i].value);
+
+ switch (state->config->ts_mode) {
+ case STV0367_DVBCI_CLOCK:
+ dprintk("Setting TSMode = STV0367_DVBCI_CLOCK\n");
+ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x03);
+ break;
+ case STV0367_SERIAL_PUNCT_CLOCK:
+ case STV0367_SERIAL_CONT_CLOCK:
+ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x01);
+ break;
+ case STV0367_PARALLEL_PUNCT_CLOCK:
+ case STV0367_OUTPUTMODE_DEFAULT:
+ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x00);
+ break;
+ }
+
+ switch (state->config->clk_pol) {
+ case STV0367_RISINGEDGE_CLOCK:
+ stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x00);
+ break;
+ case STV0367_FALLINGEDGE_CLOCK:
+ case STV0367_CLOCKPOLARITY_DEFAULT:
+ stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x01);
+ break;
+ }
+
+ stv0367_writebits(state, F367CAB_SYNC_STRIP, 0x00);
+
+ stv0367_writebits(state, F367CAB_CT_NBST, 0x01);
+
+ stv0367_writebits(state, F367CAB_TS_SWAP, 0x01);
+
+ stv0367_writebits(state, F367CAB_FIFO_BYPASS, 0x00);
+
+ stv0367_writereg(state, R367CAB_ANACTRL, 0x00);/*PLL enabled and used */
+
+ cab_state->mclk = stv0367cab_get_mclk(fe, state->config->xtal);
+ cab_state->adc_clk = stv0367cab_get_adc_freq(fe, state->config->xtal);
+
+ return 0;
+}
+static
+enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
+ struct dvb_frontend_parameters *param)
+{
+ struct dvb_qam_parameters *op = &param->u.qam;
+ struct stv0367cab_state *cab_state = state->cab_state;
+ enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC;
+ u32 QAMFEC_Lock, QAM_Lock, u32_tmp,
+ LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols,
+ CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut;
+ u8 TrackAGCAccum;
+ s32 tmp;
+
+ dprintk("%s:\n", __func__);
+
+ /* Timeouts calculation */
+ /* A max lock time of 25 ms is allowed for delayed AGC */
+ AGCTimeOut = 25;
+ /* 100000 symbols needed by the TRL as a maximum value */
+ TRLTimeOut = 100000000 / op->symbol_rate;
+ /* CRLSymbols is the needed number of symbols to achieve a lock
+ within [-4%, +4%] of the symbol rate.
+ CRL timeout is calculated
+ for a lock within [-search_range, +search_range].
+ EQL timeout can be changed depending on
+ the micro-reflections we want to handle.
+ A characterization must be performed
+ with these echoes to get new timeout values.
+ */
+ switch (op->modulation) {
+ case QAM_16:
+ CRLSymbols = 150000;
+ EQLTimeOut = 100;
+ break;
+ case QAM_32:
+ CRLSymbols = 250000;
+ EQLTimeOut = 100;
+ break;
+ case QAM_64:
+ CRLSymbols = 200000;
+ EQLTimeOut = 100;
+ break;
+ case QAM_128:
+ CRLSymbols = 250000;
+ EQLTimeOut = 100;
+ break;
+ case QAM_256:
+ CRLSymbols = 250000;
+ EQLTimeOut = 100;
+ break;
+ default:
+ CRLSymbols = 200000;
+ EQLTimeOut = 100;
+ break;
+ }
+#if 0
+ if (pIntParams->search_range < 0) {
+ CRLTimeOut = (25 * CRLSymbols *
+ (-pIntParams->search_range / 1000)) /
+ (pIntParams->symbol_rate / 1000);
+ } else
+#endif
+ CRLTimeOut = (25 * CRLSymbols * (cab_state->search_range / 1000)) /
+ (op->symbol_rate / 1000);
+
+ CRLTimeOut = (1000 * CRLTimeOut) / op->symbol_rate;
+ /* Timeouts below 50ms are coerced */
+ if (CRLTimeOut < 50)
+ CRLTimeOut = 50;
+ /* A maximum of 100 TS packets is needed to get FEC lock even in case
+ the spectrum inversion needs to be changed.
+ This is equal to 20 ms in case of the lowest symbol rate of 0.87Msps
+ */
+ FECTimeOut = 20;
+ DemodTimeOut = AGCTimeOut + TRLTimeOut + CRLTimeOut + EQLTimeOut;
+
+ dprintk("%s: DemodTimeOut=%d\n", __func__, DemodTimeOut);
+
+ /* Reset the TRL to ensure nothing starts until the
+ AGC is stable which ensures a better lock time
+ */
+ stv0367_writereg(state, R367CAB_CTRL_1, 0x04);
+ /* Set AGC accumulation time to minimum and lock threshold to maximum
+ in order to speed up the AGC lock */
+ TrackAGCAccum = stv0367_readbits(state, F367CAB_AGC_ACCUMRSTSEL);
+ stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, 0x0);
+ /* Modulus Mapper is disabled */
+ stv0367_writebits(state, F367CAB_MODULUSMAP_EN, 0);
+ /* Disable the sweep function */
+ stv0367_writebits(state, F367CAB_SWEEP_EN, 0);
+ /* The sweep function is never used, Sweep rate must be set to 0 */
+ /* Set the derotator frequency in Hz */
+ stv0367cab_set_derot_freq(state, cab_state->adc_clk,
+ (1000 * (s32)state->config->if_khz + cab_state->derot_offset));
+ /* Disable the Allpass Filter when the symbol rate is out of range */
+ if ((op->symbol_rate > 10800000) | (op->symbol_rate < 1800000)) {
+ stv0367_writebits(state, F367CAB_ADJ_EN, 0);
+ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0);
+ }
+#if 0
+ /* Check if the tuner is locked */
+ tuner_lock = stv0367cab_tuner_get_status(fe);
+ if (tuner_lock == 0)
+ return FE_367CAB_NOTUNER;
+#endif
+ /* Relase the TRL to start demodulator acquisition */
+ /* Wait for QAM lock */
+ LockTime = 0;
+ stv0367_writereg(state, R367CAB_CTRL_1, 0x00);
+ do {
+ QAM_Lock = stv0367_readbits(state, F367CAB_FSM_STATUS);
+ if ((LockTime >= (DemodTimeOut - EQLTimeOut)) &&
+ (QAM_Lock == 0x04))
+ /*
+ * We don't wait longer, the frequency/phase offset
+ * must be too big
+ */
+ LockTime = DemodTimeOut;
+ else if ((LockTime >= (AGCTimeOut + TRLTimeOut)) &&
+ (QAM_Lock == 0x02))
+ /*
+ * We don't wait longer, either there is no signal or
+ * it is not the right symbol rate or it is an analog
+ * carrier
+ */
+ {
+ LockTime = DemodTimeOut;
+ u32_tmp = stv0367_readbits(state,
+ F367CAB_AGC_PWR_WORD_LO) +
+ (stv0367_readbits(state,
+ F367CAB_AGC_PWR_WORD_ME) << 8) +
+ (stv0367_readbits(state,
+ F367CAB_AGC_PWR_WORD_HI) << 16);
+ if (u32_tmp >= 131072)
+ u32_tmp = 262144 - u32_tmp;
+ u32_tmp = u32_tmp / (1 << (11 - stv0367_readbits(state,
+ F367CAB_AGC_IF_BWSEL)));
+
+ if (u32_tmp < stv0367_readbits(state,
+ F367CAB_AGC_PWRREF_LO) +
+ 256 * stv0367_readbits(state,
+ F367CAB_AGC_PWRREF_HI) - 10)
+ QAM_Lock = 0x0f;
+ } else {
+ usleep_range(10000, 20000);
+ LockTime += 10;
+ }
+ dprintk("QAM_Lock=0x%x LockTime=%d\n", QAM_Lock, LockTime);
+ tmp = stv0367_readreg(state, R367CAB_IT_STATUS1);
+
+ dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp);
+
+ } while (((QAM_Lock != 0x0c) && (QAM_Lock != 0x0b)) &&
+ (LockTime < DemodTimeOut));
+
+ dprintk("QAM_Lock=0x%x\n", QAM_Lock);
+
+ tmp = stv0367_readreg(state, R367CAB_IT_STATUS1);
+ dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp);
+ tmp = stv0367_readreg(state, R367CAB_IT_STATUS2);
+ dprintk("R367CAB_IT_STATUS2=0x%x\n", tmp);
+
+ tmp = stv0367cab_get_derot_freq(state, cab_state->adc_clk);
+ dprintk("stv0367cab_get_derot_freq=0x%x\n", tmp);
+
+ if ((QAM_Lock == 0x0c) || (QAM_Lock == 0x0b)) {
+ /* Wait for FEC lock */
+ LockTime = 0;
+ do {
+ usleep_range(5000, 7000);
+ LockTime += 5;
+ QAMFEC_Lock = stv0367_readbits(state,
+ F367CAB_QAMFEC_LOCK);
+ } while (!QAMFEC_Lock && (LockTime < FECTimeOut));
+ } else
+ QAMFEC_Lock = 0;
+
+ if (QAMFEC_Lock) {
+ signalType = FE_CAB_DATAOK;
+ cab_state->modulation = op->modulation;
+ cab_state->spect_inv = stv0367_readbits(state,
+ F367CAB_QUAD_INV);
+#if 0
+/* not clear for me */
+ if (state->config->if_khz != 0) {
+ if (state->config->if_khz > cab_state->adc_clk / 1000) {
+ cab_state->freq_khz =
+ FE_Cab_TunerGetFrequency(pIntParams->hTuner)
+ - stv0367cab_get_derot_freq(state, cab_state->adc_clk)
+ - cab_state->adc_clk / 1000 + state->config->if_khz;
+ } else {
+ cab_state->freq_khz =
+ FE_Cab_TunerGetFrequency(pIntParams->hTuner)
+ - stv0367cab_get_derot_freq(state, cab_state->adc_clk)
+ + state->config->if_khz;
+ }
+ } else {
+ cab_state->freq_khz =
+ FE_Cab_TunerGetFrequency(pIntParams->hTuner) +
+ stv0367cab_get_derot_freq(state,
+ cab_state->adc_clk) -
+ cab_state->adc_clk / 4000;
+ }
+#endif
+ cab_state->symbol_rate = stv0367cab_GetSymbolRate(state,
+ cab_state->mclk);
+ cab_state->locked = 1;
+
+ /* stv0367_setbits(state, F367CAB_AGC_ACCUMRSTSEL,7);*/
+ } else {
+ switch (QAM_Lock) {
+ case 1:
+ signalType = FE_CAB_NOAGC;
+ break;
+ case 2:
+ signalType = FE_CAB_NOTIMING;
+ break;
+ case 3:
+ signalType = FE_CAB_TIMINGOK;
+ break;
+ case 4:
+ signalType = FE_CAB_NOCARRIER;
+ break;
+ case 5:
+ signalType = FE_CAB_CARRIEROK;
+ break;
+ case 7:
+ signalType = FE_CAB_NOBLIND;
+ break;
+ case 8:
+ signalType = FE_CAB_BLINDOK;
+ break;
+ case 10:
+ signalType = FE_CAB_NODEMOD;
+ break;
+ case 11:
+ signalType = FE_CAB_DEMODOK;
+ break;
+ case 12:
+ signalType = FE_CAB_DEMODOK;
+ break;
+ case 13:
+ signalType = FE_CAB_NODEMOD;
+ break;
+ case 14:
+ signalType = FE_CAB_NOBLIND;
+ break;
+ case 15:
+ signalType = FE_CAB_NOSIGNAL;
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ /* Set the AGC control values to tracking values */
+ stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, TrackAGCAccum);
+ return signalType;
+}
+
+static int stv0367cab_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367cab_state *cab_state = state->cab_state;
+ struct dvb_qam_parameters *op = &param->u.qam;
+ enum stv0367cab_mod QAMSize = 0;
+
+ dprintk("%s: freq = %d, srate = %d\n", __func__,
+ param->frequency, op->symbol_rate);
+
+ cab_state->derot_offset = 0;
+
+ switch (op->modulation) {
+ case QAM_16:
+ QAMSize = FE_CAB_MOD_QAM16;
+ break;
+ case QAM_32:
+ QAMSize = FE_CAB_MOD_QAM32;
+ break;
+ case QAM_64:
+ QAMSize = FE_CAB_MOD_QAM64;
+ break;
+ case QAM_128:
+ QAMSize = FE_CAB_MOD_QAM128;
+ break;
+ case QAM_256:
+ QAMSize = FE_CAB_MOD_QAM256;
+ break;
+ default:
+ break;
+ }
+
+ stv0367cab_init(fe);
+
+ /* Tuner Frequency Setting */
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.set_params(fe, param);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ stv0367cab_SetQamSize(
+ state,
+ op->symbol_rate,
+ QAMSize);
+
+ stv0367cab_set_srate(state,
+ cab_state->adc_clk,
+ cab_state->mclk,
+ op->symbol_rate,
+ QAMSize);
+ /* Search algorithm launch, [-1.1*RangeOffset, +1.1*RangeOffset] scan */
+ cab_state->state = stv0367cab_algo(state, param);
+ return 0;
+}
+
+static int stv0367cab_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct stv0367cab_state *cab_state = state->cab_state;
+ struct dvb_qam_parameters *op = &param->u.qam;
+
+ enum stv0367cab_mod QAMSize;
+
+ dprintk("%s:\n", __func__);
+
+ op->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk);
+
+ QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE);
+ switch (QAMSize) {
+ case FE_CAB_MOD_QAM16:
+ op->modulation = QAM_16;
+ break;
+ case FE_CAB_MOD_QAM32:
+ op->modulation = QAM_32;
+ break;
+ case FE_CAB_MOD_QAM64:
+ op->modulation = QAM_64;
+ break;
+ case FE_CAB_MOD_QAM128:
+ op->modulation = QAM_128;
+ break;
+ case QAM_256:
+ op->modulation = QAM_256;
+ break;
+ default:
+ break;
+ }
+
+ param->frequency = stv0367_get_tuner_freq(fe);
+
+ dprintk("%s: tuner frequency = %d\n", __func__, param->frequency);
+
+ if (state->config->if_khz == 0) {
+ param->frequency +=
+ (stv0367cab_get_derot_freq(state, cab_state->adc_clk) -
+ cab_state->adc_clk / 4000);
+ return 0;
+ }
+
+ if (state->config->if_khz > cab_state->adc_clk / 1000)
+ param->frequency += (state->config->if_khz
+ - stv0367cab_get_derot_freq(state, cab_state->adc_clk)
+ - cab_state->adc_clk / 1000);
+ else
+ param->frequency += (state->config->if_khz
+ - stv0367cab_get_derot_freq(state, cab_state->adc_clk));
+
+ return 0;
+}
+
+#if 0
+void stv0367cab_GetErrorCount(state, enum stv0367cab_mod QAMSize,
+ u32 symbol_rate, FE_367qam_Monitor *Monitor_results)
+{
+ stv0367cab_OptimiseNByteAndGetBER(state, QAMSize, symbol_rate, Monitor_results);
+ stv0367cab_GetPacketsCount(state, Monitor_results);
+
+ return;
+}
+
+static int stv0367cab_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ return 0;
+}
+#endif
+static s32 stv0367cab_get_rf_lvl(struct stv0367_state *state)
+{
+ s32 rfLevel = 0;
+ s32 RfAgcPwm = 0, IfAgcPwm = 0;
+ u8 i;
+
+ stv0367_writebits(state, F367CAB_STDBY_ADCGP, 0x0);
+
+ RfAgcPwm =
+ (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_LO) & 0x03) +
+ (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_HI) << 2);
+ RfAgcPwm = 100 * RfAgcPwm / 1023;
+
+ IfAgcPwm =
+ stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_LO) +
+ (stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_HI) << 8);
+ if (IfAgcPwm >= 2048)
+ IfAgcPwm -= 2048;
+ else
+ IfAgcPwm += 2048;
+
+ IfAgcPwm = 100 * IfAgcPwm / 4095;
+
+ /* For DTT75467 on NIM */
+ if (RfAgcPwm < 90 && IfAgcPwm < 28) {
+ for (i = 0; i < RF_LOOKUP_TABLE_SIZE; i++) {
+ if (RfAgcPwm <= stv0367cab_RF_LookUp1[0][i]) {
+ rfLevel = (-1) * stv0367cab_RF_LookUp1[1][i];
+ break;
+ }
+ }
+ if (i == RF_LOOKUP_TABLE_SIZE)
+ rfLevel = -56;
+ } else { /*if IF AGC>10*/
+ for (i = 0; i < RF_LOOKUP_TABLE2_SIZE; i++) {
+ if (IfAgcPwm <= stv0367cab_RF_LookUp2[0][i]) {
+ rfLevel = (-1) * stv0367cab_RF_LookUp2[1][i];
+ break;
+ }
+ }
+ if (i == RF_LOOKUP_TABLE2_SIZE)
+ rfLevel = -72;
+ }
+ return rfLevel;
+}
+
+static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ s32 signal = stv0367cab_get_rf_lvl(state);
+
+ dprintk("%s: signal=%d dBm\n", __func__, signal);
+
+ if (signal <= -72)
+ *strength = 65535;
+ else
+ *strength = (22 + signal) * (-1311);
+
+ dprintk("%s: strength=%d\n", __func__, (*strength));
+
+ return 0;
+}
+
+static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u32 noisepercentage;
+ enum stv0367cab_mod QAMSize;
+ u32 regval = 0, temp = 0;
+ int power, i;
+
+ QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE);
+ switch (QAMSize) {
+ case FE_CAB_MOD_QAM4:
+ power = 21904;
+ break;
+ case FE_CAB_MOD_QAM16:
+ power = 20480;
+ break;
+ case FE_CAB_MOD_QAM32:
+ power = 23040;
+ break;
+ case FE_CAB_MOD_QAM64:
+ power = 21504;
+ break;
+ case FE_CAB_MOD_QAM128:
+ power = 23616;
+ break;
+ case FE_CAB_MOD_QAM256:
+ power = 21760;
+ break;
+ case FE_CAB_MOD_QAM512:
+ power = 1;
+ break;
+ case FE_CAB_MOD_QAM1024:
+ power = 21280;
+ break;
+ default:
+ power = 1;
+ break;
+ }
+
+ for (i = 0; i < 10; i++) {
+ regval += (stv0367_readbits(state, F367CAB_SNR_LO)
+ + 256 * stv0367_readbits(state, F367CAB_SNR_HI));
+ }
+
+ regval /= 10; /*for average over 10 times in for loop above*/
+ if (regval != 0) {
+ temp = power
+ * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER)));
+ temp /= regval;
+ }
+
+ /* table values, not needed to calculate logarithms */
+ if (temp >= 5012)
+ noisepercentage = 100;
+ else if (temp >= 3981)
+ noisepercentage = 93;
+ else if (temp >= 3162)
+ noisepercentage = 86;
+ else if (temp >= 2512)
+ noisepercentage = 79;
+ else if (temp >= 1995)
+ noisepercentage = 72;
+ else if (temp >= 1585)
+ noisepercentage = 65;
+ else if (temp >= 1259)
+ noisepercentage = 58;
+ else if (temp >= 1000)
+ noisepercentage = 50;
+ else if (temp >= 794)
+ noisepercentage = 43;
+ else if (temp >= 501)
+ noisepercentage = 36;
+ else if (temp >= 316)
+ noisepercentage = 29;
+ else if (temp >= 200)
+ noisepercentage = 22;
+ else if (temp >= 158)
+ noisepercentage = 14;
+ else if (temp >= 126)
+ noisepercentage = 7;
+ else
+ noisepercentage = 0;
+
+ dprintk("%s: noisepercentage=%d\n", __func__, noisepercentage);
+
+ *snr = (noisepercentage * 65535) / 100;
+
+ return 0;
+}
+
+static struct dvb_frontend_ops stv0367cab_ops = {
+ .info = {
+ .name = "ST STV0367 DVB-C",
+ .type = FE_QAM,
+ .frequency_min = 47000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 62500,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ .caps = 0x400 |/* FE_CAN_QAM_4 */
+ FE_CAN_QAM_16 | FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 | FE_CAN_FEC_AUTO
+ },
+ .release = stv0367_release,
+ .init = stv0367cab_init,
+ .sleep = stv0367cab_sleep,
+ .i2c_gate_ctrl = stv0367cab_gate_ctrl,
+ .set_frontend = stv0367cab_set_frontend,
+ .get_frontend = stv0367cab_get_frontend,
+ .read_status = stv0367cab_read_status,
+/* .read_ber = stv0367cab_read_ber, */
+ .read_signal_strength = stv0367cab_read_strength,
+ .read_snr = stv0367cab_read_snr,
+/* .read_ucblocks = stv0367cab_read_ucblcks,*/
+ .get_tune_settings = stv0367_get_tune_settings,
+};
+
+struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stv0367_state *state = NULL;
+ struct stv0367cab_state *cab_state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL);
+ if (cab_state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ state->config = config;
+ cab_state->search_range = 280000;
+ state->cab_state = cab_state;
+ state->fe.ops = stv0367cab_ops;
+ state->fe.demodulator_priv = state;
+ state->chip_id = stv0367_readreg(state, 0xf000);
+
+ dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id);
+
+ /* check if the demod is there */
+ if ((state->chip_id != 0x50) && (state->chip_id != 0x60))
+ goto error;
+
+ return &state->fe;
+
+error:
+ kfree(cab_state);
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stv0367cab_attach);
+
+MODULE_PARM_DESC(debug, "Set debug");
+MODULE_PARM_DESC(i2c_debug, "Set i2c debug");
+
+MODULE_AUTHOR("Igor M. Liplianin");
+MODULE_DESCRIPTION("ST STV0367 DVB-C/T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stv0367.h b/drivers/media/dvb/frontends/stv0367.h
new file mode 100644
index 000000000000..93cc4a57eea0
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0367.h
@@ -0,0 +1,66 @@
+/*
+ * stv0367.h
+ *
+ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC.
+ *
+ * Copyright (C) ST Microelectronics.
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef STV0367_H
+#define STV0367_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0367_config {
+ u8 demod_address;
+ u32 xtal;
+ u32 if_khz;/*4500*/
+ int if_iq_mode;
+ int ts_mode;
+ int clk_pol;
+};
+
+#if defined(CONFIG_DVB_STV0367) || (defined(CONFIG_DVB_STV0367_MODULE) \
+ && defined(MODULE))
+extern struct
+dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c);
+extern struct
+dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct
+dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct
+dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/stv0367_priv.h b/drivers/media/dvb/frontends/stv0367_priv.h
new file mode 100644
index 000000000000..995db0689ddd
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0367_priv.h
@@ -0,0 +1,212 @@
+/*
+ * stv0367_priv.h
+ *
+ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC.
+ *
+ * Copyright (C) ST Microelectronics.
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* Common driver error constants */
+
+#ifndef STV0367_PRIV_H
+#define STV0367_PRIV_H
+
+#ifndef TRUE
+ #define TRUE (1 == 1)
+#endif
+#ifndef FALSE
+ #define FALSE (!TRUE)
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* MACRO definitions */
+#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X))
+#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
+#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
+#define INRANGE(X, Y, Z) \
+ ((((X) <= (Y)) && ((Y) <= (Z))) || \
+ (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0)
+
+#ifndef MAKEWORD
+#define MAKEWORD(X, Y) (((X) << 8) + (Y))
+#endif
+
+#define LSB(X) (((X) & 0xff))
+#define MSB(Y) (((Y) >> 8) & 0xff)
+#define MMSB(Y)(((Y) >> 16) & 0xff)
+
+enum stv0367_ter_signal_type {
+ FE_TER_NOAGC = 0,
+ FE_TER_AGCOK = 5,
+ FE_TER_NOTPS = 6,
+ FE_TER_TPSOK = 7,
+ FE_TER_NOSYMBOL = 8,
+ FE_TER_BAD_CPQ = 9,
+ FE_TER_PRFOUNDOK = 10,
+ FE_TER_NOPRFOUND = 11,
+ FE_TER_LOCKOK = 12,
+ FE_TER_NOLOCK = 13,
+ FE_TER_SYMBOLOK = 15,
+ FE_TER_CPAMPOK = 16,
+ FE_TER_NOCPAMP = 17,
+ FE_TER_SWNOK = 18
+};
+
+enum stv0367_ts_mode {
+ STV0367_OUTPUTMODE_DEFAULT,
+ STV0367_SERIAL_PUNCT_CLOCK,
+ STV0367_SERIAL_CONT_CLOCK,
+ STV0367_PARALLEL_PUNCT_CLOCK,
+ STV0367_DVBCI_CLOCK
+};
+
+enum stv0367_clk_pol {
+ STV0367_CLOCKPOLARITY_DEFAULT,
+ STV0367_RISINGEDGE_CLOCK,
+ STV0367_FALLINGEDGE_CLOCK
+};
+
+enum stv0367_ter_bw {
+ FE_TER_CHAN_BW_6M = 6,
+ FE_TER_CHAN_BW_7M = 7,
+ FE_TER_CHAN_BW_8M = 8
+};
+
+#if 0
+enum FE_TER_Rate_TPS {
+ FE_TER_TPS_1_2 = 0,
+ FE_TER_TPS_2_3 = 1,
+ FE_TER_TPS_3_4 = 2,
+ FE_TER_TPS_5_6 = 3,
+ FE_TER_TPS_7_8 = 4
+};
+#endif
+
+enum stv0367_ter_mode {
+ FE_TER_MODE_2K,
+ FE_TER_MODE_8K,
+ FE_TER_MODE_4K
+};
+#if 0
+enum FE_TER_Hierarchy_Alpha {
+ FE_TER_HIER_ALPHA_NONE, /* Regular modulation */
+ FE_TER_HIER_ALPHA_1, /* Hierarchical modulation a = 1*/
+ FE_TER_HIER_ALPHA_2, /* Hierarchical modulation a = 2*/
+ FE_TER_HIER_ALPHA_4 /* Hierarchical modulation a = 4*/
+};
+#endif
+enum stv0367_ter_hierarchy {
+ FE_TER_HIER_NONE, /*Hierarchy None*/
+ FE_TER_HIER_LOW_PRIO, /*Hierarchy : Low Priority*/
+ FE_TER_HIER_HIGH_PRIO, /*Hierarchy : High Priority*/
+ FE_TER_HIER_PRIO_ANY /*Hierarchy :Any*/
+};
+
+#if 0
+enum fe_stv0367_ter_spec {
+ FE_TER_INVERSION_NONE = 0,
+ FE_TER_INVERSION = 1,
+ FE_TER_INVERSION_AUTO = 2,
+ FE_TER_INVERSION_UNK = 4
+};
+#endif
+
+enum stv0367_ter_if_iq_mode {
+ FE_TER_NORMAL_IF_TUNER = 0,
+ FE_TER_LONGPATH_IF_TUNER = 1,
+ FE_TER_IQ_TUNER = 2
+
+};
+
+#if 0
+enum FE_TER_FECRate {
+ FE_TER_FEC_NONE = 0x00, /* no FEC rate specified */
+ FE_TER_FEC_ALL = 0xFF, /* Logical OR of all FECs */
+ FE_TER_FEC_1_2 = 1,
+ FE_TER_FEC_2_3 = (1 << 1),
+ FE_TER_FEC_3_4 = (1 << 2),
+ FE_TER_FEC_4_5 = (1 << 3),
+ FE_TER_FEC_5_6 = (1 << 4),
+ FE_TER_FEC_6_7 = (1 << 5),
+ FE_TER_FEC_7_8 = (1 << 6),
+ FE_TER_FEC_8_9 = (1 << 7)
+};
+
+enum FE_TER_Rate {
+ FE_TER_FE_1_2 = 0,
+ FE_TER_FE_2_3 = 1,
+ FE_TER_FE_3_4 = 2,
+ FE_TER_FE_5_6 = 3,
+ FE_TER_FE_6_7 = 4,
+ FE_TER_FE_7_8 = 5
+};
+#endif
+
+enum stv0367_ter_force {
+ FE_TER_FORCENONE = 0,
+ FE_TER_FORCE_M_G = 1
+};
+
+enum stv0367cab_mod {
+ FE_CAB_MOD_QAM4,
+ FE_CAB_MOD_QAM16,
+ FE_CAB_MOD_QAM32,
+ FE_CAB_MOD_QAM64,
+ FE_CAB_MOD_QAM128,
+ FE_CAB_MOD_QAM256,
+ FE_CAB_MOD_QAM512,
+ FE_CAB_MOD_QAM1024
+};
+#if 0
+enum {
+ FE_CAB_FEC_A = 1, /* J83 Annex A */
+ FE_CAB_FEC_B = (1 << 1),/* J83 Annex B */
+ FE_CAB_FEC_C = (1 << 2) /* J83 Annex C */
+} FE_CAB_FECType_t;
+#endif
+struct stv0367_cab_signal_info {
+ int locked;
+ u32 frequency; /* kHz */
+ u32 symbol_rate; /* Mbds */
+ enum stv0367cab_mod modulation;
+ fe_spectral_inversion_t spect_inv;
+ s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */
+ u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */
+ u32 BER; /* Bit error rate (x 10000000) */
+};
+
+enum stv0367_cab_signal_type {
+ FE_CAB_NOTUNER,
+ FE_CAB_NOAGC,
+ FE_CAB_NOSIGNAL,
+ FE_CAB_NOTIMING,
+ FE_CAB_TIMINGOK,
+ FE_CAB_NOCARRIER,
+ FE_CAB_CARRIEROK,
+ FE_CAB_NOBLIND,
+ FE_CAB_BLINDOK,
+ FE_CAB_NODEMOD,
+ FE_CAB_DEMODOK,
+ FE_CAB_DATAOK
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/stv0367_regs.h b/drivers/media/dvb/frontends/stv0367_regs.h
new file mode 100644
index 000000000000..a96fbdc7e25e
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0367_regs.h
@@ -0,0 +1,3614 @@
+/*
+ * stv0367_regs.h
+ *
+ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC.
+ *
+ * Copyright (C) ST Microelectronics.
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef STV0367_REGS_H
+#define STV0367_REGS_H
+
+/* ID */
+#define R367TER_ID 0xf000
+#define F367TER_IDENTIFICATIONREG 0xf00000ff
+
+/* I2CRPT */
+#define R367TER_I2CRPT 0xf001
+#define F367TER_I2CT_ON 0xf0010080
+#define F367TER_ENARPT_LEVEL 0xf0010070
+#define F367TER_SCLT_DELAY 0xf0010008
+#define F367TER_SCLT_NOD 0xf0010004
+#define F367TER_STOP_ENABLE 0xf0010002
+#define F367TER_SDAT_NOD 0xf0010001
+
+/* TOPCTRL */
+#define R367TER_TOPCTRL 0xf002
+#define F367TER_STDBY 0xf0020080
+#define F367TER_STDBY_FEC 0xf0020040
+#define F367TER_STDBY_CORE 0xf0020020
+#define F367TER_QAM_COFDM 0xf0020010
+#define F367TER_TS_DIS 0xf0020008
+#define F367TER_DIR_CLK_216 0xf0020004
+#define F367TER_TUNER_BB 0xf0020002
+#define F367TER_DVBT_H 0xf0020001
+
+/* IOCFG0 */
+#define R367TER_IOCFG0 0xf003
+#define F367TER_OP0_SD 0xf0030080
+#define F367TER_OP0_VAL 0xf0030040
+#define F367TER_OP0_OD 0xf0030020
+#define F367TER_OP0_INV 0xf0030010
+#define F367TER_OP0_DACVALUE_HI 0xf003000f
+
+/* DAc0R */
+#define R367TER_DAC0R 0xf004
+#define F367TER_OP0_DACVALUE_LO 0xf00400ff
+
+/* IOCFG1 */
+#define R367TER_IOCFG1 0xf005
+#define F367TER_IP0 0xf0050040
+#define F367TER_OP1_OD 0xf0050020
+#define F367TER_OP1_INV 0xf0050010
+#define F367TER_OP1_DACVALUE_HI 0xf005000f
+
+/* DAC1R */
+#define R367TER_DAC1R 0xf006
+#define F367TER_OP1_DACVALUE_LO 0xf00600ff
+
+/* IOCFG2 */
+#define R367TER_IOCFG2 0xf007
+#define F367TER_OP2_LOCK_CONF 0xf00700e0
+#define F367TER_OP2_OD 0xf0070010
+#define F367TER_OP2_VAL 0xf0070008
+#define F367TER_OP1_LOCK_CONF 0xf0070007
+
+/* SDFR */
+#define R367TER_SDFR 0xf008
+#define F367TER_OP0_FREQ 0xf00800f0
+#define F367TER_OP1_FREQ 0xf008000f
+
+/* STATUS */
+#define R367TER_STATUS 0xf009
+#define F367TER_TPS_LOCK 0xf0090080
+#define F367TER_SYR_LOCK 0xf0090040
+#define F367TER_AGC_LOCK 0xf0090020
+#define F367TER_PRF 0xf0090010
+#define F367TER_LK 0xf0090008
+#define F367TER_PR 0xf0090007
+
+/* AUX_CLK */
+#define R367TER_AUX_CLK 0xf00a
+#define F367TER_AUXFEC_CTL 0xf00a00c0
+#define F367TER_DIS_CKX4 0xf00a0020
+#define F367TER_CKSEL 0xf00a0018
+#define F367TER_CKDIV_PROG 0xf00a0006
+#define F367TER_AUXCLK_ENA 0xf00a0001
+
+/* FREESYS1 */
+#define R367TER_FREESYS1 0xf00b
+#define F367TER_FREE_SYS1 0xf00b00ff
+
+/* FREESYS2 */
+#define R367TER_FREESYS2 0xf00c
+#define F367TER_FREE_SYS2 0xf00c00ff
+
+/* FREESYS3 */
+#define R367TER_FREESYS3 0xf00d
+#define F367TER_FREE_SYS3 0xf00d00ff
+
+/* GPIO_CFG */
+#define R367TER_GPIO_CFG 0xf00e
+#define F367TER_GPIO7_NOD 0xf00e0080
+#define F367TER_GPIO7_CFG 0xf00e0040
+#define F367TER_GPIO6_NOD 0xf00e0020
+#define F367TER_GPIO6_CFG 0xf00e0010
+#define F367TER_GPIO5_NOD 0xf00e0008
+#define F367TER_GPIO5_CFG 0xf00e0004
+#define F367TER_GPIO4_NOD 0xf00e0002
+#define F367TER_GPIO4_CFG 0xf00e0001
+
+/* GPIO_CMD */
+#define R367TER_GPIO_CMD 0xf00f
+#define F367TER_GPIO7_VAL 0xf00f0008
+#define F367TER_GPIO6_VAL 0xf00f0004
+#define F367TER_GPIO5_VAL 0xf00f0002
+#define F367TER_GPIO4_VAL 0xf00f0001
+
+/* AGC2MAX */
+#define R367TER_AGC2MAX 0xf010
+#define F367TER_AGC2_MAX 0xf01000ff
+
+/* AGC2MIN */
+#define R367TER_AGC2MIN 0xf011
+#define F367TER_AGC2_MIN 0xf01100ff
+
+/* AGC1MAX */
+#define R367TER_AGC1MAX 0xf012
+#define F367TER_AGC1_MAX 0xf01200ff
+
+/* AGC1MIN */
+#define R367TER_AGC1MIN 0xf013
+#define F367TER_AGC1_MIN 0xf01300ff
+
+/* AGCR */
+#define R367TER_AGCR 0xf014
+#define F367TER_RATIO_A 0xf01400e0
+#define F367TER_RATIO_B 0xf0140018
+#define F367TER_RATIO_C 0xf0140007
+
+/* AGC2TH */
+#define R367TER_AGC2TH 0xf015
+#define F367TER_AGC2_THRES 0xf01500ff
+
+/* AGC12c */
+#define R367TER_AGC12C 0xf016
+#define F367TER_AGC1_IV 0xf0160080
+#define F367TER_AGC1_OD 0xf0160040
+#define F367TER_AGC1_LOAD 0xf0160020
+#define F367TER_AGC2_IV 0xf0160010
+#define F367TER_AGC2_OD 0xf0160008
+#define F367TER_AGC2_LOAD 0xf0160004
+#define F367TER_AGC12_MODE 0xf0160003
+
+/* AGCCTRL1 */
+#define R367TER_AGCCTRL1 0xf017
+#define F367TER_DAGC_ON 0xf0170080
+#define F367TER_INVERT_AGC12 0xf0170040
+#define F367TER_AGC1_MODE 0xf0170008
+#define F367TER_AGC2_MODE 0xf0170007
+
+/* AGCCTRL2 */
+#define R367TER_AGCCTRL2 0xf018
+#define F367TER_FRZ2_CTRL 0xf0180060
+#define F367TER_FRZ1_CTRL 0xf0180018
+#define F367TER_TIME_CST 0xf0180007
+
+/* AGC1VAL1 */
+#define R367TER_AGC1VAL1 0xf019
+#define F367TER_AGC1_VAL_LO 0xf01900ff
+
+/* AGC1VAL2 */
+#define R367TER_AGC1VAL2 0xf01a
+#define F367TER_AGC1_VAL_HI 0xf01a000f
+
+/* AGC2VAL1 */
+#define R367TER_AGC2VAL1 0xf01b
+#define F367TER_AGC2_VAL_LO 0xf01b00ff
+
+/* AGC2VAL2 */
+#define R367TER_AGC2VAL2 0xf01c
+#define F367TER_AGC2_VAL_HI 0xf01c000f
+
+/* AGC2PGA */
+#define R367TER_AGC2PGA 0xf01d
+#define F367TER_AGC2_PGA 0xf01d00ff
+
+/* OVF_RATE1 */
+#define R367TER_OVF_RATE1 0xf01e
+#define F367TER_OVF_RATE_HI 0xf01e000f
+
+/* OVF_RATE2 */
+#define R367TER_OVF_RATE2 0xf01f
+#define F367TER_OVF_RATE_LO 0xf01f00ff
+
+/* GAIN_SRC1 */
+#define R367TER_GAIN_SRC1 0xf020
+#define F367TER_INV_SPECTR 0xf0200080
+#define F367TER_IQ_INVERT 0xf0200040
+#define F367TER_INR_BYPASS 0xf0200020
+#define F367TER_STATUS_INV_SPECRUM 0xf0200010
+#define F367TER_GAIN_SRC_HI 0xf020000f
+
+/* GAIN_SRC2 */
+#define R367TER_GAIN_SRC2 0xf021
+#define F367TER_GAIN_SRC_LO 0xf02100ff
+
+/* INC_DEROT1 */
+#define R367TER_INC_DEROT1 0xf022
+#define F367TER_INC_DEROT_HI 0xf02200ff
+
+/* INC_DEROT2 */
+#define R367TER_INC_DEROT2 0xf023
+#define F367TER_INC_DEROT_LO 0xf02300ff
+
+/* PPM_CPAMP_DIR */
+#define R367TER_PPM_CPAMP_DIR 0xf024
+#define F367TER_PPM_CPAMP_DIRECT 0xf02400ff
+
+/* PPM_CPAMP_INV */
+#define R367TER_PPM_CPAMP_INV 0xf025
+#define F367TER_PPM_CPAMP_INVER 0xf02500ff
+
+/* FREESTFE_1 */
+#define R367TER_FREESTFE_1 0xf026
+#define F367TER_SYMBOL_NUMBER_INC 0xf02600c0
+#define F367TER_SEL_LSB 0xf0260004
+#define F367TER_AVERAGE_ON 0xf0260002
+#define F367TER_DC_ADJ 0xf0260001
+
+/* FREESTFE_2 */
+#define R367TER_FREESTFE_2 0xf027
+#define F367TER_SEL_SRCOUT 0xf02700c0
+#define F367TER_SEL_SYRTHR 0xf027001f
+
+/* DCOFFSET */
+#define R367TER_DCOFFSET 0xf028
+#define F367TER_SELECT_I_Q 0xf0280080
+#define F367TER_DC_OFFSET 0xf028007f
+
+/* EN_PROCESS */
+#define R367TER_EN_PROCESS 0xf029
+#define F367TER_FREE 0xf02900f0
+#define F367TER_ENAB_MANUAL 0xf0290001
+
+/* SDI_SMOOTHER */
+#define R367TER_SDI_SMOOTHER 0xf02a
+#define F367TER_DIS_SMOOTH 0xf02a0080
+#define F367TER_SDI_INC_SMOOTHER 0xf02a007f
+
+/* FE_LOOP_OPEN */
+#define R367TER_FE_LOOP_OPEN 0xf02b
+#define F367TER_TRL_LOOP_OP 0xf02b0002
+#define F367TER_CRL_LOOP_OP 0xf02b0001
+
+/* FREQOFF1 */
+#define R367TER_FREQOFF1 0xf02c
+#define F367TER_FREQ_OFFSET_LOOP_OPEN_VHI 0xf02c00ff
+
+/* FREQOFF2 */
+#define R367TER_FREQOFF2 0xf02d
+#define F367TER_FREQ_OFFSET_LOOP_OPEN_HI 0xf02d00ff
+
+/* FREQOFF3 */
+#define R367TER_FREQOFF3 0xf02e
+#define F367TER_FREQ_OFFSET_LOOP_OPEN_LO 0xf02e00ff
+
+/* TIMOFF1 */
+#define R367TER_TIMOFF1 0xf02f
+#define F367TER_TIM_OFFSET_LOOP_OPEN_HI 0xf02f00ff
+
+/* TIMOFF2 */
+#define R367TER_TIMOFF2 0xf030
+#define F367TER_TIM_OFFSET_LOOP_OPEN_LO 0xf03000ff
+
+/* EPQ */
+#define R367TER_EPQ 0xf031
+#define F367TER_EPQ1 0xf03100ff
+
+/* EPQAUTO */
+#define R367TER_EPQAUTO 0xf032
+#define F367TER_EPQ2 0xf03200ff
+
+/* SYR_UPDATE */
+#define R367TER_SYR_UPDATE 0xf033
+#define F367TER_SYR_PROTV 0xf0330080
+#define F367TER_SYR_PROTV_GAIN 0xf0330060
+#define F367TER_SYR_FILTER 0xf0330010
+#define F367TER_SYR_TRACK_THRES 0xf033000c
+
+/* CHPFREE */
+#define R367TER_CHPFREE 0xf034
+#define F367TER_CHP_FREE 0xf03400ff
+
+/* PPM_STATE_MAC */
+#define R367TER_PPM_STATE_MAC 0xf035
+#define F367TER_PPM_STATE_MACHINE_DECODER 0xf035003f
+
+/* INR_THRESHOLD */
+#define R367TER_INR_THRESHOLD 0xf036
+#define F367TER_INR_THRESH 0xf03600ff
+
+/* EPQ_TPS_ID_CELL */
+#define R367TER_EPQ_TPS_ID_CELL 0xf037
+#define F367TER_ENABLE_LGTH_TO_CF 0xf0370080
+#define F367TER_DIS_TPS_RSVD 0xf0370040
+#define F367TER_DIS_BCH 0xf0370020
+#define F367TER_DIS_ID_CEL 0xf0370010
+#define F367TER_TPS_ADJUST_SYM 0xf037000f
+
+/* EPQ_CFG */
+#define R367TER_EPQ_CFG 0xf038
+#define F367TER_EPQ_RANGE 0xf0380002
+#define F367TER_EPQ_SOFT 0xf0380001
+
+/* EPQ_STATUS */
+#define R367TER_EPQ_STATUS 0xf039
+#define F367TER_SLOPE_INC 0xf03900fc
+#define F367TER_TPS_FIELD 0xf0390003
+
+/* AUTORELOCK */
+#define R367TER_AUTORELOCK 0xf03a
+#define F367TER_BYPASS_BER_TEMPO 0xf03a0080
+#define F367TER_BER_TEMPO 0xf03a0070
+#define F367TER_BYPASS_COFDM_TEMPO 0xf03a0008
+#define F367TER_COFDM_TEMPO 0xf03a0007
+
+/* BER_THR_VMSB */
+#define R367TER_BER_THR_VMSB 0xf03b
+#define F367TER_BER_THRESHOLD_HI 0xf03b00ff
+
+/* BER_THR_MSB */
+#define R367TER_BER_THR_MSB 0xf03c
+#define F367TER_BER_THRESHOLD_MID 0xf03c00ff
+
+/* BER_THR_LSB */
+#define R367TER_BER_THR_LSB 0xf03d
+#define F367TER_BER_THRESHOLD_LO 0xf03d00ff
+
+/* CCD */
+#define R367TER_CCD 0xf03e
+#define F367TER_CCD_DETECTED 0xf03e0080
+#define F367TER_CCD_RESET 0xf03e0040
+#define F367TER_CCD_THRESHOLD 0xf03e000f
+
+/* SPECTR_CFG */
+#define R367TER_SPECTR_CFG 0xf03f
+#define F367TER_SPECT_CFG 0xf03f0003
+
+/* CONSTMU_MSB */
+#define R367TER_CONSTMU_MSB 0xf040
+#define F367TER_CONSTMU_FREEZE 0xf0400080
+#define F367TER_CONSTNU_FORCE_EN 0xf0400040
+#define F367TER_CONST_MU_MSB 0xf040003f
+
+/* CONSTMU_LSB */
+#define R367TER_CONSTMU_LSB 0xf041
+#define F367TER_CONST_MU_LSB 0xf04100ff
+
+/* CONSTMU_MAX_MSB */
+#define R367TER_CONSTMU_MAX_MSB 0xf042
+#define F367TER_CONST_MU_MAX_MSB 0xf042003f
+
+/* CONSTMU_MAX_LSB */
+#define R367TER_CONSTMU_MAX_LSB 0xf043
+#define F367TER_CONST_MU_MAX_LSB 0xf04300ff
+
+/* ALPHANOISE */
+#define R367TER_ALPHANOISE 0xf044
+#define F367TER_USE_ALLFILTER 0xf0440080
+#define F367TER_INTER_ON 0xf0440040
+#define F367TER_ALPHA_NOISE 0xf044001f
+
+/* MAXGP_MSB */
+#define R367TER_MAXGP_MSB 0xf045
+#define F367TER_MUFILTER_LENGTH 0xf04500f0
+#define F367TER_MAX_GP_MSB 0xf045000f
+
+/* MAXGP_LSB */
+#define R367TER_MAXGP_LSB 0xf046
+#define F367TER_MAX_GP_LSB 0xf04600ff
+
+/* ALPHAMSB */
+#define R367TER_ALPHAMSB 0xf047
+#define F367TER_CHC_DATARATE 0xf04700c0
+#define F367TER_ALPHA_MSB 0xf047003f
+
+/* ALPHALSB */
+#define R367TER_ALPHALSB 0xf048
+#define F367TER_ALPHA_LSB 0xf04800ff
+
+/* PILOT_ACCU */
+#define R367TER_PILOT_ACCU 0xf049
+#define F367TER_USE_SCAT4ADDAPT 0xf0490080
+#define F367TER_PILOT_ACC 0xf049001f
+
+/* PILOTMU_ACCU */
+#define R367TER_PILOTMU_ACCU 0xf04a
+#define F367TER_DISCARD_BAD_SP 0xf04a0080
+#define F367TER_DISCARD_BAD_CP 0xf04a0040
+#define F367TER_PILOT_MU_ACCU 0xf04a001f
+
+/* FILT_CHANNEL_EST */
+#define R367TER_FILT_CHANNEL_EST 0xf04b
+#define F367TER_USE_FILT_PILOT 0xf04b0080
+#define F367TER_FILT_CHANNEL 0xf04b007f
+
+/* ALPHA_NOPISE_FREQ */
+#define R367TER_ALPHA_NOPISE_FREQ 0xf04c
+#define F367TER_NOISE_FREQ_FILT 0xf04c0040
+#define F367TER_ALPHA_NOISE_FREQ 0xf04c003f
+
+/* RATIO_PILOT */
+#define R367TER_RATIO_PILOT 0xf04d
+#define F367TER_RATIO_MEAN_SP 0xf04d00f0
+#define F367TER_RATIO_MEAN_CP 0xf04d000f
+
+/* CHC_CTL */
+#define R367TER_CHC_CTL 0xf04e
+#define F367TER_TRACK_EN 0xf04e0080
+#define F367TER_NOISE_NORM_EN 0xf04e0040
+#define F367TER_FORCE_CHC_RESET 0xf04e0020
+#define F367TER_SHORT_TIME 0xf04e0010
+#define F367TER_FORCE_STATE_EN 0xf04e0008
+#define F367TER_FORCE_STATE 0xf04e0007
+
+/* EPQ_ADJUST */
+#define R367TER_EPQ_ADJUST 0xf04f
+#define F367TER_ADJUST_SCAT_IND 0xf04f00c0
+#define F367TER_ONE_SYMBOL 0xf04f0010
+#define F367TER_EPQ_DECAY 0xf04f000e
+#define F367TER_HOLD_SLOPE 0xf04f0001
+
+/* EPQ_THRES */
+#define R367TER_EPQ_THRES 0xf050
+#define F367TER_EPQ_THR 0xf05000ff
+
+/* OMEGA_CTL */
+#define R367TER_OMEGA_CTL 0xf051
+#define F367TER_OMEGA_RST 0xf0510080
+#define F367TER_FREEZE_OMEGA 0xf0510040
+#define F367TER_OMEGA_SEL 0xf051003f
+
+/* GP_CTL */
+#define R367TER_GP_CTL 0xf052
+#define F367TER_CHC_STATE 0xf05200e0
+#define F367TER_FREEZE_GP 0xf0520010
+#define F367TER_GP_SEL 0xf052000f
+
+/* MUMSB */
+#define R367TER_MUMSB 0xf053
+#define F367TER_MU_MSB 0xf053007f
+
+/* MULSB */
+#define R367TER_MULSB 0xf054
+#define F367TER_MU_LSB 0xf05400ff
+
+/* GPMSB */
+#define R367TER_GPMSB 0xf055
+#define F367TER_CSI_THRESHOLD 0xf05500e0
+#define F367TER_GP_MSB 0xf055000f
+
+/* GPLSB */
+#define R367TER_GPLSB 0xf056
+#define F367TER_GP_LSB 0xf05600ff
+
+/* OMEGAMSB */
+#define R367TER_OMEGAMSB 0xf057
+#define F367TER_OMEGA_MSB 0xf057007f
+
+/* OMEGALSB */
+#define R367TER_OMEGALSB 0xf058
+#define F367TER_OMEGA_LSB 0xf05800ff
+
+/* SCAT_NB */
+#define R367TER_SCAT_NB 0xf059
+#define F367TER_CHC_TEST 0xf05900f8
+#define F367TER_SCAT_NUMB 0xf0590003
+
+/* CHC_DUMMY */
+#define R367TER_CHC_DUMMY 0xf05a
+#define F367TER_CHC_DUM 0xf05a00ff
+
+/* INC_CTL */
+#define R367TER_INC_CTL 0xf05b
+#define F367TER_INC_BYPASS 0xf05b0080
+#define F367TER_INC_NDEPTH 0xf05b000c
+#define F367TER_INC_MADEPTH 0xf05b0003
+
+/* INCTHRES_COR1 */
+#define R367TER_INCTHRES_COR1 0xf05c
+#define F367TER_INC_THRES_COR1 0xf05c00ff
+
+/* INCTHRES_COR2 */
+#define R367TER_INCTHRES_COR2 0xf05d
+#define F367TER_INC_THRES_COR2 0xf05d00ff
+
+/* INCTHRES_DET1 */
+#define R367TER_INCTHRES_DET1 0xf05e
+#define F367TER_INC_THRES_DET1 0xf05e003f
+
+/* INCTHRES_DET2 */
+#define R367TER_INCTHRES_DET2 0xf05f
+#define F367TER_INC_THRES_DET2 0xf05f003f
+
+/* IIR_CELLNB */
+#define R367TER_IIR_CELLNB 0xf060
+#define F367TER_NRST_IIR 0xf0600080
+#define F367TER_IIR_CELL_NB 0xf0600007
+
+/* IIRCX_COEFF1_MSB */
+#define R367TER_IIRCX_COEFF1_MSB 0xf061
+#define F367TER_IIR_CX_COEFF1_MSB 0xf06100ff
+
+/* IIRCX_COEFF1_LSB */
+#define R367TER_IIRCX_COEFF1_LSB 0xf062
+#define F367TER_IIR_CX_COEFF1_LSB 0xf06200ff
+
+/* IIRCX_COEFF2_MSB */
+#define R367TER_IIRCX_COEFF2_MSB 0xf063
+#define F367TER_IIR_CX_COEFF2_MSB 0xf06300ff
+
+/* IIRCX_COEFF2_LSB */
+#define R367TER_IIRCX_COEFF2_LSB 0xf064
+#define F367TER_IIR_CX_COEFF2_LSB 0xf06400ff
+
+/* IIRCX_COEFF3_MSB */
+#define R367TER_IIRCX_COEFF3_MSB 0xf065
+#define F367TER_IIR_CX_COEFF3_MSB 0xf06500ff
+
+/* IIRCX_COEFF3_LSB */
+#define R367TER_IIRCX_COEFF3_LSB 0xf066
+#define F367TER_IIR_CX_COEFF3_LSB 0xf06600ff
+
+/* IIRCX_COEFF4_MSB */
+#define R367TER_IIRCX_COEFF4_MSB 0xf067
+#define F367TER_IIR_CX_COEFF4_MSB 0xf06700ff
+
+/* IIRCX_COEFF4_LSB */
+#define R367TER_IIRCX_COEFF4_LSB 0xf068
+#define F367TER_IIR_CX_COEFF4_LSB 0xf06800ff
+
+/* IIRCX_COEFF5_MSB */
+#define R367TER_IIRCX_COEFF5_MSB 0xf069
+#define F367TER_IIR_CX_COEFF5_MSB 0xf06900ff
+
+/* IIRCX_COEFF5_LSB */
+#define R367TER_IIRCX_COEFF5_LSB 0xf06a
+#define F367TER_IIR_CX_COEFF5_LSB 0xf06a00ff
+
+/* FEPATH_CFG */
+#define R367TER_FEPATH_CFG 0xf06b
+#define F367TER_DEMUX_SWAP 0xf06b0004
+#define F367TER_DIGAGC_SWAP 0xf06b0002
+#define F367TER_LONGPATH_IF 0xf06b0001
+
+/* PMC1_FUNC */
+#define R367TER_PMC1_FUNC 0xf06c
+#define F367TER_SOFT_RSTN 0xf06c0080
+#define F367TER_PMC1_AVERAGE_TIME 0xf06c0078
+#define F367TER_PMC1_WAIT_TIME 0xf06c0006
+#define F367TER_PMC1_2N_SEL 0xf06c0001
+
+/* PMC1_FOR */
+#define R367TER_PMC1_FOR 0xf06d
+#define F367TER_PMC1_FORCE 0xf06d0080
+#define F367TER_PMC1_FORCE_VALUE 0xf06d007c
+
+/* PMC2_FUNC */
+#define R367TER_PMC2_FUNC 0xf06e
+#define F367TER_PMC2_SOFT_STN 0xf06e0080
+#define F367TER_PMC2_ACCU_TIME 0xf06e0070
+#define F367TER_PMC2_CMDP_MN 0xf06e0008
+#define F367TER_PMC2_SWAP 0xf06e0004
+
+/* STATUS_ERR_DA */
+#define R367TER_STATUS_ERR_DA 0xf06f
+#define F367TER_COM_USEGAINTRK 0xf06f0080
+#define F367TER_COM_AGCLOCK 0xf06f0040
+#define F367TER_AUT_AGCLOCK 0xf06f0020
+#define F367TER_MIN_ERR_X_LSB 0xf06f000f
+
+/* DIG_AGC_R */
+#define R367TER_DIG_AGC_R 0xf070
+#define F367TER_COM_SOFT_RSTN 0xf0700080
+#define F367TER_COM_AGC_ON 0xf0700040
+#define F367TER_COM_EARLY 0xf0700020
+#define F367TER_AUT_SOFT_RESETN 0xf0700010
+#define F367TER_AUT_AGC_ON 0xf0700008
+#define F367TER_AUT_EARLY 0xf0700004
+#define F367TER_AUT_ROT_EN 0xf0700002
+#define F367TER_LOCK_SOFT_RESETN 0xf0700001
+
+/* COMAGC_TARMSB */
+#define R367TER_COMAGC_TARMSB 0xf071
+#define F367TER_COM_AGC_TARGET_MSB 0xf07100ff
+
+/* COM_AGC_TAR_ENMODE */
+#define R367TER_COM_AGC_TAR_ENMODE 0xf072
+#define F367TER_COM_AGC_TARGET_LSB 0xf07200f0
+#define F367TER_COM_ENMODE 0xf072000f
+
+/* COM_AGC_CFG */
+#define R367TER_COM_AGC_CFG 0xf073
+#define F367TER_COM_N 0xf07300f8
+#define F367TER_COM_STABMODE 0xf0730006
+#define F367TER_ERR_SEL 0xf0730001
+
+/* COM_AGC_GAIN1 */
+#define R367TER_COM_AGC_GAIN1 0xf074
+#define F367TER_COM_GAIN1aCK 0xf07400f0
+#define F367TER_COM_GAIN1TRK 0xf074000f
+
+/* AUT_AGC_TARGETMSB */
+#define R367TER_AUT_AGC_TARGETMSB 0xf075
+#define F367TER_AUT_AGC_TARGET_MSB 0xf07500ff
+
+/* LOCK_DET_MSB */
+#define R367TER_LOCK_DET_MSB 0xf076
+#define F367TER_LOCK_DETECT_MSB 0xf07600ff
+
+/* AGCTAR_LOCK_LSBS */
+#define R367TER_AGCTAR_LOCK_LSBS 0xf077
+#define F367TER_AUT_AGC_TARGET_LSB 0xf07700f0
+#define F367TER_LOCK_DETECT_LSB 0xf077000f
+
+/* AUT_GAIN_EN */
+#define R367TER_AUT_GAIN_EN 0xf078
+#define F367TER_AUT_ENMODE 0xf07800f0
+#define F367TER_AUT_GAIN2 0xf078000f
+
+/* AUT_CFG */
+#define R367TER_AUT_CFG 0xf079
+#define F367TER_AUT_N 0xf07900f8
+#define F367TER_INT_CHOICE 0xf0790006
+#define F367TER_INT_LOAD 0xf0790001
+
+/* LOCKN */
+#define R367TER_LOCKN 0xf07a
+#define F367TER_LOCK_N 0xf07a00f8
+#define F367TER_SEL_IQNTAR 0xf07a0004
+#define F367TER_LOCK_DETECT_CHOICE 0xf07a0003
+
+/* INT_X_3 */
+#define R367TER_INT_X_3 0xf07b
+#define F367TER_INT_X3 0xf07b00ff
+
+/* INT_X_2 */
+#define R367TER_INT_X_2 0xf07c
+#define F367TER_INT_X2 0xf07c00ff
+
+/* INT_X_1 */
+#define R367TER_INT_X_1 0xf07d
+#define F367TER_INT_X1 0xf07d00ff
+
+/* INT_X_0 */
+#define R367TER_INT_X_0 0xf07e
+#define F367TER_INT_X0 0xf07e00ff
+
+/* MIN_ERRX_MSB */
+#define R367TER_MIN_ERRX_MSB 0xf07f
+#define F367TER_MIN_ERR_X_MSB 0xf07f00ff
+
+/* COR_CTL */
+#define R367TER_COR_CTL 0xf080
+#define F367TER_CORE_ACTIVE 0xf0800020
+#define F367TER_HOLD 0xf0800010
+#define F367TER_CORE_STATE_CTL 0xf080000f
+
+/* COR_STAT */
+#define R367TER_COR_STAT 0xf081
+#define F367TER_SCATT_LOCKED 0xf0810080
+#define F367TER_TPS_LOCKED 0xf0810040
+#define F367TER_SYR_LOCKED_COR 0xf0810020
+#define F367TER_AGC_LOCKED_STAT 0xf0810010
+#define F367TER_CORE_STATE_STAT 0xf081000f
+
+/* COR_INTEN */
+#define R367TER_COR_INTEN 0xf082
+#define F367TER_INTEN 0xf0820080
+#define F367TER_INTEN_SYR 0xf0820020
+#define F367TER_INTEN_FFT 0xf0820010
+#define F367TER_INTEN_AGC 0xf0820008
+#define F367TER_INTEN_TPS1 0xf0820004
+#define F367TER_INTEN_TPS2 0xf0820002
+#define F367TER_INTEN_TPS3 0xf0820001
+
+/* COR_INTSTAT */
+#define R367TER_COR_INTSTAT 0xf083
+#define F367TER_INTSTAT_SYR 0xf0830020
+#define F367TER_INTSTAT_FFT 0xf0830010
+#define F367TER_INTSAT_AGC 0xf0830008
+#define F367TER_INTSTAT_TPS1 0xf0830004
+#define F367TER_INTSTAT_TPS2 0xf0830002
+#define F367TER_INTSTAT_TPS3 0xf0830001
+
+/* COR_MODEGUARD */
+#define R367TER_COR_MODEGUARD 0xf084
+#define F367TER_FORCE 0xf0840010
+#define F367TER_MODE 0xf084000c
+#define F367TER_GUARD 0xf0840003
+
+/* AGC_CTL */
+#define R367TER_AGC_CTL 0xf085
+#define F367TER_AGC_TIMING_FACTOR 0xf08500e0
+#define F367TER_AGC_LAST 0xf0850010
+#define F367TER_AGC_GAIN 0xf085000c
+#define F367TER_AGC_NEG 0xf0850002
+#define F367TER_AGC_SET 0xf0850001
+
+/* AGC_MANUAL1 */
+#define R367TER_AGC_MANUAL1 0xf086
+#define F367TER_AGC_VAL_LO 0xf08600ff
+
+/* AGC_MANUAL2 */
+#define R367TER_AGC_MANUAL2 0xf087
+#define F367TER_AGC_VAL_HI 0xf087000f
+
+/* AGC_TARG */
+#define R367TER_AGC_TARG 0xf088
+#define F367TER_AGC_TARGET 0xf08800ff
+
+/* AGC_GAIN1 */
+#define R367TER_AGC_GAIN1 0xf089
+#define F367TER_AGC_GAIN_LO 0xf08900ff
+
+/* AGC_GAIN2 */
+#define R367TER_AGC_GAIN2 0xf08a
+#define F367TER_AGC_LOCKED_GAIN2 0xf08a0010
+#define F367TER_AGC_GAIN_HI 0xf08a000f
+
+/* RESERVED_1 */
+#define R367TER_RESERVED_1 0xf08b
+#define F367TER_RESERVED1 0xf08b00ff
+
+/* RESERVED_2 */
+#define R367TER_RESERVED_2 0xf08c
+#define F367TER_RESERVED2 0xf08c00ff
+
+/* RESERVED_3 */
+#define R367TER_RESERVED_3 0xf08d
+#define F367TER_RESERVED3 0xf08d00ff
+
+/* CAS_CTL */
+#define R367TER_CAS_CTL 0xf08e
+#define F367TER_CCS_ENABLE 0xf08e0080
+#define F367TER_ACS_DISABLE 0xf08e0040
+#define F367TER_DAGC_DIS 0xf08e0020
+#define F367TER_DAGC_GAIN 0xf08e0018
+#define F367TER_CCSMU 0xf08e0007
+
+/* CAS_FREQ */
+#define R367TER_CAS_FREQ 0xf08f
+#define F367TER_CCS_FREQ 0xf08f00ff
+
+/* CAS_DAGCGAIN */
+#define R367TER_CAS_DAGCGAIN 0xf090
+#define F367TER_CAS_DAGC_GAIN 0xf09000ff
+
+/* SYR_CTL */
+#define R367TER_SYR_CTL 0xf091
+#define F367TER_SICTH_ENABLE 0xf0910080
+#define F367TER_LONG_ECHO 0xf0910078
+#define F367TER_AUTO_LE_EN 0xf0910004
+#define F367TER_SYR_BYPASS 0xf0910002
+#define F367TER_SYR_TR_DIS 0xf0910001
+
+/* SYR_STAT */
+#define R367TER_SYR_STAT 0xf092
+#define F367TER_SYR_LOCKED_STAT 0xf0920010
+#define F367TER_SYR_MODE 0xf092000c
+#define F367TER_SYR_GUARD 0xf0920003
+
+/* SYR_NCO1 */
+#define R367TER_SYR_NCO1 0xf093
+#define F367TER_SYR_NCO_LO 0xf09300ff
+
+/* SYR_NCO2 */
+#define R367TER_SYR_NCO2 0xf094
+#define F367TER_SYR_NCO_HI 0xf094003f
+
+/* SYR_OFFSET1 */
+#define R367TER_SYR_OFFSET1 0xf095
+#define F367TER_SYR_OFFSET_LO 0xf09500ff
+
+/* SYR_OFFSET2 */
+#define R367TER_SYR_OFFSET2 0xf096
+#define F367TER_SYR_OFFSET_HI 0xf096003f
+
+/* FFT_CTL */
+#define R367TER_FFT_CTL 0xf097
+#define F367TER_SHIFT_FFT_TRIG 0xf0970018
+#define F367TER_FFT_TRIGGER 0xf0970004
+#define F367TER_FFT_MANUAL 0xf0970002
+#define F367TER_IFFT_MODE 0xf0970001
+
+/* SCR_CTL */
+#define R367TER_SCR_CTL 0xf098
+#define F367TER_SYRADJDECAY 0xf0980070
+#define F367TER_SCR_CPEDIS 0xf0980002
+#define F367TER_SCR_DIS 0xf0980001
+
+/* PPM_CTL1 */
+#define R367TER_PPM_CTL1 0xf099
+#define F367TER_PPM_MAXFREQ 0xf0990030
+#define F367TER_PPM_MAXTIM 0xf0990008
+#define F367TER_PPM_INVSEL 0xf0990004
+#define F367TER_PPM_SCATDIS 0xf0990002
+#define F367TER_PPM_BYP 0xf0990001
+
+/* TRL_CTL */
+#define R367TER_TRL_CTL 0xf09a
+#define F367TER_TRL_NOMRATE_LSB 0xf09a0080
+#define F367TER_TRL_GAIN_FACTOR 0xf09a0078
+#define F367TER_TRL_LOOPGAIN 0xf09a0007
+
+/* TRL_NOMRATE1 */
+#define R367TER_TRL_NOMRATE1 0xf09b
+#define F367TER_TRL_NOMRATE_LO 0xf09b00ff
+
+/* TRL_NOMRATE2 */
+#define R367TER_TRL_NOMRATE2 0xf09c
+#define F367TER_TRL_NOMRATE_HI 0xf09c00ff
+
+/* TRL_TIME1 */
+#define R367TER_TRL_TIME1 0xf09d
+#define F367TER_TRL_TOFFSET_LO 0xf09d00ff
+
+/* TRL_TIME2 */
+#define R367TER_TRL_TIME2 0xf09e
+#define F367TER_TRL_TOFFSET_HI 0xf09e00ff
+
+/* CRL_CTL */
+#define R367TER_CRL_CTL 0xf09f
+#define F367TER_CRL_DIS 0xf09f0080
+#define F367TER_CRL_GAIN_FACTOR 0xf09f0078
+#define F367TER_CRL_LOOPGAIN 0xf09f0007
+
+/* CRL_FREQ1 */
+#define R367TER_CRL_FREQ1 0xf0a0
+#define F367TER_CRL_FOFFSET_LO 0xf0a000ff
+
+/* CRL_FREQ2 */
+#define R367TER_CRL_FREQ2 0xf0a1
+#define F367TER_CRL_FOFFSET_HI 0xf0a100ff
+
+/* CRL_FREQ3 */
+#define R367TER_CRL_FREQ3 0xf0a2
+#define F367TER_CRL_FOFFSET_VHI 0xf0a200ff
+
+/* TPS_SFRAME_CTL */
+#define R367TER_TPS_SFRAME_CTL 0xf0a3
+#define F367TER_TPS_SFRAME_SYNC 0xf0a30001
+
+/* CHC_SNR */
+#define R367TER_CHC_SNR 0xf0a4
+#define F367TER_CHCSNR 0xf0a400ff
+
+/* BDI_CTL */
+#define R367TER_BDI_CTL 0xf0a5
+#define F367TER_BDI_LPSEL 0xf0a50002
+#define F367TER_BDI_SERIAL 0xf0a50001
+
+/* DMP_CTL */
+#define R367TER_DMP_CTL 0xf0a6
+#define F367TER_DMP_SCALING_FACTOR 0xf0a6001e
+#define F367TER_DMP_SDDIS 0xf0a60001
+
+/* TPS_RCVD1 */
+#define R367TER_TPS_RCVD1 0xf0a7
+#define F367TER_TPS_CHANGE 0xf0a70040
+#define F367TER_BCH_OK 0xf0a70020
+#define F367TER_TPS_SYNC 0xf0a70010
+#define F367TER_TPS_FRAME 0xf0a70003
+
+/* TPS_RCVD2 */
+#define R367TER_TPS_RCVD2 0xf0a8
+#define F367TER_TPS_HIERMODE 0xf0a80070
+#define F367TER_TPS_CONST 0xf0a80003
+
+/* TPS_RCVD3 */
+#define R367TER_TPS_RCVD3 0xf0a9
+#define F367TER_TPS_LPCODE 0xf0a90070
+#define F367TER_TPS_HPCODE 0xf0a90007
+
+/* TPS_RCVD4 */
+#define R367TER_TPS_RCVD4 0xf0aa
+#define F367TER_TPS_GUARD 0xf0aa0030
+#define F367TER_TPS_MODE 0xf0aa0003
+
+/* TPS_ID_CELL1 */
+#define R367TER_TPS_ID_CELL1 0xf0ab
+#define F367TER_TPS_ID_CELL_LO 0xf0ab00ff
+
+/* TPS_ID_CELL2 */
+#define R367TER_TPS_ID_CELL2 0xf0ac
+#define F367TER_TPS_ID_CELL_HI 0xf0ac00ff
+
+/* TPS_RCVD5_SET1 */
+#define R367TER_TPS_RCVD5_SET1 0xf0ad
+#define F367TER_TPS_NA 0xf0ad00fC
+#define F367TER_TPS_SETFRAME 0xf0ad0003
+
+/* TPS_SET2 */
+#define R367TER_TPS_SET2 0xf0ae
+#define F367TER_TPS_SETHIERMODE 0xf0ae0070
+#define F367TER_TPS_SETCONST 0xf0ae0003
+
+/* TPS_SET3 */
+#define R367TER_TPS_SET3 0xf0af
+#define F367TER_TPS_SETLPCODE 0xf0af0070
+#define F367TER_TPS_SETHPCODE 0xf0af0007
+
+/* TPS_CTL */
+#define R367TER_TPS_CTL 0xf0b0
+#define F367TER_TPS_IMM 0xf0b00004
+#define F367TER_TPS_BCHDIS 0xf0b00002
+#define F367TER_TPS_UPDDIS 0xf0b00001
+
+/* CTL_FFTOSNUM */
+#define R367TER_CTL_FFTOSNUM 0xf0b1
+#define F367TER_SYMBOL_NUMBER 0xf0b1007f
+
+/* TESTSELECT */
+#define R367TER_TESTSELECT 0xf0b2
+#define F367TER_TEST_SELECT 0xf0b2001f
+
+/* MSC_REV */
+#define R367TER_MSC_REV 0xf0b3
+#define F367TER_REV_NUMBER 0xf0b300ff
+
+/* PIR_CTL */
+#define R367TER_PIR_CTL 0xf0b4
+#define F367TER_FREEZE 0xf0b40001
+
+/* SNR_CARRIER1 */
+#define R367TER_SNR_CARRIER1 0xf0b5
+#define F367TER_SNR_CARRIER_LO 0xf0b500ff
+
+/* SNR_CARRIER2 */
+#define R367TER_SNR_CARRIER2 0xf0b6
+#define F367TER_MEAN 0xf0b600c0
+#define F367TER_SNR_CARRIER_HI 0xf0b6001f
+
+/* PPM_CPAMP */
+#define R367TER_PPM_CPAMP 0xf0b7
+#define F367TER_PPM_CPC 0xf0b700ff
+
+/* TSM_AP0 */
+#define R367TER_TSM_AP0 0xf0b8
+#define F367TER_ADDRESS_BYTE_0 0xf0b800ff
+
+/* TSM_AP1 */
+#define R367TER_TSM_AP1 0xf0b9
+#define F367TER_ADDRESS_BYTE_1 0xf0b900ff
+
+/* TSM_AP2 */
+#define R367TER_TSM_AP2 0xf0bA
+#define F367TER_DATA_BYTE_0 0xf0ba00ff
+
+/* TSM_AP3 */
+#define R367TER_TSM_AP3 0xf0bB
+#define F367TER_DATA_BYTE_1 0xf0bb00ff
+
+/* TSM_AP4 */
+#define R367TER_TSM_AP4 0xf0bC
+#define F367TER_DATA_BYTE_2 0xf0bc00ff
+
+/* TSM_AP5 */
+#define R367TER_TSM_AP5 0xf0bD
+#define F367TER_DATA_BYTE_3 0xf0bd00ff
+
+/* TSM_AP6 */
+#define R367TER_TSM_AP6 0xf0bE
+#define F367TER_TSM_AP_6 0xf0be00ff
+
+/* TSM_AP7 */
+#define R367TER_TSM_AP7 0xf0bF
+#define F367TER_MEM_SELECT_BYTE 0xf0bf00ff
+
+/* TSTRES */
+#define R367TER_TSTRES 0xf0c0
+#define F367TER_FRES_DISPLAY 0xf0c00080
+#define F367TER_FRES_FIFO_AD 0xf0c00020
+#define F367TER_FRESRS 0xf0c00010
+#define F367TER_FRESACS 0xf0c00008
+#define F367TER_FRESFEC 0xf0c00004
+#define F367TER_FRES_PRIF 0xf0c00002
+#define F367TER_FRESCORE 0xf0c00001
+
+/* ANACTRL */
+#define R367TER_ANACTRL 0xf0c1
+#define F367TER_BYPASS_XTAL 0xf0c10040
+#define F367TER_BYPASS_PLLXN 0xf0c1000c
+#define F367TER_DIS_PAD_OSC 0xf0c10002
+#define F367TER_STDBY_PLLXN 0xf0c10001
+
+/* TSTBUS */
+#define R367TER_TSTBUS 0xf0c2
+#define F367TER_TS_BYTE_CLK_INV 0xf0c20080
+#define F367TER_CFG_IP 0xf0c20070
+#define F367TER_CFG_TST 0xf0c2000f
+
+/* TSTRATE */
+#define R367TER_TSTRATE 0xf0c6
+#define F367TER_FORCEPHA 0xf0c60080
+#define F367TER_FNEWPHA 0xf0c60010
+#define F367TER_FROT90 0xf0c60008
+#define F367TER_FR 0xf0c60007
+
+/* CONSTMODE */
+#define R367TER_CONSTMODE 0xf0cb
+#define F367TER_TST_PRIF 0xf0cb00e0
+#define F367TER_CAR_TYPE 0xf0cb0018
+#define F367TER_CONST_MODE 0xf0cb0003
+
+/* CONSTCARR1 */
+#define R367TER_CONSTCARR1 0xf0cc
+#define F367TER_CONST_CARR_LO 0xf0cc00ff
+
+/* CONSTCARR2 */
+#define R367TER_CONSTCARR2 0xf0cd
+#define F367TER_CONST_CARR_HI 0xf0cd001f
+
+/* ICONSTEL */
+#define R367TER_ICONSTEL 0xf0ce
+#define F367TER_PICONSTEL 0xf0ce00ff
+
+/* QCONSTEL */
+#define R367TER_QCONSTEL 0xf0cf
+#define F367TER_PQCONSTEL 0xf0cf00ff
+
+/* TSTBISTRES0 */
+#define R367TER_TSTBISTRES0 0xf0d0
+#define F367TER_BEND_PPM 0xf0d00080
+#define F367TER_BBAD_PPM 0xf0d00040
+#define F367TER_BEND_FFTW 0xf0d00020
+#define F367TER_BBAD_FFTW 0xf0d00010
+#define F367TER_BEND_FFT_BUF 0xf0d00008
+#define F367TER_BBAD_FFT_BUF 0xf0d00004
+#define F367TER_BEND_SYR 0xf0d00002
+#define F367TER_BBAD_SYR 0xf0d00001
+
+/* TSTBISTRES1 */
+#define R367TER_TSTBISTRES1 0xf0d1
+#define F367TER_BEND_CHC_CP 0xf0d10080
+#define F367TER_BBAD_CHC_CP 0xf0d10040
+#define F367TER_BEND_CHCI 0xf0d10020
+#define F367TER_BBAD_CHCI 0xf0d10010
+#define F367TER_BEND_BDI 0xf0d10008
+#define F367TER_BBAD_BDI 0xf0d10004
+#define F367TER_BEND_SDI 0xf0d10002
+#define F367TER_BBAD_SDI 0xf0d10001
+
+/* TSTBISTRES2 */
+#define R367TER_TSTBISTRES2 0xf0d2
+#define F367TER_BEND_CHC_INC 0xf0d20080
+#define F367TER_BBAD_CHC_INC 0xf0d20040
+#define F367TER_BEND_CHC_SPP 0xf0d20020
+#define F367TER_BBAD_CHC_SPP 0xf0d20010
+#define F367TER_BEND_CHC_CPP 0xf0d20008
+#define F367TER_BBAD_CHC_CPP 0xf0d20004
+#define F367TER_BEND_CHC_SP 0xf0d20002
+#define F367TER_BBAD_CHC_SP 0xf0d20001
+
+/* TSTBISTRES3 */
+#define R367TER_TSTBISTRES3 0xf0d3
+#define F367TER_BEND_QAM 0xf0d30080
+#define F367TER_BBAD_QAM 0xf0d30040
+#define F367TER_BEND_SFEC_VIT 0xf0d30020
+#define F367TER_BBAD_SFEC_VIT 0xf0d30010
+#define F367TER_BEND_SFEC_DLINE 0xf0d30008
+#define F367TER_BBAD_SFEC_DLINE 0xf0d30004
+#define F367TER_BEND_SFEC_HW 0xf0d30002
+#define F367TER_BBAD_SFEC_HW 0xf0d30001
+
+/* RF_AGC1 */
+#define R367TER_RF_AGC1 0xf0d4
+#define F367TER_RF_AGC1_LEVEL_HI 0xf0d400ff
+
+/* RF_AGC2 */
+#define R367TER_RF_AGC2 0xf0d5
+#define F367TER_REF_ADGP 0xf0d50080
+#define F367TER_STDBY_ADCGP 0xf0d50020
+#define F367TER_CHANNEL_SEL 0xf0d5001c
+#define F367TER_RF_AGC1_LEVEL_LO 0xf0d50003
+
+/* ANADIGCTRL */
+#define R367TER_ANADIGCTRL 0xf0d7
+#define F367TER_SEL_CLKDEM 0xf0d70020
+#define F367TER_EN_BUFFER_Q 0xf0d70010
+#define F367TER_EN_BUFFER_I 0xf0d70008
+#define F367TER_ADC_RIS_EGDE 0xf0d70004
+#define F367TER_SGN_ADC 0xf0d70002
+#define F367TER_SEL_AD12_SYNC 0xf0d70001
+
+/* PLLMDIV */
+#define R367TER_PLLMDIV 0xf0d8
+#define F367TER_PLL_MDIV 0xf0d800ff
+
+/* PLLNDIV */
+#define R367TER_PLLNDIV 0xf0d9
+#define F367TER_PLL_NDIV 0xf0d900ff
+
+/* PLLSETUP */
+#define R367TER_PLLSETUP 0xf0dA
+#define F367TER_PLL_PDIV 0xf0da0070
+#define F367TER_PLL_KDIV 0xf0da000f
+
+/* DUAL_AD12 */
+#define R367TER_DUAL_AD12 0xf0dB
+#define F367TER_FS20M 0xf0db0020
+#define F367TER_FS50M 0xf0db0010
+#define F367TER_INMODe0 0xf0db0008
+#define F367TER_POFFQ 0xf0db0004
+#define F367TER_POFFI 0xf0db0002
+#define F367TER_INMODE1 0xf0db0001
+
+/* TSTBIST */
+#define R367TER_TSTBIST 0xf0dC
+#define F367TER_TST_BYP_CLK 0xf0dc0080
+#define F367TER_TST_GCLKENA_STD 0xf0dc0040
+#define F367TER_TST_GCLKENA 0xf0dc0020
+#define F367TER_TST_MEMBIST 0xf0dc001f
+
+/* PAD_COMP_CTRL */
+#define R367TER_PAD_COMP_CTRL 0xf0dD
+#define F367TER_COMPTQ 0xf0dd0010
+#define F367TER_COMPEN 0xf0dd0008
+#define F367TER_FREEZE2 0xf0dd0004
+#define F367TER_SLEEP_INHBT 0xf0dd0002
+#define F367TER_CHIP_SLEEP 0xf0dd0001
+
+/* PAD_COMP_WR */
+#define R367TER_PAD_COMP_WR 0xf0de
+#define F367TER_WR_ASRC 0xf0de007f
+
+/* PAD_COMP_RD */
+#define R367TER_PAD_COMP_RD 0xf0df
+#define F367TER_COMPOK 0xf0df0080
+#define F367TER_RD_ASRC 0xf0df007f
+
+/* SYR_TARGET_FFTADJT_MSB */
+#define R367TER_SYR_TARGET_FFTADJT_MSB 0xf100
+#define F367TER_SYR_START 0xf1000080
+#define F367TER_SYR_TARGET_FFTADJ_HI 0xf100000f
+
+/* SYR_TARGET_FFTADJT_LSB */
+#define R367TER_SYR_TARGET_FFTADJT_LSB 0xf101
+#define F367TER_SYR_TARGET_FFTADJ_LO 0xf10100ff
+
+/* SYR_TARGET_CHCADJT_MSB */
+#define R367TER_SYR_TARGET_CHCADJT_MSB 0xf102
+#define F367TER_SYR_TARGET_CHCADJ_HI 0xf102000f
+
+/* SYR_TARGET_CHCADJT_LSB */
+#define R367TER_SYR_TARGET_CHCADJT_LSB 0xf103
+#define F367TER_SYR_TARGET_CHCADJ_LO 0xf10300ff
+
+/* SYR_FLAG */
+#define R367TER_SYR_FLAG 0xf104
+#define F367TER_TRIG_FLG1 0xf1040080
+#define F367TER_TRIG_FLG0 0xf1040040
+#define F367TER_FFT_FLG1 0xf1040008
+#define F367TER_FFT_FLG0 0xf1040004
+#define F367TER_CHC_FLG1 0xf1040002
+#define F367TER_CHC_FLG0 0xf1040001
+
+/* CRL_TARGET1 */
+#define R367TER_CRL_TARGET1 0xf105
+#define F367TER_CRL_START 0xf1050080
+#define F367TER_CRL_TARGET_VHI 0xf105000f
+
+/* CRL_TARGET2 */
+#define R367TER_CRL_TARGET2 0xf106
+#define F367TER_CRL_TARGET_HI 0xf10600ff
+
+/* CRL_TARGET3 */
+#define R367TER_CRL_TARGET3 0xf107
+#define F367TER_CRL_TARGET_LO 0xf10700ff
+
+/* CRL_TARGET4 */
+#define R367TER_CRL_TARGET4 0xf108
+#define F367TER_CRL_TARGET_VLO 0xf10800ff
+
+/* CRL_FLAG */
+#define R367TER_CRL_FLAG 0xf109
+#define F367TER_CRL_FLAG1 0xf1090002
+#define F367TER_CRL_FLAG0 0xf1090001
+
+/* TRL_TARGET1 */
+#define R367TER_TRL_TARGET1 0xf10a
+#define F367TER_TRL_TARGET_HI 0xf10a00ff
+
+/* TRL_TARGET2 */
+#define R367TER_TRL_TARGET2 0xf10b
+#define F367TER_TRL_TARGET_LO 0xf10b00ff
+
+/* TRL_CHC */
+#define R367TER_TRL_CHC 0xf10c
+#define F367TER_TRL_START 0xf10c0080
+#define F367TER_CHC_START 0xf10c0040
+#define F367TER_TRL_FLAG1 0xf10c0002
+#define F367TER_TRL_FLAG0 0xf10c0001
+
+/* CHC_SNR_TARG */
+#define R367TER_CHC_SNR_TARG 0xf10d
+#define F367TER_CHC_SNR_TARGET 0xf10d00ff
+
+/* TOP_TRACK */
+#define R367TER_TOP_TRACK 0xf10e
+#define F367TER_TOP_START 0xf10e0080
+#define F367TER_FIRST_FLAG 0xf10e0070
+#define F367TER_TOP_FLAG1 0xf10e0008
+#define F367TER_TOP_FLAG0 0xf10e0004
+#define F367TER_CHC_FLAG1 0xf10e0002
+#define F367TER_CHC_FLAG0 0xf10e0001
+
+/* TRACKER_FREE1 */
+#define R367TER_TRACKER_FREE1 0xf10f
+#define F367TER_TRACKER_FREE_1 0xf10f00ff
+
+/* ERROR_CRL1 */
+#define R367TER_ERROR_CRL1 0xf110
+#define F367TER_ERROR_CRL_VHI 0xf11000ff
+
+/* ERROR_CRL2 */
+#define R367TER_ERROR_CRL2 0xf111
+#define F367TER_ERROR_CRL_HI 0xf11100ff
+
+/* ERROR_CRL3 */
+#define R367TER_ERROR_CRL3 0xf112
+#define F367TER_ERROR_CRL_LOI 0xf11200ff
+
+/* ERROR_CRL4 */
+#define R367TER_ERROR_CRL4 0xf113
+#define F367TER_ERROR_CRL_VLO 0xf11300ff
+
+/* DEC_NCO1 */
+#define R367TER_DEC_NCO1 0xf114
+#define F367TER_DEC_NCO_VHI 0xf11400ff
+
+/* DEC_NCO2 */
+#define R367TER_DEC_NCO2 0xf115
+#define F367TER_DEC_NCO_HI 0xf11500ff
+
+/* DEC_NCO3 */
+#define R367TER_DEC_NCO3 0xf116
+#define F367TER_DEC_NCO_LO 0xf11600ff
+
+/* SNR */
+#define R367TER_SNR 0xf117
+#define F367TER_SNRATIO 0xf11700ff
+
+/* SYR_FFTADJ1 */
+#define R367TER_SYR_FFTADJ1 0xf118
+#define F367TER_SYR_FFTADJ_HI 0xf11800ff
+
+/* SYR_FFTADJ2 */
+#define R367TER_SYR_FFTADJ2 0xf119
+#define F367TER_SYR_FFTADJ_LO 0xf11900ff
+
+/* SYR_CHCADJ1 */
+#define R367TER_SYR_CHCADJ1 0xf11a
+#define F367TER_SYR_CHCADJ_HI 0xf11a00ff
+
+/* SYR_CHCADJ2 */
+#define R367TER_SYR_CHCADJ2 0xf11b
+#define F367TER_SYR_CHCADJ_LO 0xf11b00ff
+
+/* SYR_OFF */
+#define R367TER_SYR_OFF 0xf11c
+#define F367TER_SYR_OFFSET 0xf11c00ff
+
+/* PPM_OFFSET1 */
+#define R367TER_PPM_OFFSET1 0xf11d
+#define F367TER_PPM_OFFSET_HI 0xf11d00ff
+
+/* PPM_OFFSET2 */
+#define R367TER_PPM_OFFSET2 0xf11e
+#define F367TER_PPM_OFFSET_LO 0xf11e00ff
+
+/* TRACKER_FREE2 */
+#define R367TER_TRACKER_FREE2 0xf11f
+#define F367TER_TRACKER_FREE_2 0xf11f00ff
+
+/* DEBG_LT10 */
+#define R367TER_DEBG_LT10 0xf120
+#define F367TER_DEBUG_LT10 0xf12000ff
+
+/* DEBG_LT11 */
+#define R367TER_DEBG_LT11 0xf121
+#define F367TER_DEBUG_LT11 0xf12100ff
+
+/* DEBG_LT12 */
+#define R367TER_DEBG_LT12 0xf122
+#define F367TER_DEBUG_LT12 0xf12200ff
+
+/* DEBG_LT13 */
+#define R367TER_DEBG_LT13 0xf123
+#define F367TER_DEBUG_LT13 0xf12300ff
+
+/* DEBG_LT14 */
+#define R367TER_DEBG_LT14 0xf124
+#define F367TER_DEBUG_LT14 0xf12400ff
+
+/* DEBG_LT15 */
+#define R367TER_DEBG_LT15 0xf125
+#define F367TER_DEBUG_LT15 0xf12500ff
+
+/* DEBG_LT16 */
+#define R367TER_DEBG_LT16 0xf126
+#define F367TER_DEBUG_LT16 0xf12600ff
+
+/* DEBG_LT17 */
+#define R367TER_DEBG_LT17 0xf127
+#define F367TER_DEBUG_LT17 0xf12700ff
+
+/* DEBG_LT18 */
+#define R367TER_DEBG_LT18 0xf128
+#define F367TER_DEBUG_LT18 0xf12800ff
+
+/* DEBG_LT19 */
+#define R367TER_DEBG_LT19 0xf129
+#define F367TER_DEBUG_LT19 0xf12900ff
+
+/* DEBG_LT1a */
+#define R367TER_DEBG_LT1A 0xf12a
+#define F367TER_DEBUG_LT1A 0xf12a00ff
+
+/* DEBG_LT1b */
+#define R367TER_DEBG_LT1B 0xf12b
+#define F367TER_DEBUG_LT1B 0xf12b00ff
+
+/* DEBG_LT1c */
+#define R367TER_DEBG_LT1C 0xf12c
+#define F367TER_DEBUG_LT1C 0xf12c00ff
+
+/* DEBG_LT1D */
+#define R367TER_DEBG_LT1D 0xf12d
+#define F367TER_DEBUG_LT1D 0xf12d00ff
+
+/* DEBG_LT1E */
+#define R367TER_DEBG_LT1E 0xf12e
+#define F367TER_DEBUG_LT1E 0xf12e00ff
+
+/* DEBG_LT1F */
+#define R367TER_DEBG_LT1F 0xf12f
+#define F367TER_DEBUG_LT1F 0xf12f00ff
+
+/* RCCFGH */
+#define R367TER_RCCFGH 0xf200
+#define F367TER_TSRCFIFO_DVBCI 0xf2000080
+#define F367TER_TSRCFIFO_SERIAL 0xf2000040
+#define F367TER_TSRCFIFO_DISABLE 0xf2000020
+#define F367TER_TSFIFO_2TORC 0xf2000010
+#define F367TER_TSRCFIFO_HSGNLOUT 0xf2000008
+#define F367TER_TSRCFIFO_ERRMODE 0xf2000006
+#define F367TER_RCCFGH_0 0xf2000001
+
+/* RCCFGM */
+#define R367TER_RCCFGM 0xf201
+#define F367TER_TSRCFIFO_MANSPEED 0xf20100c0
+#define F367TER_TSRCFIFO_PERMDATA 0xf2010020
+#define F367TER_TSRCFIFO_NONEWSGNL 0xf2010010
+#define F367TER_RCBYTE_OVERSAMPLING 0xf201000e
+#define F367TER_TSRCFIFO_INVDATA 0xf2010001
+
+/* RCCFGL */
+#define R367TER_RCCFGL 0xf202
+#define F367TER_TSRCFIFO_BCLKDEL1cK 0xf20200c0
+#define F367TER_RCCFGL_5 0xf2020020
+#define F367TER_TSRCFIFO_DUTY50 0xf2020010
+#define F367TER_TSRCFIFO_NSGNL2dATA 0xf2020008
+#define F367TER_TSRCFIFO_DISSERMUX 0xf2020004
+#define F367TER_RCCFGL_1 0xf2020002
+#define F367TER_TSRCFIFO_STOPCKDIS 0xf2020001
+
+/* RCINSDELH */
+#define R367TER_RCINSDELH 0xf203
+#define F367TER_TSRCDEL_SYNCBYTE 0xf2030080
+#define F367TER_TSRCDEL_XXHEADER 0xf2030040
+#define F367TER_TSRCDEL_BBHEADER 0xf2030020
+#define F367TER_TSRCDEL_DATAFIELD 0xf2030010
+#define F367TER_TSRCINSDEL_ISCR 0xf2030008
+#define F367TER_TSRCINSDEL_NPD 0xf2030004
+#define F367TER_TSRCINSDEL_RSPARITY 0xf2030002
+#define F367TER_TSRCINSDEL_CRC8 0xf2030001
+
+/* RCINSDELM */
+#define R367TER_RCINSDELM 0xf204
+#define F367TER_TSRCINS_BBPADDING 0xf2040080
+#define F367TER_TSRCINS_BCHFEC 0xf2040040
+#define F367TER_TSRCINS_LDPCFEC 0xf2040020
+#define F367TER_TSRCINS_EMODCOD 0xf2040010
+#define F367TER_TSRCINS_TOKEN 0xf2040008
+#define F367TER_TSRCINS_XXXERR 0xf2040004
+#define F367TER_TSRCINS_MATYPE 0xf2040002
+#define F367TER_TSRCINS_UPL 0xf2040001
+
+/* RCINSDELL */
+#define R367TER_RCINSDELL 0xf205
+#define F367TER_TSRCINS_DFL 0xf2050080
+#define F367TER_TSRCINS_SYNCD 0xf2050040
+#define F367TER_TSRCINS_BLOCLEN 0xf2050020
+#define F367TER_TSRCINS_SIGPCOUNT 0xf2050010
+#define F367TER_TSRCINS_FIFO 0xf2050008
+#define F367TER_TSRCINS_REALPACK 0xf2050004
+#define F367TER_TSRCINS_TSCONFIG 0xf2050002
+#define F367TER_TSRCINS_LATENCY 0xf2050001
+
+/* RCSTATUS */
+#define R367TER_RCSTATUS 0xf206
+#define F367TER_TSRCFIFO_LINEOK 0xf2060080
+#define F367TER_TSRCFIFO_ERROR 0xf2060040
+#define F367TER_TSRCFIFO_DATA7 0xf2060020
+#define F367TER_RCSTATUS_4 0xf2060010
+#define F367TER_TSRCFIFO_DEMODSEL 0xf2060008
+#define F367TER_TSRC1FIFOSPEED_STORE 0xf2060004
+#define F367TER_RCSTATUS_1 0xf2060002
+#define F367TER_TSRCSERIAL_IMPOSSIBLE 0xf2060001
+
+/* RCSPEED */
+#define R367TER_RCSPEED 0xf207
+#define F367TER_TSRCFIFO_OUTSPEED 0xf20700ff
+
+/* RCDEBUGM */
+#define R367TER_RCDEBUGM 0xf208
+#define F367TER_SD_UNSYNC 0xf2080080
+#define F367TER_ULFLOCK_DETECTM 0xf2080040
+#define F367TER_SUL_SELECTOS 0xf2080020
+#define F367TER_DILUL_NOSCRBLE 0xf2080010
+#define F367TER_NUL_SCRB 0xf2080008
+#define F367TER_UL_SCRB 0xf2080004
+#define F367TER_SCRAULBAD 0xf2080002
+#define F367TER_SCRAUL_UNSYNC 0xf2080001
+
+/* RCDEBUGL */
+#define R367TER_RCDEBUGL 0xf209
+#define F367TER_RS_ERR 0xf2090080
+#define F367TER_LLFLOCK_DETECTM 0xf2090040
+#define F367TER_NOT_SUL_SELECTOS 0xf2090020
+#define F367TER_DILLL_NOSCRBLE 0xf2090010
+#define F367TER_NLL_SCRB 0xf2090008
+#define F367TER_LL_SCRB 0xf2090004
+#define F367TER_SCRALLBAD 0xf2090002
+#define F367TER_SCRALL_UNSYNC 0xf2090001
+
+/* RCOBSCFG */
+#define R367TER_RCOBSCFG 0xf20a
+#define F367TER_TSRCFIFO_OBSCFG 0xf20a00ff
+
+/* RCOBSM */
+#define R367TER_RCOBSM 0xf20b
+#define F367TER_TSRCFIFO_OBSDATA_HI 0xf20b00ff
+
+/* RCOBSL */
+#define R367TER_RCOBSL 0xf20c
+#define F367TER_TSRCFIFO_OBSDATA_LO 0xf20c00ff
+
+/* RCFECSPY */
+#define R367TER_RCFECSPY 0xf210
+#define F367TER_SPYRC_ENABLE 0xf2100080
+#define F367TER_RCNO_SYNCBYTE 0xf2100040
+#define F367TER_RCSERIAL_MODE 0xf2100020
+#define F367TER_RCUNUSUAL_PACKET 0xf2100010
+#define F367TER_BERRCMETER_DATAMODE 0xf210000c
+#define F367TER_BERRCMETER_LMODE 0xf2100002
+#define F367TER_BERRCMETER_RESET 0xf2100001
+
+/* RCFSPYCFG */
+#define R367TER_RCFSPYCFG 0xf211
+#define F367TER_FECSPYRC_INPUT 0xf21100c0
+#define F367TER_RCRST_ON_ERROR 0xf2110020
+#define F367TER_RCONE_SHOT 0xf2110010
+#define F367TER_RCI2C_MODE 0xf211000c
+#define F367TER_SPYRC_HSTERESIS 0xf2110003
+
+/* RCFSPYDATA */
+#define R367TER_RCFSPYDATA 0xf212
+#define F367TER_SPYRC_STUFFING 0xf2120080
+#define F367TER_RCNOERR_PKTJITTER 0xf2120040
+#define F367TER_SPYRC_CNULLPKT 0xf2120020
+#define F367TER_SPYRC_OUTDATA_MODE 0xf212001f
+
+/* RCFSPYOUT */
+#define R367TER_RCFSPYOUT 0xf213
+#define F367TER_FSPYRC_DIRECT 0xf2130080
+#define F367TER_RCFSPYOUT_6 0xf2130040
+#define F367TER_SPYRC_OUTDATA_BUS 0xf2130038
+#define F367TER_RCSTUFF_MODE 0xf2130007
+
+/* RCFSTATUS */
+#define R367TER_RCFSTATUS 0xf214
+#define F367TER_SPYRC_ENDSIM 0xf2140080
+#define F367TER_RCVALID_SIM 0xf2140040
+#define F367TER_RCFOUND_SIGNAL 0xf2140020
+#define F367TER_RCDSS_SYNCBYTE 0xf2140010
+#define F367TER_RCRESULT_STATE 0xf214000f
+
+/* RCFGOODPACK */
+#define R367TER_RCFGOODPACK 0xf215
+#define F367TER_RCGOOD_PACKET 0xf21500ff
+
+/* RCFPACKCNT */
+#define R367TER_RCFPACKCNT 0xf216
+#define F367TER_RCPACKET_COUNTER 0xf21600ff
+
+/* RCFSPYMISC */
+#define R367TER_RCFSPYMISC 0xf217
+#define F367TER_RCLABEL_COUNTER 0xf21700ff
+
+/* RCFBERCPT4 */
+#define R367TER_RCFBERCPT4 0xf218
+#define F367TER_FBERRCMETER_CPT_MMMMSB 0xf21800ff
+
+/* RCFBERCPT3 */
+#define R367TER_RCFBERCPT3 0xf219
+#define F367TER_FBERRCMETER_CPT_MMMSB 0xf21900ff
+
+/* RCFBERCPT2 */
+#define R367TER_RCFBERCPT2 0xf21a
+#define F367TER_FBERRCMETER_CPT_MMSB 0xf21a00ff
+
+/* RCFBERCPT1 */
+#define R367TER_RCFBERCPT1 0xf21b
+#define F367TER_FBERRCMETER_CPT_MSB 0xf21b00ff
+
+/* RCFBERCPT0 */
+#define R367TER_RCFBERCPT0 0xf21c
+#define F367TER_FBERRCMETER_CPT_LSB 0xf21c00ff
+
+/* RCFBERERR2 */
+#define R367TER_RCFBERERR2 0xf21d
+#define F367TER_FBERRCMETER_ERR_HI 0xf21d00ff
+
+/* RCFBERERR1 */
+#define R367TER_RCFBERERR1 0xf21e
+#define F367TER_FBERRCMETER_ERR 0xf21e00ff
+
+/* RCFBERERR0 */
+#define R367TER_RCFBERERR0 0xf21f
+#define F367TER_FBERRCMETER_ERR_LO 0xf21f00ff
+
+/* RCFSTATESM */
+#define R367TER_RCFSTATESM 0xf220
+#define F367TER_RCRSTATE_F 0xf2200080
+#define F367TER_RCRSTATE_E 0xf2200040
+#define F367TER_RCRSTATE_D 0xf2200020
+#define F367TER_RCRSTATE_C 0xf2200010
+#define F367TER_RCRSTATE_B 0xf2200008
+#define F367TER_RCRSTATE_A 0xf2200004
+#define F367TER_RCRSTATE_9 0xf2200002
+#define F367TER_RCRSTATE_8 0xf2200001
+
+/* RCFSTATESL */
+#define R367TER_RCFSTATESL 0xf221
+#define F367TER_RCRSTATE_7 0xf2210080
+#define F367TER_RCRSTATE_6 0xf2210040
+#define F367TER_RCRSTATE_5 0xf2210020
+#define F367TER_RCRSTATE_4 0xf2210010
+#define F367TER_RCRSTATE_3 0xf2210008
+#define F367TER_RCRSTATE_2 0xf2210004
+#define F367TER_RCRSTATE_1 0xf2210002
+#define F367TER_RCRSTATE_0 0xf2210001
+
+/* RCFSPYBER */
+#define R367TER_RCFSPYBER 0xf222
+#define F367TER_RCFSPYBER_7 0xf2220080
+#define F367TER_SPYRCOBS_XORREAD 0xf2220040
+#define F367TER_FSPYRCBER_OBSMODE 0xf2220020
+#define F367TER_FSPYRCBER_SYNCBYT 0xf2220010
+#define F367TER_FSPYRCBER_UNSYNC 0xf2220008
+#define F367TER_FSPYRCBER_CTIME 0xf2220007
+
+/* RCFSPYDISTM */
+#define R367TER_RCFSPYDISTM 0xf223
+#define F367TER_RCPKTTIME_DISTANCE_HI 0xf22300ff
+
+/* RCFSPYDISTL */
+#define R367TER_RCFSPYDISTL 0xf224
+#define F367TER_RCPKTTIME_DISTANCE_LO 0xf22400ff
+
+/* RCFSPYOBS7 */
+#define R367TER_RCFSPYOBS7 0xf228
+#define F367TER_RCSPYOBS_SPYFAIL 0xf2280080
+#define F367TER_RCSPYOBS_SPYFAIL1 0xf2280040
+#define F367TER_RCSPYOBS_ERROR 0xf2280020
+#define F367TER_RCSPYOBS_STROUT 0xf2280010
+#define F367TER_RCSPYOBS_RESULTSTATE1 0xf228000f
+
+/* RCFSPYOBS6 */
+#define R367TER_RCFSPYOBS6 0xf229
+#define F367TER_RCSPYOBS_RESULTSTATe0 0xf22900f0
+#define F367TER_RCSPYOBS_RESULTSTATEM1 0xf229000f
+
+/* RCFSPYOBS5 */
+#define R367TER_RCFSPYOBS5 0xf22a
+#define F367TER_RCSPYOBS_BYTEOFPACKET1 0xf22a00ff
+
+/* RCFSPYOBS4 */
+#define R367TER_RCFSPYOBS4 0xf22b
+#define F367TER_RCSPYOBS_BYTEVALUE1 0xf22b00ff
+
+/* RCFSPYOBS3 */
+#define R367TER_RCFSPYOBS3 0xf22c
+#define F367TER_RCSPYOBS_DATA1 0xf22c00ff
+
+/* RCFSPYOBS2 */
+#define R367TER_RCFSPYOBS2 0xf22d
+#define F367TER_RCSPYOBS_DATa0 0xf22d00ff
+
+/* RCFSPYOBS1 */
+#define R367TER_RCFSPYOBS1 0xf22e
+#define F367TER_RCSPYOBS_DATAM1 0xf22e00ff
+
+/* RCFSPYOBS0 */
+#define R367TER_RCFSPYOBS0 0xf22f
+#define F367TER_RCSPYOBS_DATAM2 0xf22f00ff
+
+/* TSGENERAL */
+#define R367TER_TSGENERAL 0xf230
+#define F367TER_TSGENERAL_7 0xf2300080
+#define F367TER_TSGENERAL_6 0xf2300040
+#define F367TER_TSFIFO_BCLK1aLL 0xf2300020
+#define F367TER_TSGENERAL_4 0xf2300010
+#define F367TER_MUXSTREAM_OUTMODE 0xf2300008
+#define F367TER_TSFIFO_PERMPARAL 0xf2300006
+#define F367TER_RST_REEDSOLO 0xf2300001
+
+/* RC1SPEED */
+#define R367TER_RC1SPEED 0xf231
+#define F367TER_TSRCFIFO1_OUTSPEED 0xf23100ff
+
+/* TSGSTATUS */
+#define R367TER_TSGSTATUS 0xf232
+#define F367TER_TSGSTATUS_7 0xf2320080
+#define F367TER_TSGSTATUS_6 0xf2320040
+#define F367TER_RSMEM_FULL 0xf2320020
+#define F367TER_RS_MULTCALC 0xf2320010
+#define F367TER_RSIN_OVERTIME 0xf2320008
+#define F367TER_TSFIFO3_DEMODSEL 0xf2320004
+#define F367TER_TSFIFO2_DEMODSEL 0xf2320002
+#define F367TER_TSFIFO1_DEMODSEL 0xf2320001
+
+
+/* FECM */
+#define R367TER_FECM 0xf233
+#define F367TER_DSS_DVB 0xf2330080
+#define F367TER_DEMOD_BYPASS 0xf2330040
+#define F367TER_CMP_SLOWMODE 0xf2330020
+#define F367TER_DSS_SRCH 0xf2330010
+#define F367TER_FECM_3 0xf2330008
+#define F367TER_DIFF_MODEVIT 0xf2330004
+#define F367TER_SYNCVIT 0xf2330002
+#define F367TER_I2CSYM 0xf2330001
+
+/* VTH12 */
+#define R367TER_VTH12 0xf234
+#define F367TER_VTH_12 0xf23400ff
+
+/* VTH23 */
+#define R367TER_VTH23 0xf235
+#define F367TER_VTH_23 0xf23500ff
+
+/* VTH34 */
+#define R367TER_VTH34 0xf236
+#define F367TER_VTH_34 0xf23600ff
+
+/* VTH56 */
+#define R367TER_VTH56 0xf237
+#define F367TER_VTH_56 0xf23700ff
+
+/* VTH67 */
+#define R367TER_VTH67 0xf238
+#define F367TER_VTH_67 0xf23800ff
+
+/* VTH78 */
+#define R367TER_VTH78 0xf239
+#define F367TER_VTH_78 0xf23900ff
+
+/* VITCURPUN */
+#define R367TER_VITCURPUN 0xf23a
+#define F367TER_VIT_MAPPING 0xf23a00e0
+#define F367TER_VIT_CURPUN 0xf23a001f
+
+/* VERROR */
+#define R367TER_VERROR 0xf23b
+#define F367TER_REGERR_VIT 0xf23b00ff
+
+/* PRVIT */
+#define R367TER_PRVIT 0xf23c
+#define F367TER_PRVIT_7 0xf23c0080
+#define F367TER_DIS_VTHLOCK 0xf23c0040
+#define F367TER_E7_8VIT 0xf23c0020
+#define F367TER_E6_7VIT 0xf23c0010
+#define F367TER_E5_6VIT 0xf23c0008
+#define F367TER_E3_4VIT 0xf23c0004
+#define F367TER_E2_3VIT 0xf23c0002
+#define F367TER_E1_2VIT 0xf23c0001
+
+/* VAVSRVIT */
+#define R367TER_VAVSRVIT 0xf23d
+#define F367TER_AMVIT 0xf23d0080
+#define F367TER_FROZENVIT 0xf23d0040
+#define F367TER_SNVIT 0xf23d0030
+#define F367TER_TOVVIT 0xf23d000c
+#define F367TER_HYPVIT 0xf23d0003
+
+/* VSTATUSVIT */
+#define R367TER_VSTATUSVIT 0xf23e
+#define F367TER_VITERBI_ON 0xf23e0080
+#define F367TER_END_LOOPVIT 0xf23e0040
+#define F367TER_VITERBI_DEPRF 0xf23e0020
+#define F367TER_PRFVIT 0xf23e0010
+#define F367TER_LOCKEDVIT 0xf23e0008
+#define F367TER_VITERBI_DELOCK 0xf23e0004
+#define F367TER_VIT_DEMODSEL 0xf23e0002
+#define F367TER_VITERBI_COMPOUT 0xf23e0001
+
+/* VTHINUSE */
+#define R367TER_VTHINUSE 0xf23f
+#define F367TER_VIT_INUSE 0xf23f00ff
+
+/* KDIV12 */
+#define R367TER_KDIV12 0xf240
+#define F367TER_KDIV12_MANUAL 0xf2400080
+#define F367TER_K_DIVIDER_12 0xf240007f
+
+/* KDIV23 */
+#define R367TER_KDIV23 0xf241
+#define F367TER_KDIV23_MANUAL 0xf2410080
+#define F367TER_K_DIVIDER_23 0xf241007f
+
+/* KDIV34 */
+#define R367TER_KDIV34 0xf242
+#define F367TER_KDIV34_MANUAL 0xf2420080
+#define F367TER_K_DIVIDER_34 0xf242007f
+
+/* KDIV56 */
+#define R367TER_KDIV56 0xf243
+#define F367TER_KDIV56_MANUAL 0xf2430080
+#define F367TER_K_DIVIDER_56 0xf243007f
+
+/* KDIV67 */
+#define R367TER_KDIV67 0xf244
+#define F367TER_KDIV67_MANUAL 0xf2440080
+#define F367TER_K_DIVIDER_67 0xf244007f
+
+/* KDIV78 */
+#define R367TER_KDIV78 0xf245
+#define F367TER_KDIV78_MANUAL 0xf2450080
+#define F367TER_K_DIVIDER_78 0xf245007f
+
+/* SIGPOWER */
+#define R367TER_SIGPOWER 0xf246
+#define F367TER_SIGPOWER_MANUAL 0xf2460080
+#define F367TER_SIG_POWER 0xf246007f
+
+/* DEMAPVIT */
+#define R367TER_DEMAPVIT 0xf247
+#define F367TER_DEMAPVIT_7 0xf2470080
+#define F367TER_K_DIVIDER_VIT 0xf247007f
+
+/* VITSCALE */
+#define R367TER_VITSCALE 0xf248
+#define F367TER_NVTH_NOSRANGE 0xf2480080
+#define F367TER_VERROR_MAXMODE 0xf2480040
+#define F367TER_KDIV_MODE 0xf2480030
+#define F367TER_NSLOWSN_LOCKED 0xf2480008
+#define F367TER_DELOCK_PRFLOSS 0xf2480004
+#define F367TER_DIS_RSFLOCK 0xf2480002
+#define F367TER_VITSCALE_0 0xf2480001
+
+/* FFEC1PRG */
+#define R367TER_FFEC1PRG 0xf249
+#define F367TER_FDSS_DVB 0xf2490080
+#define F367TER_FDSS_SRCH 0xf2490040
+#define F367TER_FFECPROG_5 0xf2490020
+#define F367TER_FFECPROG_4 0xf2490010
+#define F367TER_FFECPROG_3 0xf2490008
+#define F367TER_FFECPROG_2 0xf2490004
+#define F367TER_FTS1_DISABLE 0xf2490002
+#define F367TER_FTS2_DISABLE 0xf2490001
+
+/* FVITCURPUN */
+#define R367TER_FVITCURPUN 0xf24a
+#define F367TER_FVIT_MAPPING 0xf24a00e0
+#define F367TER_FVIT_CURPUN 0xf24a001f
+
+/* FVERROR */
+#define R367TER_FVERROR 0xf24b
+#define F367TER_FREGERR_VIT 0xf24b00ff
+
+/* FVSTATUSVIT */
+#define R367TER_FVSTATUSVIT 0xf24c
+#define F367TER_FVITERBI_ON 0xf24c0080
+#define F367TER_F1END_LOOPVIT 0xf24c0040
+#define F367TER_FVITERBI_DEPRF 0xf24c0020
+#define F367TER_FPRFVIT 0xf24c0010
+#define F367TER_FLOCKEDVIT 0xf24c0008
+#define F367TER_FVITERBI_DELOCK 0xf24c0004
+#define F367TER_FVIT_DEMODSEL 0xf24c0002
+#define F367TER_FVITERBI_COMPOUT 0xf24c0001
+
+/* DEBUG_LT1 */
+#define R367TER_DEBUG_LT1 0xf24d
+#define F367TER_DBG_LT1 0xf24d00ff
+
+/* DEBUG_LT2 */
+#define R367TER_DEBUG_LT2 0xf24e
+#define F367TER_DBG_LT2 0xf24e00ff
+
+/* DEBUG_LT3 */
+#define R367TER_DEBUG_LT3 0xf24f
+#define F367TER_DBG_LT3 0xf24f00ff
+
+/* TSTSFMET */
+#define R367TER_TSTSFMET 0xf250
+#define F367TER_TSTSFEC_METRIQUES 0xf25000ff
+
+/* SELOUT */
+#define R367TER_SELOUT 0xf252
+#define F367TER_EN_SYNC 0xf2520080
+#define F367TER_EN_TBUSDEMAP 0xf2520040
+#define F367TER_SELOUT_5 0xf2520020
+#define F367TER_SELOUT_4 0xf2520010
+#define F367TER_TSTSYNCHRO_MODE 0xf2520002
+
+/* TSYNC */
+#define R367TER_TSYNC 0xf253
+#define F367TER_CURPUN_INCMODE 0xf2530080
+#define F367TER_CERR_TSTMODE 0xf2530040
+#define F367TER_SHIFTSOF_MODE 0xf2530030
+#define F367TER_SLOWPHA_MODE 0xf2530008
+#define F367TER_PXX_BYPALL 0xf2530004
+#define F367TER_FROTA45_FIRST 0xf2530002
+#define F367TER_TST_BCHERROR 0xf2530001
+
+/* TSTERR */
+#define R367TER_TSTERR 0xf254
+#define F367TER_TST_LONGPKT 0xf2540080
+#define F367TER_TST_ISSYION 0xf2540040
+#define F367TER_TST_NPDON 0xf2540020
+#define F367TER_TSTERR_4 0xf2540010
+#define F367TER_TRACEBACK_MODE 0xf2540008
+#define F367TER_TST_RSPARITY 0xf2540004
+#define F367TER_METRIQUE_MODE 0xf2540003
+
+/* TSFSYNC */
+#define R367TER_TSFSYNC 0xf255
+#define F367TER_EN_SFECSYNC 0xf2550080
+#define F367TER_EN_SFECDEMAP 0xf2550040
+#define F367TER_SFCERR_TSTMODE 0xf2550020
+#define F367TER_SFECPXX_BYPALL 0xf2550010
+#define F367TER_SFECTSTSYNCHRO_MODE 0xf255000f
+
+/* TSTSFERR */
+#define R367TER_TSTSFERR 0xf256
+#define F367TER_TSTSTERR_7 0xf2560080
+#define F367TER_TSTSTERR_6 0xf2560040
+#define F367TER_TSTSTERR_5 0xf2560020
+#define F367TER_TSTSTERR_4 0xf2560010
+#define F367TER_SFECTRACEBACK_MODE 0xf2560008
+#define F367TER_SFEC_NCONVPROG 0xf2560004
+#define F367TER_SFECMETRIQUE_MODE 0xf2560003
+
+/* TSTTSSF1 */
+#define R367TER_TSTTSSF1 0xf258
+#define F367TER_TSTERSSF 0xf2580080
+#define F367TER_TSTTSSFEN 0xf2580040
+#define F367TER_SFEC_OUTMODE 0xf2580030
+#define F367TER_XLSF_NOFTHRESHOLD 0xf2580008
+#define F367TER_TSTTSSF_STACKSEL 0xf2580007
+
+/* TSTTSSF2 */
+#define R367TER_TSTTSSF2 0xf259
+#define F367TER_DILSF_DBBHEADER 0xf2590080
+#define F367TER_TSTTSSF_DISBUG 0xf2590040
+#define F367TER_TSTTSSF_NOBADSTART 0xf2590020
+#define F367TER_TSTTSSF_SELECT 0xf259001f
+
+/* TSTTSSF3 */
+#define R367TER_TSTTSSF3 0xf25a
+#define F367TER_TSTTSSF3_7 0xf25a0080
+#define F367TER_TSTTSSF3_6 0xf25a0040
+#define F367TER_TSTTSSF3_5 0xf25a0020
+#define F367TER_TSTTSSF3_4 0xf25a0010
+#define F367TER_TSTTSSF3_3 0xf25a0008
+#define F367TER_TSTTSSF3_2 0xf25a0004
+#define F367TER_TSTTSSF3_1 0xf25a0002
+#define F367TER_DISSF_CLKENABLE 0xf25a0001
+
+/* TSTTS1 */
+#define R367TER_TSTTS1 0xf25c
+#define F367TER_TSTERS 0xf25c0080
+#define F367TER_TSFIFO_DSSSYNCB 0xf25c0040
+#define F367TER_TSTTS_FSPYBEFRS 0xf25c0020
+#define F367TER_NFORCE_SYNCBYTE 0xf25c0010
+#define F367TER_XL_NOFTHRESHOLD 0xf25c0008
+#define F367TER_TSTTS_FRFORCEPKT 0xf25c0004
+#define F367TER_DESCR_NOTAUTO 0xf25c0002
+#define F367TER_TSTTSEN 0xf25c0001
+
+/* TSTTS2 */
+#define R367TER_TSTTS2 0xf25d
+#define F367TER_DIL_DBBHEADER 0xf25d0080
+#define F367TER_TSTTS_NOBADXXX 0xf25d0040
+#define F367TER_TSFIFO_DELSPEEDUP 0xf25d0020
+#define F367TER_TSTTS_SELECT 0xf25d001f
+
+/* TSTTS3 */
+#define R367TER_TSTTS3 0xf25e
+#define F367TER_TSTTS_NOPKTGAIN 0xf25e0080
+#define F367TER_TSTTS_NOPKTENE 0xf25e0040
+#define F367TER_TSTTS_ISOLATION 0xf25e0020
+#define F367TER_TSTTS_DISBUG 0xf25e0010
+#define F367TER_TSTTS_NOBADSTART 0xf25e0008
+#define F367TER_TSTTS_STACKSEL 0xf25e0007
+
+/* TSTTS4 */
+#define R367TER_TSTTS4 0xf25f
+#define F367TER_TSTTS4_7 0xf25f0080
+#define F367TER_TSTTS4_6 0xf25f0040
+#define F367TER_TSTTS4_5 0xf25f0020
+#define F367TER_TSTTS_DISDSTATE 0xf25f0010
+#define F367TER_TSTTS_FASTNOSYNC 0xf25f0008
+#define F367TER_EXT_FECSPYIN 0xf25f0004
+#define F367TER_TSTTS_NODPZERO 0xf25f0002
+#define F367TER_TSTTS_NODIV3 0xf25f0001
+
+/* TSTTSRC */
+#define R367TER_TSTTSRC 0xf26c
+#define F367TER_TSTTSRC_7 0xf26c0080
+#define F367TER_TSRCFIFO_DSSSYNCB 0xf26c0040
+#define F367TER_TSRCFIFO_DPUNACTIVE 0xf26c0020
+#define F367TER_TSRCFIFO_DELSPEEDUP 0xf26c0010
+#define F367TER_TSTTSRC_NODIV3 0xf26c0008
+#define F367TER_TSTTSRC_FRFORCEPKT 0xf26c0004
+#define F367TER_SAT25_SDDORIGINE 0xf26c0002
+#define F367TER_TSTTSRC_INACTIVE 0xf26c0001
+
+/* TSTTSRS */
+#define R367TER_TSTTSRS 0xf26d
+#define F367TER_TSTTSRS_7 0xf26d0080
+#define F367TER_TSTTSRS_6 0xf26d0040
+#define F367TER_TSTTSRS_5 0xf26d0020
+#define F367TER_TSTTSRS_4 0xf26d0010
+#define F367TER_TSTTSRS_3 0xf26d0008
+#define F367TER_TSTTSRS_2 0xf26d0004
+#define F367TER_TSTRS_DISRS2 0xf26d0002
+#define F367TER_TSTRS_DISRS1 0xf26d0001
+
+/* TSSTATEM */
+#define R367TER_TSSTATEM 0xf270
+#define F367TER_TSDIL_ON 0xf2700080
+#define F367TER_TSSKIPRS_ON 0xf2700040
+#define F367TER_TSRS_ON 0xf2700020
+#define F367TER_TSDESCRAMB_ON 0xf2700010
+#define F367TER_TSFRAME_MODE 0xf2700008
+#define F367TER_TS_DISABLE 0xf2700004
+#define F367TER_TSACM_MODE 0xf2700002
+#define F367TER_TSOUT_NOSYNC 0xf2700001
+
+/* TSSTATEL */
+#define R367TER_TSSTATEL 0xf271
+#define F367TER_TSNOSYNCBYTE 0xf2710080
+#define F367TER_TSPARITY_ON 0xf2710040
+#define F367TER_TSSYNCOUTRS_ON 0xf2710020
+#define F367TER_TSDVBS2_MODE 0xf2710010
+#define F367TER_TSISSYI_ON 0xf2710008
+#define F367TER_TSNPD_ON 0xf2710004
+#define F367TER_TSCRC8_ON 0xf2710002
+#define F367TER_TSDSS_PACKET 0xf2710001
+
+/* TSCFGH */
+#define R367TER_TSCFGH 0xf272
+#define F367TER_TSFIFO_DVBCI 0xf2720080
+#define F367TER_TSFIFO_SERIAL 0xf2720040
+#define F367TER_TSFIFO_TEIUPDATE 0xf2720020
+#define F367TER_TSFIFO_DUTY50 0xf2720010
+#define F367TER_TSFIFO_HSGNLOUT 0xf2720008
+#define F367TER_TSFIFO_ERRMODE 0xf2720006
+#define F367TER_RST_HWARE 0xf2720001
+
+/* TSCFGM */
+#define R367TER_TSCFGM 0xf273
+#define F367TER_TSFIFO_MANSPEED 0xf27300c0
+#define F367TER_TSFIFO_PERMDATA 0xf2730020
+#define F367TER_TSFIFO_NONEWSGNL 0xf2730010
+#define F367TER_TSFIFO_BITSPEED 0xf2730008
+#define F367TER_NPD_SPECDVBS2 0xf2730004
+#define F367TER_TSFIFO_STOPCKDIS 0xf2730002
+#define F367TER_TSFIFO_INVDATA 0xf2730001
+
+/* TSCFGL */
+#define R367TER_TSCFGL 0xf274
+#define F367TER_TSFIFO_BCLKDEL1cK 0xf27400c0
+#define F367TER_BCHERROR_MODE 0xf2740030
+#define F367TER_TSFIFO_NSGNL2dATA 0xf2740008
+#define F367TER_TSFIFO_EMBINDVB 0xf2740004
+#define F367TER_TSFIFO_DPUNACT 0xf2740002
+#define F367TER_TSFIFO_NPDOFF 0xf2740001
+
+/* TSSYNC */
+#define R367TER_TSSYNC 0xf275
+#define F367TER_TSFIFO_PERMUTE 0xf2750080
+#define F367TER_TSFIFO_FISCR3B 0xf2750060
+#define F367TER_TSFIFO_SYNCMODE 0xf2750018
+#define F367TER_TSFIFO_SYNCSEL 0xf2750007
+
+/* TSINSDELH */
+#define R367TER_TSINSDELH 0xf276
+#define F367TER_TSDEL_SYNCBYTE 0xf2760080
+#define F367TER_TSDEL_XXHEADER 0xf2760040
+#define F367TER_TSDEL_BBHEADER 0xf2760020
+#define F367TER_TSDEL_DATAFIELD 0xf2760010
+#define F367TER_TSINSDEL_ISCR 0xf2760008
+#define F367TER_TSINSDEL_NPD 0xf2760004
+#define F367TER_TSINSDEL_RSPARITY 0xf2760002
+#define F367TER_TSINSDEL_CRC8 0xf2760001
+
+/* TSINSDELM */
+#define R367TER_TSINSDELM 0xf277
+#define F367TER_TSINS_BBPADDING 0xf2770080
+#define F367TER_TSINS_BCHFEC 0xf2770040
+#define F367TER_TSINS_LDPCFEC 0xf2770020
+#define F367TER_TSINS_EMODCOD 0xf2770010
+#define F367TER_TSINS_TOKEN 0xf2770008
+#define F367TER_TSINS_XXXERR 0xf2770004
+#define F367TER_TSINS_MATYPE 0xf2770002
+#define F367TER_TSINS_UPL 0xf2770001
+
+/* TSINSDELL */
+#define R367TER_TSINSDELL 0xf278
+#define F367TER_TSINS_DFL 0xf2780080
+#define F367TER_TSINS_SYNCD 0xf2780040
+#define F367TER_TSINS_BLOCLEN 0xf2780020
+#define F367TER_TSINS_SIGPCOUNT 0xf2780010
+#define F367TER_TSINS_FIFO 0xf2780008
+#define F367TER_TSINS_REALPACK 0xf2780004
+#define F367TER_TSINS_TSCONFIG 0xf2780002
+#define F367TER_TSINS_LATENCY 0xf2780001
+
+/* TSDIVN */
+#define R367TER_TSDIVN 0xf279
+#define F367TER_TSFIFO_LOWSPEED 0xf2790080
+#define F367TER_BYTE_OVERSAMPLING 0xf2790070
+#define F367TER_TSMANUAL_PACKETNBR 0xf279000f
+
+/* TSDIVPM */
+#define R367TER_TSDIVPM 0xf27a
+#define F367TER_TSMANUAL_P_HI 0xf27a00ff
+
+/* TSDIVPL */
+#define R367TER_TSDIVPL 0xf27b
+#define F367TER_TSMANUAL_P_LO 0xf27b00ff
+
+/* TSDIVQM */
+#define R367TER_TSDIVQM 0xf27c
+#define F367TER_TSMANUAL_Q_HI 0xf27c00ff
+
+/* TSDIVQL */
+#define R367TER_TSDIVQL 0xf27d
+#define F367TER_TSMANUAL_Q_LO 0xf27d00ff
+
+/* TSDILSTKM */
+#define R367TER_TSDILSTKM 0xf27e
+#define F367TER_TSFIFO_DILSTK_HI 0xf27e00ff
+
+/* TSDILSTKL */
+#define R367TER_TSDILSTKL 0xf27f
+#define F367TER_TSFIFO_DILSTK_LO 0xf27f00ff
+
+/* TSSPEED */
+#define R367TER_TSSPEED 0xf280
+#define F367TER_TSFIFO_OUTSPEED 0xf28000ff
+
+/* TSSTATUS */
+#define R367TER_TSSTATUS 0xf281
+#define F367TER_TSFIFO_LINEOK 0xf2810080
+#define F367TER_TSFIFO_ERROR 0xf2810040
+#define F367TER_TSFIFO_DATA7 0xf2810020
+#define F367TER_TSFIFO_NOSYNC 0xf2810010
+#define F367TER_ISCR_INITIALIZED 0xf2810008
+#define F367TER_ISCR_UPDATED 0xf2810004
+#define F367TER_SOFFIFO_UNREGUL 0xf2810002
+#define F367TER_DIL_READY 0xf2810001
+
+/* TSSTATUS2 */
+#define R367TER_TSSTATUS2 0xf282
+#define F367TER_TSFIFO_DEMODSEL 0xf2820080
+#define F367TER_TSFIFOSPEED_STORE 0xf2820040
+#define F367TER_DILXX_RESET 0xf2820020
+#define F367TER_TSSERIAL_IMPOSSIBLE 0xf2820010
+#define F367TER_TSFIFO_UNDERSPEED 0xf2820008
+#define F367TER_BITSPEED_EVENT 0xf2820004
+#define F367TER_UL_SCRAMBDETECT 0xf2820002
+#define F367TER_ULDTV67_FALSELOCK 0xf2820001
+
+/* TSBITRATEM */
+#define R367TER_TSBITRATEM 0xf283
+#define F367TER_TSFIFO_BITRATE_HI 0xf28300ff
+
+/* TSBITRATEL */
+#define R367TER_TSBITRATEL 0xf284
+#define F367TER_TSFIFO_BITRATE_LO 0xf28400ff
+
+/* TSPACKLENM */
+#define R367TER_TSPACKLENM 0xf285
+#define F367TER_TSFIFO_PACKCPT 0xf28500e0
+#define F367TER_DIL_RPLEN_HI 0xf285001f
+
+/* TSPACKLENL */
+#define R367TER_TSPACKLENL 0xf286
+#define F367TER_DIL_RPLEN_LO 0xf28600ff
+
+/* TSBLOCLENM */
+#define R367TER_TSBLOCLENM 0xf287
+#define F367TER_TSFIFO_PFLEN_HI 0xf28700ff
+
+/* TSBLOCLENL */
+#define R367TER_TSBLOCLENL 0xf288
+#define F367TER_TSFIFO_PFLEN_LO 0xf28800ff
+
+/* TSDLYH */
+#define R367TER_TSDLYH 0xf289
+#define F367TER_SOFFIFO_TSTIMEVALID 0xf2890080
+#define F367TER_SOFFIFO_SPEEDUP 0xf2890040
+#define F367TER_SOFFIFO_STOP 0xf2890020
+#define F367TER_SOFFIFO_REGULATED 0xf2890010
+#define F367TER_SOFFIFO_REALSBOFF_HI 0xf289000f
+
+/* TSDLYM */
+#define R367TER_TSDLYM 0xf28a
+#define F367TER_SOFFIFO_REALSBOFF_MED 0xf28a00ff
+
+/* TSDLYL */
+#define R367TER_TSDLYL 0xf28b
+#define F367TER_SOFFIFO_REALSBOFF_LO 0xf28b00ff
+
+/* TSNPDAV */
+#define R367TER_TSNPDAV 0xf28c
+#define F367TER_TSNPD_AVERAGE 0xf28c00ff
+
+/* TSBUFSTATH */
+#define R367TER_TSBUFSTATH 0xf28d
+#define F367TER_TSISCR_3BYTES 0xf28d0080
+#define F367TER_TSISCR_NEWDATA 0xf28d0040
+#define F367TER_TSISCR_BUFSTAT_HI 0xf28d003f
+
+/* TSBUFSTATM */
+#define R367TER_TSBUFSTATM 0xf28e
+#define F367TER_TSISCR_BUFSTAT_MED 0xf28e00ff
+
+/* TSBUFSTATL */
+#define R367TER_TSBUFSTATL 0xf28f
+#define F367TER_TSISCR_BUFSTAT_LO 0xf28f00ff
+
+/* TSDEBUGM */
+#define R367TER_TSDEBUGM 0xf290
+#define F367TER_TSFIFO_ILLPACKET 0xf2900080
+#define F367TER_DIL_NOSYNC 0xf2900040
+#define F367TER_DIL_ISCR 0xf2900020
+#define F367TER_DILOUT_BSYNCB 0xf2900010
+#define F367TER_TSFIFO_EMPTYPKT 0xf2900008
+#define F367TER_TSFIFO_EMPTYRD 0xf2900004
+#define F367TER_SOFFIFO_STOPM 0xf2900002
+#define F367TER_SOFFIFO_SPEEDUPM 0xf2900001
+
+/* TSDEBUGL */
+#define R367TER_TSDEBUGL 0xf291
+#define F367TER_TSFIFO_PACKLENFAIL 0xf2910080
+#define F367TER_TSFIFO_SYNCBFAIL 0xf2910040
+#define F367TER_TSFIFO_VITLIBRE 0xf2910020
+#define F367TER_TSFIFO_BOOSTSPEEDM 0xf2910010
+#define F367TER_TSFIFO_UNDERSPEEDM 0xf2910008
+#define F367TER_TSFIFO_ERROR_EVNT 0xf2910004
+#define F367TER_TSFIFO_FULL 0xf2910002
+#define F367TER_TSFIFO_OVERFLOWM 0xf2910001
+
+/* TSDLYSETH */
+#define R367TER_TSDLYSETH 0xf292
+#define F367TER_SOFFIFO_OFFSET 0xf29200e0
+#define F367TER_SOFFIFO_SYMBOFFSET_HI 0xf292001f
+
+/* TSDLYSETM */
+#define R367TER_TSDLYSETM 0xf293
+#define F367TER_SOFFIFO_SYMBOFFSET_MED 0xf29300ff
+
+/* TSDLYSETL */
+#define R367TER_TSDLYSETL 0xf294
+#define F367TER_SOFFIFO_SYMBOFFSET_LO 0xf29400ff
+
+/* TSOBSCFG */
+#define R367TER_TSOBSCFG 0xf295
+#define F367TER_TSFIFO_OBSCFG 0xf29500ff
+
+/* TSOBSM */
+#define R367TER_TSOBSM 0xf296
+#define F367TER_TSFIFO_OBSDATA_HI 0xf29600ff
+
+/* TSOBSL */
+#define R367TER_TSOBSL 0xf297
+#define F367TER_TSFIFO_OBSDATA_LO 0xf29700ff
+
+/* ERRCTRL1 */
+#define R367TER_ERRCTRL1 0xf298
+#define F367TER_ERR_SRC1 0xf29800f0
+#define F367TER_ERRCTRL1_3 0xf2980008
+#define F367TER_NUM_EVT1 0xf2980007
+
+/* ERRCNT1H */
+#define R367TER_ERRCNT1H 0xf299
+#define F367TER_ERRCNT1_OLDVALUE 0xf2990080
+#define F367TER_ERR_CNT1 0xf299007f
+
+/* ERRCNT1M */
+#define R367TER_ERRCNT1M 0xf29a
+#define F367TER_ERR_CNT1_HI 0xf29a00ff
+
+/* ERRCNT1L */
+#define R367TER_ERRCNT1L 0xf29b
+#define F367TER_ERR_CNT1_LO 0xf29b00ff
+
+/* ERRCTRL2 */
+#define R367TER_ERRCTRL2 0xf29c
+#define F367TER_ERR_SRC2 0xf29c00f0
+#define F367TER_ERRCTRL2_3 0xf29c0008
+#define F367TER_NUM_EVT2 0xf29c0007
+
+/* ERRCNT2H */
+#define R367TER_ERRCNT2H 0xf29d
+#define F367TER_ERRCNT2_OLDVALUE 0xf29d0080
+#define F367TER_ERR_CNT2_HI 0xf29d007f
+
+/* ERRCNT2M */
+#define R367TER_ERRCNT2M 0xf29e
+#define F367TER_ERR_CNT2_MED 0xf29e00ff
+
+/* ERRCNT2L */
+#define R367TER_ERRCNT2L 0xf29f
+#define F367TER_ERR_CNT2_LO 0xf29f00ff
+
+/* FECSPY */
+#define R367TER_FECSPY 0xf2a0
+#define F367TER_SPY_ENABLE 0xf2a00080
+#define F367TER_NO_SYNCBYTE 0xf2a00040
+#define F367TER_SERIAL_MODE 0xf2a00020
+#define F367TER_UNUSUAL_PACKET 0xf2a00010
+#define F367TER_BERMETER_DATAMODE 0xf2a0000c
+#define F367TER_BERMETER_LMODE 0xf2a00002
+#define F367TER_BERMETER_RESET 0xf2a00001
+
+/* FSPYCFG */
+#define R367TER_FSPYCFG 0xf2a1
+#define F367TER_FECSPY_INPUT 0xf2a100c0
+#define F367TER_RST_ON_ERROR 0xf2a10020
+#define F367TER_ONE_SHOT 0xf2a10010
+#define F367TER_I2C_MOD 0xf2a1000c
+#define F367TER_SPY_HYSTERESIS 0xf2a10003
+
+/* FSPYDATA */
+#define R367TER_FSPYDATA 0xf2a2
+#define F367TER_SPY_STUFFING 0xf2a20080
+#define F367TER_NOERROR_PKTJITTER 0xf2a20040
+#define F367TER_SPY_CNULLPKT 0xf2a20020
+#define F367TER_SPY_OUTDATA_MODE 0xf2a2001f
+
+/* FSPYOUT */
+#define R367TER_FSPYOUT 0xf2a3
+#define F367TER_FSPY_DIRECT 0xf2a30080
+#define F367TER_FSPYOUT_6 0xf2a30040
+#define F367TER_SPY_OUTDATA_BUS 0xf2a30038
+#define F367TER_STUFF_MODE 0xf2a30007
+
+/* FSTATUS */
+#define R367TER_FSTATUS 0xf2a4
+#define F367TER_SPY_ENDSIM 0xf2a40080
+#define F367TER_VALID_SIM 0xf2a40040
+#define F367TER_FOUND_SIGNAL 0xf2a40020
+#define F367TER_DSS_SYNCBYTE 0xf2a40010
+#define F367TER_RESULT_STATE 0xf2a4000f
+
+/* FGOODPACK */
+#define R367TER_FGOODPACK 0xf2a5
+#define F367TER_FGOOD_PACKET 0xf2a500ff
+
+/* FPACKCNT */
+#define R367TER_FPACKCNT 0xf2a6
+#define F367TER_FPACKET_COUNTER 0xf2a600ff
+
+/* FSPYMISC */
+#define R367TER_FSPYMISC 0xf2a7
+#define F367TER_FLABEL_COUNTER 0xf2a700ff
+
+/* FBERCPT4 */
+#define R367TER_FBERCPT4 0xf2a8
+#define F367TER_FBERMETER_CPT5 0xf2a800ff
+
+/* FBERCPT3 */
+#define R367TER_FBERCPT3 0xf2a9
+#define F367TER_FBERMETER_CPT4 0xf2a900ff
+
+/* FBERCPT2 */
+#define R367TER_FBERCPT2 0xf2aa
+#define F367TER_FBERMETER_CPT3 0xf2aa00ff
+
+/* FBERCPT1 */
+#define R367TER_FBERCPT1 0xf2ab
+#define F367TER_FBERMETER_CPT2 0xf2ab00ff
+
+/* FBERCPT0 */
+#define R367TER_FBERCPT0 0xf2ac
+#define F367TER_FBERMETER_CPT1 0xf2ac00ff
+
+/* FBERERR2 */
+#define R367TER_FBERERR2 0xf2ad
+#define F367TER_FBERMETER_ERR_HI 0xf2ad00ff
+
+/* FBERERR1 */
+#define R367TER_FBERERR1 0xf2ae
+#define F367TER_FBERMETER_ERR_MED 0xf2ae00ff
+
+/* FBERERR0 */
+#define R367TER_FBERERR0 0xf2af
+#define F367TER_FBERMETER_ERR_LO 0xf2af00ff
+
+/* FSTATESM */
+#define R367TER_FSTATESM 0xf2b0
+#define F367TER_RSTATE_F 0xf2b00080
+#define F367TER_RSTATE_E 0xf2b00040
+#define F367TER_RSTATE_D 0xf2b00020
+#define F367TER_RSTATE_C 0xf2b00010
+#define F367TER_RSTATE_B 0xf2b00008
+#define F367TER_RSTATE_A 0xf2b00004
+#define F367TER_RSTATE_9 0xf2b00002
+#define F367TER_RSTATE_8 0xf2b00001
+
+/* FSTATESL */
+#define R367TER_FSTATESL 0xf2b1
+#define F367TER_RSTATE_7 0xf2b10080
+#define F367TER_RSTATE_6 0xf2b10040
+#define F367TER_RSTATE_5 0xf2b10020
+#define F367TER_RSTATE_4 0xf2b10010
+#define F367TER_RSTATE_3 0xf2b10008
+#define F367TER_RSTATE_2 0xf2b10004
+#define F367TER_RSTATE_1 0xf2b10002
+#define F367TER_RSTATE_0 0xf2b10001
+
+/* FSPYBER */
+#define R367TER_FSPYBER 0xf2b2
+#define F367TER_FSPYBER_7 0xf2b20080
+#define F367TER_FSPYOBS_XORREAD 0xf2b20040
+#define F367TER_FSPYBER_OBSMODE 0xf2b20020
+#define F367TER_FSPYBER_SYNCBYTE 0xf2b20010
+#define F367TER_FSPYBER_UNSYNC 0xf2b20008
+#define F367TER_FSPYBER_CTIME 0xf2b20007
+
+/* FSPYDISTM */
+#define R367TER_FSPYDISTM 0xf2b3
+#define F367TER_PKTTIME_DISTANCE_HI 0xf2b300ff
+
+/* FSPYDISTL */
+#define R367TER_FSPYDISTL 0xf2b4
+#define F367TER_PKTTIME_DISTANCE_LO 0xf2b400ff
+
+/* FSPYOBS7 */
+#define R367TER_FSPYOBS7 0xf2b8
+#define F367TER_FSPYOBS_SPYFAIL 0xf2b80080
+#define F367TER_FSPYOBS_SPYFAIL1 0xf2b80040
+#define F367TER_FSPYOBS_ERROR 0xf2b80020
+#define F367TER_FSPYOBS_STROUT 0xf2b80010
+#define F367TER_FSPYOBS_RESULTSTATE1 0xf2b8000f
+
+/* FSPYOBS6 */
+#define R367TER_FSPYOBS6 0xf2b9
+#define F367TER_FSPYOBS_RESULTSTATe0 0xf2b900f0
+#define F367TER_FSPYOBS_RESULTSTATEM1 0xf2b9000f
+
+/* FSPYOBS5 */
+#define R367TER_FSPYOBS5 0xf2ba
+#define F367TER_FSPYOBS_BYTEOFPACKET1 0xf2ba00ff
+
+/* FSPYOBS4 */
+#define R367TER_FSPYOBS4 0xf2bb
+#define F367TER_FSPYOBS_BYTEVALUE1 0xf2bb00ff
+
+/* FSPYOBS3 */
+#define R367TER_FSPYOBS3 0xf2bc
+#define F367TER_FSPYOBS_DATA1 0xf2bc00ff
+
+/* FSPYOBS2 */
+#define R367TER_FSPYOBS2 0xf2bd
+#define F367TER_FSPYOBS_DATa0 0xf2bd00ff
+
+/* FSPYOBS1 */
+#define R367TER_FSPYOBS1 0xf2be
+#define F367TER_FSPYOBS_DATAM1 0xf2be00ff
+
+/* FSPYOBS0 */
+#define R367TER_FSPYOBS0 0xf2bf
+#define F367TER_FSPYOBS_DATAM2 0xf2bf00ff
+
+/* SFDEMAP */
+#define R367TER_SFDEMAP 0xf2c0
+#define F367TER_SFDEMAP_7 0xf2c00080
+#define F367TER_SFEC_K_DIVIDER_VIT 0xf2c0007f
+
+/* SFERROR */
+#define R367TER_SFERROR 0xf2c1
+#define F367TER_SFEC_REGERR_VIT 0xf2c100ff
+
+/* SFAVSR */
+#define R367TER_SFAVSR 0xf2c2
+#define F367TER_SFEC_SUMERRORS 0xf2c20080
+#define F367TER_SERROR_MAXMODE 0xf2c20040
+#define F367TER_SN_SFEC 0xf2c20030
+#define F367TER_KDIV_MODE_SFEC 0xf2c2000c
+#define F367TER_SFAVSR_1 0xf2c20002
+#define F367TER_SFAVSR_0 0xf2c20001
+
+/* SFECSTATUS */
+#define R367TER_SFECSTATUS 0xf2c3
+#define F367TER_SFEC_ON 0xf2c30080
+#define F367TER_SFSTATUS_6 0xf2c30040
+#define F367TER_SFSTATUS_5 0xf2c30020
+#define F367TER_SFSTATUS_4 0xf2c30010
+#define F367TER_LOCKEDSFEC 0xf2c30008
+#define F367TER_SFEC_DELOCK 0xf2c30004
+#define F367TER_SFEC_DEMODSEL1 0xf2c30002
+#define F367TER_SFEC_OVFON 0xf2c30001
+
+/* SFKDIV12 */
+#define R367TER_SFKDIV12 0xf2c4
+#define F367TER_SFECKDIV12_MAN 0xf2c40080
+#define F367TER_SFEC_K_DIVIDER_12 0xf2c4007f
+
+/* SFKDIV23 */
+#define R367TER_SFKDIV23 0xf2c5
+#define F367TER_SFECKDIV23_MAN 0xf2c50080
+#define F367TER_SFEC_K_DIVIDER_23 0xf2c5007f
+
+/* SFKDIV34 */
+#define R367TER_SFKDIV34 0xf2c6
+#define F367TER_SFECKDIV34_MAN 0xf2c60080
+#define F367TER_SFEC_K_DIVIDER_34 0xf2c6007f
+
+/* SFKDIV56 */
+#define R367TER_SFKDIV56 0xf2c7
+#define F367TER_SFECKDIV56_MAN 0xf2c70080
+#define F367TER_SFEC_K_DIVIDER_56 0xf2c7007f
+
+/* SFKDIV67 */
+#define R367TER_SFKDIV67 0xf2c8
+#define F367TER_SFECKDIV67_MAN 0xf2c80080
+#define F367TER_SFEC_K_DIVIDER_67 0xf2c8007f
+
+/* SFKDIV78 */
+#define R367TER_SFKDIV78 0xf2c9
+#define F367TER_SFECKDIV78_MAN 0xf2c90080
+#define F367TER_SFEC_K_DIVIDER_78 0xf2c9007f
+
+/* SFDILSTKM */
+#define R367TER_SFDILSTKM 0xf2ca
+#define F367TER_SFEC_PACKCPT 0xf2ca00e0
+#define F367TER_SFEC_DILSTK_HI 0xf2ca001f
+
+/* SFDILSTKL */
+#define R367TER_SFDILSTKL 0xf2cb
+#define F367TER_SFEC_DILSTK_LO 0xf2cb00ff
+
+/* SFSTATUS */
+#define R367TER_SFSTATUS 0xf2cc
+#define F367TER_SFEC_LINEOK 0xf2cc0080
+#define F367TER_SFEC_ERROR 0xf2cc0040
+#define F367TER_SFEC_DATA7 0xf2cc0020
+#define F367TER_SFEC_OVERFLOW 0xf2cc0010
+#define F367TER_SFEC_DEMODSEL2 0xf2cc0008
+#define F367TER_SFEC_NOSYNC 0xf2cc0004
+#define F367TER_SFEC_UNREGULA 0xf2cc0002
+#define F367TER_SFEC_READY 0xf2cc0001
+
+/* SFDLYH */
+#define R367TER_SFDLYH 0xf2cd
+#define F367TER_SFEC_TSTIMEVALID 0xf2cd0080
+#define F367TER_SFEC_SPEEDUP 0xf2cd0040
+#define F367TER_SFEC_STOP 0xf2cd0020
+#define F367TER_SFEC_REGULATED 0xf2cd0010
+#define F367TER_SFEC_REALSYMBOFFSET 0xf2cd000f
+
+/* SFDLYM */
+#define R367TER_SFDLYM 0xf2ce
+#define F367TER_SFEC_REALSYMBOFFSET_HI 0xf2ce00ff
+
+/* SFDLYL */
+#define R367TER_SFDLYL 0xf2cf
+#define F367TER_SFEC_REALSYMBOFFSET_LO 0xf2cf00ff
+
+/* SFDLYSETH */
+#define R367TER_SFDLYSETH 0xf2d0
+#define F367TER_SFEC_OFFSET 0xf2d000e0
+#define F367TER_SFECDLYSETH_4 0xf2d00010
+#define F367TER_RST_SFEC 0xf2d00008
+#define F367TER_SFECDLYSETH_2 0xf2d00004
+#define F367TER_SFEC_DISABLE 0xf2d00002
+#define F367TER_SFEC_UNREGUL 0xf2d00001
+
+/* SFDLYSETM */
+#define R367TER_SFDLYSETM 0xf2d1
+#define F367TER_SFECDLYSETM_7 0xf2d10080
+#define F367TER_SFEC_SYMBOFFSET_HI 0xf2d1007f
+
+/* SFDLYSETL */
+#define R367TER_SFDLYSETL 0xf2d2
+#define F367TER_SFEC_SYMBOFFSET_LO 0xf2d200ff
+
+/* SFOBSCFG */
+#define R367TER_SFOBSCFG 0xf2d3
+#define F367TER_SFEC_OBSCFG 0xf2d300ff
+
+/* SFOBSM */
+#define R367TER_SFOBSM 0xf2d4
+#define F367TER_SFEC_OBSDATA_HI 0xf2d400ff
+
+/* SFOBSL */
+#define R367TER_SFOBSL 0xf2d5
+#define F367TER_SFEC_OBSDATA_LO 0xf2d500ff
+
+/* SFECINFO */
+#define R367TER_SFECINFO 0xf2d6
+#define F367TER_SFECINFO_7 0xf2d60080
+#define F367TER_SFEC_SYNCDLSB 0xf2d60070
+#define F367TER_SFCE_S1cPHASE 0xf2d6000f
+
+/* SFERRCTRL */
+#define R367TER_SFERRCTRL 0xf2d8
+#define F367TER_SFEC_ERR_SOURCE 0xf2d800f0
+#define F367TER_SFERRCTRL_3 0xf2d80008
+#define F367TER_SFEC_NUM_EVENT 0xf2d80007
+
+/* SFERRCNTH */
+#define R367TER_SFERRCNTH 0xf2d9
+#define F367TER_SFERRC_OLDVALUE 0xf2d90080
+#define F367TER_SFEC_ERR_CNT 0xf2d9007f
+
+/* SFERRCNTM */
+#define R367TER_SFERRCNTM 0xf2da
+#define F367TER_SFEC_ERR_CNT_HI 0xf2da00ff
+
+/* SFERRCNTL */
+#define R367TER_SFERRCNTL 0xf2db
+#define F367TER_SFEC_ERR_CNT_LO 0xf2db00ff
+
+/* SYMBRATEM */
+#define R367TER_SYMBRATEM 0xf2e0
+#define F367TER_DEFGEN_SYMBRATE_HI 0xf2e000ff
+
+/* SYMBRATEL */
+#define R367TER_SYMBRATEL 0xf2e1
+#define F367TER_DEFGEN_SYMBRATE_LO 0xf2e100ff
+
+/* SYMBSTATUS */
+#define R367TER_SYMBSTATUS 0xf2e2
+#define F367TER_SYMBDLINE2_OFF 0xf2e20080
+#define F367TER_SDDL_REINIT1 0xf2e20040
+#define F367TER_SDD_REINIT1 0xf2e20020
+#define F367TER_TOKENID_ERROR 0xf2e20010
+#define F367TER_SYMBRATE_OVERFLOW 0xf2e20008
+#define F367TER_SYMBRATE_UNDERFLOW 0xf2e20004
+#define F367TER_TOKENID_RSTEVENT 0xf2e20002
+#define F367TER_TOKENID_RESET1 0xf2e20001
+
+/* SYMBCFG */
+#define R367TER_SYMBCFG 0xf2e3
+#define F367TER_SYMBCFG_7 0xf2e30080
+#define F367TER_SYMBCFG_6 0xf2e30040
+#define F367TER_SYMBCFG_5 0xf2e30020
+#define F367TER_SYMBCFG_4 0xf2e30010
+#define F367TER_SYMRATE_FSPEED 0xf2e3000c
+#define F367TER_SYMRATE_SSPEED 0xf2e30003
+
+/* SYMBFIFOM */
+#define R367TER_SYMBFIFOM 0xf2e4
+#define F367TER_SYMBFIFOM_7 0xf2e40080
+#define F367TER_SYMBFIFOM_6 0xf2e40040
+#define F367TER_DEFGEN_SYMFIFO_HI 0xf2e4003f
+
+/* SYMBFIFOL */
+#define R367TER_SYMBFIFOL 0xf2e5
+#define F367TER_DEFGEN_SYMFIFO_LO 0xf2e500ff
+
+/* SYMBOFFSM */
+#define R367TER_SYMBOFFSM 0xf2e6
+#define F367TER_TOKENID_RESET2 0xf2e60080
+#define F367TER_SDDL_REINIT2 0xf2e60040
+#define F367TER_SDD_REINIT2 0xf2e60020
+#define F367TER_SYMBOFFSM_4 0xf2e60010
+#define F367TER_SYMBOFFSM_3 0xf2e60008
+#define F367TER_DEFGEN_SYMBOFFSET_HI 0xf2e60007
+
+/* SYMBOFFSL */
+#define R367TER_SYMBOFFSL 0xf2e7
+#define F367TER_DEFGEN_SYMBOFFSET_LO 0xf2e700ff
+
+/* DEBUG_LT4 */
+#define R367TER_DEBUG_LT4 0xf400
+#define F367TER_F_DEBUG_LT4 0xf40000ff
+
+/* DEBUG_LT5 */
+#define R367TER_DEBUG_LT5 0xf401
+#define F367TER_F_DEBUG_LT5 0xf40100ff
+
+/* DEBUG_LT6 */
+#define R367TER_DEBUG_LT6 0xf402
+#define F367TER_F_DEBUG_LT6 0xf40200ff
+
+/* DEBUG_LT7 */
+#define R367TER_DEBUG_LT7 0xf403
+#define F367TER_F_DEBUG_LT7 0xf40300ff
+
+/* DEBUG_LT8 */
+#define R367TER_DEBUG_LT8 0xf404
+#define F367TER_F_DEBUG_LT8 0xf40400ff
+
+/* DEBUG_LT9 */
+#define R367TER_DEBUG_LT9 0xf405
+#define F367TER_F_DEBUG_LT9 0xf40500ff
+
+#define STV0367TER_NBREGS 445
+
+/* ID */
+#define R367CAB_ID 0xf000
+#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff
+
+/* I2CRPT */
+#define R367CAB_I2CRPT 0xf001
+#define F367CAB_I2CT_ON 0xf0010080
+#define F367CAB_ENARPT_LEVEL 0xf0010070
+#define F367CAB_SCLT_DELAY 0xf0010008
+#define F367CAB_SCLT_NOD 0xf0010004
+#define F367CAB_STOP_ENABLE 0xf0010002
+#define F367CAB_SDAT_NOD 0xf0010001
+
+/* TOPCTRL */
+#define R367CAB_TOPCTRL 0xf002
+#define F367CAB_STDBY 0xf0020080
+#define F367CAB_STDBY_CORE 0xf0020020
+#define F367CAB_QAM_COFDM 0xf0020010
+#define F367CAB_TS_DIS 0xf0020008
+#define F367CAB_DIR_CLK_216 0xf0020004
+
+/* IOCFG0 */
+#define R367CAB_IOCFG0 0xf003
+#define F367CAB_OP0_SD 0xf0030080
+#define F367CAB_OP0_VAL 0xf0030040
+#define F367CAB_OP0_OD 0xf0030020
+#define F367CAB_OP0_INV 0xf0030010
+#define F367CAB_OP0_DACVALUE_HI 0xf003000f
+
+/* DAc0R */
+#define R367CAB_DAC0R 0xf004
+#define F367CAB_OP0_DACVALUE_LO 0xf00400ff
+
+/* IOCFG1 */
+#define R367CAB_IOCFG1 0xf005
+#define F367CAB_IP0 0xf0050040
+#define F367CAB_OP1_OD 0xf0050020
+#define F367CAB_OP1_INV 0xf0050010
+#define F367CAB_OP1_DACVALUE_HI 0xf005000f
+
+/* DAC1R */
+#define R367CAB_DAC1R 0xf006
+#define F367CAB_OP1_DACVALUE_LO 0xf00600ff
+
+/* IOCFG2 */
+#define R367CAB_IOCFG2 0xf007
+#define F367CAB_OP2_LOCK_CONF 0xf00700e0
+#define F367CAB_OP2_OD 0xf0070010
+#define F367CAB_OP2_VAL 0xf0070008
+#define F367CAB_OP1_LOCK_CONF 0xf0070007
+
+/* SDFR */
+#define R367CAB_SDFR 0xf008
+#define F367CAB_OP0_FREQ 0xf00800f0
+#define F367CAB_OP1_FREQ 0xf008000f
+
+/* AUX_CLK */
+#define R367CAB_AUX_CLK 0xf00a
+#define F367CAB_AUXFEC_CTL 0xf00a00c0
+#define F367CAB_DIS_CKX4 0xf00a0020
+#define F367CAB_CKSEL 0xf00a0018
+#define F367CAB_CKDIV_PROG 0xf00a0006
+#define F367CAB_AUXCLK_ENA 0xf00a0001
+
+/* FREESYS1 */
+#define R367CAB_FREESYS1 0xf00b
+#define F367CAB_FREESYS_1 0xf00b00ff
+
+/* FREESYS2 */
+#define R367CAB_FREESYS2 0xf00c
+#define F367CAB_FREESYS_2 0xf00c00ff
+
+/* FREESYS3 */
+#define R367CAB_FREESYS3 0xf00d
+#define F367CAB_FREESYS_3 0xf00d00ff
+
+/* GPIO_CFG */
+#define R367CAB_GPIO_CFG 0xf00e
+#define F367CAB_GPIO7_OD 0xf00e0080
+#define F367CAB_GPIO7_CFG 0xf00e0040
+#define F367CAB_GPIO6_OD 0xf00e0020
+#define F367CAB_GPIO6_CFG 0xf00e0010
+#define F367CAB_GPIO5_OD 0xf00e0008
+#define F367CAB_GPIO5_CFG 0xf00e0004
+#define F367CAB_GPIO4_OD 0xf00e0002
+#define F367CAB_GPIO4_CFG 0xf00e0001
+
+/* GPIO_CMD */
+#define R367CAB_GPIO_CMD 0xf00f
+#define F367CAB_GPIO7_VAL 0xf00f0008
+#define F367CAB_GPIO6_VAL 0xf00f0004
+#define F367CAB_GPIO5_VAL 0xf00f0002
+#define F367CAB_GPIO4_VAL 0xf00f0001
+
+/* TSTRES */
+#define R367CAB_TSTRES 0xf0c0
+#define F367CAB_FRES_DISPLAY 0xf0c00080
+#define F367CAB_FRES_FIFO_AD 0xf0c00020
+#define F367CAB_FRESRS 0xf0c00010
+#define F367CAB_FRESACS 0xf0c00008
+#define F367CAB_FRESFEC 0xf0c00004
+#define F367CAB_FRES_PRIF 0xf0c00002
+#define F367CAB_FRESCORE 0xf0c00001
+
+/* ANACTRL */
+#define R367CAB_ANACTRL 0xf0c1
+#define F367CAB_BYPASS_XTAL 0xf0c10040
+#define F367CAB_BYPASS_PLLXN 0xf0c1000c
+#define F367CAB_DIS_PAD_OSC 0xf0c10002
+#define F367CAB_STDBY_PLLXN 0xf0c10001
+
+/* TSTBUS */
+#define R367CAB_TSTBUS 0xf0c2
+#define F367CAB_TS_BYTE_CLK_INV 0xf0c20080
+#define F367CAB_CFG_IP 0xf0c20070
+#define F367CAB_CFG_TST 0xf0c2000f
+
+/* RF_AGC1 */
+#define R367CAB_RF_AGC1 0xf0d4
+#define F367CAB_RF_AGC1_LEVEL_HI 0xf0d400ff
+
+/* RF_AGC2 */
+#define R367CAB_RF_AGC2 0xf0d5
+#define F367CAB_REF_ADGP 0xf0d50080
+#define F367CAB_STDBY_ADCGP 0xf0d50020
+#define F367CAB_RF_AGC1_LEVEL_LO 0xf0d50003
+
+/* ANADIGCTRL */
+#define R367CAB_ANADIGCTRL 0xf0d7
+#define F367CAB_SEL_CLKDEM 0xf0d70020
+#define F367CAB_EN_BUFFER_Q 0xf0d70010
+#define F367CAB_EN_BUFFER_I 0xf0d70008
+#define F367CAB_ADC_RIS_EGDE 0xf0d70004
+#define F367CAB_SGN_ADC 0xf0d70002
+#define F367CAB_SEL_AD12_SYNC 0xf0d70001
+
+/* PLLMDIV */
+#define R367CAB_PLLMDIV 0xf0d8
+#define F367CAB_PLL_MDIV 0xf0d800ff
+
+/* PLLNDIV */
+#define R367CAB_PLLNDIV 0xf0d9
+#define F367CAB_PLL_NDIV 0xf0d900ff
+
+/* PLLSETUP */
+#define R367CAB_PLLSETUP 0xf0da
+#define F367CAB_PLL_PDIV 0xf0da0070
+#define F367CAB_PLL_KDIV 0xf0da000f
+
+/* DUAL_AD12 */
+#define R367CAB_DUAL_AD12 0xf0db
+#define F367CAB_FS20M 0xf0db0020
+#define F367CAB_FS50M 0xf0db0010
+#define F367CAB_INMODe0 0xf0db0008
+#define F367CAB_POFFQ 0xf0db0004
+#define F367CAB_POFFI 0xf0db0002
+#define F367CAB_INMODE1 0xf0db0001
+
+/* TSTBIST */
+#define R367CAB_TSTBIST 0xf0dc
+#define F367CAB_TST_BYP_CLK 0xf0dc0080
+#define F367CAB_TST_GCLKENA_STD 0xf0dc0040
+#define F367CAB_TST_GCLKENA 0xf0dc0020
+#define F367CAB_TST_MEMBIST 0xf0dc001f
+
+/* CTRL_1 */
+#define R367CAB_CTRL_1 0xf402
+#define F367CAB_SOFT_RST 0xf4020080
+#define F367CAB_EQU_RST 0xf4020008
+#define F367CAB_CRL_RST 0xf4020004
+#define F367CAB_TRL_RST 0xf4020002
+#define F367CAB_AGC_RST 0xf4020001
+
+/* CTRL_2 */
+#define R367CAB_CTRL_2 0xf403
+#define F367CAB_DEINT_RST 0xf4030008
+#define F367CAB_RS_RST 0xf4030004
+
+/* IT_STATUS1 */
+#define R367CAB_IT_STATUS1 0xf408
+#define F367CAB_SWEEP_OUT 0xf4080080
+#define F367CAB_FSM_CRL 0xf4080040
+#define F367CAB_CRL_LOCK 0xf4080020
+#define F367CAB_MFSM 0xf4080010
+#define F367CAB_TRL_LOCK 0xf4080008
+#define F367CAB_TRL_AGC_LIMIT 0xf4080004
+#define F367CAB_ADJ_AGC_LOCK 0xf4080002
+#define F367CAB_AGC_QAM_LOCK 0xf4080001
+
+/* IT_STATUS2 */
+#define R367CAB_IT_STATUS2 0xf409
+#define F367CAB_TSMF_CNT 0xf4090080
+#define F367CAB_TSMF_EOF 0xf4090040
+#define F367CAB_TSMF_RDY 0xf4090020
+#define F367CAB_FEC_NOCORR 0xf4090010
+#define F367CAB_SYNCSTATE 0xf4090008
+#define F367CAB_DEINT_LOCK 0xf4090004
+#define F367CAB_FADDING_FRZ 0xf4090002
+#define F367CAB_TAPMON_ALARM 0xf4090001
+
+/* IT_EN1 */
+#define R367CAB_IT_EN1 0xf40a
+#define F367CAB_SWEEP_OUTE 0xf40a0080
+#define F367CAB_FSM_CRLE 0xf40a0040
+#define F367CAB_CRL_LOCKE 0xf40a0020
+#define F367CAB_MFSME 0xf40a0010
+#define F367CAB_TRL_LOCKE 0xf40a0008
+#define F367CAB_TRL_AGC_LIMITE 0xf40a0004
+#define F367CAB_ADJ_AGC_LOCKE 0xf40a0002
+#define F367CAB_AGC_LOCKE 0xf40a0001
+
+/* IT_EN2 */
+#define R367CAB_IT_EN2 0xf40b
+#define F367CAB_TSMF_CNTE 0xf40b0080
+#define F367CAB_TSMF_EOFE 0xf40b0040
+#define F367CAB_TSMF_RDYE 0xf40b0020
+#define F367CAB_FEC_NOCORRE 0xf40b0010
+#define F367CAB_SYNCSTATEE 0xf40b0008
+#define F367CAB_DEINT_LOCKE 0xf40b0004
+#define F367CAB_FADDING_FRZE 0xf40b0002
+#define F367CAB_TAPMON_ALARME 0xf40b0001
+
+/* CTRL_STATUS */
+#define R367CAB_CTRL_STATUS 0xf40c
+#define F367CAB_QAMFEC_LOCK 0xf40c0004
+#define F367CAB_TSMF_LOCK 0xf40c0002
+#define F367CAB_TSMF_ERROR 0xf40c0001
+
+/* TEST_CTL */
+#define R367CAB_TEST_CTL 0xf40f
+#define F367CAB_TST_BLK_SEL 0xf40f0060
+#define F367CAB_TST_BUS_SEL 0xf40f001f
+
+/* AGC_CTL */
+#define R367CAB_AGC_CTL 0xf410
+#define F367CAB_AGC_LCK_TH 0xf41000f0
+#define F367CAB_AGC_ACCUMRSTSEL 0xf4100007
+
+/* AGC_IF_CFG */
+#define R367CAB_AGC_IF_CFG 0xf411
+#define F367CAB_AGC_IF_BWSEL 0xf41100f0
+#define F367CAB_AGC_IF_FREEZE 0xf4110002
+
+/* AGC_RF_CFG */
+#define R367CAB_AGC_RF_CFG 0xf412
+#define F367CAB_AGC_RF_BWSEL 0xf4120070
+#define F367CAB_AGC_RF_FREEZE 0xf4120002
+
+/* AGC_PWM_CFG */
+#define R367CAB_AGC_PWM_CFG 0xf413
+#define F367CAB_AGC_RF_PWM_TST 0xf4130080
+#define F367CAB_AGC_RF_PWM_INV 0xf4130040
+#define F367CAB_AGC_IF_PWM_TST 0xf4130008
+#define F367CAB_AGC_IF_PWM_INV 0xf4130004
+#define F367CAB_AGC_PWM_CLKDIV 0xf4130003
+
+/* AGC_PWR_REF_L */
+#define R367CAB_AGC_PWR_REF_L 0xf414
+#define F367CAB_AGC_PWRREF_LO 0xf41400ff
+
+/* AGC_PWR_REF_H */
+#define R367CAB_AGC_PWR_REF_H 0xf415
+#define F367CAB_AGC_PWRREF_HI 0xf4150003
+
+/* AGC_RF_TH_L */
+#define R367CAB_AGC_RF_TH_L 0xf416
+#define F367CAB_AGC_RF_TH_LO 0xf41600ff
+
+/* AGC_RF_TH_H */
+#define R367CAB_AGC_RF_TH_H 0xf417
+#define F367CAB_AGC_RF_TH_HI 0xf417000f
+
+/* AGC_IF_LTH_L */
+#define R367CAB_AGC_IF_LTH_L 0xf418
+#define F367CAB_AGC_IF_THLO_LO 0xf41800ff
+
+/* AGC_IF_LTH_H */
+#define R367CAB_AGC_IF_LTH_H 0xf419
+#define F367CAB_AGC_IF_THLO_HI 0xf419000f
+
+/* AGC_IF_HTH_L */
+#define R367CAB_AGC_IF_HTH_L 0xf41a
+#define F367CAB_AGC_IF_THHI_LO 0xf41a00ff
+
+/* AGC_IF_HTH_H */
+#define R367CAB_AGC_IF_HTH_H 0xf41b
+#define F367CAB_AGC_IF_THHI_HI 0xf41b000f
+
+/* AGC_PWR_RD_L */
+#define R367CAB_AGC_PWR_RD_L 0xf41c
+#define F367CAB_AGC_PWR_WORD_LO 0xf41c00ff
+
+/* AGC_PWR_RD_M */
+#define R367CAB_AGC_PWR_RD_M 0xf41d
+#define F367CAB_AGC_PWR_WORD_ME 0xf41d00ff
+
+/* AGC_PWR_RD_H */
+#define R367CAB_AGC_PWR_RD_H 0xf41e
+#define F367CAB_AGC_PWR_WORD_HI 0xf41e0003
+
+/* AGC_PWM_IFCMD_L */
+#define R367CAB_AGC_PWM_IFCMD_L 0xf420
+#define F367CAB_AGC_IF_PWMCMD_LO 0xf42000ff
+
+/* AGC_PWM_IFCMD_H */
+#define R367CAB_AGC_PWM_IFCMD_H 0xf421
+#define F367CAB_AGC_IF_PWMCMD_HI 0xf421000f
+
+/* AGC_PWM_RFCMD_L */
+#define R367CAB_AGC_PWM_RFCMD_L 0xf422
+#define F367CAB_AGC_RF_PWMCMD_LO 0xf42200ff
+
+/* AGC_PWM_RFCMD_H */
+#define R367CAB_AGC_PWM_RFCMD_H 0xf423
+#define F367CAB_AGC_RF_PWMCMD_HI 0xf423000f
+
+/* IQDEM_CFG */
+#define R367CAB_IQDEM_CFG 0xf424
+#define F367CAB_IQDEM_CLK_SEL 0xf4240004
+#define F367CAB_IQDEM_INVIQ 0xf4240002
+#define F367CAB_IQDEM_A2dTYPE 0xf4240001
+
+/* MIX_NCO_LL */
+#define R367CAB_MIX_NCO_LL 0xf425
+#define F367CAB_MIX_NCO_INC_LL 0xf42500ff
+
+/* MIX_NCO_HL */
+#define R367CAB_MIX_NCO_HL 0xf426
+#define F367CAB_MIX_NCO_INC_HL 0xf42600ff
+
+/* MIX_NCO_HH */
+#define R367CAB_MIX_NCO_HH 0xf427
+#define F367CAB_MIX_NCO_INVCNST 0xf4270080
+#define F367CAB_MIX_NCO_INC_HH 0xf427007f
+
+/* SRC_NCO_LL */
+#define R367CAB_SRC_NCO_LL 0xf428
+#define F367CAB_SRC_NCO_INC_LL 0xf42800ff
+
+/* SRC_NCO_LH */
+#define R367CAB_SRC_NCO_LH 0xf429
+#define F367CAB_SRC_NCO_INC_LH 0xf42900ff
+
+/* SRC_NCO_HL */
+#define R367CAB_SRC_NCO_HL 0xf42a
+#define F367CAB_SRC_NCO_INC_HL 0xf42a00ff
+
+/* SRC_NCO_HH */
+#define R367CAB_SRC_NCO_HH 0xf42b
+#define F367CAB_SRC_NCO_INC_HH 0xf42b007f
+
+/* IQDEM_GAIN_SRC_L */
+#define R367CAB_IQDEM_GAIN_SRC_L 0xf42c
+#define F367CAB_GAIN_SRC_LO 0xf42c00ff
+
+/* IQDEM_GAIN_SRC_H */
+#define R367CAB_IQDEM_GAIN_SRC_H 0xf42d
+#define F367CAB_GAIN_SRC_HI 0xf42d0003
+
+/* IQDEM_DCRM_CFG_LL */
+#define R367CAB_IQDEM_DCRM_CFG_LL 0xf430
+#define F367CAB_DCRM0_DCIN_L 0xf43000ff
+
+/* IQDEM_DCRM_CFG_LH */
+#define R367CAB_IQDEM_DCRM_CFG_LH 0xf431
+#define F367CAB_DCRM1_I_DCIN_L 0xf43100fc
+#define F367CAB_DCRM0_DCIN_H 0xf4310003
+
+/* IQDEM_DCRM_CFG_HL */
+#define R367CAB_IQDEM_DCRM_CFG_HL 0xf432
+#define F367CAB_DCRM1_Q_DCIN_L 0xf43200f0
+#define F367CAB_DCRM1_I_DCIN_H 0xf432000f
+
+/* IQDEM_DCRM_CFG_HH */
+#define R367CAB_IQDEM_DCRM_CFG_HH 0xf433
+#define F367CAB_DCRM1_FRZ 0xf4330080
+#define F367CAB_DCRM0_FRZ 0xf4330040
+#define F367CAB_DCRM1_Q_DCIN_H 0xf433003f
+
+/* IQDEM_ADJ_COEFf0 */
+#define R367CAB_IQDEM_ADJ_COEFF0 0xf434
+#define F367CAB_ADJIIR_COEFF10_L 0xf43400ff
+
+/* IQDEM_ADJ_COEFF1 */
+#define R367CAB_IQDEM_ADJ_COEFF1 0xf435
+#define F367CAB_ADJIIR_COEFF11_L 0xf43500fc
+#define F367CAB_ADJIIR_COEFF10_H 0xf4350003
+
+/* IQDEM_ADJ_COEFF2 */
+#define R367CAB_IQDEM_ADJ_COEFF2 0xf436
+#define F367CAB_ADJIIR_COEFF12_L 0xf43600f0
+#define F367CAB_ADJIIR_COEFF11_H 0xf436000f
+
+/* IQDEM_ADJ_COEFF3 */
+#define R367CAB_IQDEM_ADJ_COEFF3 0xf437
+#define F367CAB_ADJIIR_COEFF20_L 0xf43700c0
+#define F367CAB_ADJIIR_COEFF12_H 0xf437003f
+
+/* IQDEM_ADJ_COEFF4 */
+#define R367CAB_IQDEM_ADJ_COEFF4 0xf438
+#define F367CAB_ADJIIR_COEFF20_H 0xf43800ff
+
+/* IQDEM_ADJ_COEFF5 */
+#define R367CAB_IQDEM_ADJ_COEFF5 0xf439
+#define F367CAB_ADJIIR_COEFF21_L 0xf43900ff
+
+/* IQDEM_ADJ_COEFF6 */
+#define R367CAB_IQDEM_ADJ_COEFF6 0xf43a
+#define F367CAB_ADJIIR_COEFF22_L 0xf43a00fc
+#define F367CAB_ADJIIR_COEFF21_H 0xf43a0003
+
+/* IQDEM_ADJ_COEFF7 */
+#define R367CAB_IQDEM_ADJ_COEFF7 0xf43b
+#define F367CAB_ADJIIR_COEFF22_H 0xf43b000f
+
+/* IQDEM_ADJ_EN */
+#define R367CAB_IQDEM_ADJ_EN 0xf43c
+#define F367CAB_ALLPASSFILT_EN 0xf43c0008
+#define F367CAB_ADJ_AGC_EN 0xf43c0004
+#define F367CAB_ADJ_COEFF_FRZ 0xf43c0002
+#define F367CAB_ADJ_EN 0xf43c0001
+
+/* IQDEM_ADJ_AGC_REF */
+#define R367CAB_IQDEM_ADJ_AGC_REF 0xf43d
+#define F367CAB_ADJ_AGC_REF 0xf43d00ff
+
+/* ALLPASSFILT1 */
+#define R367CAB_ALLPASSFILT1 0xf440
+#define F367CAB_ALLPASSFILT_COEFF1_LO 0xf44000ff
+
+/* ALLPASSFILT2 */
+#define R367CAB_ALLPASSFILT2 0xf441
+#define F367CAB_ALLPASSFILT_COEFF1_ME 0xf44100ff
+
+/* ALLPASSFILT3 */
+#define R367CAB_ALLPASSFILT3 0xf442
+#define F367CAB_ALLPASSFILT_COEFF2_LO 0xf44200c0
+#define F367CAB_ALLPASSFILT_COEFF1_HI 0xf442003f
+
+/* ALLPASSFILT4 */
+#define R367CAB_ALLPASSFILT4 0xf443
+#define F367CAB_ALLPASSFILT_COEFF2_MEL 0xf44300ff
+
+/* ALLPASSFILT5 */
+#define R367CAB_ALLPASSFILT5 0xf444
+#define F367CAB_ALLPASSFILT_COEFF2_MEH 0xf44400ff
+
+/* ALLPASSFILT6 */
+#define R367CAB_ALLPASSFILT6 0xf445
+#define F367CAB_ALLPASSFILT_COEFF3_LO 0xf44500f0
+#define F367CAB_ALLPASSFILT_COEFF2_HI 0xf445000f
+
+/* ALLPASSFILT7 */
+#define R367CAB_ALLPASSFILT7 0xf446
+#define F367CAB_ALLPASSFILT_COEFF3_MEL 0xf44600ff
+
+/* ALLPASSFILT8 */
+#define R367CAB_ALLPASSFILT8 0xf447
+#define F367CAB_ALLPASSFILT_COEFF3_MEH 0xf44700ff
+
+/* ALLPASSFILT9 */
+#define R367CAB_ALLPASSFILT9 0xf448
+#define F367CAB_ALLPASSFILT_COEFF4_LO 0xf44800fc
+#define F367CAB_ALLPASSFILT_COEFF3_HI 0xf4480003
+
+/* ALLPASSFILT10 */
+#define R367CAB_ALLPASSFILT10 0xf449
+#define F367CAB_ALLPASSFILT_COEFF4_ME 0xf44900ff
+
+/* ALLPASSFILT11 */
+#define R367CAB_ALLPASSFILT11 0xf44a
+#define F367CAB_ALLPASSFILT_COEFF4_HI 0xf44a00ff
+
+/* TRL_AGC_CFG */
+#define R367CAB_TRL_AGC_CFG 0xf450
+#define F367CAB_TRL_AGC_FREEZE 0xf4500080
+#define F367CAB_TRL_AGC_REF 0xf450007f
+
+/* TRL_LPF_CFG */
+#define R367CAB_TRL_LPF_CFG 0xf454
+#define F367CAB_NYQPOINT_INV 0xf4540040
+#define F367CAB_TRL_SHIFT 0xf4540030
+#define F367CAB_NYQ_COEFF_SEL 0xf454000c
+#define F367CAB_TRL_LPF_FREEZE 0xf4540002
+#define F367CAB_TRL_LPF_CRT 0xf4540001
+
+/* TRL_LPF_ACQ_GAIN */
+#define R367CAB_TRL_LPF_ACQ_GAIN 0xf455
+#define F367CAB_TRL_GDIR_ACQ 0xf4550070
+#define F367CAB_TRL_GINT_ACQ 0xf4550007
+
+/* TRL_LPF_TRK_GAIN */
+#define R367CAB_TRL_LPF_TRK_GAIN 0xf456
+#define F367CAB_TRL_GDIR_TRK 0xf4560070
+#define F367CAB_TRL_GINT_TRK 0xf4560007
+
+/* TRL_LPF_OUT_GAIN */
+#define R367CAB_TRL_LPF_OUT_GAIN 0xf457
+#define F367CAB_TRL_GAIN_OUT 0xf4570007
+
+/* TRL_LOCKDET_LTH */
+#define R367CAB_TRL_LOCKDET_LTH 0xf458
+#define F367CAB_TRL_LCK_THLO 0xf4580007
+
+/* TRL_LOCKDET_HTH */
+#define R367CAB_TRL_LOCKDET_HTH 0xf459
+#define F367CAB_TRL_LCK_THHI 0xf45900ff
+
+/* TRL_LOCKDET_TRGVAL */
+#define R367CAB_TRL_LOCKDET_TRGVAL 0xf45a
+#define F367CAB_TRL_LCK_TRG 0xf45a00ff
+
+/* IQ_QAM */
+#define R367CAB_IQ_QAM 0xf45c
+#define F367CAB_IQ_INPUT 0xf45c0008
+#define F367CAB_DETECT_MODE 0xf45c0007
+
+/* FSM_STATE */
+#define R367CAB_FSM_STATE 0xf460
+#define F367CAB_CRL_DFE 0xf4600080
+#define F367CAB_DFE_START 0xf4600040
+#define F367CAB_CTRLG_START 0xf4600030
+#define F367CAB_FSM_FORCESTATE 0xf460000f
+
+/* FSM_CTL */
+#define R367CAB_FSM_CTL 0xf461
+#define F367CAB_FEC2_EN 0xf4610040
+#define F367CAB_SIT_EN 0xf4610020
+#define F367CAB_TRL_AHEAD 0xf4610010
+#define F367CAB_TRL2_EN 0xf4610008
+#define F367CAB_FSM_EQA1_EN 0xf4610004
+#define F367CAB_FSM_BKP_DIS 0xf4610002
+#define F367CAB_FSM_FORCE_EN 0xf4610001
+
+/* FSM_STS */
+#define R367CAB_FSM_STS 0xf462
+#define F367CAB_FSM_STATUS 0xf462000f
+
+/* FSM_SNR0_HTH */
+#define R367CAB_FSM_SNR0_HTH 0xf463
+#define F367CAB_SNR0_HTH 0xf46300ff
+
+/* FSM_SNR1_HTH */
+#define R367CAB_FSM_SNR1_HTH 0xf464
+#define F367CAB_SNR1_HTH 0xf46400ff
+
+/* FSM_SNR2_HTH */
+#define R367CAB_FSM_SNR2_HTH 0xf465
+#define F367CAB_SNR2_HTH 0xf46500ff
+
+/* FSM_SNR0_LTH */
+#define R367CAB_FSM_SNR0_LTH 0xf466
+#define F367CAB_SNR0_LTH 0xf46600ff
+
+/* FSM_SNR1_LTH */
+#define R367CAB_FSM_SNR1_LTH 0xf467
+#define F367CAB_SNR1_LTH 0xf46700ff
+
+/* FSM_EQA1_HTH */
+#define R367CAB_FSM_EQA1_HTH 0xf468
+#define F367CAB_SNR3_HTH_LO 0xf46800f0
+#define F367CAB_EQA1_HTH 0xf468000f
+
+/* FSM_TEMPO */
+#define R367CAB_FSM_TEMPO 0xf469
+#define F367CAB_SIT 0xf46900c0
+#define F367CAB_WST 0xf4690038
+#define F367CAB_ELT 0xf4690006
+#define F367CAB_SNR3_HTH_HI 0xf4690001
+
+/* FSM_CONFIG */
+#define R367CAB_FSM_CONFIG 0xf46a
+#define F367CAB_FEC2_DFEOFF 0xf46a0004
+#define F367CAB_PRIT_STATE 0xf46a0002
+#define F367CAB_MODMAP_STATE 0xf46a0001
+
+/* EQU_I_TESTTAP_L */
+#define R367CAB_EQU_I_TESTTAP_L 0xf474
+#define F367CAB_I_TEST_TAP_L 0xf47400ff
+
+/* EQU_I_TESTTAP_M */
+#define R367CAB_EQU_I_TESTTAP_M 0xf475
+#define F367CAB_I_TEST_TAP_M 0xf47500ff
+
+/* EQU_I_TESTTAP_H */
+#define R367CAB_EQU_I_TESTTAP_H 0xf476
+#define F367CAB_I_TEST_TAP_H 0xf476001f
+
+/* EQU_TESTAP_CFG */
+#define R367CAB_EQU_TESTAP_CFG 0xf477
+#define F367CAB_TEST_FFE_DFE_SEL 0xf4770040
+#define F367CAB_TEST_TAP_SELECT 0xf477003f
+
+/* EQU_Q_TESTTAP_L */
+#define R367CAB_EQU_Q_TESTTAP_L 0xf478
+#define F367CAB_Q_TEST_TAP_L 0xf47800ff
+
+/* EQU_Q_TESTTAP_M */
+#define R367CAB_EQU_Q_TESTTAP_M 0xf479
+#define F367CAB_Q_TEST_TAP_M 0xf47900ff
+
+/* EQU_Q_TESTTAP_H */
+#define R367CAB_EQU_Q_TESTTAP_H 0xf47a
+#define F367CAB_Q_TEST_TAP_H 0xf47a001f
+
+/* EQU_TAP_CTRL */
+#define R367CAB_EQU_TAP_CTRL 0xf47b
+#define F367CAB_MTAP_FRZ 0xf47b0010
+#define F367CAB_PRE_FREEZE 0xf47b0008
+#define F367CAB_DFE_TAPMON_EN 0xf47b0004
+#define F367CAB_FFE_TAPMON_EN 0xf47b0002
+#define F367CAB_MTAP_ONLY 0xf47b0001
+
+/* EQU_CTR_CRL_CONTROL_L */
+#define R367CAB_EQU_CTR_CRL_CONTROL_L 0xf47c
+#define F367CAB_EQU_CTR_CRL_CONTROL_LO 0xf47c00ff
+
+/* EQU_CTR_CRL_CONTROL_H */
+#define R367CAB_EQU_CTR_CRL_CONTROL_H 0xf47d
+#define F367CAB_EQU_CTR_CRL_CONTROL_HI 0xf47d00ff
+
+/* EQU_CTR_HIPOW_L */
+#define R367CAB_EQU_CTR_HIPOW_L 0xf47e
+#define F367CAB_CTR_HIPOW_L 0xf47e00ff
+
+/* EQU_CTR_HIPOW_H */
+#define R367CAB_EQU_CTR_HIPOW_H 0xf47f
+#define F367CAB_CTR_HIPOW_H 0xf47f00ff
+
+/* EQU_I_EQU_LO */
+#define R367CAB_EQU_I_EQU_LO 0xf480
+#define F367CAB_EQU_I_EQU_L 0xf48000ff
+
+/* EQU_I_EQU_HI */
+#define R367CAB_EQU_I_EQU_HI 0xf481
+#define F367CAB_EQU_I_EQU_H 0xf4810003
+
+/* EQU_Q_EQU_LO */
+#define R367CAB_EQU_Q_EQU_LO 0xf482
+#define F367CAB_EQU_Q_EQU_L 0xf48200ff
+
+/* EQU_Q_EQU_HI */
+#define R367CAB_EQU_Q_EQU_HI 0xf483
+#define F367CAB_EQU_Q_EQU_H 0xf4830003
+
+/* EQU_MAPPER */
+#define R367CAB_EQU_MAPPER 0xf484
+#define F367CAB_QUAD_AUTO 0xf4840080
+#define F367CAB_QUAD_INV 0xf4840040
+#define F367CAB_QAM_MODE 0xf4840007
+
+/* EQU_SWEEP_RATE */
+#define R367CAB_EQU_SWEEP_RATE 0xf485
+#define F367CAB_SNR_PER 0xf48500c0
+#define F367CAB_SWEEP_RATE 0xf485003f
+
+/* EQU_SNR_LO */
+#define R367CAB_EQU_SNR_LO 0xf486
+#define F367CAB_SNR_LO 0xf48600ff
+
+/* EQU_SNR_HI */
+#define R367CAB_EQU_SNR_HI 0xf487
+#define F367CAB_SNR_HI 0xf48700ff
+
+/* EQU_GAMMA_LO */
+#define R367CAB_EQU_GAMMA_LO 0xf488
+#define F367CAB_GAMMA_LO 0xf48800ff
+
+/* EQU_GAMMA_HI */
+#define R367CAB_EQU_GAMMA_HI 0xf489
+#define F367CAB_GAMMA_ME 0xf48900ff
+
+/* EQU_ERR_GAIN */
+#define R367CAB_EQU_ERR_GAIN 0xf48a
+#define F367CAB_EQA1MU 0xf48a0070
+#define F367CAB_CRL2MU 0xf48a000e
+#define F367CAB_GAMMA_HI 0xf48a0001
+
+/* EQU_RADIUS */
+#define R367CAB_EQU_RADIUS 0xf48b
+#define F367CAB_RADIUS 0xf48b00ff
+
+/* EQU_FFE_MAINTAP */
+#define R367CAB_EQU_FFE_MAINTAP 0xf48c
+#define F367CAB_FFE_MAINTAP_INIT 0xf48c00ff
+
+/* EQU_FFE_LEAKAGE */
+#define R367CAB_EQU_FFE_LEAKAGE 0xf48e
+#define F367CAB_LEAK_PER 0xf48e00f0
+#define F367CAB_EQU_OUTSEL 0xf48e0002
+#define F367CAB_PNT2dFE 0xf48e0001
+
+/* EQU_FFE_MAINTAP_POS */
+#define R367CAB_EQU_FFE_MAINTAP_POS 0xf48f
+#define F367CAB_FFE_LEAK_EN 0xf48f0080
+#define F367CAB_DFE_LEAK_EN 0xf48f0040
+#define F367CAB_FFE_MAINTAP_POS 0xf48f003f
+
+/* EQU_GAIN_WIDE */
+#define R367CAB_EQU_GAIN_WIDE 0xf490
+#define F367CAB_DFE_GAIN_WIDE 0xf49000f0
+#define F367CAB_FFE_GAIN_WIDE 0xf490000f
+
+/* EQU_GAIN_NARROW */
+#define R367CAB_EQU_GAIN_NARROW 0xf491
+#define F367CAB_DFE_GAIN_NARROW 0xf49100f0
+#define F367CAB_FFE_GAIN_NARROW 0xf491000f
+
+/* EQU_CTR_LPF_GAIN */
+#define R367CAB_EQU_CTR_LPF_GAIN 0xf492
+#define F367CAB_CTR_GTO 0xf4920080
+#define F367CAB_CTR_GDIR 0xf4920070
+#define F367CAB_SWEEP_EN 0xf4920008
+#define F367CAB_CTR_GINT 0xf4920007
+
+/* EQU_CRL_LPF_GAIN */
+#define R367CAB_EQU_CRL_LPF_GAIN 0xf493
+#define F367CAB_CRL_GTO 0xf4930080
+#define F367CAB_CRL_GDIR 0xf4930070
+#define F367CAB_SWEEP_DIR 0xf4930008
+#define F367CAB_CRL_GINT 0xf4930007
+
+/* EQU_GLOBAL_GAIN */
+#define R367CAB_EQU_GLOBAL_GAIN 0xf494
+#define F367CAB_CRL_GAIN 0xf49400f8
+#define F367CAB_CTR_INC_GAIN 0xf4940004
+#define F367CAB_CTR_FRAC 0xf4940003
+
+/* EQU_CRL_LD_SEN */
+#define R367CAB_EQU_CRL_LD_SEN 0xf495
+#define F367CAB_CTR_BADPOINT_EN 0xf4950080
+#define F367CAB_CTR_GAIN 0xf4950070
+#define F367CAB_LIMANEN 0xf4950008
+#define F367CAB_CRL_LD_SEN 0xf4950007
+
+/* EQU_CRL_LD_VAL */
+#define R367CAB_EQU_CRL_LD_VAL 0xf496
+#define F367CAB_CRL_BISTH_LIMIT 0xf4960080
+#define F367CAB_CARE_EN 0xf4960040
+#define F367CAB_CRL_LD_PER 0xf4960030
+#define F367CAB_CRL_LD_WST 0xf496000c
+#define F367CAB_CRL_LD_TFS 0xf4960003
+
+/* EQU_CRL_TFR */
+#define R367CAB_EQU_CRL_TFR 0xf497
+#define F367CAB_CRL_LD_TFR 0xf49700ff
+
+/* EQU_CRL_BISTH_LO */
+#define R367CAB_EQU_CRL_BISTH_LO 0xf498
+#define F367CAB_CRL_BISTH_LO 0xf49800ff
+
+/* EQU_CRL_BISTH_HI */
+#define R367CAB_EQU_CRL_BISTH_HI 0xf499
+#define F367CAB_CRL_BISTH_HI 0xf49900ff
+
+/* EQU_SWEEP_RANGE_LO */
+#define R367CAB_EQU_SWEEP_RANGE_LO 0xf49a
+#define F367CAB_SWEEP_RANGE_LO 0xf49a00ff
+
+/* EQU_SWEEP_RANGE_HI */
+#define R367CAB_EQU_SWEEP_RANGE_HI 0xf49b
+#define F367CAB_SWEEP_RANGE_HI 0xf49b00ff
+
+/* EQU_CRL_LIMITER */
+#define R367CAB_EQU_CRL_LIMITER 0xf49c
+#define F367CAB_BISECTOR_EN 0xf49c0080
+#define F367CAB_PHEST128_EN 0xf49c0040
+#define F367CAB_CRL_LIM 0xf49c003f
+
+/* EQU_MODULUS_MAP */
+#define R367CAB_EQU_MODULUS_MAP 0xf49d
+#define F367CAB_PNT_DEPTH 0xf49d00e0
+#define F367CAB_MODULUS_CMP 0xf49d001f
+
+/* EQU_PNT_GAIN */
+#define R367CAB_EQU_PNT_GAIN 0xf49e
+#define F367CAB_PNT_EN 0xf49e0080
+#define F367CAB_MODULUSMAP_EN 0xf49e0040
+#define F367CAB_PNT_GAIN 0xf49e003f
+
+/* FEC_AC_CTR_0 */
+#define R367CAB_FEC_AC_CTR_0 0xf4a8
+#define F367CAB_BE_BYPASS 0xf4a80020
+#define F367CAB_REFRESH47 0xf4a80010
+#define F367CAB_CT_NBST 0xf4a80008
+#define F367CAB_TEI_ENA 0xf4a80004
+#define F367CAB_DS_ENA 0xf4a80002
+#define F367CAB_TSMF_EN 0xf4a80001
+
+/* FEC_AC_CTR_1 */
+#define R367CAB_FEC_AC_CTR_1 0xf4a9
+#define F367CAB_DEINT_DEPTH 0xf4a900ff
+
+/* FEC_AC_CTR_2 */
+#define R367CAB_FEC_AC_CTR_2 0xf4aa
+#define F367CAB_DEINT_M 0xf4aa00f8
+#define F367CAB_DIS_UNLOCK 0xf4aa0004
+#define F367CAB_DESCR_MODE 0xf4aa0003
+
+/* FEC_AC_CTR_3 */
+#define R367CAB_FEC_AC_CTR_3 0xf4ab
+#define F367CAB_DI_UNLOCK 0xf4ab0080
+#define F367CAB_DI_FREEZE 0xf4ab0040
+#define F367CAB_MISMATCH 0xf4ab0030
+#define F367CAB_ACQ_MODE 0xf4ab000c
+#define F367CAB_TRK_MODE 0xf4ab0003
+
+/* FEC_STATUS */
+#define R367CAB_FEC_STATUS 0xf4ac
+#define F367CAB_DEINT_SMCNTR 0xf4ac00e0
+#define F367CAB_DEINT_SYNCSTATE 0xf4ac0018
+#define F367CAB_DEINT_SYNLOST 0xf4ac0004
+#define F367CAB_DESCR_SYNCSTATE 0xf4ac0002
+
+/* RS_COUNTER_0 */
+#define R367CAB_RS_COUNTER_0 0xf4ae
+#define F367CAB_BK_CT_L 0xf4ae00ff
+
+/* RS_COUNTER_1 */
+#define R367CAB_RS_COUNTER_1 0xf4af
+#define F367CAB_BK_CT_H 0xf4af00ff
+
+/* RS_COUNTER_2 */
+#define R367CAB_RS_COUNTER_2 0xf4b0
+#define F367CAB_CORR_CT_L 0xf4b000ff
+
+/* RS_COUNTER_3 */
+#define R367CAB_RS_COUNTER_3 0xf4b1
+#define F367CAB_CORR_CT_H 0xf4b100ff
+
+/* RS_COUNTER_4 */
+#define R367CAB_RS_COUNTER_4 0xf4b2
+#define F367CAB_UNCORR_CT_L 0xf4b200ff
+
+/* RS_COUNTER_5 */
+#define R367CAB_RS_COUNTER_5 0xf4b3
+#define F367CAB_UNCORR_CT_H 0xf4b300ff
+
+/* BERT_0 */
+#define R367CAB_BERT_0 0xf4b4
+#define F367CAB_RS_NOCORR 0xf4b40004
+#define F367CAB_CT_HOLD 0xf4b40002
+#define F367CAB_CT_CLEAR 0xf4b40001
+
+/* BERT_1 */
+#define R367CAB_BERT_1 0xf4b5
+#define F367CAB_BERT_ON 0xf4b50020
+#define F367CAB_BERT_ERR_SRC 0xf4b50010
+#define F367CAB_BERT_ERR_MODE 0xf4b50008
+#define F367CAB_BERT_NBYTE 0xf4b50007
+
+/* BERT_2 */
+#define R367CAB_BERT_2 0xf4b6
+#define F367CAB_BERT_ERRCOUNT_L 0xf4b600ff
+
+/* BERT_3 */
+#define R367CAB_BERT_3 0xf4b7
+#define F367CAB_BERT_ERRCOUNT_H 0xf4b700ff
+
+/* OUTFORMAT_0 */
+#define R367CAB_OUTFORMAT_0 0xf4b8
+#define F367CAB_CLK_POLARITY 0xf4b80080
+#define F367CAB_FEC_TYPE 0xf4b80040
+#define F367CAB_SYNC_STRIP 0xf4b80008
+#define F367CAB_TS_SWAP 0xf4b80004
+#define F367CAB_OUTFORMAT 0xf4b80003
+
+/* OUTFORMAT_1 */
+#define R367CAB_OUTFORMAT_1 0xf4b9
+#define F367CAB_CI_DIVRANGE 0xf4b900ff
+
+/* SMOOTHER_2 */
+#define R367CAB_SMOOTHER_2 0xf4be
+#define F367CAB_FIFO_BYPASS 0xf4be0020
+
+/* TSMF_CTRL_0 */
+#define R367CAB_TSMF_CTRL_0 0xf4c0
+#define F367CAB_TS_NUMBER 0xf4c0001e
+#define F367CAB_SEL_MODE 0xf4c00001
+
+/* TSMF_CTRL_1 */
+#define R367CAB_TSMF_CTRL_1 0xf4c1
+#define F367CAB_CHECK_ERROR_BIT 0xf4c10080
+#define F367CAB_CHCK_F_SYNC 0xf4c10040
+#define F367CAB_H_MODE 0xf4c10008
+#define F367CAB_D_V_MODE 0xf4c10004
+#define F367CAB_MODE 0xf4c10003
+
+/* TSMF_CTRL_3 */
+#define R367CAB_TSMF_CTRL_3 0xf4c3
+#define F367CAB_SYNC_IN_COUNT 0xf4c300f0
+#define F367CAB_SYNC_OUT_COUNT 0xf4c3000f
+
+/* TS_ON_ID_0 */
+#define R367CAB_TS_ON_ID_0 0xf4c4
+#define F367CAB_TS_ID_L 0xf4c400ff
+
+/* TS_ON_ID_1 */
+#define R367CAB_TS_ON_ID_1 0xf4c5
+#define F367CAB_TS_ID_H 0xf4c500ff
+
+/* TS_ON_ID_2 */
+#define R367CAB_TS_ON_ID_2 0xf4c6
+#define F367CAB_ON_ID_L 0xf4c600ff
+
+/* TS_ON_ID_3 */
+#define R367CAB_TS_ON_ID_3 0xf4c7
+#define F367CAB_ON_ID_H 0xf4c700ff
+
+/* RE_STATUS_0 */
+#define R367CAB_RE_STATUS_0 0xf4c8
+#define F367CAB_RECEIVE_STATUS_L 0xf4c800ff
+
+/* RE_STATUS_1 */
+#define R367CAB_RE_STATUS_1 0xf4c9
+#define F367CAB_RECEIVE_STATUS_LH 0xf4c900ff
+
+/* RE_STATUS_2 */
+#define R367CAB_RE_STATUS_2 0xf4ca
+#define F367CAB_RECEIVE_STATUS_HL 0xf4ca00ff
+
+/* RE_STATUS_3 */
+#define R367CAB_RE_STATUS_3 0xf4cb
+#define F367CAB_RECEIVE_STATUS_HH 0xf4cb003f
+
+/* TS_STATUS_0 */
+#define R367CAB_TS_STATUS_0 0xf4cc
+#define F367CAB_TS_STATUS_L 0xf4cc00ff
+
+/* TS_STATUS_1 */
+#define R367CAB_TS_STATUS_1 0xf4cd
+#define F367CAB_TS_STATUS_H 0xf4cd007f
+
+/* TS_STATUS_2 */
+#define R367CAB_TS_STATUS_2 0xf4ce
+#define F367CAB_ERROR 0xf4ce0080
+#define F367CAB_EMERGENCY 0xf4ce0040
+#define F367CAB_CRE_TS 0xf4ce0030
+#define F367CAB_VER 0xf4ce000e
+#define F367CAB_M_LOCK 0xf4ce0001
+
+/* TS_STATUS_3 */
+#define R367CAB_TS_STATUS_3 0xf4cf
+#define F367CAB_UPDATE_READY 0xf4cf0080
+#define F367CAB_END_FRAME_HEADER 0xf4cf0040
+#define F367CAB_CONTCNT 0xf4cf0020
+#define F367CAB_TS_IDENTIFIER_SEL 0xf4cf000f
+
+/* T_O_ID_0 */
+#define R367CAB_T_O_ID_0 0xf4d0
+#define F367CAB_ON_ID_I_L 0xf4d000ff
+
+/* T_O_ID_1 */
+#define R367CAB_T_O_ID_1 0xf4d1
+#define F367CAB_ON_ID_I_H 0xf4d100ff
+
+/* T_O_ID_2 */
+#define R367CAB_T_O_ID_2 0xf4d2
+#define F367CAB_TS_ID_I_L 0xf4d200ff
+
+/* T_O_ID_3 */
+#define R367CAB_T_O_ID_3 0xf4d3
+#define F367CAB_TS_ID_I_H 0xf4d300ff
+
+#define STV0367CAB_NBREGS 187
+
+#endif
diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h
index e3e35d1ce838..91c7ee8b2313 100644
--- a/drivers/media/dvb/frontends/stv0900.h
+++ b/drivers/media/dvb/frontends/stv0900.h
@@ -53,6 +53,8 @@ struct stv0900_config {
u8 tun2_type;
/* Set device param to start dma */
int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+ /* Hook for Lock LED */
+ void (*set_lock_led)(struct dvb_frontend *fe, int offon);
};
#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \
diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c
index 4f5e7d3a0e61..0ca316d6fffa 100644
--- a/drivers/media/dvb/frontends/stv0900_core.c
+++ b/drivers/media/dvb/frontends/stv0900_core.c
@@ -1604,6 +1604,9 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
p_search.standard = STV0900_AUTO_SEARCH;
p_search.iq_inversion = STV0900_IQ_AUTO;
p_search.search_algo = STV0900_BLIND_SEARCH;
+ /* Speeds up DVB-S searching */
+ if (c->delivery_system == SYS_DVBS)
+ p_search.standard = STV0900_SEARCH_DVBS1;
intp->srch_standard[demod] = p_search.standard;
intp->symbol_rate[demod] = p_search.symbol_rate;
@@ -1660,8 +1663,14 @@ static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status)
| FE_HAS_VITERBI
| FE_HAS_SYNC
| FE_HAS_LOCK;
- } else
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 1);
+ } else {
+ *status = 0;
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 0);
dprintk("DEMOD LOCK FAIL\n");
+ }
return 0;
}
@@ -1831,6 +1840,9 @@ static void stv0900_release(struct dvb_frontend *fe)
dprintk("%s\n", __func__);
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 0);
+
if ((--(state->internal->dmds_used)) <= 0) {
dprintk("%s: Actually removing\n", __func__);
@@ -1842,6 +1854,18 @@ static void stv0900_release(struct dvb_frontend *fe)
kfree(state);
}
+static int stv0900_sleep(struct dvb_frontend *fe)
+{
+ struct stv0900_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 0);
+
+ return 0;
+}
+
static int stv0900_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
@@ -1876,6 +1900,7 @@ static struct dvb_frontend_ops stv0900_ops = {
.release = stv0900_release,
.init = stv0900_init,
.get_frontend = stv0900_get_frontend,
+ .sleep = stv0900_sleep,
.get_frontend_algo = stv0900_frontend_algo,
.i2c_gate_ctrl = stv0900_i2c_gate_ctrl,
.diseqc_send_master_cmd = stv0900_send_master_cmd,
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 4e0fc2c8a41c..41d0f0a6655d 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -767,8 +767,12 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable)
* In case of any error, the lock is unlocked and exit within the
* relevant operations themselves.
*/
- if (enable)
- mutex_lock(&state->internal->tuner_lock);
+ if (enable) {
+ if (state->config->tuner_i2c_lock)
+ state->config->tuner_i2c_lock(&state->frontend, 1);
+ else
+ mutex_lock(&state->internal->tuner_lock);
+ }
reg = STV090x_READ_DEMOD(state, I2CRPT);
if (enable) {
@@ -784,13 +788,20 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable)
goto err;
}
- if (!enable)
- mutex_unlock(&state->internal->tuner_lock);
+ if (!enable) {
+ if (state->config->tuner_i2c_lock)
+ state->config->tuner_i2c_lock(&state->frontend, 0);
+ else
+ mutex_unlock(&state->internal->tuner_lock);
+ }
return 0;
err:
dprintk(FE_ERROR, 1, "I/O error");
- mutex_unlock(&state->internal->tuner_lock);
+ if (state->config->tuner_i2c_lock)
+ state->config->tuner_i2c_lock(&state->frontend, 0);
+ else
+ mutex_unlock(&state->internal->tuner_lock);
return -1;
}
@@ -2883,10 +2894,12 @@ static int stv090x_optimize_track(struct stv090x_state *state)
STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0)
- goto err;
+ if (state->internal->dev_ver >= 0x30) {
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0)
+ goto err;
+ }
if (state->frame_len == STV090x_LONG_FRAME) {
reg = STV090x_READ_DEMOD(state, DMDMODCOD);
modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD);
@@ -3846,6 +3859,7 @@ static int stv090x_sleep(struct dvb_frontend *fe)
{
struct stv090x_state *state = fe->demodulator_priv;
u32 reg;
+ u8 full_standby = 0;
if (stv090x_i2c_gate_ctrl(state, 1) < 0)
goto err;
@@ -3858,24 +3872,119 @@ static int stv090x_sleep(struct dvb_frontend *fe)
if (stv090x_i2c_gate_ctrl(state, 0) < 0)
goto err;
- dprintk(FE_DEBUG, 1, "Set %s to sleep",
- state->device == STV0900 ? "STV0900" : "STV0903");
+ dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep",
+ state->device == STV0900 ? "STV0900" : "STV0903",
+ state->demod);
- reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
- STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01);
- if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0)
- goto err;
+ mutex_lock(&state->internal->demod_lock);
- reg = stv090x_read_reg(state, STV090x_TSTTNR1);
- STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0);
- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
- goto err;
+ switch (state->demod) {
+ case STV090x_DEMODULATOR_0:
+ /* power off ADC 1 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR1);
+ STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
+ goto err;
+ /* power off DiSEqC 1 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR2);
+ STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0)
+ goto err;
+
+ /* check whether path 2 is already sleeping, that is when
+ ADC2 is off */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR3);
+ if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0)
+ full_standby = 1;
+
+ /* stop clocks */
+ reg = stv090x_read_reg(state, STV090x_STOPCLK1);
+ /* packet delineator 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1);
+ /* ADC 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1);
+ /* FEC clock is shared between the two paths, only stop it
+ when full standby is possible */
+ if (full_standby)
+ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ /* sampling 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1);
+ /* viterbi 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1);
+ /* TS clock is shared between the two paths, only stop it
+ when full standby is possible */
+ if (full_standby)
+ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ break;
+
+ case STV090x_DEMODULATOR_1:
+ /* power off ADC 2 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR3);
+ STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0)
+ goto err;
+ /* power off DiSEqC 2 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR4);
+ STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0)
+ goto err;
+
+ /* check whether path 1 is already sleeping, that is when
+ ADC1 is off */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR1);
+ if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0)
+ full_standby = 1;
+
+ /* stop clocks */
+ reg = stv090x_read_reg(state, STV090x_STOPCLK1);
+ /* packet delineator 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1);
+ /* ADC 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1);
+ /* FEC clock is shared between the two paths, only stop it
+ when full standby is possible */
+ if (full_standby)
+ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ /* sampling 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1);
+ /* viterbi 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1);
+ /* TS clock is shared between the two paths, only stop it
+ when full standby is possible */
+ if (full_standby)
+ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ break;
+ default:
+ dprintk(FE_ERROR, 1, "Wrong demodulator!");
+ break;
+ }
+
+ if (full_standby) {
+ /* general power off */
+ reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
+ STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01);
+ if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0)
+ goto err;
+ }
+
+ mutex_unlock(&state->internal->demod_lock);
return 0;
err_gateoff:
stv090x_i2c_gate_ctrl(state, 0);
err:
+ mutex_unlock(&state->internal->demod_lock);
dprintk(FE_ERROR, 1, "I/O error");
return -1;
}
@@ -3885,21 +3994,94 @@ static int stv090x_wakeup(struct dvb_frontend *fe)
struct stv090x_state *state = fe->demodulator_priv;
u32 reg;
- dprintk(FE_DEBUG, 1, "Wake %s from standby",
- state->device == STV0900 ? "STV0900" : "STV0903");
+ dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby",
+ state->device == STV0900 ? "STV0900" : "STV0903",
+ state->demod);
+
+ mutex_lock(&state->internal->demod_lock);
+ /* general power on */
reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00);
if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0)
goto err;
- reg = stv090x_read_reg(state, STV090x_TSTTNR1);
- STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1);
- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
- goto err;
+ switch (state->demod) {
+ case STV090x_DEMODULATOR_0:
+ /* power on ADC 1 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR1);
+ STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
+ goto err;
+ /* power on DiSEqC 1 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR2);
+ STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0)
+ goto err;
+
+ /* activate clocks */
+ reg = stv090x_read_reg(state, STV090x_STOPCLK1);
+ /* packet delineator 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0);
+ /* ADC 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0);
+ /* FEC clock */
+ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ /* sampling 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0);
+ /* viterbi 1 clock */
+ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0);
+ /* TS clock */
+ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ break;
+ case STV090x_DEMODULATOR_1:
+ /* power on ADC 2 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR3);
+ STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0)
+ goto err;
+ /* power on DiSEqC 2 */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR4);
+ STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0)
+ goto err;
+
+ /* activate clocks */
+ reg = stv090x_read_reg(state, STV090x_STOPCLK1);
+ /* packet delineator 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0);
+ /* ADC 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0);
+ /* FEC clock */
+ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ /* sampling 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0);
+ /* viterbi 2 clock */
+ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0);
+ /* TS clock */
+ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ break;
+
+ default:
+ dprintk(FE_ERROR, 1, "Wrong demodulator!");
+ break;
+ }
+
+ mutex_unlock(&state->internal->demod_lock);
return 0;
err:
+ mutex_unlock(&state->internal->demod_lock);
dprintk(FE_ERROR, 1, "I/O error");
return -1;
}
@@ -4169,6 +4351,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
switch (state->config->ts1_mode) {
case STV090x_TSMODE_PARALLEL_PUNCTURED:
reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00);
if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
@@ -4177,6 +4360,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_DVBCI:
reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01);
if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
@@ -4185,6 +4369,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_SERIAL_PUNCTURED:
reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00);
if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
@@ -4193,6 +4378,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_SERIAL_CONTINUOUS:
reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01);
if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
@@ -4206,6 +4392,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
switch (state->config->ts2_mode) {
case STV090x_TSMODE_PARALLEL_PUNCTURED:
reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00);
if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
@@ -4214,6 +4401,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_DVBCI:
reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01);
if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
@@ -4222,6 +4410,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_SERIAL_PUNCTURED:
reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00);
if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
@@ -4230,6 +4419,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
case STV090x_TSMODE_SERIAL_CONTINUOUS:
reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
+ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei);
STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01);
STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01);
if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
@@ -4506,16 +4696,26 @@ static int stv090x_setup(struct dvb_frontend *fe)
if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0)
goto err;
- /* workaround for stuck DiSEqC output */
- if (config->diseqc_envelope_mode)
- stv090x_send_diseqc_burst(fe, SEC_MINI_A);
-
return 0;
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
}
+int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value,
+ u8 xor_value)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u8 reg = 0;
+
+ STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir);
+ STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value);
+ STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value);
+
+ return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg);
+}
+EXPORT_SYMBOL(stv090x_set_gpio);
+
static struct dvb_frontend_ops stv090x_ops = {
.info = {
@@ -4580,39 +4780,35 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
state->internal = temp_int->internal;
state->internal->num_used++;
dprintk(FE_INFO, 1, "Found Internal Structure!");
- dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x",
- state->device == STV0900 ? "STV0900" : "STV0903",
- demod,
- state->internal->dev_ver);
- return &state->frontend;
} else {
state->internal = kmalloc(sizeof(struct stv090x_internal),
GFP_KERNEL);
+ if (!state->internal)
+ goto error;
temp_int = append_internal(state->internal);
+ if (!temp_int) {
+ kfree(state->internal);
+ goto error;
+ }
state->internal->num_used = 1;
state->internal->mclk = 0;
state->internal->dev_ver = 0;
state->internal->i2c_adap = state->i2c;
state->internal->i2c_addr = state->config->address;
dprintk(FE_INFO, 1, "Create New Internal Structure!");
- }
- mutex_init(&state->internal->demod_lock);
- mutex_init(&state->internal->tuner_lock);
+ mutex_init(&state->internal->demod_lock);
+ mutex_init(&state->internal->tuner_lock);
- if (stv090x_sleep(&state->frontend) < 0) {
- dprintk(FE_ERROR, 1, "Error putting device to sleep");
- goto error;
+ if (stv090x_setup(&state->frontend) < 0) {
+ dprintk(FE_ERROR, 1, "Error setting up device");
+ goto err_remove;
+ }
}
- if (stv090x_setup(&state->frontend) < 0) {
- dprintk(FE_ERROR, 1, "Error setting up device");
- goto error;
- }
- if (stv090x_wakeup(&state->frontend) < 0) {
- dprintk(FE_ERROR, 1, "Error waking device");
- goto error;
- }
+ /* workaround for stuck DiSEqC output */
+ if (config->diseqc_envelope_mode)
+ stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A);
dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x",
state->device == STV0900 ? "STV0900" : "STV0903",
@@ -4621,6 +4817,9 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
return &state->frontend;
+err_remove:
+ remove_dev(state->internal);
+ kfree(state->internal);
error:
kfree(state);
return NULL;
diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h
index dd1b93ae4e9d..29cdc2b71314 100644
--- a/drivers/media/dvb/frontends/stv090x.h
+++ b/drivers/media/dvb/frontends/stv090x.h
@@ -78,6 +78,9 @@ struct stv090x_config {
u32 ts1_clk;
u32 ts2_clk;
+ u8 ts1_tei : 1;
+ u8 ts2_tei : 1;
+
enum stv090x_i2crpt repeater_level;
u8 tuner_bbgain; /* default: 10db */
@@ -97,6 +100,7 @@ struct stv090x_config {
int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain);
int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk);
int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status);
+ void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock);
};
#if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE))
@@ -104,6 +108,11 @@ struct stv090x_config {
extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
struct i2c_adapter *i2c,
enum stv090x_demodulator demod);
+
+/* dir = 0 -> output, dir = 1 -> input/open-drain */
+extern int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio,
+ u8 dir, u8 value, u8 xor_value);
+
#else
static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
@@ -113,6 +122,13 @@ static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *c
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+
+static inline int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio,
+ u8 opd, u8 value, u8 xor_value)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
#endif /* CONFIG_DVB_STV090x */
#endif /* __STV090x_H */
diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h
index 2502855dd784..93741ee14297 100644
--- a/drivers/media/dvb/frontends/stv090x_reg.h
+++ b/drivers/media/dvb/frontends/stv090x_reg.h
@@ -1327,10 +1327,10 @@
#define STV090x_WIDTH_Px_NOSPLHT_UNNORMED_FIELD 8
#define STV090x_Px_NOSPLHy(__x, __y) (0xf48f - (__x - 1) * 0x200 - __y * 0x1)
-#define STv090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0)
-#define STv090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1)
-#define STv090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0)
-#define STv090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1)
+#define STV090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0)
+#define STV090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1)
+#define STV090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0)
+#define STV090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1)
#define STV090x_OFFST_Px_NOSPLH_UNNORMED_FIELD 0
#define STV090x_WIDTH_Px_NOSPLH_UNNORMED_FIELD 8
@@ -1406,7 +1406,7 @@
#define STV090x_Px_BCLC2S28(__x) (0xf49d - (__x - 1) * 0x200)
#define STV090x_P1_BCLC2S28 STV090x_Px_BCLC2S28(1)
-#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(1)
+#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(2)
#define STV090x_OFFST_Px_CAR2S2_8_BETA_M_FIELD 4
#define STV090x_WIDTH_Px_CAR2S2_8_BETA_M_FIELD 2
#define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0
@@ -1414,7 +1414,7 @@
#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200)
#define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1)
-#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(1)
+#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(2)
#define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4
#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_M_FIELD 2
#define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0
@@ -1422,7 +1422,7 @@
#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200)
#define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1)
-#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(1)
+#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(2)
#define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4
#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_M_FIELD 2
#define STV090x_OFFST_Px_CAR2S2_32A_BETA_E_FIELD 0
@@ -1602,7 +1602,7 @@
#define STV090x_Px_CCIACC(__x) (0xf4c4 - (__x - 1) * 0x200)
#define STV090x_P1_CCIACC STV090x_Px_CCIACC(1)
-#define STV090x_P2_CCIACC STV090x_Px_CCIACC(1)
+#define STV090x_P2_CCIACC STV090x_Px_CCIACC(2)
#define STV090x_OFFST_Px_CCI_VALUE_FIELD 0
#define STV090x_WIDTH_Px_CCI_VALUE_FIELD 8
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
index 0608aabb14ee..2bc96874d044 100644
--- a/drivers/media/dvb/ngene/Makefile
+++ b/drivers/media/dvb/ngene/Makefile
@@ -9,3 +9,6 @@ obj-$(CONFIG_DVB_NGENE) += ngene.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+
+# For the staging CI driver cxd2099
+EXTRA_CFLAGS += -Idrivers/staging/cxd2099/
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 4692a41ad95b..fcf4be901ec8 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -48,20 +48,27 @@
static int tuner_attach_stv6110(struct ngene_channel *chan)
{
+ struct i2c_adapter *i2c;
struct stv090x_config *feconf = (struct stv090x_config *)
chan->dev->card_info->fe_config[chan->number];
struct stv6110x_config *tunerconf = (struct stv6110x_config *)
chan->dev->card_info->tuner_config[chan->number];
struct stv6110x_devctl *ctl;
- ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf,
- &chan->i2c_adapter);
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
if (ctl == NULL) {
printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
return -ENODEV;
}
feconf->tuner_init = ctl->tuner_init;
+ feconf->tuner_sleep = ctl->tuner_sleep;
feconf->tuner_set_mode = ctl->tuner_set_mode;
feconf->tuner_set_frequency = ctl->tuner_set_frequency;
feconf->tuner_get_frequency = ctl->tuner_get_frequency;
@@ -78,29 +85,106 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
static int demod_attach_stv0900(struct ngene_channel *chan)
{
+ struct i2c_adapter *i2c;
struct stv090x_config *feconf = (struct stv090x_config *)
chan->dev->card_info->fe_config[chan->number];
- chan->fe = dvb_attach(stv090x_attach,
- feconf,
- &chan->i2c_adapter,
- chan->number == 0 ? STV090x_DEMODULATOR_0 :
- STV090x_DEMODULATOR_1);
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ /* Note: Both adapters share the same i2c bus, but the demod */
+ /* driver requires that each demod has its own i2c adapter */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
+ (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
+ : STV090x_DEMODULATOR_1);
if (chan->fe == NULL) {
printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
return -ENODEV;
}
- if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0,
+ /* store channel info */
+ if (feconf->tuner_i2c_lock)
+ chan->fe->analog_demod_priv = chan;
+
+ if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
0, chan->dev->card_info->lnb[chan->number])) {
printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
+{
+ struct ngene_channel *chan = fe->analog_demod_priv;
+
+ if (lock)
+ down(&chan->dev->pll_mutex);
+ else
+ up(&chan->dev->pll_mutex);
+}
+
+static int cineS2_probe(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct stv090x_config *fe_conf;
+ u8 buf[3];
+ struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
+ int rc;
+
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ fe_conf = chan->dev->card_info->fe_config[chan->number];
+ i2c_msg.addr = fe_conf->address;
+
+ /* probe demod */
+ i2c_msg.len = 2;
+ buf[0] = 0xf1;
+ buf[1] = 0x00;
+ rc = i2c_transfer(i2c, &i2c_msg, 1);
+ if (rc != 1)
+ return -ENODEV;
+
+ /* demod found, attach it */
+ rc = demod_attach_stv0900(chan);
+ if (rc < 0 || chan->number < 2)
+ return rc;
+
+ /* demod #2: reprogram outputs DPN1 & DPN2 */
+ i2c_msg.len = 3;
+ buf[0] = 0xf1;
+ switch (chan->number) {
+ case 2:
+ buf[1] = 0x5c;
+ buf[2] = 0xc2;
+ break;
+ case 3:
+ buf[1] = 0x61;
+ buf[2] = 0xcc;
+ break;
+ default:
return -ENODEV;
}
+ rc = i2c_transfer(i2c, &i2c_msg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+ return -EIO;
+ }
return 0;
}
+
static struct lgdt330x_config aver_m780 = {
.demod_address = 0xb2 >> 1,
.demod_chip = LGDT3303,
@@ -151,6 +235,29 @@ static struct stv090x_config fe_cineS2 = {
.adc2_range = STV090x_ADC_1Vpp,
.diseqc_envelope_mode = true,
+
+ .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+};
+
+static struct stv090x_config fe_cineS2_2 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+
+ .tuner_i2c_lock = cineS2_tuner_i2c_lock,
};
static struct stv6110x_config tuner_cineS2_0 = {
@@ -175,7 +282,8 @@ static struct ngene_info ngene_info_cineS2 = {
.tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
.lnb = {0x0b, 0x08},
.tsf = {3, 3},
- .fw_version = 15,
+ .fw_version = 18,
+ .msi_supported = true,
};
static struct ngene_info ngene_info_satixS2 = {
@@ -188,46 +296,54 @@ static struct ngene_info ngene_info_satixS2 = {
.tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
.lnb = {0x0b, 0x08},
.tsf = {3, 3},
- .fw_version = 15,
+ .fw_version = 18,
+ .msi_supported = true,
};
static struct ngene_info ngene_info_satixS2v2 = {
.type = NGENE_SIDEWINDER,
.name = "Mystique SaTiX-S2 Dual (v2)",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
- .fe_config = {&fe_cineS2, &fe_cineS2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08},
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
.tsf = {3, 3},
- .fw_version = 15,
+ .fw_version = 18,
+ .msi_supported = true,
};
static struct ngene_info ngene_info_cineS2v5 = {
.type = NGENE_SIDEWINDER,
.name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
- .fe_config = {&fe_cineS2, &fe_cineS2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08},
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
.tsf = {3, 3},
- .fw_version = 15,
+ .fw_version = 18,
+ .msi_supported = true,
};
+
static struct ngene_info ngene_info_duoFlexS2 = {
.type = NGENE_SIDEWINDER,
.name = "Digital Devices DuoFlex S2 miniPCIe",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
- .fe_config = {&fe_cineS2, &fe_cineS2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08},
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
.tsf = {3, 3},
- .fw_version = 15,
+ .fw_version = 18,
+ .msi_supported = true,
};
static struct ngene_info ngene_info_m780 = {
@@ -321,6 +437,7 @@ static struct pci_driver ngene_pci_driver = {
.probe = ngene_probe,
.remove = __devexit_p(ngene_remove),
.err_handler = &ngene_errors,
+ .shutdown = ngene_shutdown,
};
static __init int module_init_ngene(void)
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
index dc073bdc623a..175a0f6c2a4c 100644
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -45,6 +45,9 @@ static int one_adapter = 1;
module_param(one_adapter, int, 0444);
MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
+static int shutdown_workaround;
+module_param(shutdown_workaround, int, 0644);
+MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets.");
static int debug;
module_param(debug, int, 0444);
@@ -143,7 +146,7 @@ static void demux_tasklet(unsigned long data)
}
} else {
if (chan->HWState == HWSTATE_RUN) {
- u32 Flags = 0;
+ u32 Flags = chan->DataFormatFlags;
IBufferExchange *exch1 = chan->pBufferExchange;
IBufferExchange *exch2 = chan->pBufferExchange2;
if (Cur->ngeneBuffer.SR.Flags & 0x01)
@@ -474,9 +477,9 @@ static u8 SPDIFConfiguration[10] = {
/* Set NGENE I2S Config to transport stream compatible mode */
-static u8 TS_I2SConfiguration[4] = { 0x3E, 0x1A, 0x00, 0x00 }; /*3e 18 00 00 ?*/
+static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 };
-static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 };
+static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 };
static u8 ITUDecoderSetup[4][16] = {
{0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */
@@ -749,13 +752,11 @@ void set_transfer(struct ngene_channel *chan, int state)
if (chan->mode & NGENE_IO_TSOUT) {
chan->pBufferExchange = tsout_exchange;
/* 0x66666666 = 50MHz *2^33 /250MHz */
- chan->AudioDTOValue = 0x66666666;
- /* set_dto(chan, 38810700+1000); */
- /* set_dto(chan, 19392658); */
+ chan->AudioDTOValue = 0x80000000;
+ chan->AudioDTOUpdated = 1;
}
if (chan->mode & NGENE_IO_TSIN)
chan->pBufferExchange = tsin_exchange;
- /* ngwritel(0, 0x9310); */
spin_unlock_irq(&chan->state_lock);
} else
;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
@@ -1168,6 +1169,7 @@ static void ngene_release_buffers(struct ngene *dev)
iounmap(dev->iomem);
free_common_buffers(dev);
vfree(dev->tsout_buf);
+ vfree(dev->tsin_buf);
vfree(dev->ain_buf);
vfree(dev->vin_buf);
vfree(dev);
@@ -1184,6 +1186,13 @@ static int ngene_get_buffers(struct ngene *dev)
dvb_ringbuffer_init(&dev->tsout_rbuf,
dev->tsout_buf, TSOUT_BUF_SIZE);
}
+ if (dev->card_info->io_type[2]&NGENE_IO_TSIN) {
+ dev->tsin_buf = vmalloc(TSIN_BUF_SIZE);
+ if (!dev->tsin_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->tsin_rbuf,
+ dev->tsin_buf, TSIN_BUF_SIZE);
+ }
if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
dev->ain_buf = vmalloc(AIN_BUF_SIZE);
if (!dev->ain_buf)
@@ -1257,6 +1266,10 @@ static int ngene_load_firm(struct ngene *dev)
fw_name = "ngene_17.fw";
dev->cmd_timeout_workaround = true;
break;
+ case 18:
+ size = 0;
+ fw_name = "ngene_18.fw";
+ break;
}
if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
@@ -1266,6 +1279,8 @@ static int ngene_load_firm(struct ngene *dev)
": Copy %s to your hotplug directory!\n", fw_name);
return -1;
}
+ if (size == 0)
+ size = fw->size;
if (size != fw->size) {
printk(KERN_ERR DEVICE_NAME
": Firmware %s has invalid size!", fw_name);
@@ -1301,6 +1316,35 @@ static void ngene_stop(struct ngene *dev)
#endif
}
+static int ngene_buffer_config(struct ngene *dev)
+{
+ int stat;
+
+ if (dev->card_info->fw_version >= 17) {
+ u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
+ u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 };
+ u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 };
+ u8 *bconf = tsin12_config;
+
+ if (dev->card_info->io_type[2]&NGENE_IO_TSIN &&
+ dev->card_info->io_type[3]&NGENE_IO_TSIN) {
+ bconf = tsin1234_config;
+ if (dev->card_info->io_type[4]&NGENE_IO_TSOUT &&
+ dev->ci.en)
+ bconf = tsio1235_config;
+ }
+ stat = ngene_command_config_free_buf(dev, bconf);
+ } else {
+ int bconf = BUFFER_CONFIG_4422;
+
+ if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
+ bconf = BUFFER_CONFIG_3333;
+ stat = ngene_command_config_buf(dev, bconf);
+ }
+ return stat;
+}
+
+
static int ngene_start(struct ngene *dev)
{
int stat;
@@ -1365,23 +1409,6 @@ static int ngene_start(struct ngene *dev)
if (stat < 0)
goto fail;
- if (dev->card_info->fw_version == 17) {
- u8 tsin4_config[6] = {
- 3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0};
- u8 default_config[6] = {
- 4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0};
- u8 *bconf = default_config;
-
- if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
- bconf = tsin4_config;
- dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n");
- stat = ngene_command_config_free_buf(dev, bconf);
- } else {
- int bconf = BUFFER_CONFIG_4422;
- if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
- bconf = BUFFER_CONFIG_3333;
- stat = ngene_command_config_buf(dev, bconf);
- }
if (!stat)
return stat;
@@ -1397,9 +1424,6 @@ fail2:
return stat;
}
-
-
-
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
@@ -1408,20 +1432,25 @@ static void release_channel(struct ngene_channel *chan)
{
struct dvb_demux *dvbdemux = &chan->demux;
struct ngene *dev = chan->dev;
- struct ngene_info *ni = dev->card_info;
- int io = ni->io_type[chan->number];
- if (chan->dev->cmd_timeout_workaround && chan->running)
+ if (chan->running)
set_transfer(chan, 0);
tasklet_kill(&chan->demux_tasklet);
- if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
- if (chan->fe) {
- dvb_unregister_frontend(chan->fe);
- dvb_frontend_detach(chan->fe);
- chan->fe = NULL;
- }
+ if (chan->ci_dev) {
+ dvb_unregister_device(chan->ci_dev);
+ chan->ci_dev = NULL;
+ }
+
+ if (chan->fe) {
+ dvb_unregister_frontend(chan->fe);
+ dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ }
+
+ if (chan->has_demux) {
+ dvb_net_release(&chan->dvbnet);
dvbdemux->dmx.close(&dvbdemux->dmx);
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
&chan->hw_frontend);
@@ -1429,9 +1458,12 @@ static void release_channel(struct ngene_channel *chan)
&chan->mem_frontend);
dvb_dmxdev_release(&chan->dmxdev);
dvb_dmx_release(&chan->demux);
+ chan->has_demux = false;
+ }
- if (chan->number == 0 || !one_adapter)
- dvb_unregister_adapter(&dev->adapter[chan->number]);
+ if (chan->has_adapter) {
+ dvb_unregister_adapter(&dev->adapter[chan->number]);
+ chan->has_adapter = false;
}
}
@@ -1449,9 +1481,27 @@ static int init_channel(struct ngene_channel *chan)
chan->type = io;
chan->mode = chan->type; /* for now only one mode */
+ if (io & NGENE_IO_TSIN) {
+ chan->fe = NULL;
+ if (ni->demod_attach[nr]) {
+ ret = ni->demod_attach[nr](chan);
+ if (ret < 0)
+ goto err;
+ }
+ if (chan->fe && ni->tuner_attach[nr]) {
+ ret = ni->tuner_attach[nr](chan);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ if (!dev->ci.en && (io & NGENE_IO_TSOUT))
+ return 0;
+
if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
if (nr >= STREAM_AUDIOIN1)
chan->DataFormatFlags = DF_SWAP32;
+
if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
adapter = &dev->adapter[nr];
ret = dvb_register_adapter(adapter, "nGene",
@@ -1459,40 +1509,50 @@ static int init_channel(struct ngene_channel *chan)
&chan->dev->pci_dev->dev,
adapter_nr);
if (ret < 0)
- return ret;
+ goto err;
if (dev->first_adapter == NULL)
dev->first_adapter = adapter;
- } else {
+ chan->has_adapter = true;
+ } else
adapter = dev->first_adapter;
- }
+ }
+ if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
+ dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
+ set_transfer(chan, 1);
+ set_transfer(&chan->dev->channel[2], 1);
+ dvb_register_device(adapter, &chan->ci_dev,
+ &ngene_dvbdev_ci, (void *) chan,
+ DVB_DEVICE_SEC);
+ if (!chan->ci_dev)
+ goto err;
+ }
+
+ if (chan->fe) {
+ if (dvb_register_frontend(adapter, chan->fe) < 0)
+ goto err;
+ chan->has_demux = true;
+ }
+
+ if (chan->has_demux) {
ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
ngene_start_feed,
ngene_stop_feed, chan);
ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
&chan->hw_frontend,
&chan->mem_frontend, adapter);
+ ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx);
}
- if (io & NGENE_IO_TSIN) {
+ return ret;
+
+err:
+ if (chan->fe) {
+ dvb_frontend_detach(chan->fe);
chan->fe = NULL;
- if (ni->demod_attach[nr])
- ni->demod_attach[nr](chan);
- if (chan->fe) {
- if (dvb_register_frontend(adapter, chan->fe) < 0) {
- if (chan->fe->ops.release)
- chan->fe->ops.release(chan->fe);
- chan->fe = NULL;
- }
- }
- if (chan->fe && ni->tuner_attach[nr])
- if (ni->tuner_attach[nr] (chan) < 0) {
- printk(KERN_ERR DEVICE_NAME
- ": Tuner attach failed on channel %d!\n",
- nr);
- }
}
- return ret;
+ release_channel(chan);
+ return 0;
}
static int init_channels(struct ngene *dev)
@@ -1510,6 +1570,57 @@ static int init_channels(struct ngene *dev)
return 0;
}
+static void cxd_attach(struct ngene *dev)
+{
+ struct ngene_ci *ci = &dev->ci;
+
+ ci->en = cxd2099_attach(0x40, dev, &dev->channel[0].i2c_adapter);
+ ci->dev = dev;
+ return;
+}
+
+static void cxd_detach(struct ngene *dev)
+{
+ struct ngene_ci *ci = &dev->ci;
+
+ dvb_ca_en50221_release(ci->en);
+ kfree(ci->en);
+ ci->en = 0;
+}
+
+/***********************************/
+/* workaround for shutdown failure */
+/***********************************/
+
+static void ngene_unlink(struct ngene *dev)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_MEM_WRITE;
+ com.cmd.hdr.Length = 3;
+ com.cmd.MemoryWrite.address = 0x910c;
+ com.cmd.MemoryWrite.data = 0xff;
+ com.in_len = 3;
+ com.out_len = 1;
+
+ down(&dev->cmd_mutex);
+ ngwritel(0, NGENE_INT_ENABLE);
+ ngene_command_mutex(dev, &com);
+ up(&dev->cmd_mutex);
+}
+
+void ngene_shutdown(struct pci_dev *pdev)
+{
+ struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
+
+ if (!dev || !shutdown_workaround)
+ return;
+
+ printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n");
+ ngene_unlink(dev);
+ pci_disable_device(pdev);
+}
+
/****************************************************************************/
/* device probe/remove calls ************************************************/
/****************************************************************************/
@@ -1522,6 +1633,8 @@ void __devexit ngene_remove(struct pci_dev *pdev)
tasklet_kill(&dev->event_tasklet);
for (i = MAX_STREAM - 1; i >= 0; i--)
release_channel(&dev->channel[i]);
+ if (dev->ci.en)
+ cxd_detach(dev);
ngene_stop(dev);
ngene_release_buffers(dev);
pci_set_drvdata(pdev, NULL);
@@ -1557,6 +1670,13 @@ int __devinit ngene_probe(struct pci_dev *pci_dev,
if (stat < 0)
goto fail1;
+ cxd_attach(dev);
+
+ stat = ngene_buffer_config(dev);
+ if (stat < 0)
+ goto fail1;
+
+
dev->i2c_current_bus = -1;
/* Register DVB adapters and devices for both channels */
diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c
index 3832e5983c19..0b4943233166 100644
--- a/drivers/media/dvb/ngene/ngene-dvb.c
+++ b/drivers/media/dvb/ngene/ngene-dvb.c
@@ -47,6 +47,64 @@
/* COMMAND API interface ****************************************************/
/****************************************************************************/
+static ssize_t ts_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ngene_channel *chan = dvbdev->priv;
+ struct ngene *dev = chan->dev;
+
+ if (wait_event_interruptible(dev->tsout_rbuf.queue,
+ dvb_ringbuffer_free
+ (&dev->tsout_rbuf) >= count) < 0)
+ return 0;
+
+ dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count);
+
+ return count;
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ngene_channel *chan = dvbdev->priv;
+ struct ngene *dev = chan->dev;
+ int left, avail;
+
+ left = count;
+ while (left) {
+ if (wait_event_interruptible(
+ dev->tsin_rbuf.queue,
+ dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0)
+ return -EAGAIN;
+ avail = dvb_ringbuffer_avail(&dev->tsin_rbuf);
+ if (avail > left)
+ avail = left;
+ dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail);
+ left -= avail;
+ buf += avail;
+ }
+ return count;
+}
+
+static const struct file_operations ci_fops = {
+ .owner = THIS_MODULE,
+ .read = ts_read,
+ .write = ts_write,
+ .open = dvb_generic_open,
+ .release = dvb_generic_release,
+};
+
+struct dvb_device ngene_dvbdev_ci = {
+ .priv = 0,
+ .readers = -1,
+ .writers = -1,
+ .users = -1,
+ .fops = &ci_fops,
+};
+
+
/****************************************************************************/
/* DVB functions and API interface ******************************************/
/****************************************************************************/
@@ -63,10 +121,21 @@ static void swap_buffer(u32 *p, u32 len)
void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
{
struct ngene_channel *chan = priv;
+ struct ngene *dev = chan->dev;
- if (chan->users > 0)
+ if (flags & DF_SWAP32)
+ swap_buffer(buf, len);
+ if (dev->ci.en && chan->number == 2) {
+ if (dvb_ringbuffer_free(&dev->tsin_rbuf) > len) {
+ dvb_ringbuffer_write(&dev->tsin_rbuf, buf, len);
+ wake_up_interruptible(&dev->tsin_rbuf.queue);
+ }
+ return 0;
+ }
+ if (chan->users > 0) {
dvb_dmx_swfilter(&chan->demux, buf, len);
+ }
return NULL;
}
diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h
index 8fb4200f83f8..40fce9e3ae66 100644
--- a/drivers/media/dvb/ngene/ngene.h
+++ b/drivers/media/dvb/ngene/ngene.h
@@ -36,8 +36,11 @@
#include "dmxdev.h"
#include "dvbdev.h"
#include "dvb_demux.h"
+#include "dvb_ca_en50221.h"
#include "dvb_frontend.h"
#include "dvb_ringbuffer.h"
+#include "dvb_net.h"
+#include "cxd2099.h"
#define DEVICE_NAME "ngene"
@@ -636,14 +639,18 @@ struct ngene_channel {
int number;
int type;
int mode;
+ bool has_adapter;
+ bool has_demux;
struct dvb_frontend *fe;
struct dmxdev dmxdev;
struct dvb_demux demux;
+ struct dvb_net dvbnet;
struct dmx_frontend hw_frontend;
struct dmx_frontend mem_frontend;
int users;
struct video_device *v4l_dev;
+ struct dvb_device *ci_dev;
struct tasklet_struct demux_tasklet;
struct SBufferHeader *nextBuffer;
@@ -710,6 +717,15 @@ struct ngene_channel {
int running;
};
+
+struct ngene_ci {
+ struct device device;
+ struct i2c_adapter i2c_adapter;
+
+ struct ngene *dev;
+ struct dvb_ca_en50221 *en;
+};
+
struct ngene;
typedef void (rx_cb_t)(struct ngene *, u32, u8);
@@ -774,6 +790,10 @@ struct ngene {
#define TSOUT_BUF_SIZE (512*188*8)
struct dvb_ringbuffer tsout_rbuf;
+ u8 *tsin_buf;
+#define TSIN_BUF_SIZE (512*188*8)
+ struct dvb_ringbuffer tsin_rbuf;
+
u8 *ain_buf;
#define AIN_BUF_SIZE (128*1024)
struct dvb_ringbuffer ain_rbuf;
@@ -785,6 +805,8 @@ struct ngene {
unsigned long exp_val;
int prev_cmd;
+
+ struct ngene_ci ci;
};
struct ngene_info {
@@ -863,6 +885,7 @@ struct ngene_buffer {
int __devinit ngene_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id);
void __devexit ngene_remove(struct pci_dev *pdev);
+void ngene_shutdown(struct pci_dev *pdev);
int ngene_command(struct ngene *dev, struct ngene_command *com);
int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level);
void set_transfer(struct ngene_channel *chan, int state);
@@ -872,6 +895,7 @@ void FillTSBuffer(void *Buffer, int Length, u32 Flags);
int ngene_i2c_init(struct ngene *dev, int dev_nr);
/* Provided by ngene-dvb.c */
+extern struct dvb_device ngene_dvbdev_ci;
void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags);
void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags);
int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed);
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
index 40625b26ac10..cbe2f0de1442 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -334,6 +334,7 @@ static int ttusb_boot_dsp(struct ttusb *ttusb)
err = ttusb_cmd(ttusb, b, 4, 0);
done:
+ release_firmware(fw);
if (err) {
dprintk("%s: usb_bulk_msg() failed, return value %i!\n",
__func__, err);
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index ecdffa6aac66..299994c3aa74 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -441,6 +441,7 @@ config RADIO_TIMBERDALE
config RADIO_WL1273
tristate "Texas Instruments WL1273 I2C FM Radio"
depends on I2C && VIDEO_V4L2
+ select MFD_CORE
select MFD_WL1273_CORE
select FW_LOADER
---help---
@@ -454,4 +455,7 @@ config RADIO_WL1273
To compile this driver as a module, choose M here: the
module will be called radio-wl1273.
+# TI's ST based wl128x FM radio
+source "drivers/media/radio/wl128x/Kconfig"
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 717656d2f749..2faa33371986 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
+obj-$(CONFIG_RADIO_WL128X) += wl128x/
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 7ecc8e657663..9e177dccc673 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1,7 +1,7 @@
/*
* Driver for the Texas Instruments WL1273 FM radio.
*
- * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2011 Nokia Corporation
* Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com>
*
* This program is free software; you can redistribute it and/or
@@ -104,58 +104,6 @@ static unsigned int rds_buf = 100;
module_param(rds_buf, uint, 0);
MODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100");
-static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)
-{
- struct i2c_client *client = core->client;
- u8 b[2];
- int r;
-
- r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);
- if (r != 2) {
- dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);
- return -EREMOTEIO;
- }
-
- *value = (u16)b[0] << 8 | b[1];
-
- return 0;
-}
-
-static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)
-{
- struct i2c_client *client = core->client;
- u8 buf[] = { (param >> 8) & 0xff, param & 0xff };
- int r;
-
- r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);
- if (r) {
- dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);
- return r;
- }
-
- return 0;
-}
-
-static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)
-{
- struct i2c_client *client = core->client;
- struct i2c_msg msg;
- int r;
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.buf = data;
- msg.len = len;
-
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r != 1) {
- dev_err(&client->dev, "%s: write error.\n", __func__);
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
static int wl1273_fm_write_fw(struct wl1273_core *core,
__u8 *fw, int len)
{
@@ -188,94 +136,6 @@ static int wl1273_fm_write_fw(struct wl1273_core *core,
return r;
}
-/**
- * wl1273_fm_set_audio() - Set audio mode.
- * @core: A pointer to the device struct.
- * @new_mode: The new audio mode.
- *
- * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.
- */
-static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)
-{
- int r = 0;
-
- if (core->mode == WL1273_MODE_OFF ||
- core->mode == WL1273_MODE_SUSPENDED)
- return -EPERM;
-
- if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
- WL1273_PCM_DEF_MODE);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_RX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_ANALOG);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_ANALOG);
- if (r)
- goto out;
- }
-
- core->audio_mode = new_mode;
-out:
- return r;
-}
-
-/**
- * wl1273_fm_set_volume() - Set volume.
- * @core: A pointer to the device struct.
- * @volume: The new volume value.
- */
-static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
-{
- u16 val;
- int r;
-
- if (volume > WL1273_MAX_VOLUME)
- return -EINVAL;
-
- if (core->volume == volume)
- return 0;
-
- val = volume;
- r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val);
- if (r)
- return r;
-
- core->volume = volume;
- return 0;
-}
-
#define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status)
#define WL1273_RDS_CORRECTABLE_ERROR (1 << 3)
#define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4)
@@ -306,7 +166,7 @@ static int wl1273_fm_rds(struct wl1273_device *radio)
if (core->mode != WL1273_MODE_RX)
return 0;
- r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val);
+ r = core->read(core, WL1273_RDS_SYNC_GET, &val);
if (r)
return r;
@@ -374,7 +234,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
u16 flags;
int r;
- r = wl1273_fm_read_reg(core, WL1273_FLAG_GET, &flags);
+ r = core->read(core, WL1273_FLAG_GET, &flags);
if (r)
goto out;
@@ -398,7 +258,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
if (flags & WL1273_LEV_EVENT) {
u16 level;
- r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &level);
+ r = core->read(core, WL1273_RSSI_LVL_GET, &level);
if (r)
goto out;
@@ -439,8 +299,8 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
dev_dbg(radio->dev, "IRQ: FR:\n");
if (core->mode == WL1273_MODE_RX) {
- r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET,
- TUNER_MODE_STOP_SEARCH);
+ r = core->write(core, WL1273_TUNER_MODE_SET,
+ TUNER_MODE_STOP_SEARCH);
if (r) {
dev_err(radio->dev,
"%s: TUNER_MODE_SET fails: %d\n",
@@ -448,7 +308,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
goto out;
}
- r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &freq);
+ r = core->read(core, WL1273_FREQ_SET, &freq);
if (r)
goto out;
@@ -467,7 +327,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency);
} else {
- r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &freq);
+ r = core->read(core, WL1273_CHANL_SET, &freq);
if (r)
goto out;
@@ -477,8 +337,7 @@ static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
}
out:
- wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET,
- radio->irq_flags);
+ core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
complete(&radio->busy);
return IRQ_HANDLED;
@@ -512,7 +371,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq)
dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq);
/* Set the current tx channel */
- r = wl1273_fm_write_cmd(core, WL1273_CHANL_SET, freq / 10);
+ r = core->write(core, WL1273_CHANL_SET, freq / 10);
if (r)
return r;
@@ -526,7 +385,7 @@ static int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq)
dev_dbg(radio->dev, "WL1273_CHANL_SET: %d\n", r);
/* Enable the output power */
- r = wl1273_fm_write_cmd(core, WL1273_POWER_ENB_SET, 1);
+ r = core->write(core, WL1273_POWER_ENB_SET, 1);
if (r)
return r;
@@ -566,20 +425,20 @@ static int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq)
dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq);
- wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags);
+ core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
if (radio->band == WL1273_BAND_JAPAN)
f = (freq - WL1273_BAND_JAPAN_LOW) / 50;
else
f = (freq - WL1273_BAND_OTHER_LOW) / 50;
- r = wl1273_fm_write_cmd(core, WL1273_FREQ_SET, f);
+ r = core->write(core, WL1273_FREQ_SET, f);
if (r) {
dev_err(radio->dev, "FREQ_SET fails\n");
goto err;
}
- r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET);
+ r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET);
if (r) {
dev_err(radio->dev, "TUNER_MODE_SET fails\n");
goto err;
@@ -609,7 +468,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio)
int r;
if (core->mode == WL1273_MODE_RX) {
- r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &f);
+ r = core->read(core, WL1273_FREQ_SET, &f);
if (r)
return r;
@@ -619,7 +478,7 @@ static int wl1273_fm_get_freq(struct wl1273_device *radio)
else
freq = WL1273_BAND_OTHER_LOW + 50 * f;
} else {
- r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &f);
+ r = core->read(core, WL1273_CHANL_SET, &f);
if (r)
return r;
@@ -670,7 +529,7 @@ static int wl1273_fm_upload_firmware_patch(struct wl1273_device *radio)
}
/* ignore possible error here */
- wl1273_fm_write_cmd(core, WL1273_RESET, 0);
+ core->write(core, WL1273_RESET, 0);
dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r);
out:
@@ -683,14 +542,14 @@ static int wl1273_fm_stop(struct wl1273_device *radio)
struct wl1273_core *core = radio->core;
if (core->mode == WL1273_MODE_RX) {
- int r = wl1273_fm_write_cmd(core, WL1273_POWER_SET,
+ int r = core->write(core, WL1273_POWER_SET,
WL1273_POWER_SET_OFF);
if (r)
dev_err(radio->dev, "%s: POWER_SET fails: %d\n",
__func__, r);
} else if (core->mode == WL1273_MODE_TX) {
- int r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET,
- WL1273_PUPD_SET_OFF);
+ int r = core->write(core, WL1273_PUPD_SET,
+ WL1273_PUPD_SET_OFF);
if (r)
dev_err(radio->dev,
"%s: PUPD_SET fails: %d\n", __func__, r);
@@ -725,11 +584,11 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
val |= WL1273_POWER_SET_RDS;
/* If this fails try again */
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val);
+ r = core->write(core, WL1273_POWER_SET, val);
if (r) {
msleep(100);
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val);
+ r = core->write(core, WL1273_POWER_SET, val);
if (r) {
dev_err(dev, "%s: POWER_SET fails\n", __func__);
goto fail;
@@ -742,11 +601,10 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
} else if (new_mode == WL1273_MODE_TX) {
/* If this fails try again once */
- r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET,
- WL1273_PUPD_SET_ON);
+ r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON);
if (r) {
msleep(100);
- r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET,
+ r = core->write(core, WL1273_PUPD_SET,
WL1273_PUPD_SET_ON);
if (r) {
dev_err(dev, "%s: PUPD_SET fails\n", __func__);
@@ -755,9 +613,9 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
}
if (radio->rds_on)
- r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1);
+ r = core->write(core, WL1273_RDS_DATA_ENB, 1);
else
- r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0);
+ r = core->write(core, WL1273_RDS_DATA_ENB, 0);
} else {
dev_warn(dev, "%s: Illegal mode.\n", __func__);
}
@@ -777,14 +635,14 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
if (radio->rds_on)
val |= WL1273_POWER_SET_RDS;
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, val);
+ r = core->write(core, WL1273_POWER_SET, val);
if (r) {
dev_err(dev, "%s: POWER_SET fails\n", __func__);
goto fail;
}
} else if (new_mode == WL1273_MODE_TX) {
- r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET,
- WL1273_PUPD_SET_ON);
+ r = core->write(core, WL1273_PUPD_SET,
+ WL1273_PUPD_SET_ON);
if (r) {
dev_err(dev, "%s: PUPD_SET fails\n", __func__);
goto fail;
@@ -808,10 +666,10 @@ static int wl1273_fm_suspend(struct wl1273_device *radio)
/* Cannot go from OFF to SUSPENDED */
if (core->mode == WL1273_MODE_RX)
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET,
+ r = core->write(core, WL1273_POWER_SET,
WL1273_POWER_SET_RETENTION);
else if (core->mode == WL1273_MODE_TX)
- r = wl1273_fm_write_cmd(core, WL1273_PUPD_SET,
+ r = core->write(core, WL1273_PUPD_SET,
WL1273_PUPD_SET_RETENTION);
else
r = -EINVAL;
@@ -852,8 +710,7 @@ static int wl1273_fm_set_mode(struct wl1273_device *radio, int mode)
}
core->mode = mode;
- r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET,
- radio->irq_flags);
+ r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
if (r) {
dev_err(dev, "INT_MASK_SET fails.\n");
goto out;
@@ -951,22 +808,21 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio,
INIT_COMPLETION(radio->busy);
dev_dbg(radio->dev, "%s: BUSY\n", __func__);
- r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags);
+ r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
if (r)
goto out;
dev_dbg(radio->dev, "%s\n", __func__);
- r = wl1273_fm_write_cmd(core, WL1273_SEARCH_LVL_SET, level);
+ r = core->write(core, WL1273_SEARCH_LVL_SET, level);
if (r)
goto out;
- r = wl1273_fm_write_cmd(core, WL1273_SEARCH_DIR_SET, dir);
+ r = core->write(core, WL1273_SEARCH_DIR_SET, dir);
if (r)
goto out;
- r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET,
- TUNER_MODE_AUTO_SEEK);
+ r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK);
if (r)
goto out;
@@ -994,8 +850,7 @@ static int wl1273_fm_set_seek(struct wl1273_device *radio,
INIT_COMPLETION(radio->busy);
dev_dbg(radio->dev, "%s: BUSY\n", __func__);
- r = wl1273_fm_write_cmd(core, WL1273_TUNER_MODE_SET,
- TUNER_MODE_AUTO_SEEK);
+ r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK);
if (r)
goto out;
@@ -1020,7 +875,7 @@ static unsigned int wl1273_fm_get_tx_ctune(struct wl1273_device *radio)
core->mode == WL1273_MODE_SUSPENDED)
return -EPERM;
- r = wl1273_fm_read_reg(core, WL1273_READ_FMANT_TUNE_VALUE, &val);
+ r = core->read(core, WL1273_READ_FMANT_TUNE_VALUE, &val);
if (r) {
dev_err(dev, "%s: read error: %d\n", __func__, r);
goto out;
@@ -1066,7 +921,7 @@ static int wl1273_fm_set_preemphasis(struct wl1273_device *radio,
goto out;
}
- r = wl1273_fm_write_cmd(core, WL1273_PREMPH_SET, em);
+ r = core->write(core, WL1273_PREMPH_SET, em);
if (r)
goto out;
@@ -1086,7 +941,7 @@ static int wl1273_fm_rds_on(struct wl1273_device *radio)
if (radio->rds_on)
return 0;
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET,
+ r = core->write(core, WL1273_POWER_SET,
WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS);
if (r)
goto out;
@@ -1108,7 +963,7 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio)
radio->irq_flags &= ~WL1273_RDS_EVENT;
- r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET, radio->irq_flags);
+ r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
if (r)
goto out;
@@ -1120,7 +975,7 @@ static int wl1273_fm_rds_off(struct wl1273_device *radio)
dev_dbg(radio->dev, "%s\n", __func__);
- r = wl1273_fm_write_cmd(core, WL1273_POWER_SET, WL1273_POWER_SET_FM);
+ r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM);
if (r)
goto out;
@@ -1143,14 +998,14 @@ static int wl1273_fm_set_rds(struct wl1273_device *radio, unsigned int new_mode)
return -EPERM;
if (new_mode == WL1273_RDS_RESET) {
- r = wl1273_fm_write_cmd(core, WL1273_RDS_CNTRL_SET, 1);
+ r = core->write(core, WL1273_RDS_CNTRL_SET, 1);
return r;
}
if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) {
- r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 0);
+ r = core->write(core, WL1273_RDS_DATA_ENB, 0);
} else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) {
- r = wl1273_fm_write_cmd(core, WL1273_RDS_DATA_ENB, 1);
+ r = core->write(core, WL1273_RDS_DATA_ENB, 1);
} else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) {
r = wl1273_fm_rds_off(radio);
} else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) {
@@ -1171,12 +1026,13 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
+ struct wl1273_core *core = radio->core;
u16 val;
int r;
dev_dbg(radio->dev, "%s\n", __func__);
- if (radio->core->mode != WL1273_MODE_TX)
+ if (core->mode != WL1273_MODE_TX)
return count;
if (radio->rds_users == 0) {
@@ -1184,7 +1040,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf,
return 0;
}
- if (mutex_lock_interruptible(&radio->core->lock))
+ if (mutex_lock_interruptible(&core->lock))
return -EINTR;
/*
* Multiple processes can open the device, but only
@@ -1202,7 +1058,7 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf,
else
val = count;
- wl1273_fm_write_cmd(radio->core, WL1273_RDS_CONFIG_DATA_SET, val);
+ core->write(core, WL1273_RDS_CONFIG_DATA_SET, val);
if (copy_from_user(radio->write_buf + 1, buf, val)) {
r = -EFAULT;
@@ -1213,11 +1069,11 @@ static ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf,
dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf);
radio->write_buf[0] = WL1273_RDS_DATA_SET;
- wl1273_fm_write_data(radio->core, radio->write_buf, val + 1);
+ core->write_data(core, radio->write_buf, val + 1);
r = val;
out:
- mutex_unlock(&radio->core->lock);
+ mutex_unlock(&core->lock);
return r;
}
@@ -1263,8 +1119,8 @@ static int wl1273_fm_fops_open(struct file *file)
radio->irq_flags |= WL1273_RDS_EVENT;
- r = wl1273_fm_write_cmd(core, WL1273_INT_MASK_SET,
- radio->irq_flags);
+ r = core->write(core, WL1273_INT_MASK_SET,
+ radio->irq_flags);
if (r) {
mutex_unlock(&core->lock);
goto out;
@@ -1295,9 +1151,9 @@ static int wl1273_fm_fops_release(struct file *file)
radio->irq_flags &= ~WL1273_RDS_EVENT;
if (core->mode == WL1273_MODE_RX) {
- r = wl1273_fm_write_cmd(core,
- WL1273_INT_MASK_SET,
- radio->irq_flags);
+ r = core->write(core,
+ WL1273_INT_MASK_SET,
+ radio->irq_flags);
if (r) {
mutex_unlock(&core->lock);
goto out;
@@ -1324,7 +1180,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf,
dev_dbg(radio->dev, "%s\n", __func__);
- if (radio->core->mode != WL1273_MODE_RX)
+ if (core->mode != WL1273_MODE_RX)
return 0;
if (radio->rds_users == 0) {
@@ -1345,7 +1201,7 @@ static ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf,
}
radio->owner = file;
- r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val);
+ r = core->read(core, WL1273_RDS_SYNC_GET, &val);
if (r) {
dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__);
goto out;
@@ -1466,23 +1322,24 @@ static int wl1273_fm_vidioc_s_input(struct file *file, void *priv,
*/
static int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power)
{
+ struct wl1273_core *core = radio->core;
int r;
- if (radio->core->mode == WL1273_MODE_OFF ||
- radio->core->mode == WL1273_MODE_SUSPENDED)
+ if (core->mode == WL1273_MODE_OFF ||
+ core->mode == WL1273_MODE_SUSPENDED)
return -EPERM;
- mutex_lock(&radio->core->lock);
+ mutex_lock(&core->lock);
/* Convert the dBuV value to chip presentation */
- r = wl1273_fm_write_cmd(radio->core, WL1273_POWER_LEV_SET, 122 - power);
+ r = core->write(core, WL1273_POWER_LEV_SET, 122 - power);
if (r)
goto out;
radio->tx_power = power;
out:
- mutex_unlock(&radio->core->lock);
+ mutex_unlock(&core->lock);
return r;
}
@@ -1493,23 +1350,24 @@ out:
static int wl1273_fm_tx_set_spacing(struct wl1273_device *radio,
unsigned int spacing)
{
+ struct wl1273_core *core = radio->core;
int r;
if (spacing == 0) {
- r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET,
- WL1273_SPACING_100kHz);
+ r = core->write(core, WL1273_SCAN_SPACING_SET,
+ WL1273_SPACING_100kHz);
radio->spacing = 100;
} else if (spacing - 50000 < 25000) {
- r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET,
- WL1273_SPACING_50kHz);
+ r = core->write(core, WL1273_SCAN_SPACING_SET,
+ WL1273_SPACING_50kHz);
radio->spacing = 50;
} else if (spacing - 100000 < 50000) {
- r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET,
- WL1273_SPACING_100kHz);
+ r = core->write(core, WL1273_SCAN_SPACING_SET,
+ WL1273_SPACING_100kHz);
radio->spacing = 100;
} else {
- r = wl1273_fm_write_cmd(radio->core, WL1273_SCAN_SPACING_SET,
- WL1273_SPACING_200kHz);
+ r = core->write(core, WL1273_SCAN_SPACING_SET,
+ WL1273_SPACING_200kHz);
radio->spacing = 200;
}
@@ -1567,17 +1425,17 @@ static int wl1273_fm_vidioc_s_ctrl(struct v4l2_ctrl *ctrl)
return -EINTR;
if (core->mode == WL1273_MODE_RX && ctrl->val)
- r = wl1273_fm_write_cmd(core,
- WL1273_MUTE_STATUS_SET,
- WL1273_MUTE_HARD_LEFT |
- WL1273_MUTE_HARD_RIGHT);
+ r = core->write(core,
+ WL1273_MUTE_STATUS_SET,
+ WL1273_MUTE_HARD_LEFT |
+ WL1273_MUTE_HARD_RIGHT);
else if (core->mode == WL1273_MODE_RX)
- r = wl1273_fm_write_cmd(core,
- WL1273_MUTE_STATUS_SET, 0x0);
+ r = core->write(core,
+ WL1273_MUTE_STATUS_SET, 0x0);
else if (core->mode == WL1273_MODE_TX && ctrl->val)
- r = wl1273_fm_write_cmd(core, WL1273_MUTE, 1);
+ r = core->write(core, WL1273_MUTE, 1);
else if (core->mode == WL1273_MODE_TX)
- r = wl1273_fm_write_cmd(core, WL1273_MUTE, 0);
+ r = core->write(core, WL1273_MUTE, 0);
mutex_unlock(&core->lock);
break;
@@ -1672,7 +1530,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
if (mutex_lock_interruptible(&core->lock))
return -EINTR;
- r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val);
+ r = core->read(core, WL1273_STEREO_GET, &val);
if (r)
goto out;
@@ -1681,7 +1539,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
else
tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val);
+ r = core->read(core, WL1273_RSSI_LVL_GET, &val);
if (r)
goto out;
@@ -1690,7 +1548,7 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
tuner->afc = 0;
- r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val);
+ r = core->read(core, WL1273_RDS_SYNC_GET, &val);
if (r)
goto out;
@@ -1736,8 +1594,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv,
dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r);
if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
- r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET,
- WL1273_RX_MONO);
+ r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
if (r < 0) {
dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n",
__func__, r);
@@ -1745,8 +1602,7 @@ static int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv,
}
radio->stereo = false;
} else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) {
- r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET,
- WL1273_RX_STEREO);
+ r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
if (r < 0) {
dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n",
__func__, r);
@@ -1885,10 +1741,10 @@ static int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv,
r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF);
if (modulator->txsubchans & V4L2_TUNER_SUB_MONO)
- r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, WL1273_TX_MONO);
+ r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
else
- r = wl1273_fm_write_cmd(core, WL1273_MONO_SET,
- WL1273_RX_STEREO);
+ r = core->write(core, WL1273_MONO_SET,
+ WL1273_RX_STEREO);
if (r < 0)
dev_warn(radio->dev, WL1273_FM_DRIVER_NAME
"MONO_SET fails: %d\n", r);
@@ -1923,7 +1779,7 @@ static int wl1273_fm_vidioc_g_modulator(struct file *file, void *priv,
if (mutex_lock_interruptible(&core->lock))
return -EINTR;
- r = wl1273_fm_read_reg(core, WL1273_MONO_SET, &val);
+ r = core->read(core, WL1273_MONO_SET, &val);
if (r)
goto out;
@@ -1960,38 +1816,38 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
return 0;
}
- r = wl1273_fm_read_reg(core, WL1273_ASIC_ID_GET, &val);
+ r = core->read(core, WL1273_ASIC_ID_GET, &val);
if (r)
dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__);
else
dev_info(dev, "ASIC_ID: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_ASIC_VER_GET, &val);
+ r = core->read(core, WL1273_ASIC_VER_GET, &val);
if (r)
dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__);
else
dev_info(dev, "ASIC Version: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_FIRM_VER_GET, &val);
+ r = core->read(core, WL1273_FIRM_VER_GET, &val);
if (r)
dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__);
else
dev_info(dev, "FW version: %d(0x%04x)\n", val, val);
- r = wl1273_fm_read_reg(core, WL1273_BAND_SET, &val);
+ r = core->read(core, WL1273_BAND_SET, &val);
if (r)
dev_err(dev, "%s: Get BAND fails.\n", __func__);
else
dev_info(dev, "BAND: %d\n", val);
if (core->mode == WL1273_MODE_TX) {
- r = wl1273_fm_read_reg(core, WL1273_PUPD_SET, &val);
+ r = core->read(core, WL1273_PUPD_SET, &val);
if (r)
dev_err(dev, "%s: Get PUPD fails.\n", __func__);
else
dev_info(dev, "PUPD: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_CHANL_SET, &val);
+ r = core->read(core, WL1273_CHANL_SET, &val);
if (r)
dev_err(dev, "%s: Get CHANL fails.\n", __func__);
else
@@ -1999,13 +1855,13 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
} else if (core->mode == WL1273_MODE_RX) {
int bf = radio->rangelow;
- r = wl1273_fm_read_reg(core, WL1273_FREQ_SET, &val);
+ r = core->read(core, WL1273_FREQ_SET, &val);
if (r)
dev_err(dev, "%s: Get FREQ fails.\n", __func__);
else
dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50);
- r = wl1273_fm_read_reg(core, WL1273_MOST_MODE_SET, &val);
+ r = core->read(core, WL1273_MOST_MODE_SET, &val);
if (r)
dev_err(dev, "%s: Get MOST_MODE fails.\n",
__func__);
@@ -2016,7 +1872,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
else
dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val);
- r = wl1273_fm_read_reg(core, WL1273_MOST_BLEND_SET, &val);
+ r = core->read(core, WL1273_MOST_BLEND_SET, &val);
if (r)
dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__);
else if (val == 0)
@@ -2027,7 +1883,7 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
else
dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val);
- r = wl1273_fm_read_reg(core, WL1273_STEREO_GET, &val);
+ r = core->read(core, WL1273_STEREO_GET, &val);
if (r)
dev_err(dev, "%s: Get STEREO fails.\n", __func__);
else if (val == 0)
@@ -2037,25 +1893,25 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
else
dev_info(dev, "STEREO: Unexpected value: %d\n", val);
- r = wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &val);
+ r = core->read(core, WL1273_RSSI_LVL_GET, &val);
if (r)
dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__);
else
dev_info(dev, "RX signal strength: %d\n", (s16) val);
- r = wl1273_fm_read_reg(core, WL1273_POWER_SET, &val);
+ r = core->read(core, WL1273_POWER_SET, &val);
if (r)
dev_err(dev, "%s: Get POWER fails.\n", __func__);
else
dev_info(dev, "POWER: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_INT_MASK_SET, &val);
+ r = core->read(core, WL1273_INT_MASK_SET, &val);
if (r)
dev_err(dev, "%s: Get INT_MASK fails.\n", __func__);
else
dev_info(dev, "INT_MASK: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_RDS_SYNC_GET, &val);
+ r = core->read(core, WL1273_RDS_SYNC_GET, &val);
if (r)
dev_err(dev, "%s: Get RDS_SYNC fails.\n",
__func__);
@@ -2067,14 +1923,14 @@ static int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
else
dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val);
- r = wl1273_fm_read_reg(core, WL1273_I2S_MODE_CONFIG_SET, &val);
+ r = core->read(core, WL1273_I2S_MODE_CONFIG_SET, &val);
if (r)
dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n",
__func__);
else
dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val);
- r = wl1273_fm_read_reg(core, WL1273_VOLUME_SET, &val);
+ r = core->read(core, WL1273_VOLUME_SET, &val);
if (r)
dev_err(dev, "%s: Get VOLUME fails.\n", __func__);
else
@@ -2184,10 +2040,6 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
radio->stereo = true;
radio->bus_type = "I2C";
- radio->core->write = wl1273_fm_write_cmd;
- radio->core->set_audio = wl1273_fm_set_audio;
- radio->core->set_volume = wl1273_fm_set_volume;
-
if (radio->core->pdata->request_resources) {
r = radio->core->pdata->request_resources(radio->core->client);
if (r) {
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 60c176fe328e..38ae6cd65790 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -460,7 +460,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
count /= 3;
/* copy RDS block out of internal buffer and to user buffer */
- mutex_lock(&radio->lock);
while (block_count < count) {
if (radio->rd_index == radio->wr_index)
break;
diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
new file mode 100644
index 000000000000..749f67b192e7
--- /dev/null
+++ b/drivers/media/radio/wl128x/Kconfig
@@ -0,0 +1,17 @@
+#
+# TI's wl128x FM driver based on TI's ST driver.
+#
+menu "Texas Instruments WL128x FM driver (ST based)"
+config RADIO_WL128X
+ tristate "Texas Instruments WL128x FM Radio"
+ depends on VIDEO_V4L2 && RFKILL
+ select TI_ST
+ help
+ Choose Y here if you have this FM radio chip.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux 2 API. Information on
+ this API and pointers to "v4l2" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+endmenu
diff --git a/drivers/media/radio/wl128x/Makefile b/drivers/media/radio/wl128x/Makefile
new file mode 100644
index 000000000000..32a0ead09845
--- /dev/null
+++ b/drivers/media/radio/wl128x/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for TI's shared transport driver based wl128x
+# FM radio.
+#
+obj-$(CONFIG_RADIO_WL128X) += fm_drv.o
+fm_drv-objs := fmdrv_common.o fmdrv_rx.o fmdrv_tx.o fmdrv_v4l2.o
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
new file mode 100644
index 000000000000..5db6fd14cf3c
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -0,0 +1,244 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ *
+ * Common header for all FM driver sub-modules.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _FM_DRV_H
+#define _FM_DRV_H
+
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+
+#define FM_DRV_VERSION "0.10"
+/* Should match with FM_DRV_VERSION */
+#define FM_DRV_RADIO_VERSION KERNEL_VERSION(0, 0, 1)
+#define FM_DRV_NAME "ti_fmdrv"
+#define FM_DRV_CARD_SHORT_NAME "TI FM Radio"
+#define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio"
+
+/* Flag info */
+#define FM_INTTASK_RUNNING 0
+#define FM_INTTASK_SCHEDULE_PENDING 1
+#define FM_FW_DW_INPROGRESS 2
+#define FM_CORE_READY 3
+#define FM_CORE_TRANSPORT_READY 4
+#define FM_AF_SWITCH_INPROGRESS 5
+#define FM_CORE_TX_XMITING 6
+
+#define FM_TUNE_COMPLETE 0x1
+#define FM_BAND_LIMIT 0x2
+
+#define FM_DRV_TX_TIMEOUT (5*HZ) /* 5 seconds */
+#define FM_DRV_RX_SEEK_TIMEOUT (20*HZ) /* 20 seconds */
+
+#define NO_OF_ENTRIES_IN_ARRAY(array) (sizeof(array) / sizeof(array[0]))
+
+#define fmerr(format, ...) \
+ printk(KERN_ERR "fmdrv: " format, ## __VA_ARGS__)
+#define fmwarn(format, ...) \
+ printk(KERN_WARNING "fmdrv: " format, ##__VA_ARGS__)
+#ifdef DEBUG
+#define fmdbg(format, ...) \
+ printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__)
+#else /* DEBUG */
+#define fmdbg(format, ...)
+#endif
+enum {
+ FM_MODE_OFF,
+ FM_MODE_TX,
+ FM_MODE_RX,
+ FM_MODE_ENTRY_MAX
+};
+
+#define FM_RX_RDS_INFO_FIELD_MAX 8 /* 4 Group * 2 Bytes */
+
+/* RX RDS data format */
+struct fm_rdsdata_format {
+ union {
+ struct {
+ u8 buff[FM_RX_RDS_INFO_FIELD_MAX];
+ } groupdatabuff;
+ struct {
+ u16 pidata;
+ u8 blk_b[2];
+ u8 blk_c[2];
+ u8 blk_d[2];
+ } groupgeneral;
+ struct {
+ u16 pidata;
+ u8 blk_b[2];
+ u8 af[2];
+ u8 ps[2];
+ } group0A;
+ struct {
+ u16 pi[2];
+ u8 blk_b[2];
+ u8 ps[2];
+ } group0B;
+ } data;
+};
+
+/* FM region (Europe/US, Japan) info */
+struct region_info {
+ u32 chanl_space;
+ u32 bot_freq;
+ u32 top_freq;
+ u8 fm_band;
+};
+struct fmdev;
+typedef void (*int_handler_prototype) (struct fmdev *);
+
+/* FM Interrupt processing related info */
+struct fm_irq {
+ u8 stage;
+ u16 flag; /* FM interrupt flag */
+ u16 mask; /* FM interrupt mask */
+ /* Interrupt process timeout handler */
+ struct timer_list timer;
+ u8 retry;
+ int_handler_prototype *handlers;
+};
+
+/* RDS info */
+struct fm_rds {
+ u8 flag; /* RX RDS on/off status */
+ u8 last_blk_idx; /* Last received RDS block */
+
+ /* RDS buffer */
+ wait_queue_head_t read_queue;
+ u32 buf_size; /* Size is always multiple of 3 */
+ u32 wr_idx;
+ u32 rd_idx;
+ u8 *buff;
+};
+
+#define FM_RDS_MAX_AF_LIST 25
+
+/*
+ * Current RX channel Alternate Frequency cache.
+ * This info is used to switch to other freq (AF)
+ * when current channel signal strengh is below RSSI threshold.
+ */
+struct tuned_station_info {
+ u16 picode;
+ u32 af_cache[FM_RDS_MAX_AF_LIST];
+ u8 afcache_size;
+ u8 af_list_max;
+};
+
+/* FM RX mode info */
+struct fm_rx {
+ struct region_info region; /* Current selected band */
+ u32 freq; /* Current RX frquency */
+ u8 mute_mode; /* Current mute mode */
+ u8 deemphasis_mode; /* Current deemphasis mode */
+ /* RF dependent soft mute mode */
+ u8 rf_depend_mute;
+ u16 volume; /* Current volume level */
+ u16 rssi_threshold; /* Current RSSI threshold level */
+ /* Holds the index of the current AF jump */
+ u8 afjump_idx;
+ /* Will hold the frequency before the jump */
+ u32 freq_before_jump;
+ u8 rds_mode; /* RDS operation mode (RDS/RDBS) */
+ u8 af_mode; /* Alternate frequency on/off */
+ struct tuned_station_info stat_info;
+ struct fm_rds rds;
+};
+
+#define FMTX_RDS_TXT_STR_SIZE 25
+/*
+ * FM TX RDS data
+ *
+ * @ text_type: is the text following PS or RT
+ * @ text: radio text string which could either be PS or RT
+ * @ af_freq: alternate frequency for Tx
+ * TODO: to be declared in application
+ */
+struct tx_rds {
+ u8 text_type;
+ u8 text[FMTX_RDS_TXT_STR_SIZE];
+ u8 flag;
+ u32 af_freq;
+};
+/*
+ * FM TX global data
+ *
+ * @ pwr_lvl: Power Level of the Transmission from mixer control
+ * @ xmit_state: Transmission state = Updated locally upon Start/Stop
+ * @ audio_io: i2S/Analog
+ * @ tx_frq: Transmission frequency
+ */
+struct fmtx_data {
+ u8 pwr_lvl;
+ u8 xmit_state;
+ u8 audio_io;
+ u8 region;
+ u16 aud_mode;
+ u32 preemph;
+ u32 tx_frq;
+ struct tx_rds rds;
+};
+
+/* FM driver operation structure */
+struct fmdev {
+ struct video_device *radio_dev; /* V4L2 video device pointer */
+ struct snd_card *card; /* Card which holds FM mixer controls */
+ u16 asci_id;
+ spinlock_t rds_buff_lock; /* To protect access to RDS buffer */
+ spinlock_t resp_skb_lock; /* To protect access to received SKB */
+
+ long flag; /* FM driver state machine info */
+ u8 streg_cbdata; /* status of ST registration */
+
+ struct sk_buff_head rx_q; /* RX queue */
+ struct tasklet_struct rx_task; /* RX Tasklet */
+
+ struct sk_buff_head tx_q; /* TX queue */
+ struct tasklet_struct tx_task; /* TX Tasklet */
+ unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */
+ atomic_t tx_cnt; /* Number of packets can send at a time */
+
+ struct sk_buff *resp_skb; /* Response from the chip */
+ /* Main task completion handler */
+ struct completion maintask_comp;
+ /* Opcode of last command sent to the chip */
+ u8 pre_op;
+ /* Handler used for wakeup when response packet is received */
+ struct completion *resp_comp;
+ struct fm_irq irq_info;
+ u8 curr_fmmode; /* Current FM chip mode (TX, RX, OFF) */
+ struct fm_rx rx; /* FM receiver info */
+ struct fmtx_data tx_data;
+
+ /* V4L2 ctrl framwork handler*/
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* For core assisted locking */
+ struct mutex mutex;
+};
+#endif
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
new file mode 100644
index 000000000000..12f4c656e8e8
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -0,0 +1,1677 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ *
+ * This sub-module of FM driver is common for FM RX and TX
+ * functionality. This module is responsible for:
+ * 1) Forming group of Channel-8 commands to perform particular
+ * functionality (eg., frequency set require more than
+ * one Channel-8 command to be sent to the chip).
+ * 2) Sending each Channel-8 command to the chip and reading
+ * response back over Shared Transport.
+ * 3) Managing TX and RX Queues and Tasklets.
+ * 4) Handling FM Interrupt packet and taking appropriate action.
+ * 5) Loading FM firmware to the chip (common, FM TX, and FM RX
+ * firmware files based on mode selection)
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Raja Mani <raja_mani@ti.com>
+ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include "fmdrv.h"
+#include "fmdrv_v4l2.h"
+#include "fmdrv_common.h"
+#include <linux/ti_wilink_st.h>
+#include "fmdrv_rx.h"
+#include "fmdrv_tx.h"
+
+/* Region info */
+static struct region_info region_configs[] = {
+ /* Europe/US */
+ {
+ .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
+ .bot_freq = 87500, /* 87.5 MHz */
+ .top_freq = 108000, /* 108 MHz */
+ .fm_band = 0,
+ },
+ /* Japan */
+ {
+ .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
+ .bot_freq = 76000, /* 76 MHz */
+ .top_freq = 90000, /* 90 MHz */
+ .fm_band = 1,
+ },
+};
+
+/* Band selection */
+static u8 default_radio_region; /* Europe/US */
+module_param(default_radio_region, byte, 0);
+MODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan");
+
+/* RDS buffer blocks */
+static u32 default_rds_buf = 300;
+module_param(default_rds_buf, uint, 0444);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries");
+
+/* Radio Nr */
+static u32 radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* FM irq handlers forward declaration */
+static void fm_irq_send_flag_getcmd(struct fmdev *);
+static void fm_irq_handle_flag_getcmd_resp(struct fmdev *);
+static void fm_irq_handle_hw_malfunction(struct fmdev *);
+static void fm_irq_handle_rds_start(struct fmdev *);
+static void fm_irq_send_rdsdata_getcmd(struct fmdev *);
+static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *);
+static void fm_irq_handle_rds_finish(struct fmdev *);
+static void fm_irq_handle_tune_op_ended(struct fmdev *);
+static void fm_irq_handle_power_enb(struct fmdev *);
+static void fm_irq_handle_low_rssi_start(struct fmdev *);
+static void fm_irq_afjump_set_pi(struct fmdev *);
+static void fm_irq_handle_set_pi_resp(struct fmdev *);
+static void fm_irq_afjump_set_pimask(struct fmdev *);
+static void fm_irq_handle_set_pimask_resp(struct fmdev *);
+static void fm_irq_afjump_setfreq(struct fmdev *);
+static void fm_irq_handle_setfreq_resp(struct fmdev *);
+static void fm_irq_afjump_enableint(struct fmdev *);
+static void fm_irq_afjump_enableint_resp(struct fmdev *);
+static void fm_irq_start_afjump(struct fmdev *);
+static void fm_irq_handle_start_afjump_resp(struct fmdev *);
+static void fm_irq_afjump_rd_freq(struct fmdev *);
+static void fm_irq_afjump_rd_freq_resp(struct fmdev *);
+static void fm_irq_handle_low_rssi_finish(struct fmdev *);
+static void fm_irq_send_intmsk_cmd(struct fmdev *);
+static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *);
+
+/*
+ * When FM common module receives interrupt packet, following handlers
+ * will be executed one after another to service the interrupt(s)
+ */
+enum fmc_irq_handler_index {
+ FM_SEND_FLAG_GETCMD_IDX,
+ FM_HANDLE_FLAG_GETCMD_RESP_IDX,
+
+ /* HW malfunction irq handler */
+ FM_HW_MAL_FUNC_IDX,
+
+ /* RDS threshold reached irq handler */
+ FM_RDS_START_IDX,
+ FM_RDS_SEND_RDS_GETCMD_IDX,
+ FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX,
+ FM_RDS_FINISH_IDX,
+
+ /* Tune operation ended irq handler */
+ FM_HW_TUNE_OP_ENDED_IDX,
+
+ /* TX power enable irq handler */
+ FM_HW_POWER_ENB_IDX,
+
+ /* Low RSSI irq handler */
+ FM_LOW_RSSI_START_IDX,
+ FM_AF_JUMP_SETPI_IDX,
+ FM_AF_JUMP_HANDLE_SETPI_RESP_IDX,
+ FM_AF_JUMP_SETPI_MASK_IDX,
+ FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX,
+ FM_AF_JUMP_SET_AF_FREQ_IDX,
+ FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX,
+ FM_AF_JUMP_ENABLE_INT_IDX,
+ FM_AF_JUMP_ENABLE_INT_RESP_IDX,
+ FM_AF_JUMP_START_AFJUMP_IDX,
+ FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX,
+ FM_AF_JUMP_RD_FREQ_IDX,
+ FM_AF_JUMP_RD_FREQ_RESP_IDX,
+ FM_LOW_RSSI_FINISH_IDX,
+
+ /* Interrupt process post action */
+ FM_SEND_INTMSK_CMD_IDX,
+ FM_HANDLE_INTMSK_CMD_RESP_IDX,
+};
+
+/* FM interrupt handler table */
+static int_handler_prototype int_handler_table[] = {
+ fm_irq_send_flag_getcmd,
+ fm_irq_handle_flag_getcmd_resp,
+ fm_irq_handle_hw_malfunction,
+ fm_irq_handle_rds_start, /* RDS threshold reached irq handler */
+ fm_irq_send_rdsdata_getcmd,
+ fm_irq_handle_rdsdata_getcmd_resp,
+ fm_irq_handle_rds_finish,
+ fm_irq_handle_tune_op_ended,
+ fm_irq_handle_power_enb, /* TX power enable irq handler */
+ fm_irq_handle_low_rssi_start,
+ fm_irq_afjump_set_pi,
+ fm_irq_handle_set_pi_resp,
+ fm_irq_afjump_set_pimask,
+ fm_irq_handle_set_pimask_resp,
+ fm_irq_afjump_setfreq,
+ fm_irq_handle_setfreq_resp,
+ fm_irq_afjump_enableint,
+ fm_irq_afjump_enableint_resp,
+ fm_irq_start_afjump,
+ fm_irq_handle_start_afjump_resp,
+ fm_irq_afjump_rd_freq,
+ fm_irq_afjump_rd_freq_resp,
+ fm_irq_handle_low_rssi_finish,
+ fm_irq_send_intmsk_cmd, /* Interrupt process post action */
+ fm_irq_handle_intmsk_cmd_resp
+};
+
+long (*g_st_write) (struct sk_buff *skb);
+static struct completion wait_for_fmdrv_reg_comp;
+
+static inline void fm_irq_call(struct fmdev *fmdev)
+{
+ fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
+}
+
+/* Continue next function in interrupt handler table */
+static inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage)
+{
+ fmdev->irq_info.stage = stage;
+ fm_irq_call(fmdev);
+}
+
+static inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage)
+{
+ fmdev->irq_info.stage = stage;
+ mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
+}
+
+#ifdef FM_DUMP_TXRX_PKT
+ /* To dump outgoing FM Channel-8 packets */
+inline void dump_tx_skb_data(struct sk_buff *skb)
+{
+ int len, len_org;
+ u8 index;
+ struct fm_cmd_msg_hdr *cmd_hdr;
+
+ cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data;
+ printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x",
+ fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr,
+ cmd_hdr->len, cmd_hdr->op,
+ cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen);
+
+ len_org = skb->len - FM_CMD_MSG_HDR_SIZE;
+ if (len_org > 0) {
+ printk("\n data(%d): ", cmd_hdr->dlen);
+ len = min(len_org, 14);
+ for (index = 0; index < len; index++)
+ printk("%x ",
+ skb->data[FM_CMD_MSG_HDR_SIZE + index]);
+ printk("%s", (len_org > 14) ? ".." : "");
+ }
+ printk("\n");
+}
+
+ /* To dump incoming FM Channel-8 packets */
+inline void dump_rx_skb_data(struct sk_buff *skb)
+{
+ int len, len_org;
+ u8 index;
+ struct fm_event_msg_hdr *evt_hdr;
+
+ evt_hdr = (struct fm_event_msg_hdr *)skb->data;
+ printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x "
+ "opcode:%02x type:%s dlen:%02x", evt_hdr->hdr, evt_hdr->len,
+ evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op,
+ (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen);
+
+ len_org = skb->len - FM_EVT_MSG_HDR_SIZE;
+ if (len_org > 0) {
+ printk("\n data(%d): ", evt_hdr->dlen);
+ len = min(len_org, 14);
+ for (index = 0; index < len; index++)
+ printk("%x ",
+ skb->data[FM_EVT_MSG_HDR_SIZE + index]);
+ printk("%s", (len_org > 14) ? ".." : "");
+ }
+ printk("\n");
+}
+#endif
+
+void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set)
+{
+ fmdev->rx.region = region_configs[region_to_set];
+}
+
+/*
+ * FM common sub-module will schedule this tasklet whenever it receives
+ * FM packet from ST driver.
+ */
+static void recv_tasklet(unsigned long arg)
+{
+ struct fmdev *fmdev;
+ struct fm_irq *irq_info;
+ struct fm_event_msg_hdr *evt_hdr;
+ struct sk_buff *skb;
+ u8 num_fm_hci_cmds;
+ unsigned long flags;
+
+ fmdev = (struct fmdev *)arg;
+ irq_info = &fmdev->irq_info;
+ /* Process all packets in the RX queue */
+ while ((skb = skb_dequeue(&fmdev->rx_q))) {
+ if (skb->len < sizeof(struct fm_event_msg_hdr)) {
+ fmerr("skb(%p) has only %d bytes"
+ "atleast need %d bytes to decode\n", skb,
+ skb->len, sizeof(struct fm_event_msg_hdr));
+ kfree_skb(skb);
+ continue;
+ }
+
+ evt_hdr = (void *)skb->data;
+ num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds;
+
+ /* FM interrupt packet? */
+ if (evt_hdr->op == FM_INTERRUPT) {
+ /* FM interrupt handler started already? */
+ if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
+ set_bit(FM_INTTASK_RUNNING, &fmdev->flag);
+ if (irq_info->stage != 0) {
+ fmerr("Inval stage resetting to zero\n");
+ irq_info->stage = 0;
+ }
+
+ /*
+ * Execute first function in interrupt handler
+ * table.
+ */
+ irq_info->handlers[irq_info->stage](fmdev);
+ } else {
+ set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag);
+ }
+ kfree_skb(skb);
+ }
+ /* Anyone waiting for this with completion handler? */
+ else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) {
+
+ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
+ fmdev->resp_skb = skb;
+ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
+ complete(fmdev->resp_comp);
+
+ fmdev->resp_comp = NULL;
+ atomic_set(&fmdev->tx_cnt, 1);
+ }
+ /* Is this for interrupt handler? */
+ else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) {
+ if (fmdev->resp_skb != NULL)
+ fmerr("Response SKB ptr not NULL\n");
+
+ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
+ fmdev->resp_skb = skb;
+ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
+
+ /* Execute interrupt handler where state index points */
+ irq_info->handlers[irq_info->stage](fmdev);
+
+ kfree_skb(skb);
+ atomic_set(&fmdev->tx_cnt, 1);
+ } else {
+ fmerr("Nobody claimed SKB(%p),purging\n", skb);
+ }
+
+ /*
+ * Check flow control field. If Num_FM_HCI_Commands field is
+ * not zero, schedule FM TX tasklet.
+ */
+ if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt))
+ if (!skb_queue_empty(&fmdev->tx_q))
+ tasklet_schedule(&fmdev->tx_task);
+ }
+}
+
+/* FM send tasklet: is scheduled when FM packet has to be sent to chip */
+static void send_tasklet(unsigned long arg)
+{
+ struct fmdev *fmdev;
+ struct sk_buff *skb;
+ int len;
+
+ fmdev = (struct fmdev *)arg;
+
+ if (!atomic_read(&fmdev->tx_cnt))
+ return;
+
+ /* Check, is there any timeout happenned to last transmitted packet */
+ if ((jiffies - fmdev->last_tx_jiffies) > FM_DRV_TX_TIMEOUT) {
+ fmerr("TX timeout occurred\n");
+ atomic_set(&fmdev->tx_cnt, 1);
+ }
+
+ /* Send queued FM TX packets */
+ skb = skb_dequeue(&fmdev->tx_q);
+ if (!skb)
+ return;
+
+ atomic_dec(&fmdev->tx_cnt);
+ fmdev->pre_op = fm_cb(skb)->fm_op;
+
+ if (fmdev->resp_comp != NULL)
+ fmerr("Response completion handler is not NULL\n");
+
+ fmdev->resp_comp = fm_cb(skb)->completion;
+
+ /* Write FM packet to ST driver */
+ len = g_st_write(skb);
+ if (len < 0) {
+ kfree_skb(skb);
+ fmdev->resp_comp = NULL;
+ fmerr("TX tasklet failed to send skb(%p)\n", skb);
+ atomic_set(&fmdev->tx_cnt, 1);
+ } else {
+ fmdev->last_tx_jiffies = jiffies;
+ }
+}
+
+/*
+ * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for
+ * transmission
+ */
+static u32 fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
+ int payload_len, struct completion *wait_completion)
+{
+ struct sk_buff *skb;
+ struct fm_cmd_msg_hdr *hdr;
+ int size;
+
+ if (fm_op >= FM_INTERRUPT) {
+ fmerr("Invalid fm opcode - %d\n", fm_op);
+ return -EINVAL;
+ }
+ if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) {
+ fmerr("Payload data is NULL during fw download\n");
+ return -EINVAL;
+ }
+ if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag))
+ size =
+ FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len);
+ else
+ size = payload_len;
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ fmerr("No memory to create new SKB\n");
+ return -ENOMEM;
+ }
+ /*
+ * Don't fill FM header info for the commands which come from
+ * FM firmware file.
+ */
+ if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) ||
+ test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
+ /* Fill command header info */
+ hdr = (struct fm_cmd_msg_hdr *)skb_put(skb, FM_CMD_MSG_HDR_SIZE);
+ hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */
+
+ /* 3 (fm_opcode,rd_wr,dlen) + payload len) */
+ hdr->len = ((payload == NULL) ? 0 : payload_len) + 3;
+
+ /* FM opcode */
+ hdr->op = fm_op;
+
+ /* read/write type */
+ hdr->rd_wr = type;
+ hdr->dlen = payload_len;
+ fm_cb(skb)->fm_op = fm_op;
+
+ /*
+ * If firmware download has finished and the command is
+ * not a read command then payload is != NULL - a write
+ * command with u16 payload - convert to be16
+ */
+ if (payload != NULL)
+ *(u16 *)payload = cpu_to_be16(*(u16 *)payload);
+
+ } else if (payload != NULL) {
+ fm_cb(skb)->fm_op = *((u8 *)payload + 2);
+ }
+ if (payload != NULL)
+ memcpy(skb_put(skb, payload_len), payload, payload_len);
+
+ fm_cb(skb)->completion = wait_completion;
+ skb_queue_tail(&fmdev->tx_q, skb);
+ tasklet_schedule(&fmdev->tx_task);
+
+ return 0;
+}
+
+/* Sends FM Channel-8 command to the chip and waits for the response */
+u32 fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
+ unsigned int payload_len, void *response, int *response_len)
+{
+ struct sk_buff *skb;
+ struct fm_event_msg_hdr *evt_hdr;
+ unsigned long flags;
+ u32 ret;
+
+ init_completion(&fmdev->maintask_comp);
+ ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len,
+ &fmdev->maintask_comp);
+ if (ret)
+ return ret;
+
+ ret = wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT);
+ if (!ret) {
+ fmerr("Timeout(%d sec),didn't get reg"
+ "completion signal from RX tasklet\n",
+ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
+ return -ETIMEDOUT;
+ }
+ if (!fmdev->resp_skb) {
+ fmerr("Reponse SKB is missing\n");
+ return -EFAULT;
+ }
+ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
+ skb = fmdev->resp_skb;
+ fmdev->resp_skb = NULL;
+ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
+
+ evt_hdr = (void *)skb->data;
+ if (evt_hdr->status != 0) {
+ fmerr("Received event pkt status(%d) is not zero\n",
+ evt_hdr->status);
+ kfree_skb(skb);
+ return -EIO;
+ }
+ /* Send response data to caller */
+ if (response != NULL && response_len != NULL && evt_hdr->dlen) {
+ /* Skip header info and copy only response data */
+ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
+ memcpy(response, skb->data, evt_hdr->dlen);
+ *response_len = evt_hdr->dlen;
+ } else if (response_len != NULL && evt_hdr->dlen == 0) {
+ *response_len = 0;
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+/* --- Helper functions used in FM interrupt handlers ---*/
+static inline u32 check_cmdresp_status(struct fmdev *fmdev,
+ struct sk_buff **skb)
+{
+ struct fm_event_msg_hdr *fm_evt_hdr;
+ unsigned long flags;
+
+ del_timer(&fmdev->irq_info.timer);
+
+ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
+ *skb = fmdev->resp_skb;
+ fmdev->resp_skb = NULL;
+ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
+
+ fm_evt_hdr = (void *)(*skb)->data;
+ if (fm_evt_hdr->status != 0) {
+ fmerr("irq: opcode %x response status is not zero "
+ "Initiating irq recovery process\n",
+ fm_evt_hdr->op);
+
+ mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage)
+{
+ struct sk_buff *skb;
+
+ if (!check_cmdresp_status(fmdev, &skb))
+ fm_irq_call_stage(fmdev, stage);
+}
+
+/*
+ * Interrupt process timeout handler.
+ * One of the irq handler did not get proper response from the chip. So take
+ * recovery action here. FM interrupts are disabled in the beginning of
+ * interrupt process. Therefore reset stage index to re-enable default
+ * interrupts. So that next interrupt will be processed as usual.
+ */
+static void int_timeout_handler(unsigned long data)
+{
+ struct fmdev *fmdev;
+ struct fm_irq *fmirq;
+
+ fmdbg("irq: timeout,trying to re-enable fm interrupts\n");
+ fmdev = (struct fmdev *)data;
+ fmirq = &fmdev->irq_info;
+ fmirq->retry++;
+
+ if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) {
+ /* Stop recovery action (interrupt reenable process) and
+ * reset stage index & retry count values */
+ fmirq->stage = 0;
+ fmirq->retry = 0;
+ fmerr("Recovery action failed during"
+ "irq processing, max retry reached\n");
+ return;
+ }
+ fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
+}
+
+/* --------- FM interrupt handlers ------------*/
+static void fm_irq_send_flag_getcmd(struct fmdev *fmdev)
+{
+ u16 flag;
+
+ /* Send FLAG_GET command , to know the source of interrupt */
+ if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL))
+ fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX);
+}
+
+static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev)
+{
+ struct sk_buff *skb;
+ struct fm_event_msg_hdr *fm_evt_hdr;
+
+ if (check_cmdresp_status(fmdev, &skb))
+ return;
+
+ fm_evt_hdr = (void *)skb->data;
+
+ /* Skip header info and copy only response data */
+ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
+ memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen);
+
+ fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag);
+ fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag);
+
+ /* Continue next function in interrupt handler table */
+ fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX);
+}
+
+static void fm_irq_handle_hw_malfunction(struct fmdev *fmdev)
+{
+ if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask)
+ fmerr("irq: HW MAL int received - do nothing\n");
+
+ /* Continue next function in interrupt handler table */
+ fm_irq_call_stage(fmdev, FM_RDS_START_IDX);
+}
+
+static void fm_irq_handle_rds_start(struct fmdev *fmdev)
+{
+ if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) {
+ fmdbg("irq: rds threshold reached\n");
+ fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX;
+ } else {
+ /* Continue next function in interrupt handler table */
+ fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX;
+ }
+
+ fm_irq_call(fmdev);
+}
+
+static void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev)
+{
+ /* Send the command to read RDS data from the chip */
+ if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL,
+ (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL))
+ fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX);
+}
+
+/* Keeps track of current RX channel AF (Alternate Frequency) */
+static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af)
+{
+ struct tuned_station_info *stat_info = &fmdev->rx.stat_info;
+ u8 reg_idx = fmdev->rx.region.fm_band;
+ u8 index;
+ u32 freq;
+
+ /* First AF indicates the number of AF follows. Reset the list */
+ if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) {
+ fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1);
+ fmdev->rx.stat_info.afcache_size = 0;
+ fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max);
+ return;
+ }
+
+ if (af < FM_RDS_MIN_AF)
+ return;
+ if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF)
+ return;
+ if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN)
+ return;
+
+ freq = fmdev->rx.region.bot_freq + (af * 100);
+ if (freq == fmdev->rx.freq) {
+ fmdbg("Current freq(%d) is matching with received AF(%d)\n",
+ fmdev->rx.freq, freq);
+ return;
+ }
+ /* Do check in AF cache */
+ for (index = 0; index < stat_info->afcache_size; index++) {
+ if (stat_info->af_cache[index] == freq)
+ break;
+ }
+ /* Reached the limit of the list - ignore the next AF */
+ if (index == stat_info->af_list_max) {
+ fmdbg("AF cache is full\n");
+ return;
+ }
+ /*
+ * If we reached the end of the list then this AF is not
+ * in the list - add it.
+ */
+ if (index == stat_info->afcache_size) {
+ fmdbg("Storing AF %d to cache index %d\n", freq, index);
+ stat_info->af_cache[index] = freq;
+ stat_info->afcache_size++;
+ }
+}
+
+/*
+ * Converts RDS buffer data from big endian format
+ * to little endian format.
+ */
+static void fm_rdsparse_swapbytes(struct fmdev *fmdev,
+ struct fm_rdsdata_format *rds_format)
+{
+ u8 byte1;
+ u8 index = 0;
+ u8 *rds_buff;
+
+ /*
+ * Since in Orca the 2 RDS Data bytes are in little endian and
+ * in Dolphin they are in big endian, the parsing of the RDS data
+ * is chip dependent
+ */
+ if (fmdev->asci_id != 0x6350) {
+ rds_buff = &rds_format->data.groupdatabuff.buff[0];
+ while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) {
+ byte1 = rds_buff[index];
+ rds_buff[index] = rds_buff[index + 1];
+ rds_buff[index + 1] = byte1;
+ index += 2;
+ }
+ }
+}
+
+static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev)
+{
+ struct sk_buff *skb;
+ struct fm_rdsdata_format rds_fmt;
+ struct fm_rds *rds = &fmdev->rx.rds;
+ unsigned long group_idx, flags;
+ u8 *rds_data, meta_data, tmpbuf[3];
+ u8 type, blk_idx;
+ u16 cur_picode;
+ u32 rds_len;
+
+ if (check_cmdresp_status(fmdev, &skb))
+ return;
+
+ /* Skip header info */
+ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
+ rds_data = skb->data;
+ rds_len = skb->len;
+
+ /* Parse the RDS data */
+ while (rds_len >= FM_RDS_BLK_SIZE) {
+ meta_data = rds_data[2];
+ /* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */
+ type = (meta_data & 0x07);
+
+ /* Transform the blk type into index sequence (0, 1, 2, 3, 4) */
+ blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
+ fmdbg("Block index:%d(%s)\n", blk_idx,
+ (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok");
+
+ if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0)
+ break;
+
+ if (blk_idx < FM_RDS_BLK_IDX_A || blk_idx > FM_RDS_BLK_IDX_D) {
+ fmdbg("Block sequence mismatch\n");
+ rds->last_blk_idx = -1;
+ break;
+ }
+
+ /* Skip checkword (control) byte and copy only data byte */
+ memcpy(&rds_fmt.data.groupdatabuff.
+ buff[blk_idx * (FM_RDS_BLK_SIZE - 1)],
+ rds_data, (FM_RDS_BLK_SIZE - 1));
+
+ rds->last_blk_idx = blk_idx;
+
+ /* If completed a whole group then handle it */
+ if (blk_idx == FM_RDS_BLK_IDX_D) {
+ fmdbg("Good block received\n");
+ fm_rdsparse_swapbytes(fmdev, &rds_fmt);
+
+ /*
+ * Extract PI code and store in local cache.
+ * We need this during AF switch processing.
+ */
+ cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata);
+ if (fmdev->rx.stat_info.picode != cur_picode)
+ fmdev->rx.stat_info.picode = cur_picode;
+
+ fmdbg("picode:%d\n", cur_picode);
+
+ group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
+ fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2,
+ (group_idx % 2) ? "B" : "A");
+
+ group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
+ if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) {
+ fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]);
+ fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]);
+ }
+ }
+ rds_len -= FM_RDS_BLK_SIZE;
+ rds_data += FM_RDS_BLK_SIZE;
+ }
+
+ /* Copy raw rds data to internal rds buffer */
+ rds_data = skb->data;
+ rds_len = skb->len;
+
+ spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
+ while (rds_len > 0) {
+ /*
+ * Fill RDS buffer as per V4L2 specification.
+ * Store control byte
+ */
+ type = (rds_data[2] & 0x07);
+ blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
+ tmpbuf[2] = blk_idx; /* Offset name */
+ tmpbuf[2] |= blk_idx << 3; /* Received offset */
+
+ /* Store data byte */
+ tmpbuf[0] = rds_data[0];
+ tmpbuf[1] = rds_data[1];
+
+ memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE);
+ rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size;
+
+ /* Check for overflow & start over */
+ if (rds->wr_idx == rds->rd_idx) {
+ fmdbg("RDS buffer overflow\n");
+ rds->wr_idx = 0;
+ rds->rd_idx = 0;
+ break;
+ }
+ rds_len -= FM_RDS_BLK_SIZE;
+ rds_data += FM_RDS_BLK_SIZE;
+ }
+ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
+
+ /* Wakeup read queue */
+ if (rds->wr_idx != rds->rd_idx)
+ wake_up_interruptible(&rds->read_queue);
+
+ fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX);
+}
+
+static void fm_irq_handle_rds_finish(struct fmdev *fmdev)
+{
+ fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX);
+}
+
+static void fm_irq_handle_tune_op_ended(struct fmdev *fmdev)
+{
+ if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev->
+ irq_info.mask) {
+ fmdbg("irq: tune ended/bandlimit reached\n");
+ if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) {
+ fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX;
+ } else {
+ complete(&fmdev->maintask_comp);
+ fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
+ }
+ } else
+ fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
+
+ fm_irq_call(fmdev);
+}
+
+static void fm_irq_handle_power_enb(struct fmdev *fmdev)
+{
+ if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) {
+ fmdbg("irq: Power Enabled/Disabled\n");
+ complete(&fmdev->maintask_comp);
+ }
+
+ fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX);
+}
+
+static void fm_irq_handle_low_rssi_start(struct fmdev *fmdev)
+{
+ if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) &&
+ (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) &&
+ (fmdev->rx.freq != FM_UNDEFINED_FREQ) &&
+ (fmdev->rx.stat_info.afcache_size != 0)) {
+ fmdbg("irq: rssi level has fallen below threshold level\n");
+
+ /* Disable further low RSSI interrupts */
+ fmdev->irq_info.mask &= ~FM_LEV_EVENT;
+
+ fmdev->rx.afjump_idx = 0;
+ fmdev->rx.freq_before_jump = fmdev->rx.freq;
+ fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
+ } else {
+ /* Continue next function in interrupt handler table */
+ fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX;
+ }
+
+ fm_irq_call(fmdev);
+}
+
+static void fm_irq_afjump_set_pi(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ /* Set PI code - must be updated if the AF list is not empty */
+ payload = fmdev->rx.stat_info.picode;
+ if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX);
+}
+
+static void fm_irq_handle_set_pi_resp(struct fmdev *fmdev)
+{
+ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX);
+}
+
+/*
+ * Set PI mask.
+ * 0xFFFF = Enable PI code matching
+ * 0x0000 = Disable PI code matching
+ */
+static void fm_irq_afjump_set_pimask(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ payload = 0x0000;
+ if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX);
+}
+
+static void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev)
+{
+ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX);
+}
+
+static void fm_irq_afjump_setfreq(struct fmdev *fmdev)
+{
+ u16 frq_index;
+ u16 payload;
+
+ fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]);
+ frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] -
+ fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
+
+ payload = frq_index;
+ if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX);
+}
+
+static void fm_irq_handle_setfreq_resp(struct fmdev *fmdev)
+{
+ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX);
+}
+
+static void fm_irq_afjump_enableint(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ /* Enable FR (tuning operation ended) interrupt */
+ payload = FM_FR_EVENT;
+ if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX);
+}
+
+static void fm_irq_afjump_enableint_resp(struct fmdev *fmdev)
+{
+ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX);
+}
+
+static void fm_irq_start_afjump(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ payload = FM_TUNER_AF_JUMP_MODE;
+ if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
+ sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX);
+}
+
+static void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev)
+{
+ struct sk_buff *skb;
+
+ if (check_cmdresp_status(fmdev, &skb))
+ return;
+
+ fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
+ set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag);
+ clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
+}
+
+static void fm_irq_afjump_rd_freq(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX);
+}
+
+static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev)
+{
+ struct sk_buff *skb;
+ u16 read_freq;
+ u32 curr_freq, jumped_freq;
+
+ if (check_cmdresp_status(fmdev, &skb))
+ return;
+
+ /* Skip header info and copy only response data */
+ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
+ memcpy(&read_freq, skb->data, sizeof(read_freq));
+ read_freq = be16_to_cpu(read_freq);
+ curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL);
+
+ jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx];
+
+ /* If the frequency was changed the jump succeeded */
+ if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) {
+ fmdbg("Successfully switched to alternate freq %d\n", curr_freq);
+ fmdev->rx.freq = curr_freq;
+ fm_rx_reset_rds_cache(fmdev);
+
+ /* AF feature is on, enable low level RSSI interrupt */
+ if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
+ fmdev->irq_info.mask |= FM_LEV_EVENT;
+
+ fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
+ } else { /* jump to the next freq in the AF list */
+ fmdev->rx.afjump_idx++;
+
+ /* If we reached the end of the list - stop searching */
+ if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) {
+ fmdbg("AF switch processing failed\n");
+ fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
+ } else { /* AF List is not over - try next one */
+
+ fmdbg("Trying next freq in AF cache\n");
+ fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
+ }
+ }
+ fm_irq_call(fmdev);
+}
+
+static void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev)
+{
+ fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
+}
+
+static void fm_irq_send_intmsk_cmd(struct fmdev *fmdev)
+{
+ u16 payload;
+
+ /* Re-enable FM interrupts */
+ payload = fmdev->irq_info.mask;
+
+ if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL))
+ fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX);
+}
+
+static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev)
+{
+ struct sk_buff *skb;
+
+ if (check_cmdresp_status(fmdev, &skb))
+ return;
+ /*
+ * This is last function in interrupt table to be executed.
+ * So, reset stage index to 0.
+ */
+ fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
+
+ /* Start processing any pending interrupt */
+ if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag))
+ fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
+ else
+ clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
+}
+
+/* Returns availability of RDS data in internel buffer */
+u32 fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file,
+ struct poll_table_struct *pts)
+{
+ poll_wait(file, &fmdev->rx.rds.read_queue, pts);
+ if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx)
+ return 0;
+
+ return -EAGAIN;
+}
+
+/* Copies RDS data from internal buffer to user buffer */
+u32 fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file,
+ u8 __user *buf, size_t count)
+{
+ u32 block_count;
+ unsigned long flags;
+ int ret;
+
+ if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ ret = wait_event_interruptible(fmdev->rx.rds.read_queue,
+ (fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx));
+ if (ret)
+ return -EINTR;
+ }
+
+ /* Calculate block count from byte count */
+ count /= 3;
+ block_count = 0;
+ ret = 0;
+
+ spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
+
+ while (block_count < count) {
+ if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx)
+ break;
+
+ if (copy_to_user(buf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx],
+ FM_RDS_BLK_SIZE))
+ break;
+
+ fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE;
+ if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size)
+ fmdev->rx.rds.rd_idx = 0;
+
+ block_count++;
+ buf += FM_RDS_BLK_SIZE;
+ ret += FM_RDS_BLK_SIZE;
+ }
+ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
+ return ret;
+}
+
+u32 fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set)
+{
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ return fm_rx_set_freq(fmdev, freq_to_set);
+
+ case FM_MODE_TX:
+ return fm_tx_set_freq(fmdev, freq_to_set);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+u32 fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq)
+{
+ if (fmdev->rx.freq == FM_UNDEFINED_FREQ) {
+ fmerr("RX frequency is not set\n");
+ return -EPERM;
+ }
+ if (cur_tuned_frq == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ *cur_tuned_frq = fmdev->rx.freq;
+ return 0;
+
+ case FM_MODE_TX:
+ *cur_tuned_frq = 0; /* TODO : Change this later */
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+}
+
+u32 fmc_set_region(struct fmdev *fmdev, u8 region_to_set)
+{
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ return fm_rx_set_region(fmdev, region_to_set);
+
+ case FM_MODE_TX:
+ return fm_tx_set_region(fmdev, region_to_set);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+u32 fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
+{
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ return fm_rx_set_mute_mode(fmdev, mute_mode_toset);
+
+ case FM_MODE_TX:
+ return fm_tx_set_mute_mode(fmdev, mute_mode_toset);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+u32 fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode)
+{
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ return fm_rx_set_stereo_mono(fmdev, mode);
+
+ case FM_MODE_TX:
+ return fm_tx_set_stereo_mono(fmdev, mode);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+u32 fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
+{
+ switch (fmdev->curr_fmmode) {
+ case FM_MODE_RX:
+ return fm_rx_set_rds_mode(fmdev, rds_en_dis);
+
+ case FM_MODE_TX:
+ return fm_tx_set_rds_mode(fmdev, rds_en_dis);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Sends power off command to the chip */
+static u32 fm_power_down(struct fmdev *fmdev)
+{
+ u16 payload;
+ u32 ret;
+
+ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
+ fmerr("FM core is not ready\n");
+ return -EPERM;
+ }
+ if (fmdev->curr_fmmode == FM_MODE_OFF) {
+ fmdbg("FM chip is already in OFF state\n");
+ return 0;
+ }
+
+ payload = 0x0;
+ ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return fmc_release(fmdev);
+}
+
+/* Reads init command from FM firmware file and loads to the chip */
+static u32 fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name)
+{
+ const struct firmware *fw_entry;
+ struct bts_header *fw_header;
+ struct bts_action *action;
+ struct bts_action_delay *delay;
+ u8 *fw_data;
+ int ret, fw_len, cmd_cnt;
+
+ cmd_cnt = 0;
+ set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
+
+ ret = request_firmware(&fw_entry, fw_name,
+ &fmdev->radio_dev->dev);
+ if (ret < 0) {
+ fmerr("Unable to read firmware(%s) content\n", fw_name);
+ return ret;
+ }
+ fmdbg("Firmware(%s) length : %d bytes\n", fw_name, fw_entry->size);
+
+ fw_data = (void *)fw_entry->data;
+ fw_len = fw_entry->size;
+
+ fw_header = (struct bts_header *)fw_data;
+ if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) {
+ fmerr("%s not a legal TI firmware file\n", fw_name);
+ ret = -EINVAL;
+ goto rel_fw;
+ }
+ fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic);
+
+ /* Skip file header info , we already verified it */
+ fw_data += sizeof(struct bts_header);
+ fw_len -= sizeof(struct bts_header);
+
+ while (fw_data && fw_len > 0) {
+ action = (struct bts_action *)fw_data;
+
+ switch (action->type) {
+ case ACTION_SEND_COMMAND: /* Send */
+ if (fmc_send_cmd(fmdev, 0, 0, action->data,
+ action->size, NULL, NULL))
+ goto rel_fw;
+
+ cmd_cnt++;
+ break;
+
+ case ACTION_DELAY: /* Delay */
+ delay = (struct bts_action_delay *)action->data;
+ mdelay(delay->msec);
+ break;
+ }
+
+ fw_data += (sizeof(struct bts_action) + (action->size));
+ fw_len -= (sizeof(struct bts_action) + (action->size));
+ }
+ fmdbg("Firmware commands(%d) loaded to chip\n", cmd_cnt);
+rel_fw:
+ release_firmware(fw_entry);
+ clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
+
+ return ret;
+}
+
+/* Loads default RX configuration to the chip */
+static u32 load_default_rx_configuration(struct fmdev *fmdev)
+{
+ int ret;
+
+ ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME);
+ if (ret < 0)
+ return ret;
+
+ return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD);
+}
+
+/* Does FM power on sequence */
+static u32 fm_power_up(struct fmdev *fmdev, u8 mode)
+{
+ u16 payload, asic_id, asic_ver;
+ int resp_len, ret;
+ u8 fw_name[50];
+
+ if (mode >= FM_MODE_ENTRY_MAX) {
+ fmerr("Invalid firmware download option\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Initialize FM common module. FM GPIO toggling is
+ * taken care in Shared Transport driver.
+ */
+ ret = fmc_prepare(fmdev);
+ if (ret < 0) {
+ fmerr("Unable to prepare FM Common\n");
+ return ret;
+ }
+
+ payload = FM_ENABLE;
+ if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
+ sizeof(payload), NULL, NULL))
+ goto rel;
+
+ /* Allow the chip to settle down in Channel-8 mode */
+ msleep(20);
+
+ if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL,
+ sizeof(asic_id), &asic_id, &resp_len))
+ goto rel;
+
+ if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL,
+ sizeof(asic_ver), &asic_ver, &resp_len))
+ goto rel;
+
+ fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n",
+ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
+
+ sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START,
+ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
+
+ ret = fm_download_firmware(fmdev, fw_name);
+ if (ret < 0) {
+ fmdbg("Failed to download firmware file %s\n", fw_name);
+ goto rel;
+ }
+ sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ?
+ FM_RX_FW_FILE_START : FM_TX_FW_FILE_START,
+ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
+
+ ret = fm_download_firmware(fmdev, fw_name);
+ if (ret < 0) {
+ fmdbg("Failed to download firmware file %s\n", fw_name);
+ goto rel;
+ } else
+ return ret;
+rel:
+ return fmc_release(fmdev);
+}
+
+/* Set FM Modes(TX, RX, OFF) */
+u32 fmc_set_mode(struct fmdev *fmdev, u8 fm_mode)
+{
+ int ret = 0;
+
+ if (fm_mode >= FM_MODE_ENTRY_MAX) {
+ fmerr("Invalid FM mode\n");
+ return -EINVAL;
+ }
+ if (fmdev->curr_fmmode == fm_mode) {
+ fmdbg("Already fm is in mode(%d)\n", fm_mode);
+ return ret;
+ }
+
+ switch (fm_mode) {
+ case FM_MODE_OFF: /* OFF Mode */
+ ret = fm_power_down(fmdev);
+ if (ret < 0) {
+ fmerr("Failed to set OFF mode\n");
+ return ret;
+ }
+ break;
+
+ case FM_MODE_TX: /* TX Mode */
+ case FM_MODE_RX: /* RX Mode */
+ /* Power down before switching to TX or RX mode */
+ if (fmdev->curr_fmmode != FM_MODE_OFF) {
+ ret = fm_power_down(fmdev);
+ if (ret < 0) {
+ fmerr("Failed to set OFF mode\n");
+ return ret;
+ }
+ msleep(30);
+ }
+ ret = fm_power_up(fmdev, fm_mode);
+ if (ret < 0) {
+ fmerr("Failed to load firmware\n");
+ return ret;
+ }
+ }
+ fmdev->curr_fmmode = fm_mode;
+
+ /* Set default configuration */
+ if (fmdev->curr_fmmode == FM_MODE_RX) {
+ fmdbg("Loading default rx configuration..\n");
+ ret = load_default_rx_configuration(fmdev);
+ if (ret < 0)
+ fmerr("Failed to load default values\n");
+ }
+
+ return ret;
+}
+
+/* Returns current FM mode (TX, RX, OFF) */
+u32 fmc_get_mode(struct fmdev *fmdev, u8 *fmmode)
+{
+ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
+ fmerr("FM core is not ready\n");
+ return -EPERM;
+ }
+ if (fmmode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *fmmode = fmdev->curr_fmmode;
+ return 0;
+}
+
+/* Called by ST layer when FM packet is available */
+static long fm_st_receive(void *arg, struct sk_buff *skb)
+{
+ struct fmdev *fmdev;
+
+ fmdev = (struct fmdev *)arg;
+
+ if (skb == NULL) {
+ fmerr("Invalid SKB received from ST\n");
+ return -EFAULT;
+ }
+
+ if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) {
+ fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb);
+ return -EINVAL;
+ }
+
+ memcpy(skb_push(skb, 1), &skb->cb[0], 1);
+ skb_queue_tail(&fmdev->rx_q, skb);
+ tasklet_schedule(&fmdev->rx_task);
+
+ return 0;
+}
+
+/*
+ * Called by ST layer to indicate protocol registration completion
+ * status.
+ */
+static void fm_st_reg_comp_cb(void *arg, char data)
+{
+ struct fmdev *fmdev;
+
+ fmdev = (struct fmdev *)arg;
+ fmdev->streg_cbdata = data;
+ complete(&wait_for_fmdrv_reg_comp);
+}
+
+/*
+ * This function will be called from FM V4L2 open function.
+ * Register with ST driver and initialize driver data.
+ */
+u32 fmc_prepare(struct fmdev *fmdev)
+{
+ static struct st_proto_s fm_st_proto;
+ u32 ret;
+
+ if (test_bit(FM_CORE_READY, &fmdev->flag)) {
+ fmdbg("FM Core is already up\n");
+ return 0;
+ }
+
+ memset(&fm_st_proto, 0, sizeof(fm_st_proto));
+ fm_st_proto.type = ST_FM;
+ fm_st_proto.recv = fm_st_receive;
+ fm_st_proto.match_packet = NULL;
+ fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb;
+ fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */
+ fm_st_proto.priv_data = fmdev;
+
+ ret = st_register(&fm_st_proto);
+ if (ret == -EINPROGRESS) {
+ init_completion(&wait_for_fmdrv_reg_comp);
+ fmdev->streg_cbdata = -EINPROGRESS;
+ fmdbg("%s waiting for ST reg completion signal\n", __func__);
+
+ ret = wait_for_completion_timeout(&wait_for_fmdrv_reg_comp,
+ FM_ST_REG_TIMEOUT);
+
+ if (!ret) {
+ fmerr("Timeout(%d sec), didn't get reg "
+ "completion signal from ST\n",
+ jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000);
+ return -ETIMEDOUT;
+ }
+ if (fmdev->streg_cbdata != 0) {
+ fmerr("ST reg comp CB called with error "
+ "status %d\n", fmdev->streg_cbdata);
+ return -EAGAIN;
+ }
+
+ ret = 0;
+ } else if (ret == -1) {
+ fmerr("st_register failed %d\n", ret);
+ return -EAGAIN;
+ }
+
+ if (fm_st_proto.write != NULL) {
+ g_st_write = fm_st_proto.write;
+ } else {
+ fmerr("Failed to get ST write func pointer\n");
+ ret = st_unregister(ST_FM);
+ if (ret < 0)
+ fmerr("st_unregister failed %d\n", ret);
+ return -EAGAIN;
+ }
+
+ spin_lock_init(&fmdev->rds_buff_lock);
+ spin_lock_init(&fmdev->resp_skb_lock);
+
+ /* Initialize TX queue and TX tasklet */
+ skb_queue_head_init(&fmdev->tx_q);
+ tasklet_init(&fmdev->tx_task, send_tasklet, (unsigned long)fmdev);
+
+ /* Initialize RX Queue and RX tasklet */
+ skb_queue_head_init(&fmdev->rx_q);
+ tasklet_init(&fmdev->rx_task, recv_tasklet, (unsigned long)fmdev);
+
+ fmdev->irq_info.stage = 0;
+ atomic_set(&fmdev->tx_cnt, 1);
+ fmdev->resp_comp = NULL;
+
+ init_timer(&fmdev->irq_info.timer);
+ fmdev->irq_info.timer.function = &int_timeout_handler;
+ fmdev->irq_info.timer.data = (unsigned long)fmdev;
+ /*TODO: add FM_STIC_EVENT later */
+ fmdev->irq_info.mask = FM_MAL_EVENT;
+
+ /* Region info */
+ memcpy(&fmdev->rx.region, &region_configs[default_radio_region],
+ sizeof(struct region_info));
+
+ fmdev->rx.mute_mode = FM_MUTE_OFF;
+ fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF;
+ fmdev->rx.rds.flag = FM_RDS_DISABLE;
+ fmdev->rx.freq = FM_UNDEFINED_FREQ;
+ fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS;
+ fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF;
+ fmdev->irq_info.retry = 0;
+
+ fm_rx_reset_rds_cache(fmdev);
+ init_waitqueue_head(&fmdev->rx.rds.read_queue);
+
+ fm_rx_reset_station_info(fmdev);
+ set_bit(FM_CORE_READY, &fmdev->flag);
+
+ return ret;
+}
+
+/*
+ * This function will be called from FM V4L2 release function.
+ * Unregister from ST driver.
+ */
+u32 fmc_release(struct fmdev *fmdev)
+{
+ u32 ret;
+
+ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
+ fmdbg("FM Core is already down\n");
+ return 0;
+ }
+ /* Sevice pending read */
+ wake_up_interruptible(&fmdev->rx.rds.read_queue);
+
+ tasklet_kill(&fmdev->tx_task);
+ tasklet_kill(&fmdev->rx_task);
+
+ skb_queue_purge(&fmdev->tx_q);
+ skb_queue_purge(&fmdev->rx_q);
+
+ fmdev->resp_comp = NULL;
+ fmdev->rx.freq = 0;
+
+ ret = st_unregister(ST_FM);
+ if (ret < 0)
+ fmerr("Failed to de-register FM from ST %d\n", ret);
+ else
+ fmdbg("Successfully unregistered from ST\n");
+
+ clear_bit(FM_CORE_READY, &fmdev->flag);
+ return ret;
+}
+
+/*
+ * Module init function. Ask FM V4L module to register video device.
+ * Allocate memory for FM driver context and RX RDS buffer.
+ */
+static int __init fm_drv_init(void)
+{
+ struct fmdev *fmdev = NULL;
+ u32 ret = -ENOMEM;
+
+ fmdbg("FM driver version %s\n", FM_DRV_VERSION);
+
+ fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL);
+ if (NULL == fmdev) {
+ fmerr("Can't allocate operation structure memory\n");
+ return ret;
+ }
+ fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE;
+ fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL);
+ if (NULL == fmdev->rx.rds.buff) {
+ fmerr("Can't allocate rds ring buffer\n");
+ goto rel_dev;
+ }
+
+ ret = fm_v4l2_init_video_device(fmdev, radio_nr);
+ if (ret < 0)
+ goto rel_rdsbuf;
+
+ fmdev->irq_info.handlers = int_handler_table;
+ fmdev->curr_fmmode = FM_MODE_OFF;
+ fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF;
+ fmdev->tx_data.preemph = FM_TX_PREEMPH_50US;
+ return ret;
+
+rel_rdsbuf:
+ kfree(fmdev->rx.rds.buff);
+rel_dev:
+ kfree(fmdev);
+
+ return ret;
+}
+
+/* Module exit function. Ask FM V4L module to unregister video device */
+static void __exit fm_drv_exit(void)
+{
+ struct fmdev *fmdev = NULL;
+
+ fmdev = fm_v4l2_deinit_video_device();
+ if (fmdev != NULL) {
+ kfree(fmdev->rx.rds.buff);
+ kfree(fmdev);
+ }
+}
+
+module_init(fm_drv_init);
+module_exit(fm_drv_exit);
+
+/* ------------- Module Info ------------- */
+MODULE_AUTHOR("Manjunatha Halli <manjunatha_halli@ti.com>");
+MODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION);
+MODULE_VERSION(FM_DRV_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/wl128x/fmdrv_common.h b/drivers/media/radio/wl128x/fmdrv_common.h
new file mode 100644
index 000000000000..427c4164cece
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_common.h
@@ -0,0 +1,402 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * FM Common module header file
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _FMDRV_COMMON_H
+#define _FMDRV_COMMON_H
+
+#define FM_ST_REG_TIMEOUT msecs_to_jiffies(6000) /* 6 sec */
+#define FM_PKT_LOGICAL_CHAN_NUMBER 0x08 /* Logical channel 8 */
+
+#define REG_RD 0x1
+#define REG_WR 0x0
+
+struct fm_reg_table {
+ u8 opcode;
+ u8 type;
+ u8 *name;
+};
+
+#define STEREO_GET 0
+#define RSSI_LVL_GET 1
+#define IF_COUNT_GET 2
+#define FLAG_GET 3
+#define RDS_SYNC_GET 4
+#define RDS_DATA_GET 5
+#define FREQ_SET 10
+#define AF_FREQ_SET 11
+#define MOST_MODE_SET 12
+#define MOST_BLEND_SET 13
+#define DEMPH_MODE_SET 14
+#define SEARCH_LVL_SET 15
+#define BAND_SET 16
+#define MUTE_STATUS_SET 17
+#define RDS_PAUSE_LVL_SET 18
+#define RDS_PAUSE_DUR_SET 19
+#define RDS_MEM_SET 20
+#define RDS_BLK_B_SET 21
+#define RDS_MSK_B_SET 22
+#define RDS_PI_MASK_SET 23
+#define RDS_PI_SET 24
+#define RDS_SYSTEM_SET 25
+#define INT_MASK_SET 26
+#define SEARCH_DIR_SET 27
+#define VOLUME_SET 28
+#define AUDIO_ENABLE_SET 29
+#define PCM_MODE_SET 30
+#define I2S_MODE_CONFIG_SET 31
+#define POWER_SET 32
+#define INTX_CONFIG_SET 33
+#define PULL_EN_SET 34
+#define HILO_SET 35
+#define SWITCH2FREF 36
+#define FREQ_DRIFT_REPORT 37
+
+#define PCE_GET 40
+#define FIRM_VER_GET 41
+#define ASIC_VER_GET 42
+#define ASIC_ID_GET 43
+#define MAN_ID_GET 44
+#define TUNER_MODE_SET 45
+#define STOP_SEARCH 46
+#define RDS_CNTRL_SET 47
+
+#define WRITE_HARDWARE_REG 100
+#define CODE_DOWNLOAD 101
+#define RESET 102
+
+#define FM_POWER_MODE 254
+#define FM_INTERRUPT 255
+
+/* Transmitter API */
+
+#define CHANL_SET 55
+#define CHANL_BW_SET 56
+#define REF_SET 57
+#define POWER_ENB_SET 90
+#define POWER_ATT_SET 58
+#define POWER_LEV_SET 59
+#define AUDIO_DEV_SET 60
+#define PILOT_DEV_SET 61
+#define RDS_DEV_SET 62
+#define TX_BAND_SET 65
+#define PUPD_SET 91
+#define AUDIO_IO_SET 63
+#define PREMPH_SET 64
+#define MONO_SET 66
+#define MUTE 92
+#define MPX_LMT_ENABLE 67
+#define PI_SET 93
+#define ECC_SET 69
+#define PTY 70
+#define AF 71
+#define DISPLAY_MODE 74
+#define RDS_REP_SET 77
+#define RDS_CONFIG_DATA_SET 98
+#define RDS_DATA_SET 99
+#define RDS_DATA_ENB 94
+#define TA_SET 78
+#define TP_SET 79
+#define DI_SET 80
+#define MS_SET 81
+#define PS_SCROLL_SPEED 82
+#define TX_AUDIO_LEVEL_TEST 96
+#define TX_AUDIO_LEVEL_TEST_THRESHOLD 73
+#define TX_AUDIO_INPUT_LEVEL_RANGE_SET 54
+#define RX_ANTENNA_SELECT 87
+#define I2C_DEV_ADDR_SET 86
+#define REF_ERR_CALIB_PARAM_SET 88
+#define REF_ERR_CALIB_PERIODICITY_SET 89
+#define SOC_INT_TRIGGER 52
+#define SOC_AUDIO_PATH_SET 83
+#define SOC_PCMI_OVERRIDE 84
+#define SOC_I2S_OVERRIDE 85
+#define RSSI_BLOCK_SCAN_FREQ_SET 95
+#define RSSI_BLOCK_SCAN_START 97
+#define RSSI_BLOCK_SCAN_DATA_GET 5
+#define READ_FMANT_TUNE_VALUE 104
+
+/* SKB helpers */
+struct fm_skb_cb {
+ __u8 fm_op;
+ struct completion *completion;
+};
+
+#define fm_cb(skb) ((struct fm_skb_cb *)(skb->cb))
+
+/* FM Channel-8 command message format */
+struct fm_cmd_msg_hdr {
+ __u8 hdr; /* Logical Channel-8 */
+ __u8 len; /* Number of bytes follows */
+ __u8 op; /* FM Opcode */
+ __u8 rd_wr; /* Read/Write command */
+ __u8 dlen; /* Length of payload */
+} __attribute__ ((packed));
+
+#define FM_CMD_MSG_HDR_SIZE 5 /* sizeof(struct fm_cmd_msg_hdr) */
+
+/* FM Channel-8 event messgage format */
+struct fm_event_msg_hdr {
+ __u8 header; /* Logical Channel-8 */
+ __u8 len; /* Number of bytes follows */
+ __u8 status; /* Event status */
+ __u8 num_fm_hci_cmds; /* Number of pkts the host allowed to send */
+ __u8 op; /* FM Opcode */
+ __u8 rd_wr; /* Read/Write command */
+ __u8 dlen; /* Length of payload */
+} __attribute__ ((packed));
+
+#define FM_EVT_MSG_HDR_SIZE 7 /* sizeof(struct fm_event_msg_hdr) */
+
+/* TI's magic number in firmware file */
+#define FM_FW_FILE_HEADER_MAGIC 0x42535442
+
+#define FM_ENABLE 1
+#define FM_DISABLE 0
+
+/* FLAG_GET register bits */
+#define FM_FR_EVENT (1 << 0)
+#define FM_BL_EVENT (1 << 1)
+#define FM_RDS_EVENT (1 << 2)
+#define FM_BBLK_EVENT (1 << 3)
+#define FM_LSYNC_EVENT (1 << 4)
+#define FM_LEV_EVENT (1 << 5)
+#define FM_IFFR_EVENT (1 << 6)
+#define FM_PI_EVENT (1 << 7)
+#define FM_PD_EVENT (1 << 8)
+#define FM_STIC_EVENT (1 << 9)
+#define FM_MAL_EVENT (1 << 10)
+#define FM_POW_ENB_EVENT (1 << 11)
+
+/*
+ * Firmware files of FM. ASIC ID and ASIC version will be appened to this,
+ * later.
+ */
+#define FM_FMC_FW_FILE_START ("fmc_ch8")
+#define FM_RX_FW_FILE_START ("fm_rx_ch8")
+#define FM_TX_FW_FILE_START ("fm_tx_ch8")
+
+#define FM_UNDEFINED_FREQ 0xFFFFFFFF
+
+/* Band types */
+#define FM_BAND_EUROPE_US 0
+#define FM_BAND_JAPAN 1
+
+/* Seek directions */
+#define FM_SEARCH_DIRECTION_DOWN 0
+#define FM_SEARCH_DIRECTION_UP 1
+
+/* Tunner modes */
+#define FM_TUNER_STOP_SEARCH_MODE 0
+#define FM_TUNER_PRESET_MODE 1
+#define FM_TUNER_AUTONOMOUS_SEARCH_MODE 2
+#define FM_TUNER_AF_JUMP_MODE 3
+
+/* Min and Max volume */
+#define FM_RX_VOLUME_MIN 0
+#define FM_RX_VOLUME_MAX 70
+
+/* Volume gain step */
+#define FM_RX_VOLUME_GAIN_STEP 0x370
+
+/* Mute modes */
+#define FM_MUTE_ON 0
+#define FM_MUTE_OFF 1
+#define FM_MUTE_ATTENUATE 2
+
+#define FM_RX_UNMUTE_MODE 0x00
+#define FM_RX_RF_DEP_MODE 0x01
+#define FM_RX_AC_MUTE_MODE 0x02
+#define FM_RX_HARD_MUTE_LEFT_MODE 0x04
+#define FM_RX_HARD_MUTE_RIGHT_MODE 0x08
+#define FM_RX_SOFT_MUTE_FORCE_MODE 0x10
+
+/* RF dependent mute mode */
+#define FM_RX_RF_DEPENDENT_MUTE_ON 1
+#define FM_RX_RF_DEPENDENT_MUTE_OFF 0
+
+/* RSSI threshold min and max */
+#define FM_RX_RSSI_THRESHOLD_MIN -128
+#define FM_RX_RSSI_THRESHOLD_MAX 127
+
+/* Stereo/Mono mode */
+#define FM_STEREO_MODE 0
+#define FM_MONO_MODE 1
+#define FM_STEREO_SOFT_BLEND 1
+
+/* FM RX De-emphasis filter modes */
+#define FM_RX_EMPHASIS_FILTER_50_USEC 0
+#define FM_RX_EMPHASIS_FILTER_75_USEC 1
+
+/* FM RDS modes */
+#define FM_RDS_DISABLE 0
+#define FM_RDS_ENABLE 1
+
+#define FM_NO_PI_CODE 0
+
+/* FM and RX RDS block enable/disable */
+#define FM_RX_PWR_SET_FM_ON_RDS_OFF 0x1
+#define FM_RX_PWR_SET_FM_AND_RDS_BLK_ON 0x3
+#define FM_RX_PWR_SET_FM_AND_RDS_BLK_OFF 0x0
+
+/* RX RDS */
+#define FM_RX_RDS_FLUSH_FIFO 0x1
+#define FM_RX_RDS_FIFO_THRESHOLD 64 /* tuples */
+#define FM_RDS_BLK_SIZE 3 /* 3 bytes */
+
+/* RDS block types */
+#define FM_RDS_BLOCK_A 0
+#define FM_RDS_BLOCK_B 1
+#define FM_RDS_BLOCK_C 2
+#define FM_RDS_BLOCK_Ctag 3
+#define FM_RDS_BLOCK_D 4
+#define FM_RDS_BLOCK_E 5
+
+#define FM_RDS_BLK_IDX_A 0
+#define FM_RDS_BLK_IDX_B 1
+#define FM_RDS_BLK_IDX_C 2
+#define FM_RDS_BLK_IDX_D 3
+#define FM_RDS_BLK_IDX_UNKNOWN 0xF0
+
+#define FM_RDS_STATUS_ERR_MASK 0x18
+
+/*
+ * Represents an RDS group type & version.
+ * There are 15 groups, each group has 2 versions: A and B.
+ */
+#define FM_RDS_GROUP_TYPE_MASK_0A ((unsigned long)1<<0)
+#define FM_RDS_GROUP_TYPE_MASK_0B ((unsigned long)1<<1)
+#define FM_RDS_GROUP_TYPE_MASK_1A ((unsigned long)1<<2)
+#define FM_RDS_GROUP_TYPE_MASK_1B ((unsigned long)1<<3)
+#define FM_RDS_GROUP_TYPE_MASK_2A ((unsigned long)1<<4)
+#define FM_RDS_GROUP_TYPE_MASK_2B ((unsigned long)1<<5)
+#define FM_RDS_GROUP_TYPE_MASK_3A ((unsigned long)1<<6)
+#define FM_RDS_GROUP_TYPE_MASK_3B ((unsigned long)1<<7)
+#define FM_RDS_GROUP_TYPE_MASK_4A ((unsigned long)1<<8)
+#define FM_RDS_GROUP_TYPE_MASK_4B ((unsigned long)1<<9)
+#define FM_RDS_GROUP_TYPE_MASK_5A ((unsigned long)1<<10)
+#define FM_RDS_GROUP_TYPE_MASK_5B ((unsigned long)1<<11)
+#define FM_RDS_GROUP_TYPE_MASK_6A ((unsigned long)1<<12)
+#define FM_RDS_GROUP_TYPE_MASK_6B ((unsigned long)1<<13)
+#define FM_RDS_GROUP_TYPE_MASK_7A ((unsigned long)1<<14)
+#define FM_RDS_GROUP_TYPE_MASK_7B ((unsigned long)1<<15)
+#define FM_RDS_GROUP_TYPE_MASK_8A ((unsigned long)1<<16)
+#define FM_RDS_GROUP_TYPE_MASK_8B ((unsigned long)1<<17)
+#define FM_RDS_GROUP_TYPE_MASK_9A ((unsigned long)1<<18)
+#define FM_RDS_GROUP_TYPE_MASK_9B ((unsigned long)1<<19)
+#define FM_RDS_GROUP_TYPE_MASK_10A ((unsigned long)1<<20)
+#define FM_RDS_GROUP_TYPE_MASK_10B ((unsigned long)1<<21)
+#define FM_RDS_GROUP_TYPE_MASK_11A ((unsigned long)1<<22)
+#define FM_RDS_GROUP_TYPE_MASK_11B ((unsigned long)1<<23)
+#define FM_RDS_GROUP_TYPE_MASK_12A ((unsigned long)1<<24)
+#define FM_RDS_GROUP_TYPE_MASK_12B ((unsigned long)1<<25)
+#define FM_RDS_GROUP_TYPE_MASK_13A ((unsigned long)1<<26)
+#define FM_RDS_GROUP_TYPE_MASK_13B ((unsigned long)1<<27)
+#define FM_RDS_GROUP_TYPE_MASK_14A ((unsigned long)1<<28)
+#define FM_RDS_GROUP_TYPE_MASK_14B ((unsigned long)1<<29)
+#define FM_RDS_GROUP_TYPE_MASK_15A ((unsigned long)1<<30)
+#define FM_RDS_GROUP_TYPE_MASK_15B ((unsigned long)1<<31)
+
+/* RX Alternate Frequency info */
+#define FM_RDS_MIN_AF 1
+#define FM_RDS_MAX_AF 204
+#define FM_RDS_MAX_AF_JAPAN 140
+#define FM_RDS_1_AF_FOLLOWS 225
+#define FM_RDS_25_AF_FOLLOWS 249
+
+/* RDS system type (RDS/RBDS) */
+#define FM_RDS_SYSTEM_RDS 0
+#define FM_RDS_SYSTEM_RBDS 1
+
+/* AF on/off */
+#define FM_RX_RDS_AF_SWITCH_MODE_ON 1
+#define FM_RX_RDS_AF_SWITCH_MODE_OFF 0
+
+/* Retry count when interrupt process goes wrong */
+#define FM_IRQ_TIMEOUT_RETRY_MAX 5 /* 5 times */
+
+/* Audio IO set values */
+#define FM_RX_AUDIO_ENABLE_I2S 0x01
+#define FM_RX_AUDIO_ENABLE_ANALOG 0x02
+#define FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG 0x03
+#define FM_RX_AUDIO_ENABLE_DISABLE 0x00
+
+/* HI/LO set values */
+#define FM_RX_IFFREQ_TO_HI_SIDE 0x0
+#define FM_RX_IFFREQ_TO_LO_SIDE 0x1
+#define FM_RX_IFFREQ_HILO_AUTOMATIC 0x2
+
+/*
+ * Default RX mode configuration. Chip will be configured
+ * with this default values after loading RX firmware.
+ */
+#define FM_DEFAULT_RX_VOLUME 10
+#define FM_DEFAULT_RSSI_THRESHOLD 3
+
+/* Range for TX power level in units for dB/uV */
+#define FM_PWR_LVL_LOW 91
+#define FM_PWR_LVL_HIGH 122
+
+/* Chip specific default TX power level value */
+#define FM_PWR_LVL_DEF 4
+
+/* FM TX Pre-emphasis filter values */
+#define FM_TX_PREEMPH_OFF 1
+#define FM_TX_PREEMPH_50US 0
+#define FM_TX_PREEMPH_75US 2
+
+/* FM TX antenna impedence values */
+#define FM_TX_ANT_IMP_50 0
+#define FM_TX_ANT_IMP_200 1
+#define FM_TX_ANT_IMP_500 2
+
+/* Functions exported by FM common sub-module */
+u32 fmc_prepare(struct fmdev *);
+u32 fmc_release(struct fmdev *);
+
+void fmc_update_region_info(struct fmdev *, u8);
+u32 fmc_send_cmd(struct fmdev *, u8, u16,
+ void *, unsigned int, void *, int *);
+u32 fmc_is_rds_data_available(struct fmdev *, struct file *,
+ struct poll_table_struct *);
+u32 fmc_transfer_rds_from_internal_buff(struct fmdev *, struct file *,
+ u8 __user *, size_t);
+
+u32 fmc_set_freq(struct fmdev *, u32);
+u32 fmc_set_mode(struct fmdev *, u8);
+u32 fmc_set_region(struct fmdev *, u8);
+u32 fmc_set_mute_mode(struct fmdev *, u8);
+u32 fmc_set_stereo_mono(struct fmdev *, u16);
+u32 fmc_set_rds_mode(struct fmdev *, u8);
+
+u32 fmc_get_freq(struct fmdev *, u32 *);
+u32 fmc_get_region(struct fmdev *, u8 *);
+u32 fmc_get_mode(struct fmdev *, u8 *);
+
+/*
+ * channel spacing
+ */
+#define FM_CHANNEL_SPACING_50KHZ 1
+#define FM_CHANNEL_SPACING_100KHZ 2
+#define FM_CHANNEL_SPACING_200KHZ 4
+#define FM_FREQ_MUL 50
+
+#endif
+
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c
new file mode 100644
index 000000000000..ec529b55b040
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_rx.c
@@ -0,0 +1,847 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * This sub-module of FM driver implements FM RX functionality.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Raja Mani <raja_mani@ti.com>
+ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "fmdrv.h"
+#include "fmdrv_common.h"
+#include "fmdrv_rx.h"
+
+void fm_rx_reset_rds_cache(struct fmdev *fmdev)
+{
+ fmdev->rx.rds.flag = FM_RDS_DISABLE;
+ fmdev->rx.rds.last_blk_idx = 0;
+ fmdev->rx.rds.wr_idx = 0;
+ fmdev->rx.rds.rd_idx = 0;
+
+ if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
+ fmdev->irq_info.mask |= FM_LEV_EVENT;
+}
+
+void fm_rx_reset_station_info(struct fmdev *fmdev)
+{
+ fmdev->rx.stat_info.picode = FM_NO_PI_CODE;
+ fmdev->rx.stat_info.afcache_size = 0;
+ fmdev->rx.stat_info.af_list_max = 0;
+}
+
+u32 fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
+{
+ unsigned long timeleft;
+ u16 payload, curr_frq, intr_flag;
+ u32 curr_frq_in_khz;
+ u32 ret, resp_len;
+
+ if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {
+ fmerr("Invalid frequency %d\n", freq);
+ return -EINVAL;
+ }
+
+ /* Set audio enable */
+ payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;
+
+ ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set hilo to automatic selection */
+ payload = FM_RX_IFFREQ_HILO_AUTOMATIC;
+ ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Calculate frequency index and set*/
+ payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
+
+ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Read flags - just to clear any pending interrupts if we had */
+ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Enable FR, BL interrupts */
+ intr_flag = fmdev->irq_info.mask;
+ fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Start tune */
+ payload = FM_TUNER_PRESET_MODE;
+ ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ goto exit;
+
+ /* Wait for tune ended interrupt */
+ init_completion(&fmdev->maintask_comp);
+ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
+ FM_DRV_TX_TIMEOUT);
+ if (!timeleft) {
+ fmerr("Timeout(%d sec),didn't get tune ended int\n",
+ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /* Read freq back to confirm */
+ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);
+ if (ret < 0)
+ goto exit;
+
+ curr_frq = be16_to_cpu(curr_frq);
+ curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
+
+ if (curr_frq_in_khz != freq) {
+ pr_info("Frequency is set to (%d) but "
+ "requested freq is (%d)\n", curr_frq_in_khz, freq);
+ }
+
+ /* Update local cache */
+ fmdev->rx.freq = curr_frq_in_khz;
+exit:
+ /* Re-enable default FM interrupts */
+ fmdev->irq_info.mask = intr_flag;
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Reset RDS cache and current station pointers */
+ fm_rx_reset_rds_cache(fmdev);
+ fm_rx_reset_station_info(fmdev);
+
+ return ret;
+}
+
+static u32 fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)
+{
+ u16 payload;
+ u32 ret;
+
+ if (spacing > 0 && spacing <= 50000)
+ spacing = FM_CHANNEL_SPACING_50KHZ;
+ else if (spacing > 50000 && spacing <= 100000)
+ spacing = FM_CHANNEL_SPACING_100KHZ;
+ else
+ spacing = FM_CHANNEL_SPACING_200KHZ;
+
+ /* set channel spacing */
+ payload = spacing;
+ ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;
+
+ return ret;
+}
+
+u32 fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
+ u32 wrap_around, u32 spacing)
+{
+ u32 resp_len;
+ u16 curr_frq, next_frq, last_frq;
+ u16 payload, int_reason, intr_flag;
+ u16 offset, space_idx;
+ unsigned long timeleft;
+ u32 ret;
+
+ /* Set channel spacing */
+ ret = fm_rx_set_channel_spacing(fmdev, spacing);
+ if (ret < 0) {
+ fmerr("Failed to set channel spacing\n");
+ return ret;
+ }
+
+ /* Read the current frequency from chip */
+ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,
+ sizeof(curr_frq), &curr_frq, &resp_len);
+ if (ret < 0)
+ return ret;
+
+ curr_frq = be16_to_cpu(curr_frq);
+ last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
+
+ /* Check the offset in order to be aligned to the channel spacing*/
+ space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;
+ offset = curr_frq % space_idx;
+
+ next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :
+ curr_frq - space_idx /* Seek Down */ ;
+
+ /*
+ * Add or subtract offset in order to stay aligned to the channel
+ * spacing.
+ */
+ if ((short)next_frq < 0)
+ next_frq = last_frq - offset;
+ else if (next_frq > last_frq)
+ next_frq = 0 + offset;
+
+again:
+ /* Set calculated next frequency to perform seek */
+ payload = next_frq;
+ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set search direction (0:Seek Down, 1:Seek Up) */
+ payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);
+ ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Read flags - just to clear any pending interrupts if we had */
+ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Enable FR, BL interrupts */
+ intr_flag = fmdev->irq_info.mask;
+ fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Start seek */
+ payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
+ ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for tune ended/band limit reached interrupt */
+ init_completion(&fmdev->maintask_comp);
+ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
+ FM_DRV_RX_SEEK_TIMEOUT);
+ if (!timeleft) {
+ fmerr("Timeout(%d sec),didn't get tune ended int\n",
+ jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
+ return -ETIMEDOUT;
+ }
+
+ int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
+
+ /* Re-enable default FM interrupts */
+ fmdev->irq_info.mask = intr_flag;
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (int_reason & FM_BL_EVENT) {
+ if (wrap_around == 0) {
+ fmdev->rx.freq = seek_upward ?
+ fmdev->rx.region.top_freq :
+ fmdev->rx.region.bot_freq;
+ } else {
+ fmdev->rx.freq = seek_upward ?
+ fmdev->rx.region.bot_freq :
+ fmdev->rx.region.top_freq;
+ /* Calculate frequency index to write */
+ next_frq = (fmdev->rx.freq -
+ fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
+ goto again;
+ }
+ } else {
+ /* Read freq to know where operation tune operation stopped */
+ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,
+ &curr_frq, &resp_len);
+ if (ret < 0)
+ return ret;
+
+ curr_frq = be16_to_cpu(curr_frq);
+ fmdev->rx.freq = (fmdev->rx.region.bot_freq +
+ ((u32)curr_frq * FM_FREQ_MUL));
+
+ }
+ /* Reset RDS cache and current station pointers */
+ fm_rx_reset_rds_cache(fmdev);
+ fm_rx_reset_station_info(fmdev);
+
+ return ret;
+}
+
+u32 fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) {
+ fmerr("Volume is not within(%d-%d) range\n",
+ FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
+ return -EINVAL;
+ }
+ vol_to_set *= FM_RX_VOLUME_GAIN_STEP;
+
+ payload = vol_to_set;
+ ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.volume = vol_to_set;
+ return ret;
+}
+
+/* Get volume */
+u32 fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_vol == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;
+
+ return 0;
+}
+
+/* To get current band's bottom and top frequency */
+u32 fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)
+{
+ if (bot_freq != NULL)
+ *bot_freq = fmdev->rx.region.bot_freq;
+
+ if (top_freq != NULL)
+ *top_freq = fmdev->rx.region.top_freq;
+
+ return 0;
+}
+
+/* Returns current band index (0-Europe/US; 1-Japan) */
+void fm_rx_get_region(struct fmdev *fmdev, u8 *region)
+{
+ *region = fmdev->rx.region.fm_band;
+}
+
+/* Sets band (0-Europe/US; 1-Japan) */
+u32 fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)
+{
+ u16 payload;
+ u32 new_frq = 0;
+ u32 ret;
+
+ if (region_to_set != FM_BAND_EUROPE_US &&
+ region_to_set != FM_BAND_JAPAN) {
+ fmerr("Invalid band\n");
+ return -EINVAL;
+ }
+
+ if (fmdev->rx.region.fm_band == region_to_set) {
+ fmerr("Requested band is already configured\n");
+ return 0;
+ }
+
+ /* Send cmd to set the band */
+ payload = (u16)region_to_set;
+ ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmc_update_region_info(fmdev, region_to_set);
+
+ /* Check whether current RX frequency is within band boundary */
+ if (fmdev->rx.freq < fmdev->rx.region.bot_freq)
+ new_frq = fmdev->rx.region.bot_freq;
+ else if (fmdev->rx.freq > fmdev->rx.region.top_freq)
+ new_frq = fmdev->rx.region.top_freq;
+
+ if (new_frq) {
+ fmdbg("Current freq is not within band limit boundary,"
+ "switching to %d KHz\n", new_frq);
+ /* Current RX frequency is not in range. So, update it */
+ ret = fm_rx_set_freq(fmdev, new_frq);
+ }
+
+ return ret;
+}
+
+/* Reads current mute mode (Mute Off/On/Attenuate)*/
+u32 fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_mute_mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_mute_mode = fmdev->rx.mute_mode;
+
+ return 0;
+}
+
+static u32 fm_config_rx_mute_reg(struct fmdev *fmdev)
+{
+ u16 payload, muteval;
+ u32 ret;
+
+ muteval = 0;
+ switch (fmdev->rx.mute_mode) {
+ case FM_MUTE_ON:
+ muteval = FM_RX_AC_MUTE_MODE;
+ break;
+
+ case FM_MUTE_OFF:
+ muteval = FM_RX_UNMUTE_MODE;
+ break;
+
+ case FM_MUTE_ATTENUATE:
+ muteval = FM_RX_SOFT_MUTE_FORCE_MODE;
+ break;
+ }
+ if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)
+ muteval |= FM_RX_RF_DEP_MODE;
+ else
+ muteval &= ~FM_RX_RF_DEP_MODE;
+
+ payload = muteval;
+ ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Configures mute mode (Mute Off/On/Attenuate) */
+u32 fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
+{
+ u8 org_state;
+ u32 ret;
+
+ if (fmdev->rx.mute_mode == mute_mode_toset)
+ return 0;
+
+ org_state = fmdev->rx.mute_mode;
+ fmdev->rx.mute_mode = mute_mode_toset;
+
+ ret = fm_config_rx_mute_reg(fmdev);
+ if (ret < 0) {
+ fmdev->rx.mute_mode = org_state;
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Gets RF dependent soft mute mode enable/disable status */
+u32 fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_mute_mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_mute_mode = fmdev->rx.rf_depend_mute;
+
+ return 0;
+}
+
+/* Sets RF dependent soft mute mode */
+u32 fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
+{
+ u8 org_state;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&
+ rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {
+ fmerr("Invalid RF dependent soft mute\n");
+ return -EINVAL;
+ }
+ if (fmdev->rx.rf_depend_mute == rfdepend_mute)
+ return 0;
+
+ org_state = fmdev->rx.rf_depend_mute;
+ fmdev->rx.rf_depend_mute = rfdepend_mute;
+
+ ret = fm_config_rx_mute_reg(fmdev);
+ if (ret < 0) {
+ fmdev->rx.rf_depend_mute = org_state;
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Returns the signal strength level of current channel */
+u32 fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
+{
+ u16 curr_rssi_lel;
+ u32 resp_len;
+ u32 ret;
+
+ if (rssilvl == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+ /* Read current RSSI level */
+ ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,
+ &curr_rssi_lel, &resp_len);
+ if (ret < 0)
+ return ret;
+
+ *rssilvl = be16_to_cpu(curr_rssi_lel);
+
+ return 0;
+}
+
+/*
+ * Sets the signal strength level that once reached
+ * will stop the auto search process
+ */
+u32 fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)
+{
+ u16 payload;
+ u32 ret;
+
+ if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||
+ rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {
+ fmerr("Invalid RSSI threshold level\n");
+ return -EINVAL;
+ }
+ payload = (u16)rssi_lvl_toset;
+ ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.rssi_threshold = rssi_lvl_toset;
+
+ return 0;
+}
+
+/* Returns current RX RSSI threshold value */
+u32 fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_rssi_lvl == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_rssi_lvl = fmdev->rx.rssi_threshold;
+
+ return 0;
+}
+
+/* Sets RX stereo/mono modes */
+u32 fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
+{
+ u16 payload;
+ u32 ret;
+
+ if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {
+ fmerr("Invalid mode\n");
+ return -EINVAL;
+ }
+
+ /* Set stereo/mono mode */
+ payload = (u16)mode;
+ ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set stereo blending mode */
+ payload = FM_STEREO_SOFT_BLEND;
+ ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Gets current RX stereo/mono mode */
+u32 fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
+{
+ u16 curr_mode;
+ u32 ret, resp_len;
+
+ if (mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,
+ &curr_mode, &resp_len);
+ if (ret < 0)
+ return ret;
+
+ *mode = be16_to_cpu(curr_mode);
+
+ return 0;
+}
+
+/* Choose RX de-emphasis filter mode (50us/75us) */
+u32 fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&
+ mode != FM_RX_EMPHASIS_FILTER_75_USEC) {
+ fmerr("Invalid rx de-emphasis mode (%d)\n", mode);
+ return -EINVAL;
+ }
+
+ payload = mode;
+ ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.deemphasis_mode = mode;
+
+ return 0;
+}
+
+/* Gets current RX de-emphasis filter mode */
+u32 fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_deemphasis_mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_deemphasis_mode = fmdev->rx.deemphasis_mode;
+
+ return 0;
+}
+
+/* Enable/Disable RX RDS */
+u32 fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
+{
+ u16 payload;
+ u32 ret;
+
+ if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
+ fmerr("Invalid rds option\n");
+ return -EINVAL;
+ }
+
+ if (rds_en_dis == FM_RDS_ENABLE
+ && fmdev->rx.rds.flag == FM_RDS_DISABLE) {
+ /* Turn on RX RDS and RDS circuit */
+ payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;
+ ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Clear and reset RDS FIFO */
+ payload = FM_RX_RDS_FLUSH_FIFO;
+ ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Read flags - just to clear any pending interrupts. */
+ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,
+ NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set RDS FIFO threshold value */
+ payload = FM_RX_RDS_FIFO_THRESHOLD;
+ ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Enable RDS interrupt */
+ fmdev->irq_info.mask |= FM_RDS_EVENT;
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0) {
+ fmdev->irq_info.mask &= ~FM_RDS_EVENT;
+ return ret;
+ }
+
+ /* Update our local flag */
+ fmdev->rx.rds.flag = FM_RDS_ENABLE;
+ } else if (rds_en_dis == FM_RDS_DISABLE
+ && fmdev->rx.rds.flag == FM_RDS_ENABLE) {
+ /* Turn off RX RDS */
+ payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;
+ ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Reset RDS pointers */
+ fmdev->rx.rds.last_blk_idx = 0;
+ fmdev->rx.rds.wr_idx = 0;
+ fmdev->rx.rds.rd_idx = 0;
+ fm_rx_reset_station_info(fmdev);
+
+ /* Update RDS local cache */
+ fmdev->irq_info.mask &= ~(FM_RDS_EVENT);
+ fmdev->rx.rds.flag = FM_RDS_DISABLE;
+ }
+
+ return 0;
+}
+
+/* Returns current RX RDS enable/disable status */
+u32 fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (curr_rds_en_dis == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *curr_rds_en_dis = fmdev->rx.rds.flag;
+
+ return 0;
+}
+
+/* Sets RDS operation mode (RDS/RDBS) */
+u32 fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {
+ fmerr("Invalid rds mode\n");
+ return -EINVAL;
+ }
+ /* Set RDS operation mode */
+ payload = (u16)rds_mode;
+ ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.rds_mode = rds_mode;
+
+ return 0;
+}
+
+/* Returns current RDS operation mode */
+u32 fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (rds_mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *rds_mode = fmdev->rx.rds_mode;
+
+ return 0;
+}
+
+/* Configures Alternate Frequency switch mode */
+u32 fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&
+ af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {
+ fmerr("Invalid af mode\n");
+ return -EINVAL;
+ }
+ /* Enable/disable low RSSI interrupt based on af_mode */
+ if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
+ fmdev->irq_info.mask |= FM_LEV_EVENT;
+ else
+ fmdev->irq_info.mask &= ~FM_LEV_EVENT;
+
+ payload = fmdev->irq_info.mask;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->rx.af_mode = af_mode;
+
+ return 0;
+}
+
+/* Returns Alternate Frequency switch status */
+u32 fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)
+{
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ if (af_mode == NULL) {
+ fmerr("Invalid memory\n");
+ return -ENOMEM;
+ }
+
+ *af_mode = fmdev->rx.af_mode;
+
+ return 0;
+}
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h
new file mode 100644
index 000000000000..329e62f6be76
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_rx.h
@@ -0,0 +1,59 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * FM RX module header.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _FMDRV_RX_H
+#define _FMDRV_RX_H
+
+u32 fm_rx_set_freq(struct fmdev *, u32);
+u32 fm_rx_set_mute_mode(struct fmdev *, u8);
+u32 fm_rx_set_stereo_mono(struct fmdev *, u16);
+u32 fm_rx_set_rds_mode(struct fmdev *, u8);
+u32 fm_rx_set_rds_system(struct fmdev *, u8);
+u32 fm_rx_set_volume(struct fmdev *, u16);
+u32 fm_rx_set_rssi_threshold(struct fmdev *, short);
+u32 fm_rx_set_region(struct fmdev *, u8);
+u32 fm_rx_set_rfdepend_softmute(struct fmdev *, u8);
+u32 fm_rx_set_deemphasis_mode(struct fmdev *, u16);
+u32 fm_rx_set_af_switch(struct fmdev *, u8);
+
+void fm_rx_reset_rds_cache(struct fmdev *);
+void fm_rx_reset_station_info(struct fmdev *);
+
+u32 fm_rx_seek(struct fmdev *, u32, u32, u32);
+
+u32 fm_rx_get_rds_mode(struct fmdev *, u8 *);
+u32 fm_rx_get_rds_system(struct fmdev *, u8 *);
+u32 fm_rx_get_mute_mode(struct fmdev *, u8 *);
+u32 fm_rx_get_volume(struct fmdev *, u16 *);
+u32 fm_rx_get_band_freq_range(struct fmdev *,
+ u32 *, u32 *);
+u32 fm_rx_get_stereo_mono(struct fmdev *, u16 *);
+u32 fm_rx_get_rssi_level(struct fmdev *, u16 *);
+u32 fm_rx_get_rssi_threshold(struct fmdev *, short *);
+u32 fm_rx_get_rfdepend_softmute(struct fmdev *, u8 *);
+u32 fm_rx_get_deemph_mode(struct fmdev *, u16 *);
+u32 fm_rx_get_af_switch(struct fmdev *, u8 *);
+void fm_rx_get_region(struct fmdev *, u8 *);
+
+u32 fm_rx_set_chanl_spacing(struct fmdev *, u8);
+u32 fm_rx_get_chanl_spacing(struct fmdev *, u8 *);
+#endif
+
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c
new file mode 100644
index 000000000000..be54068b56a8
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_tx.c
@@ -0,0 +1,425 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * This sub-module of FM driver implements FM TX functionality.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include "fmdrv.h"
+#include "fmdrv_common.h"
+#include "fmdrv_tx.h"
+
+u32 fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->tx_data.aud_mode == mode)
+ return 0;
+
+ fmdbg("stereo mode: %d\n", mode);
+
+ /* Set Stereo/Mono mode */
+ payload = (1 - mode);
+ ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fmdev->tx_data.aud_mode = mode;
+
+ return ret;
+}
+
+static u32 set_rds_text(struct fmdev *fmdev, u8 *rds_text)
+{
+ u16 payload;
+ u32 ret;
+
+ ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text,
+ strlen(rds_text), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Scroll mode */
+ payload = (u16)0x1;
+ ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static u32 set_rds_data_mode(struct fmdev *fmdev, u8 mode)
+{
+ u16 payload;
+ u32 ret;
+
+ /* Setting unique PI TODO: how unique? */
+ payload = (u16)0xcafe;
+ ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set decoder id */
+ payload = (u16)0xa;
+ ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: RDS_MODE_GET? */
+ return 0;
+}
+
+static u32 set_rds_len(struct fmdev *fmdev, u8 type, u16 len)
+{
+ u16 payload;
+ u32 ret;
+
+ len |= type << 8;
+ payload = len;
+ ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: LENGTH_GET? */
+ return 0;
+}
+
+u32 fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
+{
+ u16 payload;
+ u32 ret;
+ u8 rds_text[] = "Zoom2\n";
+
+ fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis,
+ FM_RDS_ENABLE, FM_RDS_DISABLE);
+
+ if (rds_en_dis == FM_RDS_ENABLE) {
+ /* Set RDS length */
+ set_rds_len(fmdev, 0, strlen(rds_text));
+
+ /* Set RDS text */
+ set_rds_text(fmdev, rds_text);
+
+ /* Set RDS mode */
+ set_rds_data_mode(fmdev, 0x0);
+ }
+
+ /* Send command to enable RDS */
+ if (rds_en_dis == FM_RDS_ENABLE)
+ payload = 0x01;
+ else
+ payload = 0x00;
+
+ ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (rds_en_dis == FM_RDS_ENABLE) {
+ /* Set RDS length */
+ set_rds_len(fmdev, 0, strlen(rds_text));
+
+ /* Set RDS text */
+ set_rds_text(fmdev, rds_text);
+ }
+ fmdev->tx_data.rds.flag = rds_en_dis;
+
+ return 0;
+}
+
+u32 fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+
+ fm_tx_set_rds_mode(fmdev, 0);
+
+ /* Set RDS length */
+ set_rds_len(fmdev, rds_type, strlen(rds_text));
+
+ /* Set RDS text */
+ set_rds_text(fmdev, rds_text);
+
+ /* Set RDS mode */
+ set_rds_data_mode(fmdev, 0x0);
+
+ payload = 1;
+ ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+u32 fm_tx_set_af(struct fmdev *fmdev, u32 af)
+{
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+
+ fmdbg("AF: %d\n", af);
+
+ af = (af - 87500) / 100;
+ payload = (u16)af;
+ ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+u32 fm_tx_set_region(struct fmdev *fmdev, u8 region)
+{
+ u16 payload;
+ u32 ret;
+
+ if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) {
+ fmerr("Invalid band\n");
+ return -EINVAL;
+ }
+
+ /* Send command to set the band */
+ payload = (u16)region;
+ ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+u32 fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
+{
+ u16 payload;
+ u32 ret;
+
+ fmdbg("tx: mute mode %d\n", mute_mode_toset);
+
+ payload = mute_mode_toset;
+ ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Set TX Audio I/O */
+static u32 set_audio_io(struct fmdev *fmdev)
+{
+ struct fmtx_data *tx = &fmdev->tx_data;
+ u16 payload;
+ u32 ret;
+
+ /* Set Audio I/O Enable */
+ payload = tx->audio_io;
+ ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: is audio set? */
+ return 0;
+}
+
+/* Start TX Transmission */
+static u32 enable_xmit(struct fmdev *fmdev, u8 new_xmit_state)
+{
+ struct fmtx_data *tx = &fmdev->tx_data;
+ unsigned long timeleft;
+ u16 payload;
+ u32 ret;
+
+ /* Enable POWER_ENB interrupts */
+ payload = FM_POW_ENB_EVENT;
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set Power Enable */
+ payload = new_xmit_state;
+ ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for Power Enabled */
+ init_completion(&fmdev->maintask_comp);
+ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
+ FM_DRV_TX_TIMEOUT);
+ if (!timeleft) {
+ fmerr("Timeout(%d sec),didn't get tune ended interrupt\n",
+ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
+ return -ETIMEDOUT;
+ }
+
+ set_bit(FM_CORE_TX_XMITING, &fmdev->flag);
+ tx->xmit_state = new_xmit_state;
+
+ return 0;
+}
+
+/* Set TX power level */
+u32 fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl)
+{
+ u16 payload;
+ struct fmtx_data *tx = &fmdev->tx_data;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+ fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl);
+
+ /* If the core isn't ready update global variable */
+ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
+ tx->pwr_lvl = new_pwr_lvl;
+ return 0;
+ }
+
+ /* Set power level: Application will specify power level value in
+ * units of dB/uV, whereas range and step are specific to FM chip.
+ * For TI's WL chips, convert application specified power level value
+ * to chip specific value by subtracting 122 from it. Refer to TI FM
+ * data sheet for details.
+ * */
+
+ payload = (FM_PWR_LVL_HIGH - new_pwr_lvl);
+ ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: is the power level set? */
+ tx->pwr_lvl = new_pwr_lvl;
+
+ return 0;
+}
+
+/*
+ * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us)
+ * Convert V4L2 specified filter values to chip specific filter values.
+ */
+u32 fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis)
+{
+ struct fmtx_data *tx = &fmdev->tx_data;
+ u16 payload;
+ u32 ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+
+ switch (preemphasis) {
+ case V4L2_PREEMPHASIS_DISABLED:
+ payload = FM_TX_PREEMPH_OFF;
+ break;
+ case V4L2_PREEMPHASIS_50_uS:
+ payload = FM_TX_PREEMPH_50US;
+ break;
+ case V4L2_PREEMPHASIS_75_uS:
+ payload = FM_TX_PREEMPH_75US;
+ break;
+ }
+
+ ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ tx->preemph = payload;
+
+ return ret;
+}
+
+/* Get the TX tuning capacitor value.*/
+u32 fm_tx_get_tune_cap_val(struct fmdev *fmdev)
+{
+ u16 curr_val;
+ u32 ret, resp_len;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+
+ ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD,
+ NULL, sizeof(curr_val), &curr_val, &resp_len);
+ if (ret < 0)
+ return ret;
+
+ curr_val = be16_to_cpu(curr_val);
+
+ return curr_val;
+}
+
+/* Set TX Frequency */
+u32 fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set)
+{
+ struct fmtx_data *tx = &fmdev->tx_data;
+ u16 payload, chanl_index;
+ u32 ret;
+
+ if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) {
+ enable_xmit(fmdev, 0);
+ clear_bit(FM_CORE_TX_XMITING, &fmdev->flag);
+ }
+
+ /* Enable FR, BL interrupts */
+ payload = (FM_FR_EVENT | FM_BL_EVENT);
+ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ tx->tx_frq = (unsigned long)freq_to_set;
+ fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq);
+
+ chanl_index = freq_to_set / 10;
+
+ /* Set current tuner channel */
+ payload = chanl_index;
+ ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload,
+ sizeof(payload), NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl);
+ fm_tx_set_preemph_filter(fmdev, tx->preemph);
+
+ tx->audio_io = 0x01; /* I2S */
+ set_audio_io(fmdev);
+
+ enable_xmit(fmdev, 0x01); /* Enable transmission */
+
+ tx->aud_mode = FM_STEREO_MODE;
+ tx->rds.flag = FM_RDS_DISABLE;
+
+ return 0;
+}
+
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h
new file mode 100644
index 000000000000..e393a2bdd49e
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_tx.h
@@ -0,0 +1,37 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * FM TX module header.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _FMDRV_TX_H
+#define _FMDRV_TX_H
+
+u32 fm_tx_set_freq(struct fmdev *, u32);
+u32 fm_tx_set_pwr_lvl(struct fmdev *, u8);
+u32 fm_tx_set_region(struct fmdev *, u8);
+u32 fm_tx_set_mute_mode(struct fmdev *, u8);
+u32 fm_tx_set_stereo_mono(struct fmdev *, u16);
+u32 fm_tx_set_rds_mode(struct fmdev *, u8);
+u32 fm_tx_set_radio_text(struct fmdev *, u8 *, u8);
+u32 fm_tx_set_af(struct fmdev *, u32);
+u32 fm_tx_set_preemph_filter(struct fmdev *, u32);
+u32 fm_tx_get_tune_cap_val(struct fmdev *);
+
+#endif
+
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
new file mode 100644
index 000000000000..d50e5ac75ab6
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -0,0 +1,580 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ * This file provides interfaces to V4L2 subsystem.
+ *
+ * This module registers with V4L2 subsystem as Radio
+ * data system interface (/dev/radio). During the registration,
+ * it will expose two set of function pointers.
+ *
+ * 1) File operation related API (open, close, read, write, poll...etc).
+ * 2) Set of V4L2 IOCTL complaint API.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Raja Mani <raja_mani@ti.com>
+ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "fmdrv.h"
+#include "fmdrv_v4l2.h"
+#include "fmdrv_common.h"
+#include "fmdrv_rx.h"
+#include "fmdrv_tx.h"
+
+static struct video_device *gradio_dev;
+static u8 radio_disconnected;
+
+/* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */
+
+/* Read RX RDS data */
+static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ u8 rds_mode;
+ int ret;
+ struct fmdev *fmdev;
+
+ fmdev = video_drvdata(file);
+
+ if (!radio_disconnected) {
+ fmerr("FM device is already disconnected\n");
+ return -EIO;
+ }
+
+ /* Turn on RDS mode , if it is disabled */
+ ret = fm_rx_get_rds_mode(fmdev, &rds_mode);
+ if (ret < 0) {
+ fmerr("Unable to read current rds mode\n");
+ return ret;
+ }
+
+ if (rds_mode == FM_RDS_DISABLE) {
+ ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE);
+ if (ret < 0) {
+ fmerr("Failed to enable rds mode\n");
+ return ret;
+ }
+ }
+
+ /* Copy RDS data from internal buffer to user buffer */
+ return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count);
+}
+
+/* Write TX RDS data */
+static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct tx_rds rds;
+ int ret;
+ struct fmdev *fmdev;
+
+ ret = copy_from_user(&rds, buf, sizeof(rds));
+ fmdbg("(%d)type: %d, text %s, af %d\n",
+ ret, rds.text_type, rds.text, rds.af_freq);
+
+ fmdev = video_drvdata(file);
+ fm_tx_set_radio_text(fmdev, rds.text, rds.text_type);
+ fm_tx_set_af(fmdev, rds.af_freq);
+
+ return 0;
+}
+
+static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts)
+{
+ int ret;
+ struct fmdev *fmdev;
+
+ fmdev = video_drvdata(file);
+ ret = fmc_is_rds_data_available(fmdev, file, pts);
+ if (ret < 0)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+/*
+ * Handle open request for "/dev/radioX" device.
+ * Start with FM RX mode as default.
+ */
+static int fm_v4l2_fops_open(struct file *file)
+{
+ int ret;
+ struct fmdev *fmdev = NULL;
+
+ /* Don't allow multiple open */
+ if (radio_disconnected) {
+ fmerr("FM device is already opened\n");
+ return -EBUSY;
+ }
+
+ fmdev = video_drvdata(file);
+
+ ret = fmc_prepare(fmdev);
+ if (ret < 0) {
+ fmerr("Unable to prepare FM CORE\n");
+ return ret;
+ }
+
+ fmdbg("Load FM RX firmware..\n");
+
+ ret = fmc_set_mode(fmdev, FM_MODE_RX);
+ if (ret < 0) {
+ fmerr("Unable to load FM RX firmware\n");
+ return ret;
+ }
+ radio_disconnected = 1;
+
+ return ret;
+}
+
+static int fm_v4l2_fops_release(struct file *file)
+{
+ int ret;
+ struct fmdev *fmdev;
+
+ fmdev = video_drvdata(file);
+ if (!radio_disconnected) {
+ fmdbg("FM device is already closed\n");
+ return 0;
+ }
+
+ ret = fmc_set_mode(fmdev, FM_MODE_OFF);
+ if (ret < 0) {
+ fmerr("Unable to turn off the chip\n");
+ return ret;
+ }
+
+ ret = fmc_release(fmdev);
+ if (ret < 0) {
+ fmerr("FM CORE release failed\n");
+ return ret;
+ }
+ radio_disconnected = 0;
+
+ return ret;
+}
+
+/* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */
+static int fm_v4l2_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
+ sizeof(capability->card));
+ sprintf(capability->bus_info, "UART");
+ capability->version = FM_DRV_RADIO_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
+ V4L2_CAP_RADIO | V4L2_CAP_MODULATOR |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
+ V4L2_CAP_RDS_CAPTURE;
+
+ return 0;
+}
+
+static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fmdev *fmdev = container_of(ctrl->handler,
+ struct fmdev, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ ctrl->val = fm_tx_get_tune_cap_val(fmdev);
+ break;
+ default:
+ fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fmdev *fmdev = container_of(ctrl->handler,
+ struct fmdev, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME: /* set volume */
+ return fm_rx_set_volume(fmdev, (u16)ctrl->val);
+
+ case V4L2_CID_AUDIO_MUTE: /* set mute */
+ return fmc_set_mute_mode(fmdev, (u8)ctrl->val);
+
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ /* set TX power level - ext control */
+ return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val);
+
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ memset(audio, 0, sizeof(*audio));
+ strcpy(audio->name, "Radio");
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Get tuner attributes. If current mode is NOT RX, return error */
+static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+ u32 bottom_freq;
+ u32 top_freq;
+ u16 stereo_mono_mode;
+ u16 rssilvl;
+ int ret;
+
+ if (tuner->index != 0)
+ return -EINVAL;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX)
+ return -EPERM;
+
+ ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq);
+ if (ret != 0)
+ return ret;
+
+ ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode);
+ if (ret != 0)
+ return ret;
+
+ ret = fm_rx_get_rssi_level(fmdev, &rssilvl);
+ if (ret != 0)
+ return ret;
+
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+ /* Store rangelow and rangehigh freq in unit of 62.5 Hz */
+ tuner->rangelow = bottom_freq * 16;
+ tuner->rangehigh = top_freq * 16;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO |
+ ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0);
+ tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+ V4L2_TUNER_CAP_LOW;
+ tuner->audmode = (stereo_mono_mode ?
+ V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO);
+
+ /*
+ * Actual rssi value lies in between -128 to +127.
+ * Convert this range from 0 to 255 by adding +128
+ */
+ rssilvl += 128;
+
+ /*
+ * Return signal strength value should be within 0 to 65535.
+ * Find out correct signal radio by multiplying (65535/255) = 257
+ */
+ tuner->signal = rssilvl * 257;
+ tuner->afc = 0;
+
+ return ret;
+}
+
+/*
+ * Set tuner attributes. If current mode is NOT RX, set to RX.
+ * Currently, we set only audio mode (mono/stereo) and RDS state (on/off).
+ * Should we set other tuner attributes, too?
+ */
+static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+ u16 aud_mode;
+ u8 rds_mode;
+ int ret;
+
+ if (tuner->index != 0)
+ return -EINVAL;
+
+ aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ?
+ FM_STEREO_MODE : FM_MONO_MODE;
+ rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ?
+ FM_RDS_ENABLE : FM_RDS_DISABLE;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX) {
+ ret = fmc_set_mode(fmdev, FM_MODE_RX);
+ if (ret < 0) {
+ fmerr("Failed to set RX mode\n");
+ return ret;
+ }
+ }
+
+ ret = fmc_set_stereo_mono(fmdev, aud_mode);
+ if (ret < 0) {
+ fmerr("Failed to set RX stereo/mono mode\n");
+ return ret;
+ }
+
+ ret = fmc_set_rds_mode(fmdev, rds_mode);
+ if (ret < 0)
+ fmerr("Failed to set RX RDS mode\n");
+
+ return ret;
+}
+
+/* Get tuner or modulator radio frequency */
+static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+ int ret;
+
+ ret = fmc_get_freq(fmdev, &freq->frequency);
+ if (ret < 0) {
+ fmerr("Failed to get frequency\n");
+ return ret;
+ }
+
+ /* Frequency unit of 62.5 Hz*/
+ freq->frequency = (u32) freq->frequency * 16;
+
+ return 0;
+}
+
+/* Set tuner or modulator radio frequency */
+static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+
+ /*
+ * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency
+ * in units of 62.5 Hz.
+ */
+ freq->frequency = (u32)(freq->frequency / 16);
+
+ return fmc_set_freq(fmdev, freq->frequency);
+}
+
+/* Set hardware frequency seek. If current mode is NOT RX, set it RX. */
+static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+ int ret;
+
+ if (fmdev->curr_fmmode != FM_MODE_RX) {
+ ret = fmc_set_mode(fmdev, FM_MODE_RX);
+ if (ret != 0) {
+ fmerr("Failed to set RX mode\n");
+ return ret;
+ }
+ }
+
+ ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around,
+ seek->spacing);
+ if (ret < 0)
+ fmerr("RX seek failed - %d\n", ret);
+
+ return ret;
+}
+/* Get modulator attributes. If mode is not TX, return no attributes. */
+static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *mod)
+{
+ struct fmdev *fmdev = video_drvdata(file);;
+
+ if (mod->index != 0)
+ return -EINVAL;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX)
+ return -EPERM;
+
+ mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) |
+ ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ?
+ V4L2_TUNER_SUB_RDS : 0);
+
+ mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+ V4L2_TUNER_CAP_LOW;
+
+ return 0;
+}
+
+/* Set modulator attributes. If mode is not TX, set to TX. */
+static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *mod)
+{
+ struct fmdev *fmdev = video_drvdata(file);
+ u8 rds_mode;
+ u16 aud_mode;
+ int ret;
+
+ if (mod->index != 0)
+ return -EINVAL;
+
+ if (fmdev->curr_fmmode != FM_MODE_TX) {
+ ret = fmc_set_mode(fmdev, FM_MODE_TX);
+ if (ret != 0) {
+ fmerr("Failed to set TX mode\n");
+ return ret;
+ }
+ }
+
+ aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ?
+ FM_STEREO_MODE : FM_MONO_MODE;
+ rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ?
+ FM_RDS_ENABLE : FM_RDS_DISABLE;
+ ret = fm_tx_set_stereo_mono(fmdev, aud_mode);
+ if (ret < 0) {
+ fmerr("Failed to set mono/stereo mode for TX\n");
+ return ret;
+ }
+ ret = fm_tx_set_rds_mode(fmdev, rds_mode);
+ if (ret < 0)
+ fmerr("Failed to set rds mode for TX\n");
+
+ return ret;
+}
+
+static const struct v4l2_file_operations fm_drv_fops = {
+ .owner = THIS_MODULE,
+ .read = fm_v4l2_fops_read,
+ .write = fm_v4l2_fops_write,
+ .poll = fm_v4l2_fops_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .open = fm_v4l2_fops_open,
+ .release = fm_v4l2_fops_release,
+};
+
+static const struct v4l2_ctrl_ops fm_ctrl_ops = {
+ .s_ctrl = fm_v4l2_s_ctrl,
+ .g_volatile_ctrl = fm_g_volatile_ctrl,
+};
+static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = {
+ .vidioc_querycap = fm_v4l2_vidioc_querycap,
+ .vidioc_g_audio = fm_v4l2_vidioc_g_audio,
+ .vidioc_s_audio = fm_v4l2_vidioc_s_audio,
+ .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner,
+ .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner,
+ .vidioc_g_frequency = fm_v4l2_vidioc_g_freq,
+ .vidioc_s_frequency = fm_v4l2_vidioc_s_freq,
+ .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek,
+ .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator,
+ .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator
+};
+
+/* V4L2 RADIO device parent structure */
+static struct video_device fm_viddev_template = {
+ .fops = &fm_drv_fops,
+ .ioctl_ops = &fm_drv_ioctl_ops,
+ .name = FM_DRV_NAME,
+ .release = video_device_release,
+};
+
+int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ /* Init mutex for core locking */
+ mutex_init(&fmdev->mutex);
+
+ /* Allocate new video device */
+ gradio_dev = video_device_alloc();
+ if (NULL == gradio_dev) {
+ fmerr("Can't allocate video device\n");
+ return -ENOMEM;
+ }
+
+ /* Setup FM driver's V4L2 properties */
+ memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template));
+
+ video_set_drvdata(gradio_dev, fmdev);
+
+ gradio_dev->lock = &fmdev->mutex;
+
+ /* Register with V4L2 subsystem as RADIO device */
+ if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
+ video_device_release(gradio_dev);
+ fmerr("Could not register video device\n");
+ return -ENOMEM;
+ }
+
+ fmdev->radio_dev = gradio_dev;
+
+ /* Register to v4l2 ctrl handler framework */
+ fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler;
+
+ ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5);
+ if (ret < 0) {
+ fmerr("(fmdev): Can't init ctrl handler\n");
+ v4l2_ctrl_handler_free(&fmdev->ctrl_handler);
+ return -EBUSY;
+ }
+
+ /*
+ * Following controls are handled by V4L2 control framework.
+ * Added in ascending ID order.
+ */
+ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN,
+ FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX);
+
+ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+
+ v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops,
+ V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS,
+ 0, V4L2_PREEMPHASIS_75_uS);
+
+ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
+ V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW,
+ FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH);
+
+ ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
+ V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0,
+ 255, 1, 255);
+
+ if (ctrl)
+ ctrl->is_volatile = 1;
+
+ return 0;
+}
+
+void *fm_v4l2_deinit_video_device(void)
+{
+ struct fmdev *fmdev;
+
+
+ fmdev = video_get_drvdata(gradio_dev);
+
+ /* Unregister to v4l2 ctrl handler framework*/
+ v4l2_ctrl_handler_free(&fmdev->ctrl_handler);
+
+ /* Unregister RADIO device from V4L2 subsystem */
+ video_unregister_device(gradio_dev);
+
+ return fmdev;
+}
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h
new file mode 100644
index 000000000000..0ba79d745e2f
--- /dev/null
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.h
@@ -0,0 +1,33 @@
+/*
+ * FM Driver for Connectivity chip of Texas Instruments.
+ *
+ * FM V4L2 module header.
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _FMDRV_V4L2_H
+#define _FMDRV_V4L2_H
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+
+int fm_v4l2_init_video_device(struct fmdev *, int);
+void *fm_v4l2_deinit_video_device(void);
+
+#endif
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 0659e9f50144..f0c80559b303 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-real-audio-220-32-keys.o \
rc-streamzap.o \
rc-tbs-nec.o \
+ rc-technisat-usb2.o \
rc-terratec-cinergy-xs.o \
rc-terratec-slim.o \
rc-tevii-nec.o \
diff --git a/drivers/media/rc/keymaps/rc-technisat-usb2.c b/drivers/media/rc/keymaps/rc-technisat-usb2.c
new file mode 100644
index 000000000000..4afe5774f192
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-technisat-usb2.c
@@ -0,0 +1,93 @@
+/* rc-technisat-usb2.c - Keytable for SkyStar HD USB
+ *
+ * Copyright (C) 2010 Patrick Boettcher,
+ * Kernel Labs Inc. PO Box 745, St James, NY 11780
+ *
+ * Development was sponsored by Technisat Digital UK Limited, whose
+ * registered office is Witan Gate House 500 - 600 Witan Gate West,
+ * Milton Keynes, MK9 1SH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ *
+ * You 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.
+ *
+ * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND
+ * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER
+ * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the
+ * GNU General Public License for more details.
+ */
+
+#include <media/rc-map.h>
+
+static struct rc_map_table technisat_usb2[] = {
+ {0x0a0c, KEY_POWER},
+ {0x0a01, KEY_1},
+ {0x0a02, KEY_2},
+ {0x0a03, KEY_3},
+ {0x0a0d, KEY_MUTE},
+ {0x0a04, KEY_4},
+ {0x0a05, KEY_5},
+ {0x0a06, KEY_6},
+ {0x0a38, KEY_VIDEO}, /* EXT */
+ {0x0a07, KEY_7},
+ {0x0a08, KEY_8},
+ {0x0a09, KEY_9},
+ {0x0a00, KEY_0},
+ {0x0a4f, KEY_INFO},
+ {0x0a20, KEY_CHANNELUP},
+ {0x0a52, KEY_MENU},
+ {0x0a11, KEY_VOLUMEUP},
+ {0x0a57, KEY_OK},
+ {0x0a10, KEY_VOLUMEDOWN},
+ {0x0a2f, KEY_EPG},
+ {0x0a21, KEY_CHANNELDOWN},
+ {0x0a22, KEY_REFRESH},
+ {0x0a3c, KEY_TEXT},
+ {0x0a76, KEY_ENTER}, /* HOOK */
+ {0x0a0f, KEY_HELP},
+ {0x0a6b, KEY_RED},
+ {0x0a6c, KEY_GREEN},
+ {0x0a6d, KEY_YELLOW},
+ {0x0a6e, KEY_BLUE},
+ {0x0a29, KEY_STOP},
+ {0x0a23, KEY_LANGUAGE},
+ {0x0a53, KEY_TV},
+ {0x0a0a, KEY_PROGRAM},
+};
+
+static struct rc_map_list technisat_usb2_map = {
+ .map = {
+ .scan = technisat_usb2,
+ .size = ARRAY_SIZE(technisat_usb2),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_TECHNISAT_USB2,
+ }
+};
+
+static int __init init_rc_map(void)
+{
+ return rc_map_register(&technisat_usb2_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+ rc_map_unregister(&technisat_usb2_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@kernellabs.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index aa021600e9df..e2f5a69aa400 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -42,8 +42,30 @@ config VIDEO_TUNER
config V4L2_MEM2MEM_DEV
tristate
- depends on VIDEOBUF_GEN
+ depends on VIDEOBUF2_CORE
+config VIDEOBUF2_CORE
+ tristate
+
+config VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
+
+
+config VIDEOBUF2_DMA_SG
+ #depends on HAS_DMA
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
#
# Multimedia Video device configuration
#
@@ -527,7 +549,7 @@ config VIDEO_VIVI
depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
select FONT_8x16
- select VIDEOBUF_VMALLOC
+ select VIDEOBUF2_VMALLOC
default n
---help---
Enables a virtual video driver. This device shows a color bar
@@ -718,10 +740,17 @@ config VIDEO_VIA_CAMERA
Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
with ov7670 sensors.
+config VIDEO_NOON010PC30
+ tristate "NOON010PC30 CIF camera sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This driver supports NOON010PC30 CIF camera from Siliconfile
+
config SOC_CAMERA
tristate "SoC camera support"
depends on VIDEO_V4L2 && HAS_DMA && I2C
select VIDEOBUF_GEN
+ select VIDEOBUF2_CORE
help
SoC Camera is a common API to several cameras, not connecting
over a bus like PCI or USB. For example some i2c camera connected
@@ -809,6 +838,12 @@ config SOC_CAMERA_OV9640
help
This is a ov9640 camera driver
+config SOC_CAMERA_OV9740
+ tristate "ov9740 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov9740 camera driver
+
config MX1_VIDEO
bool
@@ -848,7 +883,7 @@ config VIDEO_SH_MOBILE_CSI2
config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
@@ -967,7 +1002,7 @@ if V4L_MEM2MEM_DRIVERS
config VIDEO_MEM2MEM_TESTDEV
tristate "Virtual test device for mem2mem framework"
depends on VIDEO_DEV && VIDEO_V4L2
- select VIDEOBUF_VMALLOC
+ select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
default n
---help---
@@ -977,7 +1012,7 @@ config VIDEO_MEM2MEM_TESTDEV
config VIDEO_SAMSUNG_S5P_FIMC
tristate "Samsung S5P FIMC (video postprocessor) driver"
depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This is a v4l2 driver for the S5P camera interface
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a509d317e258..ac54652396e3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
+obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
@@ -78,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
+obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
@@ -111,6 +113,12 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o
+obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o
+obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o
+obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o
+obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o
+obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o
+
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c
index 41b2930d0ce4..021fab23070d 100644
--- a/drivers/media/video/adv7343.c
+++ b/drivers/media/video/adv7343.c
@@ -29,6 +29,7 @@
#include <media/adv7343.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include "adv7343_regs.h"
@@ -41,15 +42,13 @@ MODULE_PARM_DESC(debug, "Debug level 0-1");
struct adv7343_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
u8 reg00;
u8 reg01;
u8 reg02;
u8 reg35;
u8 reg80;
u8 reg82;
- int bright;
- int hue;
- int gain;
u32 output;
v4l2_std_id std;
};
@@ -59,6 +58,11 @@ static inline struct adv7343_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct adv7343_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd;
+}
+
static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -268,111 +272,22 @@ static int adv7343_log_status(struct v4l2_subdev *sd)
return 0;
}
-static int adv7343_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, ADV7343_BRIGHTNESS_MIN,
- ADV7343_BRIGHTNESS_MAX, 1,
- ADV7343_BRIGHTNESS_DEF);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, ADV7343_HUE_MIN,
- ADV7343_HUE_MAX, 1 ,
- ADV7343_HUE_DEF);
- case V4L2_CID_GAIN:
- return v4l2_ctrl_query_fill(qc, ADV7343_GAIN_MIN,
- ADV7343_GAIN_MAX, 1,
- ADV7343_GAIN_DEF);
- default:
- break;
- }
-
- return 0;
-}
-
-static int adv7343_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct adv7343_state *state = to_state(sd);
- int err = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < ADV7343_BRIGHTNESS_MIN ||
- ctrl->value > ADV7343_BRIGHTNESS_MAX) {
- v4l2_dbg(1, debug, sd,
- "invalid brightness settings %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- state->bright = ctrl->value;
- err = adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS,
- state->bright);
- break;
-
- case V4L2_CID_HUE:
- if (ctrl->value < ADV7343_HUE_MIN ||
- ctrl->value > ADV7343_HUE_MAX) {
- v4l2_dbg(1, debug, sd, "invalid hue settings %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- state->hue = ctrl->value;
- err = adv7343_write(sd, ADV7343_SD_HUE_REG, state->hue);
- break;
-
- case V4L2_CID_GAIN:
- if (ctrl->value < ADV7343_GAIN_MIN ||
- ctrl->value > ADV7343_GAIN_MAX) {
- v4l2_dbg(1, debug, sd, "invalid gain settings %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- if ((ctrl->value > POSITIVE_GAIN_MAX) &&
- (ctrl->value < NEGATIVE_GAIN_MIN)) {
- v4l2_dbg(1, debug, sd,
- "gain settings not within the specified range\n");
- return -ERANGE;
- }
-
- state->gain = ctrl->value;
- err = adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, state->gain);
- break;
-
- default:
- return -EINVAL;
- }
-
- if (err < 0)
- v4l2_err(sd, "Failed to set the encoder controls\n");
-
- return err;
-}
-
-static int adv7343_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct adv7343_state *state = to_state(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->bright;
- break;
+ return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS,
+ ctrl->val);
case V4L2_CID_HUE:
- ctrl->value = state->hue;
- break;
+ return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val);
case V4L2_CID_GAIN:
- ctrl->value = state->gain;
- break;
-
- default:
- return -EINVAL;
+ return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val);
}
-
- return 0;
+ return -EINVAL;
}
static int adv7343_g_chip_ident(struct v4l2_subdev *sd,
@@ -383,12 +298,20 @@ static int adv7343_g_chip_ident(struct v4l2_subdev *sd,
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0);
}
+static const struct v4l2_ctrl_ops adv7343_ctrl_ops = {
+ .s_ctrl = adv7343_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops adv7343_core_ops = {
- .log_status = adv7343_log_status,
- .g_chip_ident = adv7343_g_chip_ident,
- .g_ctrl = adv7343_g_ctrl,
- .s_ctrl = adv7343_s_ctrl,
- .queryctrl = adv7343_queryctrl,
+ .log_status = adv7343_log_status,
+ .g_chip_ident = adv7343_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
@@ -468,6 +391,7 @@ static int adv7343_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7343_state *state;
+ int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -490,15 +414,46 @@ static int adv7343_probe(struct i2c_client *client,
state->std = V4L2_STD_NTSC;
v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops);
- return adv7343_initialize(&state->sd);
+
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN,
+ ADV7343_BRIGHTNESS_MAX, 1,
+ ADV7343_BRIGHTNESS_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_HUE, ADV7343_HUE_MIN,
+ ADV7343_HUE_MAX, 1,
+ ADV7343_HUE_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_GAIN, ADV7343_GAIN_MIN,
+ ADV7343_GAIN_MAX, 1,
+ ADV7343_GAIN_DEF);
+ state->sd.ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ err = adv7343_initialize(&state->sd);
+ if (err) {
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ }
+ return err;
}
static int adv7343_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7343_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h
index 3431045b33da..446606764346 100644
--- a/drivers/media/video/adv7343_regs.h
+++ b/drivers/media/video/adv7343_regs.h
@@ -102,10 +102,6 @@ struct adv7343_std_info {
/* Bit masks for DAC output levels */
#define DAC_OUTPUT_LEVEL_MASK (0xFF)
-#define POSITIVE_GAIN_MAX (0x40)
-#define POSITIVE_GAIN_MIN (0x00)
-#define NEGATIVE_GAIN_MAX (0xFF)
-#define NEGATIVE_GAIN_MIN (0xC0)
/* Bit masks for soft reset register */
#define SOFT_RESET (0x02)
@@ -178,8 +174,8 @@ struct adv7343_std_info {
#define ADV7343_HUE_MAX (255)
#define ADV7343_HUE_MIN (0)
#define ADV7343_HUE_DEF (127)
-#define ADV7343_GAIN_MAX (255)
-#define ADV7343_GAIN_MIN (0)
+#define ADV7343_GAIN_MAX (64)
+#define ADV7343_GAIN_MIN (-64)
#define ADV7343_GAIN_DEF (0)
#endif
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
index 01be89fa5c78..39fc923fc46b 100644
--- a/drivers/media/video/au0828/au0828-cards.c
+++ b/drivers/media/video/au0828/au0828-cards.c
@@ -185,8 +185,7 @@ void au0828_card_setup(struct au0828_dev *dev)
static u8 eeprom[256];
struct tuner_setup tun_setup;
struct v4l2_subdev *sd;
- unsigned int mode_mask = T_ANALOG_TV |
- T_DIGITAL_TV;
+ unsigned int mode_mask = T_ANALOG_TV;
dprintk(1, "%s()\n", __func__);
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index c38300fc0b1d..f87204461cb4 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/bt819.h>
MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
@@ -52,16 +53,13 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct bt819 {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
unsigned char reg[32];
v4l2_std_id norm;
int ident;
int input;
int enable;
- int bright;
- int contrast;
- int hue;
- int sat;
};
static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
@@ -69,6 +67,11 @@ static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
return container_of(sd, struct bt819, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct bt819, hdl)->sd;
+}
+
struct timing {
int hactive;
int hdelay;
@@ -333,71 +336,35 @@ static int bt819_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int bt819_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- break;
-
- case V4L2_CID_CONTRAST:
- v4l2_ctrl_query_fill(qc, 0, 511, 1, 256);
- break;
-
- case V4L2_CID_SATURATION:
- v4l2_ctrl_query_fill(qc, 0, 511, 1, 256);
- break;
-
- case V4L2_CID_HUE:
- v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int bt819_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct bt819 *decoder = to_bt819(sd);
int temp;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if (decoder->bright == ctrl->value)
- break;
- decoder->bright = ctrl->value;
- bt819_write(decoder, 0x0a, decoder->bright);
+ bt819_write(decoder, 0x0a, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- if (decoder->contrast == ctrl->value)
- break;
- decoder->contrast = ctrl->value;
- bt819_write(decoder, 0x0c, decoder->contrast & 0xff);
- bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 8) & 0x01));
+ bt819_write(decoder, 0x0c, ctrl->val & 0xff);
+ bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01));
break;
case V4L2_CID_SATURATION:
- if (decoder->sat == ctrl->value)
- break;
- decoder->sat = ctrl->value;
- bt819_write(decoder, 0x0d, (decoder->sat >> 7) & 0xff);
- bt819_setbit(decoder, 0x0b, 1, ((decoder->sat >> 15) & 0x01));
+ bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff);
+ bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01));
/* Ratio between U gain and V gain must stay the same as
the ratio between the default U and V gain values. */
- temp = (decoder->sat * 180) / 254;
+ temp = (ctrl->val * 180) / 254;
bt819_write(decoder, 0x0e, (temp >> 7) & 0xff);
bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01);
break;
case V4L2_CID_HUE:
- if (decoder->hue == ctrl->value)
- break;
- decoder->hue = ctrl->value;
- bt819_write(decoder, 0x0f, decoder->hue);
+ bt819_write(decoder, 0x0f, ctrl->val);
break;
default:
@@ -406,29 +373,6 @@ static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
-static int bt819_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct bt819 *decoder = to_bt819(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = decoder->bright;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = decoder->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = decoder->sat;
- break;
- case V4L2_CID_HUE:
- ctrl->value = decoder->hue;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct bt819 *decoder = to_bt819(sd);
@@ -439,11 +383,19 @@ static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops bt819_ctrl_ops = {
+ .s_ctrl = bt819_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops bt819_core_ops = {
.g_chip_ident = bt819_g_chip_ident,
- .g_ctrl = bt819_g_ctrl,
- .s_ctrl = bt819_s_ctrl,
- .queryctrl = bt819_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = bt819_s_std,
};
@@ -505,23 +457,40 @@ static int bt819_probe(struct i2c_client *client,
decoder->norm = V4L2_STD_NTSC;
decoder->input = 0;
decoder->enable = 1;
- decoder->bright = 0;
- decoder->contrast = 0xd8; /* 100% of original signal */
- decoder->hue = 0;
- decoder->sat = 0xfe; /* 100% of original signal */
i = bt819_init(sd);
if (i < 0)
v4l2_dbg(1, debug, sd, "init status %d\n", i);
+
+ v4l2_ctrl_handler_init(&decoder->hdl, 4);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 511, 1, 0xd8);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 511, 1, 0xfe);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
return 0;
}
static int bt819_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct bt819 *decoder = to_bt819(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_bt819(sd));
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 7f58756d72c8..242f0d512238 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -3616,7 +3616,7 @@ void __devinit bttv_init_tuner(struct bttv *btv)
&btv->c.i2c_adap, "tuner",
0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
- tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ tun_setup.mode_mask = T_ANALOG_TV;
tun_setup.type = btv->tuner_type;
tun_setup.addr = addr;
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 9bad39842936..363fe098b748 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -395,10 +395,14 @@ static int sync(struct camera_data *cam, int frame_nr)
*
*****************************************************************************/
-static int ioctl_set_gpio(void *arg, struct camera_data *cam)
+static long cpia2_default(struct file *file, void *fh, int cmd, void *arg)
{
+ struct camera_data *cam = video_drvdata(file);
__u32 gpio_val;
+ if (cmd != CPIA2_CID_GPIO)
+ return -EINVAL;
+
gpio_val = *(__u32*) arg;
if (gpio_val &~ 0xFFU)
@@ -415,11 +419,10 @@ static int ioctl_set_gpio(void *arg, struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_querycap(void *arg, struct camera_data *cam)
+static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc)
{
- struct v4l2_capability *vc = arg;
+ struct camera_data *cam = video_drvdata(file);
- memset(vc, 0, sizeof(*vc));
strcpy(vc->driver, "cpia2");
if (cam->params.pnp_id.product == 0x151)
@@ -479,22 +482,26 @@ static int ioctl_querycap(void *arg, struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_input(unsigned int ioclt_nr,void *arg,struct camera_data *cam)
+static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
- struct v4l2_input *i = arg;
-
- if(ioclt_nr != VIDIOC_G_INPUT) {
- if (i->index != 0)
- return -EINVAL;
- }
-
- memset(i, 0, sizeof(*i));
+ if (i->index)
+ return -EINVAL;
strcpy(i->name, "Camera");
i->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+static int cpia2_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
return 0;
}
+static int cpia2_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
/******************************************************************************
*
* ioctl_enum_fmt
@@ -503,9 +510,9 @@ static int ioctl_input(unsigned int ioclt_nr,void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_enum_fmt(void *arg,struct camera_data *cam)
+static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
{
- struct v4l2_fmtdesc *f = arg;
int index = f->index;
if (index < 0 || index > 1)
@@ -539,12 +546,10 @@ static int ioctl_enum_fmt(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_try_fmt(void *arg,struct camera_data *cam)
+static int cpia2_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
{
- struct v4l2_format *f = arg;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ struct camera_data *cam = video_drvdata(file);
if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
@@ -603,12 +608,17 @@ static int ioctl_try_fmt(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_set_fmt(void *arg,struct camera_data *cam, struct cpia2_fh *fh)
+static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh,
+ struct v4l2_format *f)
{
- struct v4l2_format *f = arg;
+ struct camera_data *cam = video_drvdata(file);
+ struct cpia2_fh *fh = _fh;
int err, frame;
- err = ioctl_try_fmt(arg, cam);
+ err = v4l2_prio_check(&cam->prio, fh->prio);
+ if (err)
+ return err;
+ err = cpia2_try_fmt_vid_cap(file, _fh, f);
if(err != 0)
return err;
@@ -658,12 +668,10 @@ static int ioctl_set_fmt(void *arg,struct camera_data *cam, struct cpia2_fh *fh)
*
*****************************************************************************/
-static int ioctl_get_fmt(void *arg,struct camera_data *cam)
+static int cpia2_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
{
- struct v4l2_format *f = arg;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ struct camera_data *cam = video_drvdata(file);
f->fmt.pix.width = cam->width;
f->fmt.pix.height = cam->height;
@@ -686,9 +694,9 @@ static int ioctl_get_fmt(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_cropcap(void *arg,struct camera_data *cam)
+static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c)
{
- struct v4l2_cropcap *c = arg;
+ struct camera_data *cam = video_drvdata(file);
if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -715,9 +723,9 @@ static int ioctl_cropcap(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_queryctrl(void *arg,struct camera_data *cam)
+static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
{
- struct v4l2_queryctrl *c = arg;
+ struct camera_data *cam = video_drvdata(file);
int i;
for(i=0; i<NUM_CONTROLS; ++i) {
@@ -783,12 +791,9 @@ static int ioctl_queryctrl(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_querymenu(void *arg,struct camera_data *cam)
+static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m)
{
- struct v4l2_querymenu *m = arg;
-
- memset(m->name, 0, sizeof(m->name));
- m->reserved = 0;
+ struct camera_data *cam = video_drvdata(file);
switch(m->id) {
case CPIA2_CID_FLICKER_MODE:
@@ -837,9 +842,9 @@ static int ioctl_querymenu(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_g_ctrl(void *arg,struct camera_data *cam)
+static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
{
- struct v4l2_control *c = arg;
+ struct camera_data *cam = video_drvdata(file);
switch(c->id) {
case V4L2_CID_BRIGHTNESS:
@@ -955,9 +960,9 @@ static int ioctl_g_ctrl(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_s_ctrl(void *arg,struct camera_data *cam)
+static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
{
- struct v4l2_control *c = arg;
+ struct camera_data *cam = video_drvdata(file);
int i;
int retval = 0;
@@ -1031,9 +1036,9 @@ static int ioctl_s_ctrl(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_g_jpegcomp(void *arg,struct camera_data *cam)
+static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
{
- struct v4l2_jpegcompression *parms = arg;
+ struct camera_data *cam = video_drvdata(file);
memset(parms, 0, sizeof(*parms));
@@ -1072,9 +1077,9 @@ static int ioctl_g_jpegcomp(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_s_jpegcomp(void *arg,struct camera_data *cam)
+static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
{
- struct v4l2_jpegcompression *parms = arg;
+ struct camera_data *cam = video_drvdata(file);
DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n",
parms->APP_len, parms->COM_len);
@@ -1121,9 +1126,9 @@ static int ioctl_s_jpegcomp(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_reqbufs(void *arg,struct camera_data *cam)
+static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req)
{
- struct v4l2_requestbuffers *req = arg;
+ struct camera_data *cam = video_drvdata(file);
if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
req->memory != V4L2_MEMORY_MMAP)
@@ -1144,9 +1149,9 @@ static int ioctl_reqbufs(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_querybuf(void *arg,struct camera_data *cam)
+static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- struct v4l2_buffer *buf = arg;
+ struct camera_data *cam = video_drvdata(file);
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
buf->index > cam->num_frames)
@@ -1192,9 +1197,9 @@ static int ioctl_querybuf(void *arg,struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_qbuf(void *arg,struct camera_data *cam)
+static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- struct v4l2_buffer *buf = arg;
+ struct camera_data *cam = video_drvdata(file);
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
buf->memory != V4L2_MEMORY_MMAP ||
@@ -1248,9 +1253,9 @@ static int find_earliest_filled_buffer(struct camera_data *cam)
*
*****************************************************************************/
-static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file)
+static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
- struct v4l2_buffer *buf = arg;
+ struct camera_data *cam = video_drvdata(file);
int frame;
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
@@ -1296,210 +1301,56 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file)
return 0;
}
-/******************************************************************************
- *
- * cpia2_ioctl
- *
- *****************************************************************************/
-static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p)
{
- struct camera_data *cam = video_drvdata(file);
- long retval = 0;
-
- if (!cam)
- return -ENOTTY;
-
- if (!cam->present)
- return -ENODEV;
-
- /* Priority check */
- switch (cmd) {
- case VIDIOC_S_FMT:
- {
- struct cpia2_fh *fh = file->private_data;
- retval = v4l2_prio_check(&cam->prio, fh->prio);
- if (retval)
- return retval;
- break;
- }
- default:
- break;
- }
-
- switch (cmd) {
- /* CPIA2 extension to Video4Linux API */
- case CPIA2_IOC_SET_GPIO:
- retval = ioctl_set_gpio(arg, cam);
- break;
- case VIDIOC_QUERYCAP:
- retval = ioctl_querycap(arg,cam);
- break;
-
- case VIDIOC_ENUMINPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_S_INPUT:
- retval = ioctl_input(cmd, arg, cam);
- break;
-
- case VIDIOC_ENUM_FMT:
- retval = ioctl_enum_fmt(arg,cam);
- break;
- case VIDIOC_TRY_FMT:
- retval = ioctl_try_fmt(arg,cam);
- break;
- case VIDIOC_G_FMT:
- retval = ioctl_get_fmt(arg,cam);
- break;
- case VIDIOC_S_FMT:
- retval = ioctl_set_fmt(arg,cam,file->private_data);
- break;
+ struct cpia2_fh *fh = _fh;
- case VIDIOC_CROPCAP:
- retval = ioctl_cropcap(arg,cam);
- break;
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP:
- // TODO: I think cropping can be implemented - SJB
- retval = -EINVAL;
- break;
-
- case VIDIOC_QUERYCTRL:
- retval = ioctl_queryctrl(arg,cam);
- break;
- case VIDIOC_QUERYMENU:
- retval = ioctl_querymenu(arg,cam);
- break;
- case VIDIOC_G_CTRL:
- retval = ioctl_g_ctrl(arg,cam);
- break;
- case VIDIOC_S_CTRL:
- retval = ioctl_s_ctrl(arg,cam);
- break;
-
- case VIDIOC_G_JPEGCOMP:
- retval = ioctl_g_jpegcomp(arg,cam);
- break;
- case VIDIOC_S_JPEGCOMP:
- retval = ioctl_s_jpegcomp(arg,cam);
- break;
-
- case VIDIOC_G_PRIORITY:
- {
- struct cpia2_fh *fh = file->private_data;
- *(enum v4l2_priority*)arg = fh->prio;
- break;
- }
- case VIDIOC_S_PRIORITY:
- {
- struct cpia2_fh *fh = file->private_data;
- enum v4l2_priority prio;
- prio = *(enum v4l2_priority*)arg;
- if(cam->streaming &&
- prio != fh->prio &&
- fh->prio == V4L2_PRIORITY_RECORD) {
- /* Can't drop record priority while streaming */
- retval = -EBUSY;
- } else if(prio == V4L2_PRIORITY_RECORD &&
- prio != fh->prio &&
- v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) {
- /* Only one program can record at a time */
- retval = -EBUSY;
- } else {
- retval = v4l2_prio_change(&cam->prio, &fh->prio, prio);
- }
- break;
- }
-
- case VIDIOC_REQBUFS:
- retval = ioctl_reqbufs(arg,cam);
- break;
- case VIDIOC_QUERYBUF:
- retval = ioctl_querybuf(arg,cam);
- break;
- case VIDIOC_QBUF:
- retval = ioctl_qbuf(arg,cam);
- break;
- case VIDIOC_DQBUF:
- retval = ioctl_dqbuf(arg,cam,file);
- break;
- case VIDIOC_STREAMON:
- {
- int type;
- DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
- type = *(int*)arg;
- if(!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- retval = -EINVAL;
-
- if(!cam->streaming) {
- retval = cpia2_usb_stream_start(cam,
- cam->params.camera_state.stream_mode);
- } else {
- retval = -EINVAL;
- }
-
- break;
- }
- case VIDIOC_STREAMOFF:
- {
- int type;
- DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
- type = *(int*)arg;
- if(!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- retval = -EINVAL;
-
- if(cam->streaming) {
- retval = cpia2_usb_stream_stop(cam);
- } else {
- retval = -EINVAL;
- }
-
- break;
- }
-
- case VIDIOC_ENUMOUTPUT:
- case VIDIOC_G_OUTPUT:
- case VIDIOC_S_OUTPUT:
- case VIDIOC_G_MODULATOR:
- case VIDIOC_S_MODULATOR:
-
- case VIDIOC_ENUMAUDIO:
- case VIDIOC_G_AUDIO:
- case VIDIOC_S_AUDIO:
+ *p = fh->prio;
+ return 0;
+}
- case VIDIOC_ENUMAUDOUT:
- case VIDIOC_G_AUDOUT:
- case VIDIOC_S_AUDOUT:
+static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct cpia2_fh *fh = fh;
- case VIDIOC_ENUMSTD:
- case VIDIOC_QUERYSTD:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
+ if (cam->streaming && prio != fh->prio &&
+ fh->prio == V4L2_PRIORITY_RECORD)
+ /* Can't drop record priority while streaming */
+ return -EBUSY;
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY:
+ if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio &&
+ v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD)
+ /* Only one program can record at a time */
+ return -EBUSY;
+ return v4l2_prio_change(&cam->prio, &fh->prio, prio);
+}
- case VIDIOC_OVERLAY:
- case VIDIOC_G_FBUF:
- case VIDIOC_S_FBUF:
+static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct camera_data *cam = video_drvdata(file);
- case VIDIOC_G_PARM:
- case VIDIOC_S_PARM:
- retval = -EINVAL;
- break;
- default:
- retval = -ENOIOCTLCMD;
- break;
- }
+ DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
- return retval;
+ if (!cam->streaming)
+ return cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ return -EINVAL;
}
-static long cpia2_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
{
- return video_usercopy(file, cmd, arg, cpia2_do_ioctl);
+ struct camera_data *cam = video_drvdata(file);
+
+ DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->streaming)
+ return cpia2_usb_stream_stop(cam);
+ return -EINVAL;
}
/******************************************************************************
@@ -1550,6 +1401,33 @@ static void reset_camera_struct_v4l(struct camera_data *cam)
v4l2_prio_init(&cam->prio);
}
+static const struct v4l2_ioctl_ops cpia2_ioctl_ops = {
+ .vidioc_querycap = cpia2_querycap,
+ .vidioc_enum_input = cpia2_enum_input,
+ .vidioc_g_input = cpia2_g_input,
+ .vidioc_s_input = cpia2_s_input,
+ .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap,
+ .vidioc_queryctrl = cpia2_queryctrl,
+ .vidioc_querymenu = cpia2_querymenu,
+ .vidioc_g_ctrl = cpia2_g_ctrl,
+ .vidioc_s_ctrl = cpia2_s_ctrl,
+ .vidioc_g_jpegcomp = cpia2_g_jpegcomp,
+ .vidioc_s_jpegcomp = cpia2_s_jpegcomp,
+ .vidioc_cropcap = cpia2_cropcap,
+ .vidioc_reqbufs = cpia2_reqbufs,
+ .vidioc_querybuf = cpia2_querybuf,
+ .vidioc_qbuf = cpia2_qbuf,
+ .vidioc_dqbuf = cpia2_dqbuf,
+ .vidioc_streamon = cpia2_streamon,
+ .vidioc_streamoff = cpia2_streamoff,
+ .vidioc_g_priority = cpia2_g_priority,
+ .vidioc_s_priority = cpia2_s_priority,
+ .vidioc_default = cpia2_default,
+};
+
/***
* The v4l video device structure initialized for this device
***/
@@ -1559,7 +1437,7 @@ static const struct v4l2_file_operations cpia2_fops = {
.release = cpia2_close,
.read = cpia2_v4l_read,
.poll = cpia2_v4l_poll,
- .unlocked_ioctl = cpia2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
.mmap = cpia2_mmap,
};
@@ -1567,6 +1445,7 @@ static struct video_device cpia2_template = {
/* I could not find any place for the old .initialize initializer?? */
.name = "CPiA2 Camera",
.fops = &cpia2_fops,
+ .ioctl_ops = &cpia2_ioctl_ops,
.release = video_device_release,
};
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
index 9358fe77e562..5909f2557ab4 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/video/cs5345.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
MODULE_AUTHOR("Hans Verkuil");
@@ -36,6 +37,20 @@ module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+struct cs5345_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs5345_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cs5345_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd;
+}
/* ----------------------------------------------------------------------- */
@@ -65,33 +80,20 @@ static int cs5345_s_routing(struct v4l2_subdev *sd,
return 0;
}
-static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl)
{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0;
- return 0;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = cs5345_read(sd, 0x07) & 0x3f;
- if (ctrl->value >= 32)
- ctrl->value = ctrl->value - 64;
- return 0;
-}
+ struct v4l2_subdev *sd = to_sd(ctrl);
-static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0);
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f);
+ cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f);
return 0;
}
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 24 || ctrl->value < -24)
- return -EINVAL;
- cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f);
- cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f);
- return 0;
+ return -EINVAL;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -144,11 +146,20 @@ static int cs5345_log_status(struct v4l2_subdev *sd)
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cs5345_ctrl_ops = {
+ .s_ctrl = cs5345_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cs5345_core_ops = {
.log_status = cs5345_log_status,
.g_chip_ident = cs5345_g_chip_ident,
- .g_ctrl = cs5345_g_ctrl,
- .s_ctrl = cs5345_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cs5345_g_register,
.s_register = cs5345_s_register,
@@ -169,6 +180,7 @@ static const struct v4l2_subdev_ops cs5345_ops = {
static int cs5345_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct cs5345_state *state;
struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
@@ -178,11 +190,28 @@ static int cs5345_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
- if (sd == NULL)
+ state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL);
+ if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cs5345_ops);
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ /* set volume/mute */
+ v4l2_ctrl_handler_setup(&state->hdl);
+
cs5345_write(sd, 0x02, 0x00);
cs5345_write(sd, 0x04, 0x01);
cs5345_write(sd, 0x09, 0x01);
@@ -194,9 +223,11 @@ static int cs5345_probe(struct i2c_client *client,
static int cs5345_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cs5345_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
index 43d09a24b262..4a24ffb17a7d 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -342,17 +342,6 @@ void cx18_av_audio_set_path(struct cx18 *cx)
}
}
-static int get_volume(struct cx18 *cx)
-{
- /* Volume runs +18dB to -96dB in 1/2dB steps
- * change to fit the msp3400 -114dB to +12dB range */
-
- /* check PATH1_VOLUME */
- int vol = 228 - cx18_av_read(cx, 0x8d4);
- vol = (vol / 2) + 23;
- return vol << 9;
-}
-
static void set_volume(struct cx18 *cx, int volume)
{
/* First convert the volume to msp3400 values (0-127) */
@@ -369,52 +358,18 @@ static void set_volume(struct cx18 *cx, int volume)
cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
}
-static int get_bass(struct cx18 *cx)
-{
- /* bass is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_BASS_VOL */
- int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
- bass = (((48 - bass) * 0xffff) + 47) / 48;
- return bass;
-}
-
static void set_bass(struct cx18 *cx, int bass)
{
/* PATH1_EQ_BASS_VOL */
cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
}
-static int get_treble(struct cx18 *cx)
-{
- /* treble is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_TREBLE_VOL */
- int treble = cx18_av_read(cx, 0x8db) & 0x3f;
- treble = (((48 - treble) * 0xffff) + 47) / 48;
- return treble;
-}
-
static void set_treble(struct cx18 *cx, int treble)
{
/* PATH1_EQ_TREBLE_VOL */
cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
}
-static int get_balance(struct cx18 *cx)
-{
- /* balance is 7 bit, 0 to -96dB */
-
- /* check PATH1_BAL_LEVEL */
- int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
- /* check PATH1_BAL_LEFT */
- if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
- balance = 0x80 - balance;
- else
- balance = 0x80 + balance;
- return balance << 8;
-}
-
static void set_balance(struct cx18 *cx, int balance)
{
int bal = balance >> 8;
@@ -431,12 +386,6 @@ static void set_balance(struct cx18 *cx, int balance)
}
}
-static int get_mute(struct cx18 *cx)
-{
- /* check SRC1_MUTE_EN */
- return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
-}
-
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
@@ -490,50 +439,33 @@ int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return retval;
}
-int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = get_volume(cx);
- break;
- case V4L2_CID_AUDIO_BASS:
- ctrl->value = get_bass(cx);
- break;
- case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = get_treble(cx);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = get_balance(cx);
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = get_mute(cx);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
-int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl)
-{
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
- set_volume(cx, ctrl->value);
+ set_volume(cx, ctrl->val);
break;
case V4L2_CID_AUDIO_BASS:
- set_bass(cx, ctrl->value);
+ set_bass(cx, ctrl->val);
break;
case V4L2_CID_AUDIO_TREBLE:
- set_treble(cx, ctrl->value);
+ set_treble(cx, ctrl->val);
break;
case V4L2_CID_AUDIO_BALANCE:
- set_balance(cx, ctrl->value);
+ set_balance(cx, ctrl->val);
break;
case V4L2_CID_AUDIO_MUTE:
- set_mute(cx, ctrl->value);
+ set_mute(cx, ctrl->val);
break;
default:
return -EINVAL;
}
return 0;
}
+
+const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = {
+ .s_ctrl = cx18_av_audio_s_ctrl,
+};
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index a41951cab276..f164b7f610a5 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -129,6 +129,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
{
struct cx18_av_state *state = to_cx18_av_state(sd);
struct cx18 *cx = v4l2_get_subdevdata(sd);
+ int default_volume;
u32 v;
cx18_av_loadfw(cx);
@@ -247,8 +248,23 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
/* } */
cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
- state->default_volume = 228 - cx18_av_read(cx, 0x8d4);
- state->default_volume = ((state->default_volume / 2) + 23) << 9;
+ default_volume = cx18_av_read(cx, 0x8d4);
+ /*
+ * Enforce the legacy volume scale mapping limits to avoid
+ * -ERANGE errors when initializing the volume control
+ */
+ if (default_volume > 228) {
+ /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */
+ default_volume = 228;
+ cx18_av_write(cx, 0x8d4, 228);
+ } else if (default_volume < 20) {
+ /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */
+ default_volume = 20;
+ cx18_av_write(cx, 0x8d4, 20);
+ }
+ default_volume = (((228 - default_volume) >> 1) + 23) << 9;
+ state->volume->cur.val = state->volume->default_value = default_volume;
+ v4l2_ctrl_handler_setup(&state->hdl);
}
static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
@@ -901,126 +917,35 @@ static int cx18_av_s_radio(struct v4l2_subdev *sd)
return 0;
}
-static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct cx18 *cx = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- CX18_ERR_DEV(sd, "invalid brightness setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx18_av_write(cx, 0x414, ctrl->value - 128);
+ cx18_av_write(cx, 0x414, ctrl->val - 128);
break;
case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- CX18_ERR_DEV(sd, "invalid contrast setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx18_av_write(cx, 0x415, ctrl->value << 1);
+ cx18_av_write(cx, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- CX18_ERR_DEV(sd, "invalid saturation setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx18_av_write(cx, 0x420, ctrl->value << 1);
- cx18_av_write(cx, 0x421, ctrl->value << 1);
+ cx18_av_write(cx, 0x420, ctrl->val << 1);
+ cx18_av_write(cx, 0x421, ctrl->val << 1);
break;
case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- CX18_ERR_DEV(sd, "invalid hue setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx18_av_write(cx, 0x422, ctrl->value);
+ cx18_av_write(cx, 0x422, ctrl->val);
break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- return cx18_av_audio_s_ctrl(cx, ctrl);
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = cx18_av_read(cx, 0x415) >> 1;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = cx18_av_read(cx, 0x420) >> 1;
- break;
- case V4L2_CID_HUE:
- ctrl->value = (s8)cx18_av_read(cx, 0x422);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- return cx18_av_audio_g_ctrl(cx, ctrl);
default:
return -EINVAL;
}
return 0;
}
-static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct cx18_av_state *state = to_cx18_av_state(sd);
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- default:
- break;
- }
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- default:
- return -EINVAL;
- }
- return -EINVAL;
-}
-
static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
{
struct cx18_av_state *state = to_cx18_av_state(sd);
@@ -1356,14 +1281,22 @@ static int cx18_av_s_register(struct v4l2_subdev *sd,
}
#endif
+static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = {
+ .s_ctrl = cx18_av_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
.g_chip_ident = cx18_av_g_chip_ident,
.log_status = cx18_av_log_status,
.load_fw = cx18_av_load_fw,
.reset = cx18_av_reset,
- .queryctrl = cx18_av_queryctrl,
- .g_ctrl = cx18_av_g_ctrl,
- .s_ctrl = cx18_av_s_ctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = cx18_av_s_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx18_av_g_register,
@@ -1427,8 +1360,42 @@ int cx18_av_probe(struct cx18 *cx)
snprintf(sd->name, sizeof(sd->name),
"%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
sd->grp_id = CX18_HW_418_AV;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65535, 65535 / 100, 0);
+ v4l2_ctrl_new_std(&state->hdl,
+ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 0, 65535, 65535 / 100, 32768);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ return err;
+ }
err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
- if (!err)
+ if (err)
+ v4l2_ctrl_handler_free(&state->hdl);
+ else
cx18_av_init(cx);
return err;
}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index 1956991795e3..188c9c3d2db1 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -26,6 +26,7 @@
#define _CX18_AV_CORE_H_
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
struct cx18;
@@ -95,13 +96,14 @@ enum cx18_av_audio_input {
struct cx18_av_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *volume;
int radio;
v4l2_std_id std;
enum cx18_av_video_input vid_input;
enum cx18_av_audio_input aud_input;
u32 audclk_freq;
int audmode;
- int default_volume;
u32 id;
u32 rev;
int is_initialized;
@@ -347,6 +349,11 @@ static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx18_av_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
/* cx18_av-core.c */
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
@@ -369,10 +376,9 @@ int cx18_av_loadfw(struct cx18 *cx);
/* ----------------------------------------------------------------------- */
/* cx18_av-audio.c */
-int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
-int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
void cx18_av_audio_set_path(struct cx18 *cx);
+extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops;
/* ----------------------------------------------------------------------- */
/* cx18_av-vbi.c */
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 97d7b7e100a3..282a3d29fdaa 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -30,152 +30,11 @@
#include "cx18-mailbox.h"
#include "cx18-controls.h"
-/* Must be sorted from low to high control ID! */
-static const u32 user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_BALANCE,
- V4L2_CID_AUDIO_BASS,
- V4L2_CID_AUDIO_TREBLE,
- V4L2_CID_AUDIO_MUTE,
- V4L2_CID_AUDIO_LOUDNESS,
- 0
-};
-
-static const u32 *ctrl_classes[] = {
- user_ctrls,
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
-{
- struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
- const char *name;
-
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- switch (qctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_USER_CLASS:
- return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- default:
- if (cx2341x_ctrl_query(&cx->params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
- }
- strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
- qctrl->name[sizeof(qctrl->name) - 1] = 0;
- return 0;
-}
-
-int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
+static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
- struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- cx18_queryctrl(file, fh, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&cx->params, qmenu->id));
-}
-
-static int cx18_try_ctrl(struct file *file, void *fh,
- struct v4l2_ext_control *vctrl)
-{
- struct v4l2_queryctrl qctrl;
- const char * const *menu_items = NULL;
- int err;
-
- qctrl.id = vctrl->id;
- err = cx18_queryctrl(file, fh, &qctrl);
- if (err)
- return err;
- if (qctrl.type == V4L2_CTRL_TYPE_MENU)
- menu_items = v4l2_ctrl_get_menu(qctrl.id);
- return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
-}
-
-static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
-{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
-
- default:
- CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
-{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+ int type = cxhdl->stream_type->val;
- default:
- CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int cx18_setup_vbi_fmt(struct cx18 *cx,
- enum v4l2_mpeg_stream_vbi_fmt fmt,
- enum v4l2_mpeg_stream_type type)
-{
- if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
- return -EINVAL;
if (atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
@@ -230,121 +89,43 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx,
return 0;
}
-int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
{
- struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
- struct v4l2_control ctrl;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = cx18_g_ctrl(cx, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&cx->params, 0, c, VIDIOC_G_EXT_CTRLS);
- return -EINVAL;
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_mbus_framefmt fmt;
+
+ /* fix videodecoder resolution */
+ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+ fmt.height = cxhdl->height;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
+ return 0;
}
-int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
{
- struct cx18_open_id *id = fh;
- struct cx18 *cx = id->cx;
- int ret;
- struct v4l2_control ctrl;
-
- ret = v4l2_prio_check(&cx->prio, id->prio);
- if (ret)
- return ret;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = cx18_s_ctrl(cx, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- static u32 freqs[3] = { 44100, 48000, 32000 };
- struct cx18_api_func_private priv;
- struct cx2341x_mpeg_params p = cx->params;
- int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
- c, VIDIOC_S_EXT_CTRLS);
- unsigned int idx;
-
- if (err)
- return err;
+ static const u32 freqs[3] = { 44100, 48000, 32000 };
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
- if (p.video_encoding != cx->params.video_encoding) {
- int is_mpeg1 = p.video_encoding ==
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
- struct v4l2_mbus_framefmt fmt;
-
- /* fix videodecoder resolution */
- fmt.width = cx->params.width / (is_mpeg1 ? 2 : 1);
- fmt.height = cx->params.height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
- v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
- }
- priv.cx = cx;
- priv.s = &cx->streams[id->type];
- err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
- if (!err &&
- (cx->params.stream_vbi_fmt != p.stream_vbi_fmt ||
- cx->params.stream_type != p.stream_type))
- err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt,
- p.stream_type);
- cx->params = p;
- cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
- idx = p.audio_properties & 0x03;
- /* The audio clock of the digitizer must match the codec sample
- rate otherwise you get some very strange effects. */
- if (idx < ARRAY_SIZE(freqs))
- cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
- return err;
- }
- return -EINVAL;
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < ARRAY_SIZE(freqs))
+ cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
+ return 0;
}
-int cx18_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
{
- struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- err = cx18_try_ctrl(file, fh, &c->controls[i]);
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&cx->params,
- atomic_read(&cx->ana_capturing),
- c, VIDIOC_TRY_EXT_CTRLS);
- return -EINVAL;
+ cx->dualwatch_stereo_mode = val;
+ return 0;
}
+
+struct cx2341x_handler_ops cx18_cxhdl_ops = {
+ .s_audio_mode = cx18_s_audio_mode,
+ .s_audio_sampling_freq = cx18_s_audio_sampling_freq,
+ .s_video_encoding = cx18_s_video_encoding,
+ .s_stream_vbi_fmt = cx18_s_stream_vbi_fmt,
+};
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h
index e46323700b81..cb5dfc7b2054 100644
--- a/drivers/media/video/cx18/cx18-controls.h
+++ b/drivers/media/video/cx18/cx18-controls.h
@@ -21,9 +21,4 @@
* 02111-1307 USA
*/
-int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
-int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int cx18_try_ext_ctrls(struct file *file, void *fh,
- struct v4l2_ext_controls *a);
-int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
+extern struct cx2341x_handler_ops cx18_cxhdl_ops;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index b1c3cbd92743..321c1b79794c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -36,6 +36,7 @@
#include "cx18-scb.h"
#include "cx18-mailbox.h"
#include "cx18-ioctl.h"
+#include "cx18-controls.h"
#include "tuner-xc2028.h"
#include <media/tveeprom.h>
@@ -729,15 +730,22 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
cx->open_id = 1;
/* Initial settings */
- cx2341x_fill_defaults(&cx->params);
- cx->temporal_strength = cx->params.video_temporal_filter;
- cx->spatial_strength = cx->params.video_spatial_filter;
- cx->filter_mode = cx->params.video_spatial_filter_mode |
- (cx->params.video_temporal_filter_mode << 1) |
- (cx->params.video_median_filter_type << 2);
- cx->params.port = CX2341X_PORT_MEMORY;
- cx->params.capabilities =
- CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+ cx->cxhdl.port = CX2341X_PORT_MEMORY;
+ cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+ cx->cxhdl.ops = &cx18_cxhdl_ops;
+ cx->cxhdl.func = cx18_api_func;
+ cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ ret = cx2341x_handler_init(&cx->cxhdl, 50);
+ if (ret)
+ return ret;
+ cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+
+ cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+ cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+ cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+ (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+ (cx->cxhdl.video_median_filter_type->cur.val << 2);
+
init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq);
@@ -1049,7 +1057,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
else
cx->is_50hz = 1;
- cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
+ cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
@@ -1095,7 +1103,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
/* Load cx18 submodules (cx18-alsa) */
request_modules(cx);
-
return 0;
free_streams:
@@ -1278,6 +1285,8 @@ static void cx18_remove(struct pci_dev *pci_dev)
for (i = 0; i < CX18_VBI_FRAMES; i++)
kfree(cx->vbi.sliced_mpeg_data[i]);
+ v4l2_ctrl_handler_free(&cx->av_state.hdl);
+
CX18_INFO("Removed %s\n", cx->card_name);
v4l2_device_unregister(v4l2_dev);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index f736679d2517..de2457741e26 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -565,7 +565,7 @@ struct cx18 {
struct cx18_av_state av_state;
/* codec settings */
- struct cx2341x_mpeg_params params;
+ struct cx2341x_handler cxhdl;
u32 filter_mode;
u32 temporal_strength;
u32 spatial_strength;
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 9f23b90732f2..98ef33e4326a 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -160,13 +160,10 @@ EXPORT_SYMBOL(cx18_release_stream);
static void cx18_dualwatch(struct cx18 *cx)
{
struct v4l2_tuner vt;
- u32 new_bitmap;
u32 new_stereo_mode;
- const u32 stereo_mask = 0x0300;
const u32 dual = 0x0200;
- u32 h;
- new_stereo_mode = cx->params.audio_properties & stereo_mask;
+ new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
memset(&vt, 0, sizeof(vt));
cx18_call_all(cx, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
@@ -176,25 +173,10 @@ static void cx18_dualwatch(struct cx18 *cx)
if (new_stereo_mode == cx->dualwatch_stereo_mode)
return;
- new_bitmap = new_stereo_mode
- | (cx->params.audio_properties & ~stereo_mask);
-
- CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. "
- "new audio_bitmask=0x%ux\n",
- cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
-
- h = cx18_find_handle(cx);
- if (h == CX18_INVALID_TASK_HANDLE) {
- CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n");
- return;
- }
-
- if (cx18_vapi(cx,
- CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) {
- cx->dualwatch_stereo_mode = new_stereo_mode;
- return;
- }
- CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+ CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ cx->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode))
+ CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
@@ -724,8 +706,8 @@ int cx18_v4l2_close(struct file *filp)
if (atomic_read(&cx->ana_capturing) > 0) {
/* Undo video mute */
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
- cx->params.video_mute |
- (cx->params.video_mute_yuv << 8));
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8)));
}
/* Done! Unmute and continue. */
cx18_unmute(cx);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 7150195740dc..36b018c943e5 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -152,8 +152,8 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
struct cx18 *cx = id->cx;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- pixfmt->width = cx->params.width;
- pixfmt->height = cx->params.height;
+ pixfmt->width = cx->cxhdl.width;
+ pixfmt->height = cx->cxhdl.height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
pixfmt->priv = 0;
@@ -287,14 +287,14 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
- if (cx->params.width == w && cx->params.height == h)
+ if (cx->cxhdl.width == w && cx->cxhdl.height == h)
return 0;
if (atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
- mbus_fmt.width = cx->params.width = w;
- mbus_fmt.height = cx->params.height = h;
+ mbus_fmt.width = cx->cxhdl.width = w;
+ mbus_fmt.height = cx->cxhdl.height = h;
mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
return cx18_g_fmt_vid_cap(file, fh, fmt);
@@ -696,9 +696,10 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
cx->std = *std;
cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
- cx->params.width = 720;
- cx->params.height = cx->is_50hz ? 576 : 480;
+ cx->is_50hz = !cx->is_60hz;
+ cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
+ cx->cxhdl.width = 720;
+ cx->cxhdl.height = cx->is_50hz ? 576 : 480;
cx->vbi.count = cx->is_50hz ? 18 : 12;
cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
@@ -1035,7 +1036,7 @@ static int cx18_log_status(struct file *file, void *fh)
mutex_unlock(&cx->gpio_lock);
CX18_INFO("Tuner: %s\n",
test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&cx->params, cx->v4l2_dev.name);
+ v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
for (i = 0; i < CX18_MAX_STREAMS; i++) {
struct cx18_stream *s = &cx->streams[i];
@@ -1136,11 +1137,6 @@ static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
.vidioc_s_register = cx18_s_register,
#endif
.vidioc_default = cx18_default,
- .vidioc_queryctrl = cx18_queryctrl,
- .vidioc_querymenu = cx18_querymenu,
- .vidioc_g_ext_ctrls = cx18_g_ext_ctrls,
- .vidioc_s_ext_ctrls = cx18_s_ext_ctrls,
- .vidioc_try_ext_ctrls = cx18_try_ext_ctrls,
};
void cx18_set_funcs(struct video_device *vdev)
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index c545f3beef78..9605d54bd083 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -716,9 +716,8 @@ static int cx18_set_filter_param(struct cx18_stream *s)
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA])
{
- struct cx18_api_func_private *api_priv = priv;
- struct cx18 *cx = api_priv->cx;
- struct cx18_stream *s = api_priv->s;
+ struct cx18_stream *s = priv;
+ struct cx18 *cx = s->cx;
switch (cmd) {
case CX2341X_ENC_SET_OUTPUT_PORT:
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index 077952fcbcca..05fe6bdbe062 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -81,11 +81,6 @@ struct cx18_mailbox {
struct cx18_stream;
-struct cx18_api_func_private {
- struct cx18 *cx;
- struct cx18_stream *s;
-};
-
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int args, ...);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 94f5d7967c5c..2d248560770e 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -572,7 +572,7 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s)
* Set the MDL size to the exact size needed for one frame.
* Use enough buffers per MDL to cover the MDL size
*/
- s->mdl_size = 720 * s->cx->params.height * 3 / 2;
+ s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
s->bufs_per_mdl = s->mdl_size / s->buf_size;
if (s->mdl_size % s->buf_size)
s->bufs_per_mdl++;
@@ -607,7 +607,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
int captype = 0;
- struct cx18_api_func_private priv;
struct cx18_stream *s_idx;
if (!cx18_stream_enabled(s))
@@ -620,7 +619,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_MPEG;
cx->mpg_data_received = cx->vbi_data_inserted = 0;
cx->dualwatch_jiffies = jiffies;
- cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300;
+ cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
cx->search_pack_header = 0;
break;
@@ -710,21 +709,21 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
/* Call out to the common CX2341x API setup for user controls */
- priv.cx = cx;
- priv.s = s;
- cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
+ cx->cxhdl.priv = s;
+ cx2341x_handler_setup(&cx->cxhdl);
/*
* When starting a capture and we're set for radio,
* ensure the video is muted, despite the user control.
*/
- if (!cx->params.video_mute &&
+ if (!cx->cxhdl.video_mute &&
test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
- (cx->params.video_mute_yuv << 8) | 1);
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
}
if (atomic_read(&cx->tot_capturing) == 0) {
+ cx2341x_handler_set_busy(&cx->cxhdl, 1);
clear_bit(CX18_F_I_EOS, &cx->i_flags);
cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
}
@@ -826,6 +825,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
if (atomic_read(&cx->tot_capturing) > 0)
return 0;
+ cx2341x_handler_set_busy(&cx->cxhdl, 0);
cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
wake_up(&s->waitq);
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 7e40035028d2..935f557acbd0 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -477,7 +477,7 @@
/* The are no buffers ready. Try again soon! */
#define CXERR_NODATA_AGAIN 0x00001E
-/* The stream is stopping. Function not alllowed now! */
+/* The stream is stopping. Function not allowed now! */
#define CXERR_STOPPING_STATUS 0x00001F
/* Trying to access hardware when the power is turned OFF */
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
index fc9526a5b746..f8f0e59cd583 100644
--- a/drivers/media/video/cx231xx/cx231xx-417.c
+++ b/drivers/media/video/cx231xx/cx231xx-417.c
@@ -942,13 +942,13 @@ static int cx231xx_load_firmware(struct cx231xx *dev)
p_current_fw = vmalloc(1884180 * 4);
p_fw = p_current_fw;
- if (p_current_fw == 0) {
+ if (p_current_fw == NULL) {
dprintk(2, "FAIL!!!\n");
return -1;
}
p_buffer = vmalloc(4096);
- if (p_buffer == 0) {
+ if (p_buffer == NULL) {
dprintk(2, "FAIL!!!\n");
return -1;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index c53e97295a0d..62843d39817c 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -759,11 +759,8 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev,
case CX231XX_VMUX_TELEVISION:
case CX231XX_VMUX_CABLE:
default:
- switch (dev->model) {
- case CX231XX_BOARD_CNXT_CARRAERA:
- case CX231XX_BOARD_CNXT_RDE_250:
- case CX231XX_BOARD_CNXT_SHELBY:
- case CX231XX_BOARD_CNXT_RDU_250:
+ /* TODO: Test if this is also needed for xc2028/xc3028 */
+ if (dev->board.tuner_type == TUNER_XC5000) {
/* Disable the use of DIF */
status = vid_blk_read_word(dev, AFE_CTRL, &value);
@@ -820,8 +817,7 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev,
MODE_CTRL, FLD_INPUT_MODE,
cx231xx_set_field(FLD_INPUT_MODE,
INPUT_MODE_CVBS_0));
- break;
- default:
+ } else {
/* Enable the DIF for the tuner */
/* Reinitialize the DIF */
@@ -1275,6 +1271,8 @@ int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3)
int status = 0;
bool current_is_port_3;
+ if (dev->board.dont_use_port_3)
+ is_port_3 = false;
status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
PWR_CTL_EN, value, 4);
if (status < 0)
@@ -2550,7 +2548,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
case 4: /* ts1 */
cx231xx_info("%s: set ts1 registers", __func__);
- if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+ if (dev->board.has_417) {
cx231xx_info(" MPEG\n");
value &= 0xFFFFFFFC;
value |= 0x3;
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 588f3e8f028b..f49230d170e6 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -261,6 +261,9 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x1c,
.gpio_pin_status_mask = 0x4001000,
.norm = V4L2_STD_PAL,
+ .no_alt_vanc = 1,
+ .external_av = 1,
+ .has_417 = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
@@ -357,19 +360,19 @@ struct cx231xx_board cx231xx_boards[] = {
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
} },
},
[CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = {
@@ -382,18 +385,20 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.norm = V4L2_STD_NTSC,
+ .no_alt_vanc = 1,
+ .external_av = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
} },
},
[CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = {
@@ -420,21 +425,50 @@ struct cx231xx_board cx231xx_boards[] = {
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
} },
},
+ [CX231XX_BOARD_PV_XCAPTURE_USB] = {
+ .name = "Pixelview Xcapture USB",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .norm = V4L2_STD_NTSC,
+ .no_alt_vanc = 1,
+ .external_av = 1,
+ .dont_use_port_3 = 1,
+
+ .input = {{
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
@@ -464,6 +498,8 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2},
{USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001),
.driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID},
+ {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014),
+ .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB},
{},
};
@@ -772,7 +808,7 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
/* Reset other chips required if they are tied up with GPIO pins */
cx231xx_add_into_devlist(dev);
- if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+ if (dev->board.has_417) {
printk(KERN_INFO "attach 417 %d\n", dev->model);
if (cx231xx_417_register(dev) < 0) {
printk(KERN_ERR
@@ -844,110 +880,110 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
- if (ifnum == 1) {
- /*
- * Interface number 0 - IR interface
- */
- /* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
- cx231xx_devused |= 1 << nr;
-
- if (nr >= CX231XX_MAXBOARDS) {
- cx231xx_err(DRIVER_NAME
- ": Supports only %i cx231xx boards.\n", CX231XX_MAXBOARDS);
- cx231xx_devused &= ~(1 << nr);
- return -ENOMEM;
- }
-
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- cx231xx_err(DRIVER_NAME ": out of memory!\n");
- cx231xx_devused &= ~(1 << nr);
- return -ENOMEM;
- }
-
- snprintf(dev->name, 29, "cx231xx #%d", nr);
- dev->devno = nr;
- dev->model = id->driver_info;
- dev->video_mode.alt = -1;
- dev->interface_count++;
-
- /* reset gpio dir and value */
- dev->gpio_dir = 0;
- dev->gpio_val = 0;
- dev->xc_fw_load_done = 0;
- dev->has_alsa_audio = 1;
- dev->power_mode = -1;
- atomic_set(&dev->devlist_count, 0);
-
- /* 0 - vbi ; 1 -sliced cc mode */
- dev->vbi_or_sliced_cc_mode = 0;
-
- /* get maximum no.of IAD interfaces */
- assoc_desc = udev->actconfig->intf_assoc[0];
- dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
-
- /* init CIR module TBD */
+ /*
+ * Interface number 0 - IR interface (handled by mceusb driver)
+ * Interface number 1 - AV interface (handled by this driver)
+ */
+ if (ifnum != 1)
+ return -ENODEV;
- /* store the current interface */
- lif = interface;
+ /* Check to see next free device and mark as used */
+ nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
+ cx231xx_devused |= 1 << nr;
- /*mode_tv: digital=1 or analog=0*/
- dev->mode_tv = 0;
+ if (nr >= CX231XX_MAXBOARDS) {
+ cx231xx_err(DRIVER_NAME
+ ": Supports only %i cx231xx boards.\n", CX231XX_MAXBOARDS);
+ cx231xx_devused &= ~(1 << nr);
+ return -ENOMEM;
+ }
- dev->USE_ISO = transfer_mode;
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ cx231xx_err(DRIVER_NAME ": out of memory!\n");
+ cx231xx_devused &= ~(1 << nr);
+ return -ENOMEM;
+ }
- switch (udev->speed) {
- case USB_SPEED_LOW:
- speed = "1.5";
- break;
- case USB_SPEED_UNKNOWN:
- case USB_SPEED_FULL:
- speed = "12";
- break;
- case USB_SPEED_HIGH:
- speed = "480";
- break;
- default:
- speed = "unknown";
- }
+ snprintf(dev->name, 29, "cx231xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->video_mode.alt = -1;
+
+ dev->interface_count++;
+ /* reset gpio dir and value */
+ dev->gpio_dir = 0;
+ dev->gpio_val = 0;
+ dev->xc_fw_load_done = 0;
+ dev->has_alsa_audio = 1;
+ dev->power_mode = -1;
+ atomic_set(&dev->devlist_count, 0);
+
+ /* 0 - vbi ; 1 -sliced cc mode */
+ dev->vbi_or_sliced_cc_mode = 0;
+
+ /* get maximum no.of IAD interfaces */
+ assoc_desc = udev->actconfig->intf_assoc[0];
+ dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
+
+ /* init CIR module TBD */
+
+ /* store the current interface */
+ lif = interface;
+
+ /*mode_tv: digital=1 or analog=0*/
+ dev->mode_tv = 0;
+
+ dev->USE_ISO = transfer_mode;
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
- if (udev->manufacturer)
- strlcpy(descr, udev->manufacturer, sizeof(descr));
+ if (udev->manufacturer)
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
- if (udev->product) {
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
- strlcat(descr, udev->product, sizeof(descr));
- }
+ if (udev->product) {
if (*descr)
strlcat(descr, " ", sizeof(descr));
-
- cx231xx_info("New device %s@ %s Mbps "
- "(%04x:%04x) with %d interfaces\n",
- descr,
- speed,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- dev->max_iad_interface_count);
-
- /* store the interface 0 back */
- lif = udev->actconfig->interface[0];
-
- /* increment interface count */
- dev->interface_count++;
-
- /* get device number */
- nr = dev->devno;
-
- assoc_desc = udev->actconfig->intf_assoc[0];
- if (assoc_desc->bFirstInterface != ifnum) {
- cx231xx_err(DRIVER_NAME ": Not found "
- "matching IAD interface\n");
- return -ENODEV;
- }
- } else {
+ strlcat(descr, udev->product, sizeof(descr));
+ }
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+
+ cx231xx_info("New device %s@ %s Mbps "
+ "(%04x:%04x) with %d interfaces\n",
+ descr,
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ dev->max_iad_interface_count);
+
+ /* store the interface 0 back */
+ lif = udev->actconfig->interface[0];
+
+ /* increment interface count */
+ dev->interface_count++;
+
+ /* get device number */
+ nr = dev->devno;
+
+ assoc_desc = udev->actconfig->intf_assoc[0];
+ if (assoc_desc->bFirstInterface != ifnum) {
+ cx231xx_err(DRIVER_NAME ": Not found "
+ "matching IAD interface\n");
return -ENODEV;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index 7d62d58617f5..abe500feb7dd 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -571,6 +571,8 @@ int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
alt];
break;
case INDEX_VANC:
+ if (dev->board.no_alt_vanc)
+ return 0;
usb_interface_index =
dev->current_pcb_config.hs_config_info[0].interface_info.
vanc_index + 1;
@@ -600,8 +602,7 @@ int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
usb_interface_index, alt);
/*To workaround error number=-71 on EP0 for videograbber,
need add following codes.*/
- if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
- dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+ if (dev->board.no_alt_vanc)
return -1;
}
@@ -1301,8 +1302,7 @@ int cx231xx_dev_init(struct cx231xx *dev)
/* init hardware */
/* Note : with out calling set power mode function,
afe can not be set up correctly */
- if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
- dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) {
+ if (dev->board.external_av) {
errCode = cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ENXTERNAL_AV);
if (errCode < 0) {
@@ -1322,11 +1322,9 @@ int cx231xx_dev_init(struct cx231xx *dev)
}
}
- /* reset the Tuner */
- if ((dev->model == CX231XX_BOARD_CNXT_CARRAERA) ||
- (dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
- (dev->model == CX231XX_BOARD_CNXT_SHELBY) ||
- (dev->model == CX231XX_BOARD_CNXT_RDU_250))
+ /* reset the Tuner, if it is a Xceive tuner */
+ if ((dev->board.tuner_type == TUNER_XC5000) ||
+ (dev->board.tuner_type == TUNER_XC2028))
cx231xx_gpio_set(dev, dev->board.tuner_gpio);
/* initialize Colibri block */
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
index 835670623dfb..925f3a04e53c 100644
--- a/drivers/media/video/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
@@ -54,6 +54,21 @@ do { \
} \
} while (0)
+static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
+ const struct i2c_msg *msg, int tuner_type)
+{
+ if (bus->nr != dev->board.tuner_i2c_master)
+ return false;
+
+ if (msg->addr != dev->board.tuner_addr)
+ return false;
+
+ if (dev->tuner_type != tuner_type)
+ return false;
+
+ return true;
+}
+
/*
* cx231xx_i2c_send_bytes()
*/
@@ -71,9 +86,7 @@ int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
u16 saddr = 0;
u8 need_gpio = 0;
- if ((bus->nr == 1) && (msg->addr == 0x61)
- && (dev->tuner_type == TUNER_XC5000)) {
-
+ if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
size = msg->len;
if (size == 2) { /* register write sub addr */
@@ -180,9 +193,7 @@ static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
u16 saddr = 0;
u8 need_gpio = 0;
- if ((bus->nr == 1) && (msg->addr == 0x61)
- && dev->tuner_type == TUNER_XC5000) {
-
+ if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
if (msg->len == 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
@@ -274,9 +285,7 @@ static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
else if (msg1->len == 1)
saddr = msg1->buf[0];
- if ((bus->nr == 1) && (msg2->addr == 0x61)
- && dev->tuner_type == TUNER_XC5000) {
-
+ if (is_tuner(dev, bus, msg2, TUNER_XC5000)) {
if ((msg2->len < 16)) {
dprintk1(1,
@@ -454,8 +463,8 @@ static char *i2c_devs[128] = {
[0x32 >> 1] = "GeminiIII",
[0x02 >> 1] = "Aquarius",
[0xa0 >> 1] = "eeprom",
- [0xc0 >> 1] = "tuner/XC3028",
- [0xc2 >> 1] = "tuner/XC5000",
+ [0xc0 >> 1] = "tuner",
+ [0xc2 >> 1] = "tuner",
};
/*
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index 7e3e8c4f19b7..ffd5af914c44 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -2190,8 +2190,7 @@ static int cx231xx_v4l2_open(struct file *filp)
dev->height = norm_maxh(dev);
/* Power up in Analog TV mode */
- if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
- dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+ if (dev->board.external_av)
cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ENXTERNAL_AV);
else
@@ -2231,9 +2230,7 @@ static int cx231xx_v4l2_open(struct file *filp)
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* Set the required alternate setting VBI interface works in
Bulk mode only */
- if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
- dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
- cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
NULL, &dev->vbi_mode.slock,
@@ -2275,7 +2272,7 @@ void cx231xx_release_analog_resources(struct cx231xx *dev)
cx231xx_info("V4L2 device %s deregistered\n",
video_device_node_name(dev->vdev));
- if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER)
+ if (dev->board.has_417)
cx231xx_417_unregister(dev);
if (video_is_registered(dev->vdev))
@@ -2302,10 +2299,13 @@ static int cx231xx_v4l2_close(struct file *filp)
if (res_check(fh))
res_free(fh);
- /*To workaround error number=-71 on EP0 for VideoGrabber,
- need exclude following.*/
- if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
- dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+ /*
+ * To workaround error number=-71 on EP0 for VideoGrabber,
+ * need exclude following.
+ * FIXME: It is probably safe to remove most of these, as we're
+ * now avoiding the alternate setting for INDEX_VANC
+ */
+ if (!dev->board.no_alt_vanc)
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index 72bbea2bcd56..bd4a9cf29577 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -64,6 +64,7 @@
#define CX231XX_BOARD_HAUPPAUGE_EXETER 8
#define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9
#define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10
+#define CX231XX_BOARD_PV_XCAPTURE_USB 11
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
@@ -353,7 +354,11 @@ struct cx231xx_board {
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
+ unsigned int has_417:1;
unsigned int valid:1;
+ unsigned int no_alt_vanc:1;
+ unsigned int external_av:1;
+ unsigned int dont_use_port_3:1;
unsigned char xclk, i2c_speed;
@@ -464,7 +469,7 @@ struct cx231xx_fh {
#define I2C_STOP 0x0
/* 1-- do not transmit STOP at end of transaction */
#define I2C_NOSTOP 0x1
-/* 1--alllow slave to insert clock wait states */
+/* 1--allow slave to insert clock wait states */
#define I2C_SYNC 0x1
struct cx231xx_i2c {
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index 6b4a516addfe..3b6e7f28568e 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -1,6 +1,7 @@
config VIDEO_CX23885
tristate "Conexant cx23885 (2388x successor) support"
- depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT
+ depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
+ select SND_PCM
select I2C_ALGOBIT
select VIDEO_BTCX
select VIDEO_TUNER
@@ -33,3 +34,12 @@ config VIDEO_CX23885
To compile this driver as a module, choose M here: the
module will be called cx23885
+config MEDIA_ALTERA_CI
+ tristate "Altera FPGA based CI module"
+ depends on VIDEO_CX23885 && DVB_CORE
+ select STAPL_ALTERA
+ ---help---
+ An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called altera-ci
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index e2ee95f660d8..23293c7b6ac7 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -5,6 +5,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
+obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c
new file mode 100644
index 000000000000..ad6cc68d4b0c
--- /dev/null
+++ b/drivers/media/video/cx23885/altera-ci.c
@@ -0,0 +1,832 @@
+/*
+ * altera-ci.c
+ *
+ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * currently cx23885 GPIO's used.
+ * GPIO-0 ~INT in
+ * GPIO-1 TMS out
+ * GPIO-2 ~reset chips out
+ * GPIO-3 to GPIO-10 data/addr for CA in/out
+ * GPIO-11 ~CS out
+ * GPIO-12 AD_RG out
+ * GPIO-13 ~WR out
+ * GPIO-14 ~RD out
+ * GPIO-15 ~RDY in
+ * GPIO-16 TCK out
+ * GPIO-17 TDO in
+ * GPIO-18 TDI out
+ */
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ * bits 31-16
+ * +-----------+
+ * | Reserved |
+ * +-----------+
+ * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#include <linux/version.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include "altera-ci.h"
+#include "dvb_ca_en50221.h"
+
+/* FPGA regs */
+#define NETUP_CI_INT_CTRL 0x00
+#define NETUP_CI_BUSCTRL2 0x01
+#define NETUP_CI_ADDR0 0x04
+#define NETUP_CI_ADDR1 0x05
+#define NETUP_CI_DATA 0x06
+#define NETUP_CI_BUSCTRL 0x07
+#define NETUP_CI_PID_ADDR0 0x08
+#define NETUP_CI_PID_ADDR1 0x09
+#define NETUP_CI_PID_DATA 0x0a
+#define NETUP_CI_TSA_DIV 0x0c
+#define NETUP_CI_TSB_DIV 0x0d
+#define NETUP_CI_REVISION 0x0f
+
+/* const for ci op */
+#define NETUP_CI_FLG_CTL 1
+#define NETUP_CI_FLG_RD 1
+#define NETUP_CI_FLG_AD 1
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int pid_dbg;
+module_param(pid_dbg, int, 0644);
+MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
+
+MODULE_DESCRIPTION("altera FPGA CI module");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@netup.ru>");
+MODULE_LICENSE("GPL");
+
+#define ci_dbg_print(args...) \
+ do { \
+ if (ci_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+#define pid_dbg_print(args...) \
+ do { \
+ if (pid_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+struct altera_ci_state;
+struct netup_hw_pid_filter;
+
+struct fpga_internal {
+ void *dev;
+ struct mutex fpga_mutex;/* two CI's on the same fpga */
+ struct netup_hw_pid_filter *pid_filt[2];
+ struct altera_ci_state *state[2];
+ struct work_struct work;
+ int (*fpga_rw) (void *dev, int flag, int data, int rw);
+ int cis_used;
+ int filts_used;
+ int strt_wrk;
+};
+
+/* stores all private variables for communication with CI */
+struct altera_ci_state {
+ struct fpga_internal *internal;
+ struct dvb_ca_en50221 ca;
+ int status;
+ int nr;
+};
+
+/* stores all private variables for hardware pid filtering */
+struct netup_hw_pid_filter {
+ struct fpga_internal *internal;
+ struct dvb_demux *demux;
+ /* save old functions */
+ int (*start_feed)(struct dvb_demux_feed *feed);
+ int (*stop_feed)(struct dvb_demux_feed *feed);
+
+ int status;
+ int nr;
+};
+
+/* internal params node */
+struct fpga_inode {
+ /* pointer for internal params, one for each pair of CI's */
+ struct fpga_internal *internal;
+ struct fpga_inode *next_inode;
+};
+
+/* first internal params */
+static struct fpga_inode *fpga_first_inode;
+
+/* find chip by dev */
+static struct fpga_inode *find_inode(void *dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+
+ if (temp_chip == NULL)
+ return temp_chip;
+
+ /*
+ Search for the last fpga CI chip or
+ find it by dev */
+ while ((temp_chip != NULL) &&
+ (temp_chip->internal->dev != dev))
+ temp_chip = temp_chip->next_inode;
+
+ return temp_chip;
+}
+/* check demux */
+static struct fpga_internal *check_filter(struct fpga_internal *temp_int,
+ void *demux_dev, int filt_nr)
+{
+ if (temp_int == NULL)
+ return NULL;
+
+ if ((temp_int->pid_filt[filt_nr]) == NULL)
+ return NULL;
+
+ if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
+ return temp_int;
+
+ return NULL;
+}
+
+/* find chip by demux */
+static struct fpga_inode *find_dinode(void *demux_dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+ struct fpga_internal *temp_int;
+
+ /*
+ * Search of the last fpga CI chip or
+ * find it by demux
+ */
+ while (temp_chip != NULL) {
+ if (temp_chip->internal != NULL) {
+ temp_int = temp_chip->internal;
+ if (check_filter(temp_int, demux_dev, 0))
+ break;
+ if (check_filter(temp_int, demux_dev, 1))
+ break;
+ }
+
+ temp_chip = temp_chip->next_inode;
+ }
+
+ return temp_chip;
+}
+
+/* deallocating chip */
+static void remove_inode(struct fpga_internal *internal)
+{
+ struct fpga_inode *prev_node = fpga_first_inode;
+ struct fpga_inode *del_node = find_inode(internal->dev);
+
+ if (del_node != NULL) {
+ if (del_node == fpga_first_inode) {
+ fpga_first_inode = del_node->next_inode;
+ } else {
+ while (prev_node->next_inode != del_node)
+ prev_node = prev_node->next_inode;
+
+ if (del_node->next_inode == NULL)
+ prev_node->next_inode = NULL;
+ else
+ prev_node->next_inode =
+ prev_node->next_inode->next_inode;
+ }
+
+ kfree(del_node);
+ }
+}
+
+/* allocating new chip */
+static struct fpga_inode *append_internal(struct fpga_internal *internal)
+{
+ struct fpga_inode *new_node = fpga_first_inode;
+
+ if (new_node == NULL) {
+ new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ fpga_first_inode = new_node;
+ } else {
+ while (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+
+ new_node->next_inode =
+ kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ if (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+ else
+ new_node = NULL;
+ }
+
+ if (new_node != NULL) {
+ new_node->internal = internal;
+ new_node->next_inode = NULL;
+ }
+
+ return new_node;
+}
+
+static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
+ u8 val, u8 read)
+{
+ inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
+ return inter->fpga_rw(inter->dev, 0, val, read);
+}
+
+/* flag - mem/io, read - read/write */
+int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+ u8 flag, u8 read, int addr, u8 val)
+{
+
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+
+ u8 store;
+ int mem = 0;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ store &= 0x3f;
+ store |= ((state->nr << 7) | (flag << 6));
+
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
+ mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
+ (read) ? "read" : "write", addr,
+ (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
+ (read) ? mem : val);
+
+ return mem;
+}
+
+int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
+{
+ return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
+ NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
+}
+
+int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ /* reasonable timeout for CI reset is 10 seconds */
+ unsigned long t_out = jiffies + msecs_to_jiffies(9999);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ ret | (1 << (5 - state->nr)), 0);
+
+ for (;;) {
+ mdelay(50);
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ 0, NETUP_CI_FLG_RD);
+ if ((ret & (1 << (5 - state->nr))) == 0)
+ break;
+ if (time_after(jiffies, t_out))
+ break;
+ }
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: %d msecs\n", __func__,
+ jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
+
+ return 0;
+}
+
+int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+ /* not implemented */
+ return 0;
+}
+
+int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ ret | (1 << (3 - state->nr)), 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+ struct fpga_internal *inter =
+ container_of(work, struct fpga_internal, work);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+ /* ack' irq */
+ ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ if (inter->state[1] != NULL) {
+ inter->state[1]->status =
+ ((ret & 1) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
+ __func__, inter->state[1]->status);
+ };
+
+ if (inter->state[0] != NULL) {
+ inter->state[0]->status =
+ ((ret & 2) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
+ __func__, inter->state[0]->status);
+ };
+}
+
+/* CI irq handler */
+int altera_ci_irq(void *dev)
+{
+ struct fpga_inode *temp_int = NULL;
+ struct fpga_internal *inter = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (dev != NULL) {
+ temp_int = find_inode(dev);
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ schedule_work(&inter->work);
+ }
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(altera_ci_irq);
+
+int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
+ int open)
+{
+ struct altera_ci_state *state = en50221->data;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ return state->status;
+}
+
+void altera_hw_filt_release(void *main_dev, int filt_nr)
+{
+ struct fpga_inode *temp_int = find_inode(main_dev);
+ struct netup_hw_pid_filter *pid_filt = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
+ /* stored old feed controls */
+ pid_filt->demux->start_feed = pid_filt->start_feed;
+ pid_filt->demux->stop_feed = pid_filt->stop_feed;
+
+ if (((--(temp_int->internal->filts_used)) <= 0) &&
+ ((temp_int->internal->cis_used) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(pid_filt->internal);
+ }
+
+ kfree(pid_filt);
+
+ }
+
+}
+EXPORT_SYMBOL(altera_hw_filt_release);
+
+void altera_ci_release(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct altera_ci_state *state = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ state = temp_int->internal->state[ci_nr - 1];
+ altera_hw_filt_release(dev, ci_nr);
+
+
+ if (((temp_int->internal->filts_used) <= 0) &&
+ ((--(temp_int->internal->cis_used)) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(state->internal);
+ }
+
+ if (state != NULL) {
+ if (state->ca.data != NULL)
+ dvb_ca_en50221_release(&state->ca);
+
+ kfree(state);
+ }
+ }
+
+}
+EXPORT_SYMBOL(altera_ci_release);
+
+static void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
+ u16 pid, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+
+ /* pid 0-0x1f always enabled, don't touch them */
+ if ((pid == 0x2000) || (pid < 0x20))
+ return;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
+
+ if (onoff)/* 0 - on, 1 - off */
+ store |= (1 << (pid & 7));
+ else
+ store &= ~(1 << (pid & 7));
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
+ pid_filt->nr, pid, pid, onoff ? "off" : "on");
+}
+
+static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
+ int filt_nr, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+ int i;
+
+ pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr,
+ onoff ? "off" : "on");
+
+ if (onoff)/* 0 - on, 1 - off */
+ store = 0xff;/* ignore pid */
+ else
+ store = 0;/* enable pid */
+
+ mutex_lock(&inter->fpga_mutex);
+
+ for (i = 0; i < 1024; i++) {
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
+ /* pid 0-0x1f always enabled */
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
+ (i > 3 ? store : 0), 0);
+ }
+
+ mutex_unlock(&inter->fpga_mutex);
+}
+
+int altera_pid_feed_control(void *demux_dev, int filt_nr,
+ struct dvb_demux_feed *feed, int onoff)
+{
+ struct fpga_inode *temp_int = find_dinode(demux_dev);
+ struct fpga_internal *inter = temp_int->internal;
+ struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
+
+ altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
+ /* call old feed proc's */
+ if (onoff)
+ pid_filt->start_feed(feed);
+ else
+ pid_filt->stop_feed(feed);
+
+ if (feed->pid == 0x2000)
+ altera_toggle_fullts_streaming(pid_filt, filt_nr,
+ onoff ? 0 : 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_pid_feed_control);
+
+int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 1);
+
+ return 0;
+}
+
+int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 0);
+
+ return 0;
+}
+
+int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 1);
+}
+
+int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 1);
+}
+
+int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 2);
+}
+
+int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 2);
+}
+
+int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
+{
+ struct netup_hw_pid_filter *pid_filt = NULL;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+
+ pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!pid_filt) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->filts_used)++;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->filts_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting hw pid filter = 0x%x for ci = %d\n", __func__,
+ (int)pid_filt, hw_filt_nr - 1);
+ inter->pid_filt[hw_filt_nr - 1] = pid_filt;
+ pid_filt->demux = config->demux;
+ pid_filt->internal = inter;
+ pid_filt->nr = hw_filt_nr - 1;
+ /* store old feed controls */
+ pid_filt->start_feed = config->demux->start_feed;
+ pid_filt->stop_feed = config->demux->stop_feed;
+ /* replace with new feed controls */
+ if (hw_filt_nr == 1) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_1;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
+ } else if (hw_filt_nr == 2) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_2;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
+ }
+
+ altera_toggle_fullts_streaming(pid_filt, 0, 1);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
+ __func__, ret);
+
+ kfree(pid_filt);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_hw_filt_init);
+
+int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+ struct altera_ci_state *state;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+ u8 store = 0;
+
+ state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!state) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->cis_used)++;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->cis_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting state = 0x%x for ci = %d\n", __func__,
+ (int)state, ci_nr - 1);
+ inter->state[ci_nr - 1] = state;
+ state->internal = inter;
+ state->nr = ci_nr - 1;
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
+ state->ca.read_cam_control = altera_ci_read_cam_ctl;
+ state->ca.write_cam_control = altera_ci_write_cam_ctl;
+ state->ca.slot_reset = altera_ci_slot_reset;
+ state->ca.slot_shutdown = altera_ci_slot_shutdown;
+ state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
+ state->ca.poll_slot_status = altera_poll_ci_slot_status;
+ state->ca.data = state;
+
+ ret = dvb_ca_en50221_init(config->adapter,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret)
+ goto err;
+
+ altera_hw_filt_init(config, ci_nr);
+
+ if (inter->strt_wrk) {
+ INIT_WORK(&inter->work, netup_read_ci_status);
+ inter->strt_wrk = 0;
+ }
+
+ ci_dbg_print("%s: CI initialized!\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+
+ /* Enable div */
+ netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
+
+ /* enable TS out */
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store |= (3 << 4);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
+ /* enable irq */
+ netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
+
+ schedule_work(&inter->work);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+
+ kfree(state);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_ci_init);
+
+int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct fpga_internal *inter = NULL;
+ u8 store;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int == NULL)
+ return -1;
+
+ if (temp_int->internal == NULL)
+ return -1;
+
+ inter = temp_int->internal;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store &= ~(4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+ msleep(100);
+ store |= (4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_ci_tuner_reset);
diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/video/cx23885/altera-ci.h
new file mode 100644
index 000000000000..bf0aa2c9b070
--- /dev/null
+++ b/drivers/media/video/cx23885/altera-ci.h
@@ -0,0 +1,100 @@
+/*
+ * altera-ci.c
+ *
+ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __ALTERA_CI_H
+#define __ALTERA_CI_H
+
+#define ALT_DATA 0x000000ff
+#define ALT_TDI 0x00008000
+#define ALT_TDO 0x00004000
+#define ALT_TCK 0x00002000
+#define ALT_RDY 0x00001000
+#define ALT_RD 0x00000800
+#define ALT_WR 0x00000400
+#define ALT_AD_RG 0x00000200
+#define ALT_CS 0x00000100
+
+struct altera_ci_config {
+ void *dev;/* main dev, for example cx23885_dev */
+ void *adapter;/* for CI to connect to */
+ struct dvb_demux *demux;/* for hardware PID filter to connect to */
+ int (*fpga_rw) (void *dev, int ad_rg, int val, int rw);
+};
+
+#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \
+ && defined(MODULE))
+
+extern int altera_ci_init(struct altera_ci_config *config, int ci_nr);
+extern void altera_ci_release(void *dev, int ci_nr);
+extern int altera_ci_irq(void *dev);
+extern int altera_ci_tuner_reset(void *dev, int ci_nr);
+
+#else
+
+static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline void altera_ci_release(void *dev, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_ci_irq(void *dev)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+#endif
+#if 0
+static inline int altera_hw_filt_init(struct altera_ci_config *config,
+ int hw_filt_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline void altera_hw_filt_release(void *dev, int filt_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_pid_feed_control(void *dev, int filt_nr,
+ struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+#endif /* CONFIG_MEDIA_ALTERA_CI */
+
+#endif /* __ALTERA_CI_H */
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index b298b730943c..ea88722cb4ab 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -24,10 +24,14 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include <media/cx25840.h>
+#include <linux/firmware.h>
+#include <staging/altera.h>
#include "cx23885.h"
#include "tuner-xc2028.h"
#include "netup-init.h"
+#include "altera-ci.h"
+#include "xc5000.h"
#include "cx23888-ir.h"
static unsigned int enable_885_ir;
@@ -90,6 +94,7 @@ struct cx23885_board cx23885_boards[] = {
.portc = CX23885_MPEG_DVB,
.tuner_type = TUNER_PHILIPS_TDA8290,
.tuner_addr = 0x42, /* 0x84 >> 1 */
+ .tuner_bus = 1,
.input = {{
.type = CX23885_VMUX_TELEVISION,
.vmux = CX25840_VIN7_CH3 |
@@ -187,7 +192,7 @@ struct cx23885_board cx23885_boards[] = {
.portb = CX23885_MPEG_DVB,
},
[CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
- .cimax = 1,
+ .ci_type = 1,
.name = "NetUP Dual DVB-S2 CI",
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
@@ -212,6 +217,7 @@ struct cx23885_board cx23885_boards[] = {
.name = "Mygica X8506 DMB-TH",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
+ .tuner_bus = 1,
.porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
.input = {
@@ -241,6 +247,7 @@ struct cx23885_board cx23885_boards[] = {
.name = "Magic-Pro ProHDTV Extreme 2",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
+ .tuner_bus = 1,
.porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
.input = {
@@ -289,6 +296,7 @@ struct cx23885_board cx23885_boards[] = {
.porta = CX23885_ANALOG_VIDEO,
.tuner_type = TUNER_XC2028,
.tuner_addr = 0x61,
+ .tuner_bus = 1,
.input = {{
.type = CX23885_VMUX_TELEVISION,
.vmux = CX25840_VIN2_CH1 |
@@ -313,6 +321,7 @@ struct cx23885_board cx23885_boards[] = {
.name = "GoTView X5 3D Hybrid",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x64,
+ .tuner_bus = 1,
.porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
.input = {{
@@ -329,6 +338,21 @@ struct cx23885_board cx23885_boards[] = {
CX25840_SVIDEO_CHROMA4,
} },
},
+ [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = {
+ .ci_type = 2,
+ .name = "NetUP Dual DVB-T/C-CI RF",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ .num_fds_portb = 2,
+ .num_fds_portc = 2,
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x64,
+ .input = { {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE1,
+ } },
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -520,6 +544,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x5654,
.subdevice = 0x2390,
.card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID,
+ }, {
+ .subvendor = 0x1b55,
+ .subdevice = 0xe2e4,
+ .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -740,6 +768,9 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
/* Tuner Reset Command */
bitmask = 0x02;
break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_tuner_reset(dev, port->nr);
+ break;
}
if (bitmask) {
@@ -998,6 +1029,33 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ /* GPIO-0 ~INT in
+ GPIO-1 TMS out
+ GPIO-2 ~reset chips out
+ GPIO-3 to GPIO-10 data/addr for CA in/out
+ GPIO-11 ~CS out
+ GPIO-12 ADDR out
+ GPIO-13 ~WR out
+ GPIO-14 ~RD out
+ GPIO-15 ~RDY in
+ GPIO-16 TCK out
+ GPIO-17 TDO in
+ GPIO-18 TDI out
+ */
+ cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */
+ /* GPIO-0 as INT, reset & TMS low */
+ cx_clear(GP0_IO, 0x00010006);
+ mdelay(100);/* reset delay */
+ cx_set(GP0_IO, 0x00000004); /* reset high */
+ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */
+ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */
+ cx_write(MC417_OEN, 0x00005000);
+ /* ~RD, ~WR high; ADDR low; ~CS high */
+ cx_write(MC417_RWD, 0x00000d00);
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+ break;
}
}
@@ -1113,6 +1171,31 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
}
}
+int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
+{
+ int data;
+ int tdo = 0;
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ /*TMS*/
+ data = ((cx_read(GP0_IO)) & (~0x00000002));
+ data |= (tms ? 0x00020002 : 0x00020000);
+ cx_write(GP0_IO, data);
+
+ /*TDI*/
+ data = ((cx_read(MC417_RWD)) & (~0x0000a000));
+ data |= (tdi ? 0x00008000 : 0);
+ cx_write(MC417_RWD, data);
+ if (read_tdo)
+ tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/
+
+ cx_write(MC417_RWD, data | 0x00002000);
+ udelay(1);
+ /*TCK*/
+ cx_write(MC417_RWD, data);
+
+ return tdo;
+}
+
void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
{
switch (dev->board) {
@@ -1212,6 +1295,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -1271,6 +1355,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_MYGICA_X8506:
@@ -1293,6 +1378,29 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
netup_initialize(dev);
break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ int ret;
+ const struct firmware *fw;
+ const char *filename = "dvb-netup-altera-01.fw";
+ char *action = "configure";
+ struct altera_config netup_config = {
+ .dev = dev,
+ .action = action,
+ .jtag_io = netup_jtag_io,
+ };
+
+ netup_initialize(dev);
+
+ ret = request_firmware(&fw, filename, &dev->pci->dev);
+ if (ret != 0)
+ printk(KERN_ERR "did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details "
+ "on firmware-problems.", filename);
+ else
+ altera_init(&netup_config, fw);
+
+ break;
+ }
}
}
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 359882419b7f..9933810b4e33 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -29,9 +29,11 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/div64.h>
+#include <linux/firmware.h>
#include "cx23885.h"
#include "cimax2.h"
+#include "altera-ci.h"
#include "cx23888-ir.h"
#include "cx23885-ir.h"
#include "cx23885-av.h"
@@ -902,8 +904,6 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
cx23885_irq_add(dev, 0x001f00);
- if (cx23885_boards[dev->board].cimax > 0)
- cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
@@ -970,11 +970,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
/* Assume some sensible defaults */
dev->tuner_type = cx23885_boards[dev->board].tuner_type;
dev->tuner_addr = cx23885_boards[dev->board].tuner_addr;
+ dev->tuner_bus = cx23885_boards[dev->board].tuner_bus;
dev->radio_type = cx23885_boards[dev->board].radio_type;
dev->radio_addr = cx23885_boards[dev->board].radio_addr;
- dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n",
- __func__, dev->tuner_type, dev->tuner_addr);
+ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x tuner_bus = %d\n",
+ __func__, dev->tuner_type, dev->tuner_addr, dev->tuner_bus);
dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
__func__, dev->radio_type, dev->radio_addr);
@@ -1004,6 +1005,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}
if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].num_fds_portb)
+ dev->ts1.num_frontends =
+ cx23885_boards[dev->board].num_fds_portb;
if (cx23885_dvb_register(&dev->ts1) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
__func__);
@@ -1018,6 +1022,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}
if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].num_fds_portc)
+ dev->ts2.num_frontends =
+ cx23885_boards[dev->board].num_fds_portc;
if (cx23885_dvb_register(&dev->ts2) < 0) {
printk(KERN_ERR
"%s() Failed to register dvb on VID_C\n",
@@ -1034,6 +1041,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_dev_checkrevision(dev);
+ /* disable MSI for NetUP cards, otherwise CI is not working */
+ if (cx23885_boards[dev->board].ci_type > 0)
+ cx_clear(RDR_RDRCTL1, 1 << 8);
+
return 0;
}
@@ -1822,14 +1833,13 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
PCI_MSK_IR);
}
- if (cx23885_boards[dev->board].cimax > 0 &&
- ((pci_status & PCI_MSK_GPIO0) ||
- (pci_status & PCI_MSK_GPIO1))) {
-
- if (cx23885_boards[dev->board].cimax > 0)
- handled += netup_ci_slot_status(dev, pci_status);
+ if (cx23885_boards[dev->board].ci_type == 1 &&
+ (pci_status & (PCI_MSK_GPIO1 | PCI_MSK_GPIO0)))
+ handled += netup_ci_slot_status(dev, pci_status);
- }
+ if (cx23885_boards[dev->board].ci_type == 2 &&
+ (pci_status & PCI_MSK_GPIO0))
+ handled += altera_ci_irq(dev);
if (ts1_status) {
if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
@@ -2064,7 +2074,10 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
+ cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ cx23885_irq_add_enable(dev, PCI_MSK_GPIO0);
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 5958cb882e93..cf36b9b4794e 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -58,6 +58,8 @@
#include "atbm8830.h"
#include "ds3000.h"
#include "cx23885-f300.h"
+#include "altera-ci.h"
+#include "stv0367.h"
static unsigned int debug;
@@ -108,6 +110,22 @@ static void dvb_buf_release(struct videobuf_queue *q,
cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
}
+static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
+{
+ struct videobuf_dvb_frontends *f;
+ struct videobuf_dvb_frontend *fe;
+
+ f = &port->frontends;
+
+ if (f->gate <= 1) /* undefined or fe0 */
+ fe = videobuf_dvb_get_frontend(f, 1);
+ else
+ fe = videobuf_dvb_get_frontend(f, f->gate);
+
+ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+}
+
static struct videobuf_queue_ops dvb_qops = {
.buf_setup = dvb_buf_setup,
.buf_prepare = dvb_buf_prepare,
@@ -570,12 +588,84 @@ static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
.i2c_address = 0x60,
.osc_clk = 20
};
+static struct stv0367_config netup_stv0367_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ },
+};
+
+static struct xc5000_config netup_xc5000_config[] = {
+ {
+ .i2c_address = 0x61,
+ .if_khz = 4500,
+ }, {
+ .i2c_address = 0x64,
+ .if_khz = 4500,
+ },
+};
+
+int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ int mem = 0;
+
+ cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
+ if (read)
+ cx_set(MC417_OEN, ALT_DATA);
+ else {
+ cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */
+ mem = cx_read(MC417_RWD);
+ mem &= ~ALT_DATA;
+ mem |= (data & ALT_DATA);
+ cx_write(MC417_RWD, mem);
+ }
+
+ if (flag)
+ cx_set(MC417_RWD, ALT_AD_RG);/* ADDR */
+ else
+ cx_clear(MC417_RWD, ALT_AD_RG);/* VAL */
+
+ cx_clear(MC417_RWD, ALT_CS);/* ~CS */
+ if (read)
+ cx_clear(MC417_RWD, ALT_RD);
+ else
+ cx_clear(MC417_RWD, ALT_WR);
+
+ for (;;) {
+ mem = cx_read(MC417_RWD);
+ if ((mem & ALT_RDY) == 0)
+ break;
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(1);
+ }
+
+ cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
+ if (read)
+ return mem & ALT_DATA;
+
+ return 0;
+};
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
- struct videobuf_dvb_frontend *fe0;
+ struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ int mfe_shared = 0; /* bus not shared by default */
int ret;
/* Get the first frontend */
@@ -586,6 +676,12 @@ static int dvb_register(struct cx23885_tsport *port)
/* init struct videobuf_dvb */
fe0->dvb.name = dev->name;
+ /* multi-frontend gate control is undefined or defaults to fe0 */
+ port->frontends.gate = 0;
+
+ /* Sets the gate control callback to be used by i2c command calls */
+ port->gate_ctrl = cx23885_dvb_gate_ctrl;
+
/* init frontend */
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -966,20 +1062,61 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
-
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ i2c_bus = &dev->i2c_bus[0];
+ mfe_shared = 1;/* MFE */
+ port->frontends.gate = 0;/* not clear for me yet */
+ /* ports B, C */
+ /* MFE frontend 1 DVB-T */
+ fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ if (NULL == dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
+ if (fe1 == NULL)
+ goto frontend_detach;
+ /* DVB-C init */
+ fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe1->dvb.frontend != NULL) {
+ fe1->dvb.frontend->id = 1;
+ if (NULL == dvb_attach(xc5000_attach,
+ fe1->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ }
+ break;
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
dev->name);
break;
}
- if (NULL == fe0->dvb.frontend) {
+
+ if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) {
printk(KERN_ERR "%s: frontend initialization failed\n",
- dev->name);
- return -1;
+ dev->name);
+ goto frontend_detach;
}
+
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = cx23885_tuner_callback;
+ if (fe1)
+ fe1->dvb.frontend->callback = cx23885_tuner_callback;
+#if 0
+ /* Ensure all frontends negotiate bus access */
+ fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+ if (fe1)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+#endif
/* Put the analog decoder in standby to keep it quiet */
call_all(dev, core, s_power, 0);
@@ -989,10 +1126,10 @@ static int dvb_register(struct cx23885_tsport *port)
/* register everything */
ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
- &dev->pci->dev, adapter_nr, 0,
+ &dev->pci->dev, adapter_nr, mfe_shared,
cx23885_dvb_fe_ioctl_override);
if (ret)
- return ret;
+ goto frontend_detach;
/* init CI & MAC */
switch (dev->board) {
@@ -1008,6 +1145,17 @@ static int dvb_register(struct cx23885_tsport *port)
netup_ci_init(port);
break;
}
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ struct altera_ci_config netup_ci_cfg = {
+ .dev = dev,/* magic number to identify*/
+ .adapter = &port->frontends.adapter,/* for CI */
+ .demux = &fe0->dvb.demux,/* for hw pid filter */
+ .fpga_rw = netup_altera_fpga_rw,
+ };
+
+ altera_ci_init(&netup_ci_cfg, port->nr);
+ break;
+ }
case CX23885_BOARD_TEVII_S470: {
u8 eeprom[256]; /* 24C02 i2c eeprom */
@@ -1024,6 +1172,11 @@ static int dvb_register(struct cx23885_tsport *port)
}
return ret;
+
+frontend_detach:
+ port->gate_ctrl = NULL;
+ videobuf_dvb_dealloc_frontends(&port->frontends);
+ return -EINVAL;
}
int cx23885_dvb_register(struct cx23885_tsport *port)
@@ -1100,8 +1253,13 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
netup_ci_exit(port);
break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_release(port->dev, port->nr);
+ break;
}
+ port->gate_ctrl = NULL;
+
return 0;
}
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index a28772db11f0..c87ac682ebbe 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -292,6 +292,7 @@ Channel manager Data Structure entry = 20 DWORD
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
#define RDR_CFG2 0x00050008
+#define RDR_RDRCTL1 0x0005030c
#define RDR_TLCTL0 0x00050318
/* APB DMAC Current Buffer Pointer */
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 644fcb808c0b..ee57f6bedbe3 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -1468,16 +1468,17 @@ int cx23885_video_register(struct cx23885_dev *dev)
cx23885_irq_add_enable(dev, 0x01);
- if (TUNER_ABSENT != dev->tuner_type) {
+ if ((TUNER_ABSENT != dev->tuner_type) &&
+ ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
struct v4l2_subdev *sd = NULL;
if (dev->tuner_addr)
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[1].i2c_adap,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
"tuner", dev->tuner_addr, NULL);
else
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[1].i2c_adap,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
if (sd) {
struct tuner_setup tun_setup;
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 62e41ab65810..8db2797bc7c3 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -85,6 +85,7 @@
#define CX23885_BOARD_MYGICA_X8558PRO 27
#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
+#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -204,10 +205,12 @@ typedef enum {
struct cx23885_board {
char *name;
port_t porta, portb, portc;
+ int num_fds_portb, num_fds_portc;
unsigned int tuner_type;
unsigned int radio_type;
unsigned char tuner_addr;
unsigned char radio_addr;
+ unsigned int tuner_bus;
/* Vendors can and do run the PCIe bridge at different
* clock rates, driven physically by crystals on the PCBs.
@@ -220,7 +223,7 @@ struct cx23885_board {
*/
u32 clk_freq;
struct cx23885_input input[MAX_CX23885_INPUT];
- int cimax; /* for NetUP */
+ int ci_type; /* for NetUP */
};
struct cx23885_subid {
@@ -303,6 +306,7 @@ struct cx23885_tsport {
/* Allow a single tsport to have multiple frontends */
u32 num_frontends;
+ void (*gate_ctrl)(struct cx23885_tsport *port, int open);
void *port_priv;
};
@@ -362,6 +366,7 @@ struct cx23885_dev {
v4l2_std_id tvnorm;
unsigned int tuner_type;
unsigned char tuner_addr;
+ unsigned int tuner_bus;
unsigned int radio_type;
unsigned char radio_addr;
unsigned int has_radio;
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 54b7fcd469a8..a2d688ebed90 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -40,6 +40,7 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#include <media/wm8775.h>
#include "cx88.h"
#include "cx88-reg.h"
@@ -577,6 +578,35 @@ static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+ int left = value->value.integer.value[0];
+ int right = value->value.integer.value[1];
+ int v, b;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+
+ /* Pass volume & balance onto any WM8775 */
+ if (left >= right) {
+ v = left << 10;
+ b = left ? (0x8000 * right) / left : 0x8000;
+ } else {
+ v = right << 10;
+ b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
+ }
+ client_ctl.value = v;
+ client_ctl.id = V4L2_CID_AUDIO_VOLUME;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+ client_ctl.value = b;
+ client_ctl.id = V4L2_CID_AUDIO_BALANCE;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+}
+
/* OK - TODO: test it */
static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
@@ -587,25 +617,28 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
int changed = 0;
u32 old;
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_cx88_wm8775_volume_put(kcontrol, value);
+
left = value->value.integer.value[0] & 0x3f;
right = value->value.integer.value[1] & 0x3f;
b = right - left;
if (b < 0) {
- v = 0x3f - left;
- b = (-b) | 0x40;
+ v = 0x3f - left;
+ b = (-b) | 0x40;
} else {
- v = 0x3f - right;
+ v = 0x3f - right;
}
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq(&chip->reg_lock);
old = cx_read(AUD_VOL_CTL);
if (v != (old & 0x3f)) {
- cx_write(AUD_VOL_CTL, (old & ~0x3f) | v);
- changed = 1;
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
+ changed = 1;
}
- if (cx_read(AUD_BAL_CTL) != b) {
- cx_write(AUD_BAL_CTL, b);
- changed = 1;
+ if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
+ cx_write(AUD_BAL_CTL, b);
+ changed = 1;
}
spin_unlock_irq(&chip->reg_lock);
@@ -618,7 +651,7 @@ static const struct snd_kcontrol_new snd_cx88_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- .name = "Playback Volume",
+ .name = "Analog-TV Volume",
.info = snd_cx88_volume_info,
.get = snd_cx88_volume_get,
.put = snd_cx88_volume_put,
@@ -649,7 +682,17 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
vol = cx_read(AUD_VOL_CTL);
if (value->value.integer.value[0] != !(vol & bit)) {
vol ^= bit;
- cx_write(AUD_VOL_CTL, vol);
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
+ /* Pass mute onto any WM8775 */
+ if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
+ ((1<<6) == bit)) {
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.value = 0 != (vol & bit);
+ client_ctl.id = V4L2_CID_AUDIO_MUTE;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+ }
ret = 1;
}
spin_unlock_irq(&chip->reg_lock);
@@ -658,7 +701,7 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new snd_cx88_dac_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Playback Switch",
+ .name = "Audio-Out Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
@@ -667,13 +710,51 @@ static const struct snd_kcontrol_new snd_cx88_dac_switch = {
static const struct snd_kcontrol_new snd_cx88_source_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Switch",
+ .name = "Analog-TV Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
.private_value = (1<<6),
};
+static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+ call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
+ value->value.integer.value[0] = client_ctl.value ? 1 : 0;
+
+ return 0;
+}
+
+static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.value = 0 != value->value.integer.value[0];
+ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_cx88_alc_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-In ALC Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_alc_get,
+ .put = snd_cx88_alc_put,
+};
+
/****************************************************************************
Basic Flow for Sound Devices
****************************************************************************/
@@ -724,7 +805,8 @@ static void snd_cx88_dev_free(struct snd_card * card)
static int devno;
static int __devinit snd_cx88_create(struct snd_card *card,
struct pci_dev *pci,
- snd_cx88_card_t **rchip)
+ snd_cx88_card_t **rchip,
+ struct cx88_core **core_ptr)
{
snd_cx88_card_t *chip;
struct cx88_core *core;
@@ -750,7 +832,7 @@ static int __devinit snd_cx88_create(struct snd_card *card,
if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
- cx88_core_put(core,pci);
+ cx88_core_put(core, pci);
return err;
}
@@ -786,6 +868,7 @@ static int __devinit snd_cx88_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
+ *core_ptr = core;
return 0;
}
@@ -795,6 +878,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
{
struct snd_card *card;
snd_cx88_card_t *chip;
+ struct cx88_core *core;
int err;
if (devno >= SNDRV_CARDS)
@@ -812,7 +896,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
card->private_free = snd_cx88_dev_free;
- err = snd_cx88_create(card, pci, &chip);
+ err = snd_cx88_create(card, pci, &chip, &core);
if (err < 0)
goto error;
@@ -830,6 +914,10 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
if (err < 0)
goto error;
+ /* If there's a wm8775 then add a Line-In ALC switch */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
+
strcpy (card->driver, "CX88x");
sprintf(card->shortname, "Conexant CX%x", pci->device);
sprintf(card->longname, "%s at %#llx",
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 4e6ee5584cb3..27222c92b603 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -970,7 +970,8 @@ static const struct cx88_board cx88_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .i2sinputcntl = 2,
.input = {{
.type = CX88_VMUX_DVB,
.vmux = 0,
@@ -1952,6 +1953,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_TEVII_S464] = {
+ .name = "TeVii S464 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_OMICOM_SS4_PCI] = {
.name = "Omicom SS4 DVB-S/S2 PCI",
.tuner_type = UNSET,
@@ -2528,6 +2541,10 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x9022,
.card = CX88_BOARD_TEVII_S460,
}, {
+ .subvendor = 0xd464,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S464,
+ }, {
.subvendor = 0xA044,
.subdevice = 0x2011,
.card = CX88_BOARD_OMICOM_SS4_PCI,
@@ -3165,9 +3182,7 @@ static void cx88_card_setup(struct cx88_core *core)
{
static u8 eeprom[256];
struct tuner_setup tun_setup;
- unsigned int mode_mask = T_RADIO |
- T_ANALOG_TV |
- T_DIGITAL_TV;
+ unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
memset(&tun_setup, 0, sizeof(tun_setup));
@@ -3287,6 +3302,7 @@ static void cx88_card_setup(struct cx88_core *core)
}
case CX88_BOARD_TEVII_S420:
case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S464:
case CX88_BOARD_OMICOM_SS4_PCI:
case CX88_BOARD_TBS_8910:
case CX88_BOARD_TBS_8920:
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 90717ee944ec..7b8c9d3b6efc 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -57,6 +57,7 @@
#include "stb6100.h"
#include "stb6100_proc.h"
#include "mb86a16.h"
+#include "ds3000.h"
MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -648,6 +649,20 @@ static const struct cx24116_config tevii_s460_config = {
.reset_device = cx24116_reset_device,
};
+static int ds3000_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 4;
+
+ return 0;
+}
+
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
+ .set_ts_params = ds3000_set_ts_param,
+};
+
static const struct stv0900_config prof_7301_stv0900_config = {
.demod_address = 0x6a,
/* demod_mode = 0,*/
@@ -1381,6 +1396,14 @@ static int dvb_register(struct cx8802_dev *dev)
if (fe0->dvb.frontend != NULL)
fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
break;
+ case CX88_BOARD_TEVII_S464:
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ break;
case CX88_BOARD_OMICOM_SS4_PCI:
case CX88_BOARD_TBS_8920:
case CX88_BOARD_PROF_7300:
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 06f7d1d00944..fbfbba5bec5e 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -373,6 +373,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir_codes = RC_MAP_TBS_NEC;
ir->sampling = 0xff00; /* address */
break;
+ case CX88_BOARD_TEVII_S464:
case CX88_BOARD_TEVII_S460:
case CX88_BOARD_TEVII_S420:
ir_codes = RC_MAP_TEVII_NEC;
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 08220de3d74d..770ec05b5e9b 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -786,8 +786,12 @@ void cx88_set_tvaudio(struct cx88_core *core)
break;
case WW_I2SADC:
set_audio_start(core, 0x01);
- /* Slave/Philips/Autobaud */
- cx_write(AUD_I2SINPUTCNTL, 0);
+ /*
+ * Slave/Philips/Autobaud
+ * NB on Nova-S bit1 NPhilipsSony appears to be inverted:
+ * 0= Sony, 1=Philips
+ */
+ cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);
/* Switch to "I2S ADC mode" */
cx_write(AUD_I2SCNTL, 0x1);
set_audio_finish(core, EN_I2SIN_ENABLE);
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 508dabbed986..287a41ee1c4f 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -40,6 +40,7 @@
#include "cx88.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/wm8775.h>
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
@@ -989,6 +990,32 @@ int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
ctl->value = c->v.minimum;
if (ctl->value > c->v.maximum)
ctl->value = c->v.maximum;
+
+ /* Pass changes onto any WM8775 */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ struct v4l2_control client_ctl;
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.id = ctl->id;
+
+ switch (ctl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ client_ctl.value = ctl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ client_ctl.value = (ctl->value) ?
+ (0x90 + ctl->value) << 8 : 0;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ client_ctl.value = ctl->value << 9;
+ break;
+ default:
+ client_ctl.id = 0;
+ break;
+ }
+ if (client_ctl.id)
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+ }
+
mask=c->mask;
switch (ctl->id) {
case V4L2_CID_AUDIO_BALANCE:
@@ -1526,7 +1553,9 @@ static int radio_queryctrl (struct file *file, void *priv,
if (c->id < V4L2_CID_BASE ||
c->id >= V4L2_CID_LASTP1)
return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
+ if (c->id == V4L2_CID_AUDIO_MUTE ||
+ c->id == V4L2_CID_AUDIO_VOLUME ||
+ c->id == V4L2_CID_AUDIO_BALANCE) {
for (i = 0; i < CX8800_CTLS; i++) {
if (cx8800_ctls[i].v.id == c->id)
break;
@@ -1672,7 +1701,7 @@ static const struct v4l2_file_operations video_fops =
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -1722,7 +1751,7 @@ static const struct v4l2_file_operations radio_fops =
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
@@ -1856,9 +1885,24 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
/* load and configure helper modules */
- if (core->board.audio_chip == V4L2_IDENT_WM8775)
- v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
- "wm8775", 0x36 >> 1, NULL);
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ struct i2c_board_info wm8775_info = {
+ .type = "wm8775",
+ .addr = 0x36 >> 1,
+ .platform_data = &core->wm8775_data,
+ };
+ struct v4l2_subdev *sd;
+
+ if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
+ core->wm8775_data.is_nova_s = true;
+ else
+ core->wm8775_data.is_nova_s = false;
+
+ sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
+ &wm8775_info, NULL);
+ if (sd != NULL)
+ sd->grp_id = WM8775_GID;
+ }
if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
/* This probes for a tda9874 as is used on some
@@ -1882,6 +1926,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
request_module("ir-kbd-i2c");
}
+ /* Sets device info at pci_dev */
+ pci_set_drvdata(pci_dev, dev);
+
+ /* initial device configuration */
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core, core->tvnorm);
+ init_controls(core);
+ cx88_video_mux(core, 0);
+
/* register v4l devices */
dev->video_dev = cx88_vdev_init(core,dev->pci,
&cx8800_video_template,"video");
@@ -1923,16 +1976,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
core->name, video_device_node_name(dev->radio_dev));
}
- /* everything worked */
- pci_set_drvdata(pci_dev,dev);
-
- /* initial device configuration */
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core,core->tvnorm);
- init_controls(core);
- cx88_video_mux(core,0);
- mutex_unlock(&core->lock);
-
/* start tvaudio thread */
if (core->board.tuner_type != TUNER_ABSENT) {
core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
@@ -1942,11 +1985,14 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
core->name, err);
}
}
+ mutex_unlock(&core->lock);
+
return 0;
fail_unreg:
cx8800_unregister_video(dev);
free_irq(pci_dev->irq, dev);
+ mutex_unlock(&core->lock);
fail_core:
cx88_core_put(core,dev->pci);
fail_free:
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index c9981e77416a..9b3742a7746c 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -33,6 +33,7 @@
#include <media/cx2341x.h>
#include <media/videobuf-dvb.h>
#include <media/ir-kbd-i2c.h>
+#include <media/wm8775.h>
#include "btcx-risc.h"
#include "cx88-reg.h"
@@ -240,6 +241,7 @@ extern const struct sram_channel const cx88_sram_channels[];
#define CX88_BOARD_PROF_7301 83
#define CX88_BOARD_SAMSUNG_SMT_7020 84
#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
+#define CX88_BOARD_TEVII_S464 86
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -273,6 +275,9 @@ struct cx88_board {
enum cx88_board_type mpeg;
unsigned int audio_chip;
int num_frontends;
+
+ /* Used for I2S devices */
+ int i2sinputcntl;
};
struct cx88_subid {
@@ -379,6 +384,7 @@ struct cx88_core {
/* I2C remote data */
struct IR_i2c_init_data init_data;
+ struct wm8775_platform_data wm8775_data;
struct mutex lock;
/* various v4l controls */
@@ -398,17 +404,21 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
}
-#define call_all(core, o, f, args...) \
+#define WM8775_GID (1 << 0)
+
+#define call_hw(core, grpid, o, f, args...) \
do { \
if (!core->i2c_rc) { \
if (core->gate_ctrl) \
core->gate_ctrl(core, 1); \
- v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \
+ v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
if (core->gate_ctrl) \
core->gate_ctrl(core, 0); \
} \
} while (0)
+#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+
struct cx8800_dev;
struct cx8802_dev;
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index f34d524ccb09..a83131bd00b2 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -1387,6 +1387,27 @@ static int vidioc_queryctrl(struct file *file, void *priv,
return -EINVAL;
}
+/*
+ * FIXME: This is an indirect way to check if a control exists at a
+ * subdev. Instead of that hack, maybe the better would be to change all
+ * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported.
+ */
+static int check_subdev_ctrl(struct em28xx *dev, int id)
+{
+ struct v4l2_queryctrl qc;
+
+ memset(&qc, 0, sizeof(qc));
+ qc.id = id;
+
+ /* enumerate V4L2 device controls */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc);
+
+ if (qc.type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
@@ -1399,7 +1420,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
return rc;
rc = 0;
-
/* Set an AC97 control */
if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
rc = ac97_get_ctrl(dev, ctrl);
@@ -1408,6 +1428,9 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
/* It were not an AC97 control. Sends it to the v4l2 dev interface */
if (rc == 1) {
+ if (check_subdev_ctrl(dev, ctrl->id))
+ return -EINVAL;
+
v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
rc = 0;
}
@@ -1434,8 +1457,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
/* It isn't an AC97 control. Sends it to the v4l2 dev interface */
if (rc == 1) {
- rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
-
+ rc = check_subdev_ctrl(dev, ctrl->id);
+ if (!rc)
+ v4l2_device_call_all(&dev->v4l2_dev, 0,
+ core, s_ctrl, ctrl);
/*
* In the case of non-AC97 volume controls, we still need
* to do some setups at em28xx, in order to mute/unmute
@@ -1452,7 +1477,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
rc = em28xx_audio_analog_set(dev);
}
}
- return rc;
+ return (rc < 0) ? rc : 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index dda56ff834f4..a20f6ae88250 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -346,6 +346,16 @@ config USB_GSPCA_VC032X
To compile this driver as a module, choose M here: the
module will be called gspca_vc032x.
+config USB_GSPCA_VICAM
+ tristate "ViCam USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the 3com homeconnect camera
+ (vicam).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_vicam.
+
config USB_GSPCA_XIRLINK_CIT
tristate "Xirlink C-It USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 24e695b8b077..a0dbcfbab29c 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o
obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
@@ -73,6 +74,7 @@ gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
gspca_tv8532-objs := tv8532.o
gspca_vc032x-objs := vc032x.o
+gspca_vicam-objs := vicam.o
gspca_xirlink_cit-objs := xirlink_cit.o
gspca_zc3xx-objs := zc3xx.o
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
index 4bf2cab98d64..2a4a428f2018 100644
--- a/drivers/media/video/gspca/cpia1.c
+++ b/drivers/media/video/gspca/cpia1.c
@@ -1,7 +1,7 @@
/*
* cpia CPiA (1) gspca driver
*
- * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
*
* This module is adapted from the in kernel v4l1 cpia driver which is :
*
@@ -1400,7 +1400,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev)
if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
sd->exposure_status == EXPOSURE_DARK) &&
sd->exposure_count >= DARK_TIME * framerate &&
- sd->params.sensorFps.divisor < 3) {
+ sd->params.sensorFps.divisor < 2) {
/* dark for too long */
++sd->params.sensorFps.divisor;
@@ -1456,7 +1456,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev)
if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
sd->exposure_status == EXPOSURE_DARK) &&
sd->exposure_count >= DARK_TIME * framerate &&
- sd->params.sensorFps.divisor < 3) {
+ sd->params.sensorFps.divisor < 2) {
/* dark for too long */
++sd->params.sensorFps.divisor;
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 8ab2c452c25e..42670ec7ef3b 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -118,6 +118,7 @@ struct sd {
};
enum sensors {
SEN_OV2610,
+ SEN_OV2610AE,
SEN_OV3610,
SEN_OV6620,
SEN_OV6630,
@@ -239,6 +240,8 @@ static const struct ctrl sd_ctrls[] = {
static const unsigned ctrl_dis[] = {
[SEN_OV2610] = (1 << NCTRL) - 1, /* no control */
+[SEN_OV2610AE] = (1 << NCTRL) - 1, /* no control */
+
[SEN_OV3610] = (1 << NCTRL) - 1, /* no control */
[SEN_OV6620] = (1 << HFLIP) |
@@ -428,6 +431,11 @@ static const struct v4l2_pix_format ovfx2_cif_mode[] = {
.priv = 0},
};
static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
{1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 1600,
.sizeimage = 1600 * 1200,
@@ -544,6 +552,7 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
* buffers, there are some pretty strict real time constraints for
* isochronous transfer for larger frame sizes).
*/
+/*jfm: this value works well for 1600x1200, but not 800x600 - see isoc_init */
#define OVFX2_BULK_SIZE (13 * 4096)
/* I2C registers */
@@ -656,6 +665,24 @@ static const struct ov_i2c_regvals norm_2610[] = {
{ 0x12, 0x80 }, /* reset */
};
+static const struct ov_i2c_regvals norm_2610ae[] = {
+ {0x12, 0x80}, /* reset */
+ {0x13, 0xcd},
+ {0x09, 0x01},
+ {0x0d, 0x00},
+ {0x11, 0x80},
+ {0x12, 0x20}, /* 1600x1200 */
+ {0x33, 0x0c},
+ {0x35, 0x90},
+ {0x36, 0x37},
+/* ms-win traces */
+ {0x11, 0x83}, /* clock / 3 ? */
+ {0x2d, 0x00}, /* 60 Hz filter */
+ {0x24, 0xb0}, /* normal colors */
+ {0x25, 0x90},
+ {0x10, 0x43},
+};
+
static const struct ov_i2c_regvals norm_3620b[] = {
/*
* From the datasheet: "Note that after writing to register COMH
@@ -2621,6 +2648,9 @@ static void ov_hires_configure(struct sd *sd)
if (high == 0x96 && low == 0x40) {
PDEBUG(D_PROBE, "Sensor is an OV2610");
sd->sensor = SEN_OV2610;
+ } else if (high == 0x96 && low == 0x41) {
+ PDEBUG(D_PROBE, "Sensor is an OV2610AE");
+ sd->sensor = SEN_OV2610AE;
} else if (high == 0x36 && (low & 0x0f) == 0x00) {
PDEBUG(D_PROBE, "Sensor is an OV3610");
sd->sensor = SEN_OV3610;
@@ -3295,15 +3325,22 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
break;
case BRIDGE_OVFX2:
- if (sd->sensor == SEN_OV2610) {
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ case SEN_OV2610AE:
cam->cam_mode = ovfx2_ov2610_mode;
cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
- } else if (sd->sensor == SEN_OV3610) {
+ break;
+ case SEN_OV3610:
cam->cam_mode = ovfx2_ov3610_mode;
cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
- } else if (sd->sif) {
- cam->cam_mode = ov519_sif_mode;
- cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ break;
+ default:
+ if (sd->sif) {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
}
break;
case BRIDGE_W9968CF:
@@ -3325,6 +3362,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* Enable autogain, autoexpo, awb, bandfilter */
i2c_w_mask(sd, 0x13, 0x27, 0x27);
break;
+ case SEN_OV2610AE:
+ write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae));
+
+ /* enable autoexpo */
+ i2c_w_mask(sd, 0x13, 0x05, 0x05);
+ break;
case SEN_OV3610:
write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b));
@@ -3397,6 +3440,22 @@ error:
return -EINVAL;
}
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_OVFX2:
+ if (gspca_dev->width == 1600)
+ gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE;
+ else
+ gspca_dev->cam.bulk_size = 7 * 4096;
+ break;
+ }
+ return 0;
+}
+
/* Set up the OV511/OV511+ with the given image parameters.
*
* Do not put any sensor-specific code in here (including I2C I/O functions)
@@ -3827,6 +3886,25 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
return;
+ case SEN_OV2610AE: {
+ u8 v;
+
+ /* frame rates:
+ * 10fps / 5 fps for 1600x1200
+ * 40fps / 20fps for 800x600
+ */
+ v = 80;
+ if (qvga) {
+ if (sd->frame_rate < 25)
+ v = 0x81;
+ } else {
+ if (sd->frame_rate < 10)
+ v = 0x81;
+ }
+ i2c_w(sd, 0x11, v);
+ i2c_w(sd, 0x12, qvga ? 0x60 : 0x20);
+ return;
+ }
case SEN_OV3610:
if (qvga) {
xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
@@ -3975,6 +4053,7 @@ static void set_ov_sensor_window(struct sd *sd)
/* mode setup is fully handled in mode_init_ov_sensor_regs for these */
switch (sd->sensor) {
case SEN_OV2610:
+ case SEN_OV2610AE:
case SEN_OV3610:
case SEN_OV7670:
mode_init_ov_sensor_regs(sd);
@@ -4731,6 +4810,7 @@ static const struct sd_desc sd_desc = {
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 04da22802736..0c6369b7fe18 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -1,5 +1,5 @@
/*
- * ov534-ov772x gspca driver
+ * ov534-ov7xxx gspca driver
*
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
* Copyright (C) 2008 Jim Paris <jim@jtan.com>
@@ -49,54 +49,59 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
MODULE_LICENSE("GPL");
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ GAIN,
+ EXPOSURE,
+ AGC,
+ AWB,
+ AEC,
+ SHARPNESS,
+ HFLIP,
+ VFLIP,
+ COLORS,
+ LIGHTFREQ,
+ NCTRLS /* number of controls */
+};
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
__u32 last_pts;
u16 last_fid;
u8 frame_rate;
- u8 brightness;
- u8 contrast;
- u8 gain;
- u8 exposure;
- u8 agc;
- u8 awb;
- u8 aec;
- s8 sharpness;
- u8 hflip;
- u8 vflip;
- u8 freqfltr;
+ u8 sensor;
+};
+enum sensors {
+ SENSOR_OV767x,
+ SENSOR_OV772x,
+ NSENSORS
};
/* V4L2 controls supported by the driver */
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu);
+static void setawb(struct gspca_dev *gspca_dev);
+static void setaec(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+
+static int sd_start(struct gspca_dev *gspca_dev);
+static void sd_stopN(struct gspca_dev *gspca_dev);
static const struct ctrl sd_ctrls[] = {
- { /* 0 */
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -104,13 +109,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define BRIGHTNESS_DEF 0
- .default_value = BRIGHTNESS_DEF,
+ .default_value = 0,
},
- .set = sd_setbrightness,
- .get = sd_getbrightness,
+ .set_control = setbrightness
},
- { /* 1 */
+[CONTRAST] = {
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -118,13 +121,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define CONTRAST_DEF 32
- .default_value = CONTRAST_DEF,
+ .default_value = 32,
},
- .set = sd_setcontrast,
- .get = sd_getcontrast,
+ .set_control = setcontrast
},
- { /* 2 */
+[GAIN] = {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -132,13 +133,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 63,
.step = 1,
-#define GAIN_DEF 20
- .default_value = GAIN_DEF,
+ .default_value = 20,
},
- .set = sd_setgain,
- .get = sd_getgain,
+ .set_control = setgain
},
- { /* 3 */
+[EXPOSURE] = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -146,13 +145,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define EXPO_DEF 120
- .default_value = EXPO_DEF,
+ .default_value = 120,
},
- .set = sd_setexposure,
- .get = sd_getexposure,
+ .set_control = setexposure
},
- { /* 4 */
+[AGC] = {
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -160,14 +157,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AGC_DEF 1
- .default_value = AGC_DEF,
+ .default_value = 1,
},
- .set = sd_setagc,
- .get = sd_getagc,
+ .set = sd_setagc
},
-#define AWB_IDX 5
- { /* 5 */
+[AWB] = {
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -175,13 +169,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AWB_DEF 1
- .default_value = AWB_DEF,
+ .default_value = 1,
},
- .set = sd_setawb,
- .get = sd_getawb,
+ .set_control = setawb
},
- { /* 6 */
+[AEC] = {
{
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -189,13 +181,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AEC_DEF 1
- .default_value = AEC_DEF,
+ .default_value = 1,
},
- .set = sd_setaec,
- .get = sd_getaec,
+ .set_control = setaec
},
- { /* 7 */
+[SHARPNESS] = {
{
.id = V4L2_CID_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -203,13 +193,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 63,
.step = 1,
-#define SHARPNESS_DEF 0
- .default_value = SHARPNESS_DEF,
+ .default_value = 0,
},
- .set = sd_setsharpness,
- .get = sd_getsharpness,
+ .set_control = setsharpness
},
- { /* 8 */
+[HFLIP] = {
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -217,13 +205,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define HFLIP_DEF 0
- .default_value = HFLIP_DEF,
+ .default_value = 0,
},
- .set = sd_sethflip,
- .get = sd_gethflip,
+ .set_control = sethvflip
},
- { /* 9 */
+[VFLIP] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -231,13 +217,23 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define VFLIP_DEF 0
- .default_value = VFLIP_DEF,
+ .default_value = 0,
},
- .set = sd_setvflip,
- .get = sd_getvflip,
+ .set_control = sethvflip
},
- { /* 10 */
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 3,
+ },
+ .set_control = setcolors
+ },
+[LIGHTFREQ] = {
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.type = V4L2_CTRL_TYPE_MENU,
@@ -245,11 +241,9 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define FREQFLTR_DEF 0
- .default_value = FREQFLTR_DEF,
+ .default_value = 0,
},
- .set = sd_setfreqfltr,
- .get = sd_getfreqfltr,
+ .set_control = setlightfreq
},
};
@@ -265,6 +259,16 @@ static const struct v4l2_pix_format ov772x_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
+static const struct v4l2_pix_format ov767x_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30};
static const u8 vga_rates[] = {60, 50, 40, 30, 15};
@@ -280,7 +284,288 @@ static const struct framerates ov772x_framerates[] = {
},
};
-static const u8 bridge_init[][2] = {
+struct reg_array {
+ const u8 (*val)[2];
+ int len;
+};
+
+static const u8 bridge_init_767x[][2] = {
+/* comments from the ms-win file apollo7670.set */
+/* str1 */
+ {0xf1, 0x42},
+ {0x88, 0xf8},
+ {0x89, 0xff},
+ {0x76, 0x03},
+ {0x92, 0x03},
+ {0x95, 0x10},
+ {0xe2, 0x00},
+ {0xe7, 0x3e},
+ {0x8d, 0x1c},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x1f, 0x00},
+ {0xc3, 0xf9},
+ {0x89, 0xff},
+ {0x88, 0xf8},
+ {0x76, 0x03},
+ {0x92, 0x01},
+ {0x93, 0x18},
+ {0x1c, 0x00},
+ {0x1d, 0x48},
+ {0x1d, 0x00},
+ {0x1d, 0xff},
+ {0x1d, 0x02},
+ {0x1d, 0x58},
+ {0x1d, 0x00},
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x1d, 0x0e},
+ {0xc0, 0x50}, /* HSize 640 */
+ {0xc1, 0x3c}, /* VSize 480 */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc2, 0x0c}, /* Input YUV */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xe7, 0x2e}, /* this solves failure of "SuspendResumeTest" */
+ {0x31, 0xf9}, /* enable 1.8V Suspend */
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0x25, 0x42}, /* GPIO[8]:Input */
+ {0x94, 0x11}, /* If the default setting is loaded when
+ * system boots up, this flag is closed here */
+};
+static const u8 sensor_init_767x[][2] = {
+ {0x12, 0x80},
+ {0x11, 0x03},
+ {0x3a, 0x04},
+ {0x12, 0x00},
+ {0x17, 0x13},
+ {0x18, 0x01},
+ {0x32, 0xb6},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+ {0x03, 0x0a},
+ {0x0c, 0x00},
+ {0x3e, 0x00},
+ {0x70, 0x3a},
+ {0x71, 0x35},
+ {0x72, 0x11},
+ {0x73, 0xf0},
+ {0xa2, 0x02},
+ {0x7a, 0x2a}, /* set Gamma=1.6 below */
+ {0x7b, 0x12},
+ {0x7c, 0x1d},
+ {0x7d, 0x2d},
+ {0x7e, 0x45},
+ {0x7f, 0x50},
+ {0x80, 0x59},
+ {0x81, 0x62},
+ {0x82, 0x6b},
+ {0x83, 0x73},
+ {0x84, 0x7b},
+ {0x85, 0x8a},
+ {0x86, 0x98},
+ {0x87, 0xb2},
+ {0x88, 0xca},
+ {0x89, 0xe0},
+ {0x13, 0xe0},
+ {0x00, 0x00},
+ {0x10, 0x00},
+ {0x0d, 0x40},
+ {0x14, 0x38}, /* gain max 16x */
+ {0xa5, 0x05},
+ {0xab, 0x07},
+ {0x24, 0x95},
+ {0x25, 0x33},
+ {0x26, 0xe3},
+ {0x9f, 0x78},
+ {0xa0, 0x68},
+ {0xa1, 0x03},
+ {0xa6, 0xd8},
+ {0xa7, 0xd8},
+ {0xa8, 0xf0},
+ {0xa9, 0x90},
+ {0xaa, 0x94},
+ {0x13, 0xe5},
+ {0x0e, 0x61},
+ {0x0f, 0x4b},
+ {0x16, 0x02},
+ {0x21, 0x02},
+ {0x22, 0x91},
+ {0x29, 0x07},
+ {0x33, 0x0b},
+ {0x35, 0x0b},
+ {0x37, 0x1d},
+ {0x38, 0x71},
+ {0x39, 0x2a},
+ {0x3c, 0x78},
+ {0x4d, 0x40},
+ {0x4e, 0x20},
+ {0x69, 0x00},
+ {0x6b, 0x4a},
+ {0x74, 0x10},
+ {0x8d, 0x4f},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x90, 0x00},
+ {0x91, 0x00},
+ {0x96, 0x00},
+ {0x9a, 0x80},
+ {0xb0, 0x84},
+ {0xb1, 0x0c},
+ {0xb2, 0x0e},
+ {0xb3, 0x82},
+ {0xb8, 0x0a},
+ {0x43, 0x0a},
+ {0x44, 0xf0},
+ {0x45, 0x34},
+ {0x46, 0x58},
+ {0x47, 0x28},
+ {0x48, 0x3a},
+ {0x59, 0x88},
+ {0x5a, 0x88},
+ {0x5b, 0x44},
+ {0x5c, 0x67},
+ {0x5d, 0x49},
+ {0x5e, 0x0e},
+ {0x6c, 0x0a},
+ {0x6d, 0x55},
+ {0x6e, 0x11},
+ {0x6f, 0x9f},
+ {0x6a, 0x40},
+ {0x01, 0x40},
+ {0x02, 0x40},
+ {0x13, 0xe7},
+ {0x4f, 0x80},
+ {0x50, 0x80},
+ {0x51, 0x00},
+ {0x52, 0x22},
+ {0x53, 0x5e},
+ {0x54, 0x80},
+ {0x58, 0x9e},
+ {0x41, 0x08},
+ {0x3f, 0x00},
+ {0x75, 0x04},
+ {0x76, 0xe1},
+ {0x4c, 0x00},
+ {0x77, 0x01},
+ {0x3d, 0xc2},
+ {0x4b, 0x09},
+ {0xc9, 0x60},
+ {0x41, 0x38}, /* jfm: auto sharpness + auto de-noise */
+ {0x56, 0x40},
+ {0x34, 0x11},
+ {0x3b, 0xc2},
+ {0xa4, 0x8a}, /* Night mode trigger point */
+ {0x96, 0x00},
+ {0x97, 0x30},
+ {0x98, 0x20},
+ {0x99, 0x20},
+ {0x9a, 0x84},
+ {0x9b, 0x29},
+ {0x9c, 0x03},
+ {0x9d, 0x4c},
+ {0x9e, 0x3f},
+ {0x78, 0x04},
+ {0x79, 0x01},
+ {0xc8, 0xf0},
+ {0x79, 0x0f},
+ {0xc8, 0x00},
+ {0x79, 0x10},
+ {0xc8, 0x7e},
+ {0x79, 0x0a},
+ {0xc8, 0x80},
+ {0x79, 0x0b},
+ {0xc8, 0x01},
+ {0x79, 0x0c},
+ {0xc8, 0x0f},
+ {0x79, 0x0d},
+ {0xc8, 0x20},
+ {0x79, 0x09},
+ {0xc8, 0x80},
+ {0x79, 0x02},
+ {0xc8, 0xc0},
+ {0x79, 0x03},
+ {0xc8, 0x20},
+ {0x79, 0x26},
+};
+static const u8 bridge_start_vga_767x[][2] = {
+/* str59 JPG */
+ {0x94, 0xaa},
+ {0xf1, 0x42},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0xc2, 0x0c},
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0xda, 0x00}, /* for higher clock rate(30fps) */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x8c, 0x00}, /* CIF VSize LSB[2:0] */
+ {0x8d, 0x1c}, /* output YUV */
+/* {0x34, 0x05}, * enable Audio Suspend mode (?) */
+ {0x50, 0x00}, /* H/V divider=0 */
+ {0x51, 0xa0}, /* input H=640/4 */
+ {0x52, 0x3c}, /* input V=480/4 */
+ {0x53, 0x00}, /* offset X=0 */
+ {0x54, 0x00}, /* offset Y=0 */
+ {0x55, 0x00}, /* H/V size[8]=0 */
+ {0x57, 0x00}, /* H-size[9]=0 */
+ {0x5c, 0x00}, /* output size[9:8]=0 */
+ {0x5a, 0xa0}, /* output H=640/4 */
+ {0x5b, 0x78}, /* output V=480/4 */
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x94, 0x11},
+};
+static const u8 sensor_start_vga_767x[][2] = {
+ {0x11, 0x01},
+ {0x1e, 0x04},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+};
+static const u8 bridge_start_qvga_767x[][2] = {
+/* str86 JPG */
+ {0x94, 0xaa},
+ {0xf1, 0x42},
+ {0xe5, 0x04},
+ {0xc0, 0x80},
+ {0xc1, 0x60},
+ {0xc2, 0x0c},
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0xc0, 0x50}, /* CIF HSize 640 */
+ {0xc1, 0x3c}, /* CIF VSize 480 */
+ {0x8c, 0x00}, /* CIF VSize LSB[2:0] */
+ {0x8d, 0x1c}, /* output YUV */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc2, 0x4c}, /* output YUV and Enable DCW */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x1c, 0x00}, /* indirect addressing */
+ {0x1d, 0x48}, /* output YUV422 */
+ {0x50, 0x89}, /* H/V divider=/2; plus DCW AVG */
+ {0x51, 0xa0}, /* DCW input H=640/4 */
+ {0x52, 0x78}, /* DCW input V=480/4 */
+ {0x53, 0x00}, /* offset X=0 */
+ {0x54, 0x00}, /* offset Y=0 */
+ {0x55, 0x00}, /* H/V size[8]=0 */
+ {0x57, 0x00}, /* H-size[9]=0 */
+ {0x5c, 0x00}, /* DCW output size[9:8]=0 */
+ {0x5a, 0x50}, /* DCW output H=320/4 */
+ {0x5b, 0x3c}, /* DCW output V=240/4 */
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x94, 0x11},
+};
+static const u8 sensor_start_qvga_767x[][2] = {
+ {0x11, 0x01},
+ {0x1e, 0x04},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+};
+
+static const u8 bridge_init_772x[][2] = {
{ 0xc2, 0x0c },
{ 0x88, 0xf8 },
{ 0xc3, 0x69 },
@@ -338,7 +623,7 @@ static const u8 bridge_init[][2] = {
{ 0xc1, 0x3c },
{ 0xc2, 0x0c },
};
-static const u8 sensor_init[][2] = {
+static const u8 sensor_init_772x[][2] = {
{ 0x12, 0x80 },
{ 0x11, 0x01 },
/*fixme: better have a delay?*/
@@ -431,7 +716,7 @@ static const u8 sensor_init[][2] = {
{ 0x8e, 0x00 }, /* De-noise threshold */
{ 0x0c, 0xd0 }
};
-static const u8 bridge_start_vga[][2] = {
+static const u8 bridge_start_vga_772x[][2] = {
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -442,7 +727,7 @@ static const u8 bridge_start_vga[][2] = {
{0xc0, 0x50},
{0xc1, 0x3c},
};
-static const u8 sensor_start_vga[][2] = {
+static const u8 sensor_start_vga_772x[][2] = {
{0x12, 0x00},
{0x17, 0x26},
{0x18, 0xa0},
@@ -452,7 +737,7 @@ static const u8 sensor_start_vga[][2] = {
{0x2c, 0xf0},
{0x65, 0x20},
};
-static const u8 bridge_start_qvga[][2] = {
+static const u8 bridge_start_qvga_772x[][2] = {
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -463,7 +748,7 @@ static const u8 bridge_start_qvga[][2] = {
{0xc0, 0x28},
{0xc1, 0x1e},
};
-static const u8 sensor_start_qvga[][2] = {
+static const u8 sensor_start_qvga_772x[][2] = {
{0x12, 0x40},
{0x17, 0x3f},
{0x18, 0x50},
@@ -646,6 +931,8 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
{30, 0x04, 0x41, 0x04},
};
+ if (sd->sensor != SENSOR_OV772x)
+ return;
if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
r = rate_0;
i = ARRAY_SIZE(rate_0);
@@ -669,15 +956,28 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int val;
- sccb_reg_write(gspca_dev, 0x9b, sd->brightness);
+ val = sd->ctrls[BRIGHTNESS].val;
+ if (sd->sensor == SENSOR_OV767x) {
+ if (val < 0)
+ val = 0x80 - val;
+ sccb_reg_write(gspca_dev, 0x55, val); /* bright */
+ } else {
+ sccb_reg_write(gspca_dev, 0x9b, val);
+ }
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
- sccb_reg_write(gspca_dev, 0x9c, sd->contrast);
+ val = sd->ctrls[CONTRAST].val;
+ if (sd->sensor == SENSOR_OV767x)
+ sccb_reg_write(gspca_dev, 0x56, val); /* contras */
+ else
+ sccb_reg_write(gspca_dev, 0x9c, val);
}
static void setgain(struct gspca_dev *gspca_dev)
@@ -685,10 +985,10 @@ static void setgain(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- if (sd->agc)
+ if (sd->ctrls[AGC].val)
return;
- val = sd->gain;
+ val = sd->ctrls[GAIN].val;
switch (val & 0x30) {
case 0x00:
val &= 0x0f;
@@ -715,25 +1015,32 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- if (sd->aec)
+ if (sd->ctrls[AEC].val)
return;
- /* 'val' is one byte and represents half of the exposure value we are
- * going to set into registers, a two bytes value:
- *
- * MSB: ((u16) val << 1) >> 8 == val >> 7
- * LSB: ((u16) val << 1) & 0xff == val << 1
- */
- val = sd->exposure;
- sccb_reg_write(gspca_dev, 0x08, val >> 7);
- sccb_reg_write(gspca_dev, 0x10, val << 1);
+ val = sd->ctrls[EXPOSURE].val;
+ if (sd->sensor == SENSOR_OV767x) {
+
+ /* set only aec[9:2] */
+ sccb_reg_write(gspca_dev, 0x10, val); /* aech */
+ } else {
+
+ /* 'val' is one byte and represents half of the exposure value
+ * we are going to set into registers, a two bytes value:
+ *
+ * MSB: ((u16) val << 1) >> 8 == val >> 7
+ * LSB: ((u16) val << 1) & 0xff == val << 1
+ */
+ sccb_reg_write(gspca_dev, 0x08, val >> 7);
+ sccb_reg_write(gspca_dev, 0x10, val << 1);
+ }
}
static void setagc(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->agc) {
+ if (sd->ctrls[AGC].val) {
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) | 0x04);
sccb_reg_write(gspca_dev, 0x64,
@@ -752,15 +1059,17 @@ static void setawb(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->awb) {
+ if (sd->ctrls[AWB].val) {
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) | 0x02);
- sccb_reg_write(gspca_dev, 0x63,
+ if (sd->sensor == SENSOR_OV772x)
+ sccb_reg_write(gspca_dev, 0x63,
sccb_reg_read(gspca_dev, 0x63) | 0xc0);
} else {
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) & ~0x02);
- sccb_reg_write(gspca_dev, 0x63,
+ if (sd->sensor == SENSOR_OV772x)
+ sccb_reg_write(gspca_dev, 0x63,
sccb_reg_read(gspca_dev, 0x63) & ~0xc0);
}
}
@@ -768,14 +1077,22 @@ static void setawb(struct gspca_dev *gspca_dev)
static void setaec(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
- if (sd->aec)
+ data = sd->sensor == SENSOR_OV767x ?
+ 0x05 : /* agc + aec */
+ 0x01; /* agc */
+ if (sd->ctrls[AEC].val)
sccb_reg_write(gspca_dev, 0x13,
- sccb_reg_read(gspca_dev, 0x13) | 0x01);
+ sccb_reg_read(gspca_dev, 0x13) | data);
else {
sccb_reg_write(gspca_dev, 0x13,
- sccb_reg_read(gspca_dev, 0x13) & ~0x01);
- setexposure(gspca_dev);
+ sccb_reg_read(gspca_dev, 0x13) & ~data);
+ if (sd->sensor == SENSOR_OV767x)
+ sd->ctrls[EXPOSURE].val =
+ sccb_reg_read(gspca_dev, 10); /* aech */
+ else
+ setexposure(gspca_dev);
}
}
@@ -784,43 +1101,67 @@ static void setsharpness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- val = sd->sharpness;
+ val = sd->ctrls[SHARPNESS].val;
sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */
sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */
}
-static void sethflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
- if (sd->hflip == 0)
- sccb_reg_write(gspca_dev, 0x0c,
- sccb_reg_read(gspca_dev, 0x0c) | 0x40);
- else
- sccb_reg_write(gspca_dev, 0x0c,
- sccb_reg_read(gspca_dev, 0x0c) & ~0x40);
+ if (sd->sensor == SENSOR_OV767x) {
+ val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */
+ val &= ~0x30;
+ if (sd->ctrls[HFLIP].val)
+ val |= 0x20;
+ if (sd->ctrls[VFLIP].val)
+ val |= 0x10;
+ sccb_reg_write(gspca_dev, 0x1e, val);
+ } else {
+ val = sccb_reg_read(gspca_dev, 0x0c);
+ val &= ~0xc0;
+ if (sd->ctrls[HFLIP].val == 0)
+ val |= 0x40;
+ if (sd->ctrls[VFLIP].val == 0)
+ val |= 0x80;
+ sccb_reg_write(gspca_dev, 0x0c, val);
+ }
}
-static void setvflip(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ int i;
+ static u8 color_tb[][6] = {
+ {0x42, 0x42, 0x00, 0x11, 0x30, 0x41},
+ {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52},
+ {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66},
+ {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80},
+ {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a},
+ {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8},
+ {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd},
+ };
- if (sd->vflip == 0)
- sccb_reg_write(gspca_dev, 0x0c,
- sccb_reg_read(gspca_dev, 0x0c) | 0x80);
- else
- sccb_reg_write(gspca_dev, 0x0c,
- sccb_reg_read(gspca_dev, 0x0c) & ~0x80);
+ val = sd->ctrls[COLORS].val;
+ for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++)
+ sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]);
}
-static void setfreqfltr(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
- if (sd->freqfltr == 0)
- sccb_reg_write(gspca_dev, 0x2b, 0x00);
- else
- sccb_reg_write(gspca_dev, 0x2b, 0x9e);
+ val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
+ if (sd->sensor == SENSOR_OV767x) {
+ sccb_reg_write(gspca_dev, 0x2a, 0x00);
+ if (val)
+ val = 0x9d; /* insert dummy to 25fps for 50Hz */
+ }
+ sccb_reg_write(gspca_dev, 0x2b, val);
}
@@ -833,39 +1174,33 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
+ cam->ctrls = sd->ctrls;
+
+ /* the auto white balance control works only when auto gain is set */
+ if (sd_ctrls[AGC].qctrl.default_value == 0)
+ gspca_dev->ctrl_inac |= (1 << AWB);
+
cam->cam_mode = ov772x_mode;
cam->nmodes = ARRAY_SIZE(ov772x_mode);
- cam->mode_framerates = ov772x_framerates;
-
- cam->bulk = 1;
- cam->bulk_size = 16384;
- cam->bulk_nurbs = 2;
sd->frame_rate = 30;
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->gain = GAIN_DEF;
- sd->exposure = EXPO_DEF;
-#if AGC_DEF != 0
- sd->agc = AGC_DEF;
-#else
- gspca_dev->ctrl_inac |= (1 << AWB_IDX);
-#endif
- sd->awb = AWB_DEF;
- sd->aec = AEC_DEF;
- sd->sharpness = SHARPNESS_DEF;
- sd->hflip = HFLIP_DEF;
- sd->vflip = VFLIP_DEF;
- sd->freqfltr = FREQFLTR_DEF;
-
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
u16 sensor_id;
+ static const struct reg_array bridge_init[NSENSORS] = {
+ [SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)},
+ [SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)},
+ };
+ static const struct reg_array sensor_init[NSENSORS] = {
+ [SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)},
+ [SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)},
+ };
/* reset bridge */
ov534_reg_write(gspca_dev, 0xe7, 0x3a);
@@ -886,48 +1221,100 @@ static int sd_init(struct gspca_dev *gspca_dev)
sensor_id |= sccb_reg_read(gspca_dev, 0x0b);
PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+ if ((sensor_id & 0xfff0) == 0x7670) {
+ sd->sensor = SENSOR_OV767x;
+ gspca_dev->ctrl_dis = (1 << GAIN) |
+ (1 << AGC) |
+ (1 << SHARPNESS); /* auto */
+ sd->ctrls[BRIGHTNESS].min = -127;
+ sd->ctrls[BRIGHTNESS].max = 127;
+ sd->ctrls[BRIGHTNESS].def = 0;
+ sd->ctrls[CONTRAST].max = 0x80;
+ sd->ctrls[CONTRAST].def = 0x40;
+ sd->ctrls[EXPOSURE].min = 0x08;
+ sd->ctrls[EXPOSURE].max = 0x60;
+ sd->ctrls[EXPOSURE].def = 0x13;
+ sd->ctrls[SHARPNESS].max = 9;
+ sd->ctrls[SHARPNESS].def = 4;
+ sd->ctrls[HFLIP].def = 1;
+ gspca_dev->cam.cam_mode = ov767x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
+ } else {
+ sd->sensor = SENSOR_OV772x;
+ gspca_dev->ctrl_dis = (1 << COLORS);
+ gspca_dev->cam.bulk = 1;
+ gspca_dev->cam.bulk_size = 16384;
+ gspca_dev->cam.bulk_nurbs = 2;
+ gspca_dev->cam.mode_framerates = ov772x_framerates;
+ }
+
/* initialize */
- reg_w_array(gspca_dev, bridge_init,
- ARRAY_SIZE(bridge_init));
+ reg_w_array(gspca_dev, bridge_init[sd->sensor].val,
+ bridge_init[sd->sensor].len);
ov534_set_led(gspca_dev, 1);
- sccb_w_array(gspca_dev, sensor_init,
- ARRAY_SIZE(sensor_init));
- ov534_reg_write(gspca_dev, 0xe0, 0x09);
- ov534_set_led(gspca_dev, 0);
- set_frame_rate(gspca_dev);
+ sccb_w_array(gspca_dev, sensor_init[sd->sensor].val,
+ sensor_init[sd->sensor].len);
+ if (sd->sensor == SENSOR_OV767x)
+ sd_start(gspca_dev);
+ sd_stopN(gspca_dev);
+/* set_frame_rate(gspca_dev); */
return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
int mode;
+ static const struct reg_array bridge_start[NSENSORS][2] = {
+ [SENSOR_OV767x] = {{bridge_start_qvga_767x,
+ ARRAY_SIZE(bridge_start_qvga_767x)},
+ {bridge_start_vga_767x,
+ ARRAY_SIZE(bridge_start_vga_767x)}},
+ [SENSOR_OV772x] = {{bridge_start_qvga_772x,
+ ARRAY_SIZE(bridge_start_qvga_772x)},
+ {bridge_start_vga_772x,
+ ARRAY_SIZE(bridge_start_vga_772x)}},
+ };
+ static const struct reg_array sensor_start[NSENSORS][2] = {
+ [SENSOR_OV767x] = {{sensor_start_qvga_767x,
+ ARRAY_SIZE(sensor_start_qvga_767x)},
+ {sensor_start_vga_767x,
+ ARRAY_SIZE(sensor_start_vga_767x)}},
+ [SENSOR_OV772x] = {{sensor_start_qvga_772x,
+ ARRAY_SIZE(sensor_start_qvga_772x)},
+ {sensor_start_vga_772x,
+ ARRAY_SIZE(sensor_start_vga_772x)}},
+ };
+
+ /* (from ms-win trace) */
+ if (sd->sensor == SENSOR_OV767x)
+ sccb_reg_write(gspca_dev, 0x1e, 0x04);
+ /* black sun enable ? */
+
+ mode = gspca_dev->curr_mode; /* 0: 320x240, 1: 640x480 */
+ reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val,
+ bridge_start[sd->sensor][mode].len);
+ sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val,
+ sensor_start[sd->sensor][mode].len);
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- if (mode != 0) { /* 320x240 */
- reg_w_array(gspca_dev, bridge_start_qvga,
- ARRAY_SIZE(bridge_start_qvga));
- sccb_w_array(gspca_dev, sensor_start_qvga,
- ARRAY_SIZE(sensor_start_qvga));
- } else { /* 640x480 */
- reg_w_array(gspca_dev, bridge_start_vga,
- ARRAY_SIZE(bridge_start_vga));
- sccb_w_array(gspca_dev, sensor_start_vga,
- ARRAY_SIZE(sensor_start_vga));
- }
set_frame_rate(gspca_dev);
- setagc(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << AGC)))
+ setagc(gspca_dev);
setawb(gspca_dev);
setaec(gspca_dev);
- setgain(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
+ setgain(gspca_dev);
setexposure(gspca_dev);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
- setsharpness(gspca_dev);
- setvflip(gspca_dev);
- sethflip(gspca_dev);
- setfreqfltr(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << COLORS)))
+ setcolors(gspca_dev);
+ setlightfreq(gspca_dev);
ov534_set_led(gspca_dev, 1);
ov534_reg_write(gspca_dev, 0xe0, 0x00);
@@ -957,9 +1344,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
__u32 this_pts;
u16 this_fid;
int remaining_len = len;
+ int payload_len;
+ payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
do {
- len = min(remaining_len, 2048);
+ len = min(remaining_len, payload_len);
/* Payloads are prefixed with a UVC-style header. We
consider a frame to start when the FID toggles, or the PTS
@@ -999,8 +1388,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* If this packet is marked as EOF, end the frame */
} else if (data[1] & UVC_STREAM_EOF) {
sd->last_pts = 0;
- if (gspca_dev->image_len + len - 12 !=
- gspca_dev->width * gspca_dev->height * 2) {
+ if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV
+ && gspca_dev->image_len + len - 12 !=
+ gspca_dev->width * gspca_dev->height * 2) {
PDEBUG(D_PACK, "wrong sized frame");
goto discard;
}
@@ -1026,212 +1416,27 @@ scan_next:
} while (remaining_len > 0);
}
-/* controls */
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- sd->agc = val;
-
- if (gspca_dev->streaming) {
+ sd->ctrls[AGC].val = val;
- /* the auto white balance control works only
- * when auto gain is set */
- if (val)
- gspca_dev->ctrl_inac &= ~(1 << AWB_IDX);
- else
- gspca_dev->ctrl_inac |= (1 << AWB_IDX);
- setagc(gspca_dev);
+ /* the auto white balance control works only
+ * when auto gain is set */
+ if (val) {
+ gspca_dev->ctrl_inac &= ~(1 << AWB);
+ } else {
+ gspca_dev->ctrl_inac |= (1 << AWB);
+ if (sd->ctrls[AWB].val) {
+ sd->ctrls[AWB].val = 0;
+ if (gspca_dev->streaming)
+ setawb(gspca_dev);
+ }
}
- return 0;
-}
-
-static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->agc;
- return 0;
-}
-
-static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->awb = val;
- if (gspca_dev->streaming)
- setawb(gspca_dev);
- return 0;
-}
-
-static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->awb;
- return 0;
-}
-
-static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->aec = val;
- if (gspca_dev->streaming)
- setaec(gspca_dev);
- return 0;
-}
-
-static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->aec;
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming)
- setsharpness(gspca_dev);
- return 0;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming)
- sethflip(gspca_dev);
- return 0;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hflip;
- return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->vflip = val;
- if (gspca_dev->streaming)
- setvflip(gspca_dev);
- return 0;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->vflip;
- return 0;
-}
-
-static int sd_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->freqfltr = val;
if (gspca_dev->streaming)
- setfreqfltr(gspca_dev);
- return 0;
-}
-
-static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->freqfltr;
- return 0;
+ setagc(gspca_dev);
+ return gspca_dev->usb_err;
}
static int sd_querymenu(struct gspca_dev *gspca_dev,
@@ -1302,6 +1507,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x1415, 0x2000)},
+ {USB_DEVICE(0x06f8, 0x3002)},
{}
};
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index fcf29897b713..c431900cd292 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -152,6 +152,13 @@ static const struct dmi_system_id flip_dmi_table[] = {
}
},
{
+ .ident = "MSI MS-1633X",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1633X")
+ }
+ },
+ {
.ident = "MSI MS-1635X",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
@@ -369,7 +376,7 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = SCALE_160x120},
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = SCALE_320x240 | MODE_JPEG},
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -384,7 +391,7 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = SCALE_320x240},
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = SCALE_640x480 | MODE_JPEG},
{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -417,7 +424,7 @@ static const struct v4l2_pix_format sxga_mode[] = {
.priv = SCALE_160x120},
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = SCALE_320x240 | MODE_JPEG},
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -432,7 +439,7 @@ static const struct v4l2_pix_format sxga_mode[] = {
.priv = SCALE_320x240},
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = SCALE_640x480 | MODE_JPEG},
{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -884,6 +891,9 @@ static struct i2c_reg_u8 ov7660_init[] = {
{0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3},
{0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40},
{0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a},
+ /* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using
+ 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */
+ {0x17, 0x10}, {0x18, 0x61},
{0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43},
{0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6},
{0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50},
@@ -1332,10 +1342,8 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
return -ENODEV;
}
}
- /* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
- sd->hstart = 1;
- sd->vstart = 1;
+ sd->hstart = 3;
+ sd->vstart = 3;
return 0;
}
@@ -1608,6 +1616,18 @@ static int set_hvflip(struct gspca_dev *gspca_dev)
}
switch (sd->sensor) {
+ case SENSOR_OV7660:
+ value = 0x01;
+ if (hflip)
+ value |= 0x20;
+ if (vflip) {
+ value |= 0x10;
+ sd->vstart = 2;
+ } else
+ sd->vstart = 3;
+ reg_w1(gspca_dev, 0x1182, sd->vstart);
+ i2c_w1(gspca_dev, 0x1e, value);
+ break;
case SENSOR_OV9650:
i2c_r1(gspca_dev, 0x1e, &value);
value &= ~0x30;
@@ -2482,7 +2502,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)},
{USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)},
- {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)},
{USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)},
{USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)},
@@ -2494,7 +2514,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
{USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
{USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)},
- {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)},
{USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
{USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)},
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index d6f39ce1b7e1..6415aff5cbd1 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -29,8 +29,6 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
MODULE_LICENSE("GPL");
-static int starcam;
-
/* controls */
enum e_ctrl {
BRIGHTNESS,
@@ -57,11 +55,18 @@ struct sd {
atomic_t avg_lum;
u32 exposure;
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ u8 nchg;
+ s8 short_mark;
+
u8 quality; /* image quality */
-#define QUALITY_MIN 60
-#define QUALITY_MAX 95
-#define QUALITY_DEF 80
- u8 jpegqual; /* webcam quality */
+#define QUALITY_MIN 25
+#define QUALITY_MAX 90
+#define QUALITY_DEF 70
u8 reg01;
u8 reg17;
@@ -99,6 +104,8 @@ enum sensors {
SENSOR_SP80708,
};
+static void qual_upd(struct work_struct *work);
+
/* device flags */
#define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */
#define F_ILLUM 0x02 /* presence of illuminator */
@@ -401,7 +408,7 @@ static const u8 sn_hv7131[0x1c] = {
static const u8 sn_mi0360[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
@@ -847,18 +854,8 @@ static const u8 mt9v111_sensor_init[][8] = {
{0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
{0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
{0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */
- {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x03, 0x01, 0xe1, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x04, 0x02, 0x81, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
- {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x03, 0x01, 0xe6, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x04, 0x02, 0x86, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
- {0xb1, 0x5c, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */
{0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */
{0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */
@@ -872,15 +869,10 @@ static const u8 mt9v111_sensor_init[][8] = {
{}
};
static const u8 mt9v111_sensor_param1[][8] = {
- {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10},
- {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, /* green1 gain */
- {0xd1, 0x5c, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, /* red gain */
- /*******/
- {0xb1, 0x5c, 0x06, 0x00, 0x1e, 0x00, 0x00, 0x10}, /* vert blanking */
- {0xb1, 0x5c, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, /* horiz blanking */
- {0xd1, 0x5c, 0x2c, 0x00, 0xad, 0x00, 0xad, 0x10}, /* blue gain */
+ {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */
+ {0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */
+ {0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */
+ {0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */
{0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
{}
};
@@ -1784,7 +1776,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->ag_cnt = -1;
sd->quality = QUALITY_DEF;
- sd->jpegqual = 80;
+
+ /* if USB 1.1, let some bandwidth for the audio device */
+ if (gspca_dev->audio && gspca_dev->dev->speed < USB_SPEED_HIGH)
+ gspca_dev->nbalt--;
+
+ INIT_WORK(&sd->work, qual_upd);
return 0;
}
@@ -1794,7 +1791,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const u8 *sn9c1xx;
- u8 regGpio[] = { 0x29, 0x74 }; /* with audio */
+ u8 regGpio[] = { 0x29, 0x70 }; /* no audio */
u8 regF1;
/* setup a selector by bridge */
@@ -1806,6 +1803,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
return gspca_dev->usb_err;
PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1);
+ if (gspca_dev->audio)
+ regGpio[1] |= 0x04; /* with audio */
switch (sd->bridge) {
case BRIDGE_SN9C102P:
case BRIDGE_SN9C105:
@@ -1838,14 +1837,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
case BRIDGE_SN9C102P:
reg_w1(gspca_dev, 0x02, regGpio[1]);
break;
- case BRIDGE_SN9C105:
- reg_w(gspca_dev, 0x01, regGpio, 2);
- break;
- case BRIDGE_SN9C110:
- reg_w1(gspca_dev, 0x02, 0x62);
- break;
- case BRIDGE_SN9C120:
- regGpio[1] = 0x70; /* no audio */
+ default:
reg_w(gspca_dev, 0x01, regGpio, 2);
break;
}
@@ -1944,10 +1936,10 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
u8 expo_c1[] =
{ 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
- if (expo > 0x0280)
- expo = 0x0280;
- else if (expo < 0x0040)
- expo = 0x0040;
+ if (expo > 0x0390)
+ expo = 0x0390;
+ else if (expo < 0x0060)
+ expo = 0x0060;
expo_c1[3] = expo >> 8;
expo_c1[4] = expo;
i2c_w8(gspca_dev, expo_c1);
@@ -2004,10 +1996,13 @@ static void setbrightness(struct gspca_dev *gspca_dev)
sd->exposure = setexposure(gspca_dev, expo);
break;
case SENSOR_GC0307:
- case SENSOR_MT9V111:
expo = brightness;
sd->exposure = setexposure(gspca_dev, expo);
return; /* don't set the Y offset */
+ case SENSOR_MT9V111:
+ expo = brightness << 2;
+ sd->exposure = setexposure(gspca_dev, expo);
+ return; /* don't set the Y offset */
case SENSOR_OM6802:
expo = brightness << 2;
sd->exposure = setexposure(gspca_dev, expo);
@@ -2199,14 +2194,11 @@ static void setillum(struct gspca_dev *gspca_dev)
sd->ctrls[ILLUM].val ? 0x64 : 0x60);
break;
case SENSOR_MT9V111:
- if (starcam)
- reg_w1(gspca_dev, 0x02,
- sd->ctrls[ILLUM].val ?
- 0x55 : 0x54); /* 370i */
- else
- reg_w1(gspca_dev, 0x02,
- sd->ctrls[ILLUM].val ?
- 0x66 : 0x64); /* Clip */
+ reg_w1(gspca_dev, 0x02,
+ sd->ctrls[ILLUM].val ? 0x77 : 0x74);
+/* should have been: */
+/* 0x55 : 0x54); * 370i */
+/* 0x66 : 0x64); * Clip */
break;
}
}
@@ -2271,18 +2263,12 @@ static void setfreq(struct gspca_dev *gspca_dev)
static void setjpegqual(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i, sc;
- if (sd->jpegqual < 50)
- sc = 5000 / sd->jpegqual;
- else
- sc = 200 - sd->jpegqual * 2;
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
#if USB_BUF_SZ < 64
#error "No room enough in usb_buf for quantization table"
#endif
- for (i = 0; i < 64; i++)
- gspca_dev->usb_buf[i] =
- (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x08,
@@ -2290,9 +2276,7 @@ static void setjpegqual(struct gspca_dev *gspca_dev)
0x0100, 0,
gspca_dev->usb_buf, 64,
500);
- for (i = 0; i < 64; i++)
- gspca_dev->usb_buf[i] =
- (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x08,
@@ -2305,6 +2289,19 @@ static void setjpegqual(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x18, sd->reg18);
}
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality);
+ setjpegqual(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -2338,7 +2335,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
/* initialize the bridge */
sn9c1xx = sn_tb[sd->sensor];
@@ -2619,6 +2615,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
setcolors(gspca_dev);
setautogain(gspca_dev);
setfreq(gspca_dev);
+
+ sd->pktsz = sd->npkt = 0;
+ sd->nchg = sd->short_mark = 0;
+ sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+
return gspca_dev->usb_err;
}
@@ -2695,6 +2696,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
/* reg_w1(gspca_dev, 0xf1, 0x01); */
}
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
+}
+
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2732,6 +2747,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
(unsigned int) (expotimes << 8));
break;
case SENSOR_OM6802:
+ case SENSOR_MT9V111:
expotimes = sd->exposure;
expotimes += (luma_mean - delta) >> 2;
if (expotimes < 0)
@@ -2744,7 +2760,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
/* case SENSOR_MO4000: */
/* case SENSOR_MI0360: */
/* case SENSOR_MI0360B: */
-/* case SENSOR_MT9V111: */
expotimes = sd->exposure;
expotimes += (luma_mean - delta) >> 6;
if (expotimes < 0)
@@ -2757,6 +2772,29 @@ static void do_autogain(struct gspca_dev *gspca_dev)
}
}
+/* set the average luminosity from an isoc marker */
+static void set_lum(struct sd *sd,
+ u8 *data)
+{
+ int avg_lum;
+
+ /* w0 w1 w2
+ * w3 w4 w5
+ * w6 w7 w8
+ */
+ avg_lum = (data[27] << 8) + data[28] /* w3 */
+
+ + (data[31] << 8) + data[32] /* w5 */
+
+ + (data[23] << 8) + data[24] /* w1 */
+
+ + (data[35] << 8) + data[36] /* w7 */
+
+ + (data[29] << 10) + (data[30] << 2); /* w4 * 4 */
+ avg_lum >>= 10;
+ atomic_set(&sd->avg_lum, avg_lum);
+}
+
/* scan the URB packets */
/* This function is run at interrupt level. */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -2764,70 +2802,141 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
- int sof, avg_lum;
-
- /* the image ends on a 64 bytes block starting with
- * ff d9 ff ff 00 c4 c4 96
- * and followed by various information including luminosity */
- /* this block may be splitted between two packets */
- /* a new image always starts in a new packet */
- switch (gspca_dev->last_packet_type) {
- case DISCARD_PACKET: /* restart image building */
- sof = len - 64;
- if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9)
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- return;
- case LAST_PACKET: /* put the JPEG 422 header */
+ int i, new_qual;
+
+ /*
+ * A frame ends on the marker
+ * ff ff 00 c4 c4 96 ..
+ * which is 62 bytes long and is followed by various information
+ * including statuses and luminosity.
+ *
+ * A marker may be splitted on two packets.
+ *
+ * The 6th byte of a marker contains the bits:
+ * 0x08: USB full
+ * 0xc0: frame sequence
+ * When the bit 'USB full' is set, the frame must be discarded;
+ * this is also the case when the 2 bytes before the marker are
+ * not the JPEG end of frame ('ff d9').
+ */
+
+/*fixme: assumption about the following code:
+ * - there can be only one marker in a packet
+ */
+
+ /* skip the remaining bytes of a short marker */
+ i = sd->short_mark;
+ if (i != 0) {
+ sd->short_mark = 0;
+ if (i < 0 /* if 'ff' at end of previous packet */
+ && data[0] == 0xff
+ && data[1] == 0x00)
+ goto marker_found;
+ if (data[0] == 0xff && data[1] == 0xff) {
+ i = 0;
+ goto marker_found;
+ }
+ len -= i;
+ if (len <= 0)
+ return;
+ data += i;
+ }
+
+ /* count the packets and their size */
+ sd->npkt++;
+ sd->pktsz += len;
+
+ /* search backwards if there is a marker in the packet */
+ for (i = len - 1; --i >= 0; ) {
+ if (data[i] != 0xff) {
+ i--;
+ continue;
+ }
+ if (data[i + 1] == 0xff) {
+
+ /* (there may be 'ff ff' inside a marker) */
+ if (i + 2 >= len || data[i + 2] == 0x00)
+ goto marker_found;
+ }
+ }
+
+ /* no marker found */
+ /* add the JPEG header if first fragment */
+ if (data[len - 1] == 0xff)
+ sd->short_mark = -1;
+ if (gspca_dev->last_packet_type == LAST_PACKET)
gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
- break;
- }
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ return;
+
+ /* marker found */
+ /* if some error, discard the frame and decrease the quality */
+marker_found:
+ new_qual = 0;
+ if (i > 2) {
+ if (data[i - 2] != 0xff || data[i - 1] != 0xd9) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -3;
+ }
+ } else if (i + 6 < len) {
+ if (data[i + 6] & 0x08) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -5;
+ }
+ }
- data = gspca_dev->image;
- if (data == NULL)
- return;
- sof = gspca_dev->image_len - 64;
- if (data[sof] != 0xff
- || data[sof + 1] != 0xd9)
- return;
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
- /* end of image found - remove the trailing data */
- gspca_dev->image_len = sof + 2;
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- if (sd->ag_cnt < 0)
- return;
-/* w1 w2 w3 */
-/* w4 w5 w6 */
-/* w7 w8 */
-/* w4 */
- avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6;
-/* w6 */
- avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6;
-/* w2 */
- avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6;
-/* w8 */
- avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6;
-/* w5 */
- avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
- avg_lum >>= 4;
- atomic_set(&sd->avg_lum, avg_lum);
-}
+ /* compute the filling rate and a new JPEG quality */
+ if (new_qual == 0) {
+ int r;
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ r = (sd->pktsz * 100) /
+ (sd->npkt *
+ gspca_dev->urb[0]->iso_frame_desc[0].length);
+ if (r >= 85)
+ new_qual = -3;
+ else if (r < 75)
+ new_qual = 2;
+ }
+ if (new_qual != 0) {
+ sd->nchg += new_qual;
+ if (sd->nchg < -6 || sd->nchg >= 12) {
+ sd->nchg = 0;
+ new_qual += sd->quality;
+ if (new_qual < QUALITY_MIN)
+ new_qual = QUALITY_MIN;
+ else if (new_qual > QUALITY_MAX)
+ new_qual = QUALITY_MAX;
+ if (new_qual != sd->quality) {
+ sd->quality = new_qual;
+ queue_work(sd->work_thread, &sd->work);
+ }
+ }
+ } else {
+ sd->nchg = 0;
+ }
+ sd->pktsz = sd->npkt = 0;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return 0;
+ /* if the marker is smaller than 62 bytes,
+ * memorize the number of bytes to skip in the next packet */
+ if (i + 62 > len) { /* no more usable data */
+ sd->short_mark = i + 62 - len;
+ return;
+ }
+ if (sd->ag_cnt >= 0)
+ set_lum(sd, data + i);
+
+ /* if more data, start a new frame */
+ i += 62;
+ if (i < len) {
+ data += i;
+ len -= i;
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -2891,10 +3000,10 @@ static const struct sd_desc sd_desc = {
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
+ .stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
.get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
.querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.int_pkt_scan = sd_int_pkt_scan,
@@ -3004,7 +3113,3 @@ static void __exit sd_mod_exit(void)
module_init(sd_mod_init);
module_exit(sd_mod_exit);
-
-module_param(starcam, int, 0644);
-MODULE_PARM_DESC(starcam,
- "StarCam model. 0: Clip, 1: 370i");
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
new file mode 100644
index 000000000000..84dfbab923b5
--- /dev/null
+++ b/drivers/media/video/gspca/vicam.c
@@ -0,0 +1,381 @@
+/*
+ * gspca ViCam subdriver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo vicam driver, which is:
+ *
+ * Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
+ * Christopher L Cheney (ccheney@cheney.cx),
+ * Pavel Machek (pavel@ucw.cz),
+ * John Tyner (jtyner@cs.ucr.edu),
+ * Monroe Williams (monroe@pobox.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "vicam"
+#define HEADER_SIZE 64
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+enum e_ctrl {
+ GAIN,
+ EXPOSURE,
+ NCTRL /* number of controls */
+};
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+ struct gspca_ctrl ctrls[NCTRL];
+};
+
+/* The vicam sensor has a resolution of 512 x 244, with I believe square
+ pixels, but this is forced to a 4:3 ratio by optics. So it has
+ non square pixels :( */
+static struct v4l2_pix_format vicam_mode[] = {
+ { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 122,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+ /* 2 modes with somewhat more square pixels */
+ { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 200,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+ { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+#if 0 /* This mode has extremely non square pixels, testing use only */
+ { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 512,
+ .sizeimage = 512 * 122,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+#endif
+ { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 512,
+ .sizeimage = 512 * 244,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+};
+
+static const struct ctrl sd_ctrls[] = {
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 200,
+ },
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 2047,
+ .step = 1,
+ .default_value = 256,
+ },
+ },
+};
+
+static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
+ u16 value, u16 index, u8 *data, u16 len)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, data, len, 1000);
+ if (ret < 0)
+ err("control msg req %02X error %d", request, ret);
+
+ return ret;
+}
+
+static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state)
+{
+ int ret;
+
+ ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0);
+
+ return ret;
+}
+
+/*
+ * request and read a block of data - see warning on vicam_command.
+ */
+static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int ret, unscaled_height, act_len = 0;
+ u8 *req_data = gspca_dev->usb_buf;
+
+ memset(req_data, 0, 16);
+ req_data[0] = sd->ctrls[GAIN].val;
+ if (gspca_dev->width == 256)
+ req_data[1] |= 0x01; /* low nibble x-scale */
+ if (gspca_dev->height <= 122) {
+ req_data[1] |= 0x10; /* high nibble y-scale */
+ unscaled_height = gspca_dev->height * 2;
+ } else
+ unscaled_height = gspca_dev->height;
+ req_data[2] = 0x90; /* unknown, does not seem to do anything */
+ if (unscaled_height <= 200)
+ req_data[3] = 0x06; /* vend? */
+ else if (unscaled_height <= 242) /* Yes 242 not 240 */
+ req_data[3] = 0x07; /* vend? */
+ else /* Up to 244 lines with req_data[3] == 0x08 */
+ req_data[3] = 0x08; /* vend? */
+
+ if (sd->ctrls[EXPOSURE].val < 256) {
+ /* Frame rate maxed out, use partial frame expo time */
+ req_data[4] = 255 - sd->ctrls[EXPOSURE].val;
+ req_data[5] = 0x00;
+ req_data[6] = 0x00;
+ req_data[7] = 0x01;
+ } else {
+ /* Modify frame rate */
+ req_data[4] = 0x00;
+ req_data[5] = 0x00;
+ req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF;
+ req_data[7] = sd->ctrls[EXPOSURE].val >> 8;
+ }
+ req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */
+ /* bytes 9-15 do not seem to affect exposure or image quality */
+
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ data, size, &act_len, 10000);
+ /* successful, it returns 0, otherwise negative */
+ if (ret < 0 || act_len != size) {
+ err("bulk read fail (%d) len %d/%d",
+ ret, act_len, size);
+ return -EIO;
+ }
+ return 0;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface we take the gspca
+ * usb_lock when performing USB operations. In practice the only thing we need
+ * to protect against is the usb_set_interface call that gspca makes during
+ * stream_off as the camera doesn't provide any controls that the user could try
+ * to change.
+ */
+static void vicam_dostream(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ int ret, frame_sz;
+ u8 *buffer;
+
+ frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage +
+ HEADER_SIZE;
+ buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ err("Couldn't allocate USB buffer");
+ goto exit;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+ ret = vicam_read_frame(gspca_dev, buffer, frame_sz);
+ if (ret < 0)
+ break;
+
+ /* Note the frame header contents seem to be completely
+ constant, they do not change with either image, or
+ settings. So we simply discard it. The frames have
+ a very similar 64 byte footer, which we don't even
+ bother reading from the cam */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ buffer + HEADER_SIZE,
+ frame_sz - HEADER_SIZE);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ }
+exit:
+ kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk = 1;
+ cam->bulk_size = 64;
+ cam->cam_mode = vicam_mode;
+ cam->nmodes = ARRAY_SIZE(vicam_mode);
+ cam->ctrls = sd->ctrls;
+
+ INIT_WORK(&sd->work_struct, vicam_dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int ret;
+ const struct ihex_binrec *rec;
+ const struct firmware *uninitialized_var(fw);
+ u8 *firmware_buf;
+
+ ret = request_ihex_firmware(&fw, "vicam/firmware.fw",
+ &gspca_dev->dev->dev);
+ if (ret) {
+ err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
+ return ret;
+ }
+
+ firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!firmware_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+ memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+ ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
+ be16_to_cpu(rec->len));
+ if (ret < 0)
+ break;
+ }
+
+ kfree(firmware_buf);
+exit:
+ release_firmware(fw);
+ return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int ret;
+
+ ret = vicam_set_camera_power(gspca_dev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Start the workqueue function to do the streaming */
+ sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(sd->work_thread, &sd->work_struct);
+
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *)gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for vicam_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+
+ vicam_set_camera_power(gspca_dev, 0);
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04c1, 0x009d)},
+ {USB_DEVICE(0x0602, 0x1001)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ return usb_register(&sd_driver);
+}
+
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 47236a58bf33..b2014915f9c1 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -6414,6 +6414,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
gspca_dev->cam.ctrls = sd->ctrls;
sd->quality = QUALITY_DEF;
+ /* if USB 1.1, let some bandwidth for the audio device */
+ if (gspca_dev->audio && gspca_dev->dev->speed < USB_SPEED_HIGH)
+ gspca_dev->nbalt--;
+
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
index 1daf1dd65bf7..69cc8166b20b 100644
--- a/drivers/media/video/ivtv/ivtv-udma.c
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -132,7 +132,12 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
if (user_dma.page_count != err) {
IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
err, user_dma.page_count);
- return -EINVAL;
+ if (err >= 0) {
+ for (i = 0; i < err; i++)
+ put_page(dma->map[i]);
+ return -EINVAL;
+ }
+ return err;
}
dma->page_count = user_dma.page_count;
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
index 2dfa957b0fd5..b6eb51ce7735 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -174,7 +174,7 @@ ivtv_write_vbi_from_user(struct ivtv *itv,
ret = -EFAULT;
break;
}
- ivtv_write_vbi_line(itv, sliced + i, &cc, &found_cc);
+ ivtv_write_vbi_line(itv, &d, &cc, &found_cc);
}
if (found_cc)
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index c0875378acc2..dcbab6ad4c26 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -77,23 +77,51 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* Get user pages for DMA Xfer */
down_read(&current->mm->mmap_sem);
y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
- uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL);
+ uv_pages = 0; /* silence gcc. value is set and consumed only if: */
+ if (y_pages == y_dma.page_count) {
+ uv_pages = get_user_pages(current, current->mm,
+ uv_dma.uaddr, uv_dma.page_count, 0, 1,
+ &dma->map[y_pages], NULL);
+ }
up_read(&current->mm->mmap_sem);
- dma->page_count = y_dma.page_count + uv_dma.page_count;
-
- if (y_pages + uv_pages != dma->page_count) {
- IVTV_DEBUG_WARN
- ("failed to map user pages, returned %d instead of %d\n",
- y_pages + uv_pages, dma->page_count);
-
- for (i = 0; i < dma->page_count; i++) {
- put_page(dma->map[i]);
+ if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
+ int rc = -EFAULT;
+
+ if (y_pages == y_dma.page_count) {
+ IVTV_DEBUG_WARN
+ ("failed to map uv user pages, returned %d "
+ "expecting %d\n", uv_pages, uv_dma.page_count);
+
+ if (uv_pages >= 0) {
+ for (i = 0; i < uv_pages; i++)
+ put_page(dma->map[y_pages + i]);
+ rc = -EFAULT;
+ } else {
+ rc = uv_pages;
+ }
+ } else {
+ IVTV_DEBUG_WARN
+ ("failed to map y user pages, returned %d "
+ "expecting %d\n", y_pages, y_dma.page_count);
}
- dma->page_count = 0;
- return -EINVAL;
+ if (y_pages >= 0) {
+ for (i = 0; i < y_pages; i++)
+ put_page(dma->map[i]);
+ /*
+ * Inherit the -EFAULT from rc's
+ * initialization, but allow it to be
+ * overriden by uv_pages above if it was an
+ * actual errno.
+ */
+ } else {
+ rc = y_pages;
+ }
+ return rc;
}
+ dma->page_count = y_pages + uv_pages;
+
/* Fill & map SG List */
if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index e7e717800ee2..e1f96ea45bcb 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -28,7 +28,7 @@
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
@@ -201,11 +201,6 @@ struct m2mtest_ctx {
struct v4l2_m2m_ctx *m2m_ctx;
};
-struct m2mtest_buffer {
- /* vb must be first! */
- struct videobuf_buffer vb;
-};
-
static struct v4l2_queryctrl *get_ctrl(int id)
{
int i;
@@ -219,37 +214,41 @@ static struct v4l2_queryctrl *get_ctrl(int id)
}
static int device_process(struct m2mtest_ctx *ctx,
- struct m2mtest_buffer *in_buf,
- struct m2mtest_buffer *out_buf)
+ struct vb2_buffer *in_vb,
+ struct vb2_buffer *out_vb)
{
struct m2mtest_dev *dev = ctx->dev;
+ struct m2mtest_q_data *q_data;
u8 *p_in, *p_out;
int x, y, t, w;
int tile_w, bytes_left;
- struct videobuf_queue *src_q;
- struct videobuf_queue *dst_q;
+ int width, height, bytesperline;
- src_q = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
- dst_q = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
- p_in = videobuf_queue_to_vaddr(src_q, &in_buf->vb);
- p_out = videobuf_queue_to_vaddr(dst_q, &out_buf->vb);
+ q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ width = q_data->width;
+ height = q_data->height;
+ bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
+
+ p_in = vb2_plane_vaddr(in_vb, 0);
+ p_out = vb2_plane_vaddr(out_vb, 0);
if (!p_in || !p_out) {
v4l2_err(&dev->v4l2_dev,
"Acquiring kernel pointers to buffers failed\n");
return -EFAULT;
}
- if (in_buf->vb.size > out_buf->vb.size) {
+ if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) {
v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
return -EINVAL;
}
- tile_w = (in_buf->vb.width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
+ tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
/ MEM2MEM_NUM_TILES;
- bytes_left = in_buf->vb.bytesperline - tile_w * MEM2MEM_NUM_TILES;
+ bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
w = 0;
- for (y = 0; y < in_buf->vb.height; ++y) {
+ for (y = 0; y < height; ++y) {
for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
if (w & 0x1) {
for (x = 0; x < tile_w; ++x)
@@ -301,6 +300,21 @@ static void job_abort(void *priv)
ctx->aborting = 1;
}
+static void m2mtest_lock(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ mutex_lock(&dev->dev_mutex);
+}
+
+static void m2mtest_unlock(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ mutex_unlock(&dev->dev_mutex);
+}
+
+
/* device_run() - prepares and starts the device
*
* This simulates all the immediate preparations required before starting
@@ -311,7 +325,7 @@ static void device_run(void *priv)
{
struct m2mtest_ctx *ctx = priv;
struct m2mtest_dev *dev = ctx->dev;
- struct m2mtest_buffer *src_buf, *dst_buf;
+ struct vb2_buffer *src_buf, *dst_buf;
src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
@@ -322,12 +336,11 @@ static void device_run(void *priv)
schedule_irq(dev, ctx->transtime);
}
-
static void device_isr(unsigned long priv)
{
struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;
struct m2mtest_ctx *curr_ctx;
- struct m2mtest_buffer *src_buf, *dst_buf;
+ struct vb2_buffer *src_vb, *dst_vb;
unsigned long flags;
curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
@@ -338,31 +351,26 @@ static void device_isr(unsigned long priv)
return;
}
- src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
curr_ctx->num_processed++;
+ spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
+
if (curr_ctx->num_processed == curr_ctx->translen
|| curr_ctx->aborting) {
dprintk(curr_ctx->dev, "Finishing transaction\n");
curr_ctx->num_processed = 0;
- spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
- src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
- wake_up(&src_buf->vb.done);
- wake_up(&dst_buf->vb.done);
- spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
} else {
- spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
- src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
- wake_up(&src_buf->vb.done);
- wake_up(&dst_buf->vb.done);
- spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
device_run(curr_ctx);
}
}
-
/*
* video ioctls
*/
@@ -423,7 +431,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
struct m2mtest_q_data *q_data;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
@@ -434,7 +442,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
- f->fmt.pix.field = vq->field;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.pixelformat = q_data->fmt->fourcc;
f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
f->fmt.pix.sizeimage = q_data->sizeimage;
@@ -523,7 +531,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
{
struct m2mtest_q_data *q_data;
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (!vq)
@@ -533,7 +541,7 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
if (!q_data)
return -EINVAL;
- if (videobuf_queue_is_busy(vq)) {
+ if (vb2_is_busy(vq)) {
v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
return -EBUSY;
}
@@ -543,7 +551,6 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
q_data->height = f->fmt.pix.height;
q_data->sizeimage = q_data->width * q_data->height
* q_data->fmt->depth >> 3;
- vq->field = f->fmt.pix.field;
dprintk(ctx->dev,
"Setting format for type %d, wxh: %dx%d, fmt: %d\n",
@@ -733,120 +740,94 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
* Queue operations
*/
-static void m2mtest_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct m2mtest_ctx *ctx = vq->priv_data;
-
- dprintk(ctx->dev, "type: %d, index: %d, state: %d\n",
- vq->type, vb->i, vb->state);
-
- videobuf_vmalloc_free(vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int m2mtest_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned long sizes[],
+ void *alloc_ctxs[])
{
- struct m2mtest_ctx *ctx = vq->priv_data;
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
struct m2mtest_q_data *q_data;
+ unsigned int size, count = *nbuffers;
q_data = get_q_data(vq->type);
- *size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
- dprintk(ctx->dev, "size:%d, w/h %d/%d, depth: %d\n",
- *size, q_data->width, q_data->height, q_data->fmt->depth);
+ size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
- if (0 == *count)
- *count = MEM2MEM_DEF_NUM_BUFS;
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
- while (*size * *count > MEM2MEM_VID_MEM_LIMIT)
- (*count)--;
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
- v4l2_info(&ctx->dev->v4l2_dev,
- "%d buffers of size %d set up.\n", *count, *size);
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
return 0;
}
-static int m2mtest_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int m2mtest_buf_prepare(struct vb2_buffer *vb)
{
- struct m2mtest_ctx *ctx = vq->priv_data;
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct m2mtest_q_data *q_data;
- int ret;
- dprintk(ctx->dev, "type: %d, index: %d, state: %d\n",
- vq->type, vb->i, vb->state);
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
- q_data = get_q_data(vq->type);
+ q_data = get_q_data(vb->vb2_queue->type);
- if (vb->baddr) {
- /* User-provided buffer */
- if (vb->bsize < q_data->sizeimage) {
- /* Buffer too small to fit a frame */
- v4l2_err(&ctx->dev->v4l2_dev,
- "User-provided buffer too small\n");
- return -EINVAL;
- }
- } else if (vb->state != VIDEOBUF_NEEDS_INIT
- && vb->bsize < q_data->sizeimage) {
- /* We provide the buffer, but it's already been initialized
- * and is too small */
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
return -EINVAL;
}
- vb->width = q_data->width;
- vb->height = q_data->height;
- vb->bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
- vb->size = q_data->sizeimage;
- vb->field = field;
-
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Iolock failed\n");
- goto fail;
- }
- }
-
- vb->state = VIDEOBUF_PREPARED;
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
return 0;
-fail:
- m2mtest_buf_release(vq, vb);
- return ret;
}
-static void m2mtest_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void m2mtest_buf_queue(struct vb2_buffer *vb)
{
- struct m2mtest_ctx *ctx = vq->priv_data;
-
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
}
-static struct videobuf_queue_ops m2mtest_qops = {
- .buf_setup = m2mtest_buf_setup,
- .buf_prepare = m2mtest_buf_prepare,
- .buf_queue = m2mtest_buf_queue,
- .buf_release = m2mtest_buf_release,
+static struct vb2_ops m2mtest_qops = {
+ .queue_setup = m2mtest_queue_setup,
+ .buf_prepare = m2mtest_buf_prepare,
+ .buf_queue = m2mtest_buf_queue,
};
-static void queue_init(void *priv, struct videobuf_queue *vq,
- enum v4l2_buf_type type)
+static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
{
struct m2mtest_ctx *ctx = priv;
- struct m2mtest_dev *dev = ctx->dev;
+ int ret;
- videobuf_queue_vmalloc_init(vq, &m2mtest_qops, dev->v4l2_dev.dev,
- &dev->irqlock, type, V4L2_FIELD_NONE,
- sizeof(struct m2mtest_buffer), priv,
- &dev->dev_mutex);
-}
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &m2mtest_qops;
+ src_vq->mem_ops = &vb2_vmalloc_memops;
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &m2mtest_qops;
+ dst_vq->mem_ops = &vb2_vmalloc_memops;
+
+ return vb2_queue_init(dst_vq);
+}
/*
* File operations
@@ -866,7 +847,8 @@ static int m2mtest_open(struct file *file)
ctx->transtime = MEM2MEM_DEF_TRANSTIME;
ctx->num_processed = 0;
- ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init);
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
if (IS_ERR(ctx->m2m_ctx)) {
int ret = PTR_ERR(ctx->m2m_ctx);
@@ -932,6 +914,8 @@ static struct v4l2_m2m_ops m2m_ops = {
.device_run = device_run,
.job_ready = job_ready,
.job_abort = job_abort,
+ .lock = m2mtest_lock,
+ .unlock = m2mtest_unlock,
};
static int m2mtest_probe(struct platform_device *pdev)
@@ -990,6 +974,7 @@ static int m2mtest_probe(struct platform_device *pdev)
return 0;
+ v4l2_m2m_release(dev->m2m_dev);
err_m2m:
video_unregister_device(dev->vfd);
rel_vdev:
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index b9cb4a436959..502e2a40964c 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -21,7 +21,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
@@ -62,10 +62,16 @@
#define MAX_VIDEO_MEM 16
+enum csi_buffer_state {
+ CSI_BUF_NEEDS_INIT,
+ CSI_BUF_PREPARED,
+};
+
struct mx3_camera_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- enum v4l2_mbus_pixelcode code;
+ struct vb2_buffer vb;
+ enum csi_buffer_state state;
+ struct list_head queue;
/* One descriptot per scatterlist (per frame) */
struct dma_async_tx_descriptor *txd;
@@ -108,6 +114,9 @@ struct mx3_camera_dev {
struct list_head capture;
spinlock_t lock; /* Protects video buffer lists */
struct mx3_camera_buffer *active;
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ int sequence;
/* IDMAC / dmaengine interface */
struct idmac_channel *idmac_channel[1]; /* We need one channel */
@@ -130,6 +139,11 @@ static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
__raw_writel(value, mx3->base + reg);
}
+static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct mx3_camera_buffer, vb);
+}
+
/* Called from the IPU IDMAC ISR */
static void mx3_cam_dma_done(void *arg)
{
@@ -137,20 +151,20 @@ static void mx3_cam_dma_done(void *arg)
struct dma_chan *chan = desc->txd.chan;
struct idmac_channel *ichannel = to_idmac_chan(chan);
struct mx3_camera_dev *mx3_cam = ichannel->client;
- struct videobuf_buffer *vb;
dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
spin_lock(&mx3_cam->lock);
if (mx3_cam->active) {
- vb = &mx3_cam->active->vb;
-
- list_del_init(&vb->queue);
- vb->state = VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
- vb->field_count++;
- wake_up(&vb->done);
+ struct vb2_buffer *vb = &mx3_cam->active->vb;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ list_del_init(&buf->queue);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.field = mx3_cam->field;
+ vb->v4l2_buf.sequence = mx3_cam->sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
}
if (list_empty(&mx3_cam->capture)) {
@@ -165,50 +179,22 @@ static void mx3_cam_dma_done(void *arg)
}
mx3_cam->active = list_entry(mx3_cam->capture.next,
- struct mx3_camera_buffer, vb.queue);
- mx3_cam->active->vb.state = VIDEOBUF_ACTIVE;
+ struct mx3_camera_buffer, queue);
spin_unlock(&mx3_cam->lock);
}
-static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct videobuf_buffer *vb = &buf->vb;
- struct dma_async_tx_descriptor *txd = buf->txd;
- struct idmac_channel *ichan;
-
- BUG_ON(in_interrupt());
-
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
-
- /*
- * This waits until this buffer is out of danger, i.e., until it is no
- * longer in STATE_QUEUED or STATE_ACTIVE
- */
- videobuf_waiton(vq, vb, 0, 0);
- if (txd) {
- ichan = to_idmac_chan(txd->chan);
- async_tx_ack(txd);
- }
- videobuf_dma_contig_free(vq, vb);
- buf->txd = NULL;
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
/*
* Videobuf operations
*/
/*
* Calculate the __buffer__ (not data) size and number of buffers.
- * Called with .vb_lock held
*/
-static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int mx3_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned long sizes[], void *alloc_ctxs[])
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
@@ -220,162 +206,133 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- *size = bytes_per_line * icd->user_height;
+ *num_planes = 1;
+
+ mx3_cam->sequence = 0;
+ sizes[0] = bytes_per_line * icd->user_height;
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
if (!*count)
*count = 32;
- if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = MAX_VIDEO_MEM * 1024 * 1024 / *size;
+ if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0];
return 0;
}
-/* Called with .vb_lock held */
-static int mx3_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
+static int mx3_videobuf_prepare(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf =
- container_of(vb, struct mx3_camera_buffer, vb);
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct scatterlist *sg;
+ struct mx3_camera_buffer *buf;
size_t new_size;
- int ret;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
if (bytes_per_line < 0)
return bytes_per_line;
- new_size = bytes_per_line * icd->user_height;
+ buf = to_mx3_vb(vb);
+ sg = &buf->sg;
- /*
- * I think, in buf_prepare you only have to protect global data,
- * the actual buffer is yours
- */
-
- if (buf->code != icd->current_fmt->code ||
- vb->width != icd->user_width ||
- vb->height != icd->user_height ||
- vb->field != field) {
- buf->code = icd->current_fmt->code;
- vb->width = icd->user_width;
- vb->height = icd->user_height;
- vb->field = field;
- if (vb->state != VIDEOBUF_NEEDS_INIT)
- free_buffer(vq, buf);
- }
+ new_size = bytes_per_line * icd->user_height;
- if (vb->baddr && vb->bsize < new_size) {
- /* User provided buffer, but it is too small */
- ret = -ENOMEM;
- goto out;
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->dev.parent, "Buffer too small (%lu < %zu)\n",
+ vb2_plane_size(vb, 0), new_size);
+ return -ENOBUFS;
}
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct scatterlist *sg = &buf->sg;
-
- /*
- * The total size of video-buffers that will be allocated / mapped.
- * *size that we calculated in videobuf_setup gets assigned to
- * vb->bsize, and now we use the same calculation to get vb->size.
- */
- vb->size = new_size;
-
- /* This actually (allocates and) maps buffers */
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret)
- goto fail;
-
- /*
- * We will have to configure the IDMAC channel. It has two slots
- * for DMA buffers, we shall enter the first two buffers there,
- * and then submit new buffers in DMA-ready interrupts
- */
- sg_init_table(sg, 1);
- sg_dma_address(sg) = videobuf_to_dma_contig(vb);
- sg_dma_len(sg) = vb->size;
+ if (buf->state == CSI_BUF_NEEDS_INIT) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_paddr(vb, 0);
+ sg_dma_len(sg) = new_size;
buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
&ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT);
- if (!buf->txd) {
- ret = -EIO;
- goto fail;
- }
+ if (!buf->txd)
+ return -EIO;
buf->txd->callback_param = buf->txd;
buf->txd->callback = mx3_cam_dma_done;
- vb->state = VIDEOBUF_PREPARED;
+ buf->state = CSI_BUF_PREPARED;
}
- return 0;
+ vb2_set_plane_payload(vb, 0, new_size);
-fail:
- free_buffer(vq, buf);
-out:
- return ret;
+ return 0;
}
static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
{
/* Add more formats as need arises and test possibilities appear... */
switch (fourcc) {
- case V4L2_PIX_FMT_RGB565:
- return IPU_PIX_FMT_RGB565;
case V4L2_PIX_FMT_RGB24:
return IPU_PIX_FMT_RGB24;
- case V4L2_PIX_FMT_RGB332:
- return IPU_PIX_FMT_RGB332;
- case V4L2_PIX_FMT_YUV422P:
- return IPU_PIX_FMT_YVU422P;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
default:
return IPU_PIX_FMT_GENERIC;
}
}
-/*
- * Called with .vb_lock mutex held and
- * under spinlock_irqsave(&mx3_cam->lock, ...)
- */
-static void mx3_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void mx3_videobuf_queue(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf =
- container_of(vb, struct mx3_camera_buffer, vb);
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
struct dma_async_tx_descriptor *txd = buf->txd;
struct idmac_channel *ichan = to_idmac_chan(txd->chan);
struct idmac_video_param *video = &ichan->params.video;
dma_cookie_t cookie;
u32 fourcc = icd->current_fmt->host_fmt->fourcc;
-
- BUG_ON(!irqs_disabled());
+ unsigned long flags;
/* This is the configuration of one sg-element */
video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc);
- video->out_width = icd->user_width;
- video->out_height = icd->user_height;
- video->out_stride = icd->user_width;
+
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ /*
+ * If the IPU DMA channel is configured to transport
+ * generic 8-bit data, we have to set up correctly the
+ * geometry parameters upon the current pixel format.
+ * So, since the DMA horizontal parameters are expressed
+ * in bytes not pixels, convert these in the right unit.
+ */
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ BUG_ON(bytes_per_line <= 0);
+
+ video->out_width = bytes_per_line;
+ video->out_height = icd->user_height;
+ video->out_stride = bytes_per_line;
+ } else {
+ /*
+ * For IPU known formats the pixel unit will be managed
+ * successfully by the IPU code
+ */
+ video->out_width = icd->user_width;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->user_width;
+ }
#ifdef DEBUG
/* helps to see what DMA actually has written */
- memset((void *)vb->baddr, 0xaa, vb->bsize);
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
#endif
- list_add_tail(&vb->queue, &mx3_cam->capture);
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+ list_add_tail(&buf->queue, &mx3_cam->capture);
- if (!mx3_cam->active) {
+ if (!mx3_cam->active)
mx3_cam->active = buf;
- vb->state = VIDEOBUF_ACTIVE;
- } else {
- vb->state = VIDEOBUF_QUEUED;
- }
spin_unlock_irq(&mx3_cam->lock);
@@ -383,67 +340,87 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n",
cookie, sg_dma_address(&buf->sg));
- spin_lock_irq(&mx3_cam->lock);
-
if (cookie >= 0)
return;
- /* Submit error */
- vb->state = VIDEOBUF_PREPARED;
+ spin_lock_irq(&mx3_cam->lock);
- list_del_init(&vb->queue);
+ /* Submit error */
+ list_del_init(&buf->queue);
if (mx3_cam->active == buf)
mx3_cam->active = NULL;
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
-/* Called with .vb_lock held */
-static void mx3_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void mx3_videobuf_release(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf =
- container_of(vb, struct mx3_camera_buffer, vb);
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ struct dma_async_tx_descriptor *txd = buf->txd;
unsigned long flags;
dev_dbg(icd->dev.parent,
- "Release%s DMA 0x%08x (state %d), queue %sempty\n",
+ "Release%s DMA 0x%08x, queue %sempty\n",
mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
- vb->state, list_empty(&vb->queue) ? "" : "not ");
+ list_empty(&buf->queue) ? "" : "not ");
+
spin_lock_irqsave(&mx3_cam->lock, flags);
- if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
- !list_empty(&vb->queue)) {
- vb->state = VIDEOBUF_ERROR;
- list_del_init(&vb->queue);
- if (mx3_cam->active == buf)
- mx3_cam->active = NULL;
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
+ buf->state = CSI_BUF_NEEDS_INIT;
+
+ if (txd) {
+ buf->txd = NULL;
+ if (mx3_cam->idmac_channel[0])
+ async_tx_ack(txd);
}
+
spin_unlock_irqrestore(&mx3_cam->lock, flags);
- free_buffer(vq, buf);
}
-static struct videobuf_queue_ops mx3_videobuf_ops = {
- .buf_setup = mx3_videobuf_setup,
- .buf_prepare = mx3_videobuf_prepare,
- .buf_queue = mx3_videobuf_queue,
- .buf_release = mx3_videobuf_release,
+static int mx3_videobuf_init(struct vb2_buffer *vb)
+{
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
+
+ buf->state = CSI_BUF_NEEDS_INIT;
+ buf->txd = NULL;
+
+ return 0;
+}
+
+static struct vb2_ops mx3_videobuf_ops = {
+ .queue_setup = mx3_videobuf_setup,
+ .buf_prepare = mx3_videobuf_prepare,
+ .buf_queue = mx3_videobuf_queue,
+ .buf_cleanup = mx3_videobuf_release,
+ .buf_init = mx3_videobuf_init,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
};
-static void mx3_camera_init_videobuf(struct videobuf_queue *q,
+static int mx3_camera_init_videobuf(struct vb2_queue *q,
struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, icd->dev.parent,
- &mx3_cam->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
- sizeof(struct mx3_camera_buffer), icd,
- &icd->video_lock);
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx3_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+
+ return vb2_queue_init(q);
}
/* First part of ipu_csi_init_interface() */
@@ -538,18 +515,6 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
icd->devnum);
}
-static bool channel_change_requested(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
-
- /* Do buffers have to be re-allocated or channel re-configured? */
- return ichan && rect->width * rect->height >
- icd->user_width * icd->user_height;
-}
-
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
unsigned char buswidth, unsigned long *flags)
{
@@ -734,18 +699,36 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
if (xlate) {
xlate->host_fmt = fmt;
xlate->code = code;
+ dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+ (fmt->fourcc >> (0*8)) & 0xFF,
+ (fmt->fourcc >> (1*8)) & 0xFF,
+ (fmt->fourcc >> (2*8)) & 0xFF,
+ (fmt->fourcc >> (3*8)) & 0xFF);
xlate++;
- dev_dbg(dev, "Providing format %x in pass-through mode\n",
- xlate->host_fmt->fourcc);
}
return formats;
}
static void configure_geometry(struct mx3_camera_dev *mx3_cam,
- unsigned int width, unsigned int height)
+ unsigned int width, unsigned int height,
+ enum v4l2_mbus_pixelcode code)
{
u32 ctrl, width_field, height_field;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ BUG_ON(!fmt);
+
+ if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+ /*
+ * As the CSI will be configured to output BAYER, here
+ * the width parameter count the number of samples to
+ * capture to complete the whole image width.
+ */
+ width *= soc_mbus_samples_per_pixel(fmt);
+ BUG_ON(width < 0);
+ }
/* Setup frame size - this cannot be changed on-the-fly... */
width_field = width - 1;
@@ -772,18 +755,6 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
struct dma_chan_request rq = {.mx3_cam = mx3_cam,
.id = IDMAC_IC_7};
- if (*ichan) {
- struct videobuf_buffer *vb, *_vb;
- dma_release_channel(&(*ichan)->dma_chan);
- *ichan = NULL;
- mx3_cam->active = NULL;
- list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) {
- list_del_init(&vb->queue);
- vb->state = VIDEOBUF_ERROR;
- wake_up(&vb->done);
- }
- }
-
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_PRIVATE, mask);
@@ -843,19 +814,8 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
return ret;
}
- if (mf.width != icd->user_width || mf.height != icd->user_height) {
- /*
- * We now know pixel formats and can decide upon DMA-channel(s)
- * So far only direct camera-to-memory is supported
- */
- if (channel_change_requested(icd, rect)) {
- ret = acquire_dma_channel(mx3_cam);
- if (ret < 0)
- return ret;
- }
-
- configure_geometry(mx3_cam, mf.width, mf.height);
- }
+ if (mf.width != icd->user_width || mf.height != icd->user_height)
+ configure_geometry(mx3_cam, mf.width, mf.height, mf.code);
dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
mf.width, mf.height);
@@ -887,17 +847,13 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
stride_align(&pix->width);
dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height);
- ret = acquire_dma_channel(mx3_cam);
- if (ret < 0)
- return ret;
-
/*
* Might have to perform a complete interface initialisation like in
* ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
* mxc_v4l2_s_fmt()
*/
- configure_geometry(mx3_cam, pix->width, pix->height);
+ configure_geometry(mx3_cam, pix->width, pix->height, xlate->code);
mf.width = pix->width;
mf.height = pix->height;
@@ -912,12 +868,25 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
if (mf.code != xlate->code)
return -EINVAL;
+ if (!mx3_cam->idmac_channel[0]) {
+ ret = acquire_dma_channel(mx3_cam);
+ if (ret < 0)
+ return ret;
+ }
+
pix->width = mf.width;
pix->height = mf.height;
pix->field = mf.field;
+ mx3_cam->field = mf.field;
pix->colorspace = mf.colorspace;
icd->current_fmt = xlate;
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+ pix->sizeimage = pix->height * pix->bytesperline;
+
dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
return ret;
@@ -991,7 +960,7 @@ static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
- return videobuf_poll_stream(file, &icd->vb_vidq, pt);
+ return vb2_poll(&icd->vb2_vidq, file, pt);
}
static int mx3_camera_querycap(struct soc_camera_host *ici,
@@ -1165,7 +1134,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
.set_fmt = mx3_camera_set_fmt,
.try_fmt = mx3_camera_try_fmt,
.get_formats = mx3_camera_get_formats,
- .init_videobuf = mx3_camera_init_videobuf,
+ .init_videobuf2 = mx3_camera_init_videobuf,
.reqbufs = mx3_camera_reqbufs,
.poll = mx3_camera_poll,
.querycap = mx3_camera_querycap,
@@ -1241,6 +1210,12 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
soc_host->v4l2_dev.dev = &pdev->dev;
soc_host->nr = pdev->id;
+ mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(mx3_cam->alloc_ctx)) {
+ err = PTR_ERR(mx3_cam->alloc_ctx);
+ goto eallocctx;
+ }
+
err = soc_camera_host_register(soc_host);
if (err)
goto ecamhostreg;
@@ -1251,6 +1226,8 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
return 0;
ecamhostreg:
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+eallocctx:
iounmap(base);
eioremap:
clk_put(mx3_cam->clk);
@@ -1280,6 +1257,8 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev)
if (WARN_ON(mx3_cam->idmac_channel[0]))
dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+
vfree(mx3_cam);
dmaengine_put();
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c
new file mode 100644
index 000000000000..35f722a88f76
--- /dev/null
+++ b/drivers/media/video/noon010pc30.c
@@ -0,0 +1,792 @@
+/*
+ * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * Initial register configuration based on a driver authored by
+ * HeungJun Kim <riverful.kim@samsung.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later vergsion.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <media/noon010pc30.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable.");
+
+#define MODULE_NAME "NOON010PC30"
+
+/*
+ * Register offsets within a page
+ * b15..b8 - page id, b7..b0 - register address
+ */
+#define POWER_CTRL_REG 0x0001
+#define PAGEMODE_REG 0x03
+#define DEVICE_ID_REG 0x0004
+#define NOON010PC30_ID 0x86
+#define VDO_CTL_REG(n) (0x0010 + (n))
+#define SYNC_CTL_REG 0x0012
+/* Window size and position */
+#define WIN_ROWH_REG 0x0013
+#define WIN_ROWL_REG 0x0014
+#define WIN_COLH_REG 0x0015
+#define WIN_COLL_REG 0x0016
+#define WIN_HEIGHTH_REG 0x0017
+#define WIN_HEIGHTL_REG 0x0018
+#define WIN_WIDTHH_REG 0x0019
+#define WIN_WIDTHL_REG 0x001A
+#define HBLANKH_REG 0x001B
+#define HBLANKL_REG 0x001C
+#define VSYNCH_REG 0x001D
+#define VSYNCL_REG 0x001E
+/* VSYNC control */
+#define VS_CTL_REG(n) (0x00A1 + (n))
+/* page 1 */
+#define ISP_CTL_REG(n) (0x0110 + (n))
+#define YOFS_REG 0x0119
+#define DARK_YOFS_REG 0x011A
+#define SAT_CTL_REG 0x0120
+#define BSAT_REG 0x0121
+#define RSAT_REG 0x0122
+/* Color correction */
+#define CMC_CTL_REG 0x0130
+#define CMC_OFSGH_REG 0x0133
+#define CMC_OFSGL_REG 0x0135
+#define CMC_SIGN_REG 0x0136
+#define CMC_GOFS_REG 0x0137
+#define CMC_COEF_REG(n) (0x0138 + (n))
+#define CMC_OFS_REG(n) (0x0141 + (n))
+/* Gamma correction */
+#define GMA_CTL_REG 0x0160
+#define GMA_COEF_REG(n) (0x0161 + (n))
+/* Lens Shading */
+#define LENS_CTRL_REG 0x01D0
+#define LENS_XCEN_REG 0x01D1
+#define LENS_YCEN_REG 0x01D2
+#define LENS_RC_REG 0x01D3
+#define LENS_GC_REG 0x01D4
+#define LENS_BC_REG 0x01D5
+#define L_AGON_REG 0x01D6
+#define L_AGOFF_REG 0x01D7
+/* Page 3 - Auto Exposure */
+#define AE_CTL_REG(n) (0x0310 + (n))
+#define AE_CTL9_REG 0x032C
+#define AE_CTL10_REG 0x032D
+#define AE_YLVL_REG 0x031C
+#define AE_YTH_REG(n) (0x031D + (n))
+#define AE_WGT_REG 0x0326
+#define EXP_TIMEH_REG 0x0333
+#define EXP_TIMEM_REG 0x0334
+#define EXP_TIMEL_REG 0x0335
+#define EXP_MMINH_REG 0x0336
+#define EXP_MMINL_REG 0x0337
+#define EXP_MMAXH_REG 0x0338
+#define EXP_MMAXM_REG 0x0339
+#define EXP_MMAXL_REG 0x033A
+/* Page 4 - Auto White Balance */
+#define AWB_CTL_REG(n) (0x0410 + (n))
+#define AWB_ENABE 0x80
+#define AWB_WGHT_REG 0x0419
+#define BGAIN_PAR_REG(n) (0x044F + (n))
+/* Manual white balance, when AWB_CTL2[0]=1 */
+#define MWB_RGAIN_REG 0x0466
+#define MWB_BGAIN_REG 0x0467
+
+/* The token to mark an array end */
+#define REG_TERM 0xFFFF
+
+struct noon010_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 ispctl1_reg;
+};
+
+struct noon010_frmsize {
+ u16 width;
+ u16 height;
+ int vid_ctl1;
+};
+
+static const char * const noon010_supply_name[] = {
+ "vdd_core", "vddio", "vdda"
+};
+
+#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name)
+
+struct noon010_info {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ const struct noon010pc30_platform_data *pdata;
+ const struct noon010_format *curr_fmt;
+ const struct noon010_frmsize *curr_win;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ unsigned int power:1;
+ u8 i2c_reg_page;
+ struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
+ u32 gpio_nreset;
+ u32 gpio_nstby;
+};
+
+struct i2c_regval {
+ u16 addr;
+ u16 val;
+};
+
+/* Supported resolutions. */
+static const struct noon010_frmsize noon010_sizes[] = {
+ {
+ .width = 352,
+ .height = 288,
+ .vid_ctl1 = 0,
+ }, {
+ .width = 176,
+ .height = 144,
+ .vid_ctl1 = 0x10,
+ }, {
+ .width = 88,
+ .height = 72,
+ .vid_ctl1 = 0x20,
+ },
+};
+
+/* Supported pixel formats. */
+static const struct noon010_format noon010_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x03,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x02,
+ }, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x01,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x40,
+ },
+};
+
+static const struct i2c_regval noon010_base_regs[] = {
+ { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C },
+ /* Color corection and saturation */
+ { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 },
+ { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 },
+ { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 },
+ { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C },
+ { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F },
+ { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 },
+ { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 },
+ { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 },
+ { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B },
+ { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 },
+ { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 },
+ { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C },
+ { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 },
+ { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 },
+ /* Automatic white balance */
+ { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E },
+ { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 },
+ /* Auto exposure */
+ { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 },
+ { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 },
+ { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 },
+ { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E },
+ { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 },
+ { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 },
+ { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 },
+ /* Lens shading compensation */
+ { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 },
+ { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 },
+ { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E },
+ { REG_TERM, 0 },
+};
+
+static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct noon010_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct noon010_info, hdl)->sd;
+}
+
+static inline int set_i2c_page(struct noon010_info *info,
+ struct i2c_client *client, unsigned int reg)
+{
+ u32 page = reg >> 8 & 0xFF;
+ int ret = 0;
+
+ if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) {
+ ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page);
+ if (!ret)
+ info->i2c_reg_page = page;
+ }
+ return ret;
+}
+
+static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = set_i2c_page(info, client, reg_addr);
+
+ if (ret)
+ return ret;
+ return i2c_smbus_read_byte_data(client, reg_addr & 0xFF);
+}
+
+static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = set_i2c_page(info, client, reg_addr);
+
+ if (ret)
+ return ret;
+ return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val);
+}
+
+static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd,
+ const struct i2c_regval *msg)
+{
+ while (msg->addr != REG_TERM) {
+ int ret = cam_i2c_write(sd, msg->addr, msg->val);
+
+ if (ret)
+ return ret;
+ msg++;
+ }
+ return 0;
+}
+
+/* Device reset and sleep mode control */
+static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep)
+{
+ struct noon010_info *info = to_noon010(sd);
+ u8 reg = sleep ? 0xF1 : 0xF0;
+ int ret = 0;
+
+ if (reset)
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+ if (!ret) {
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
+ if (reset && !ret)
+ info->i2c_reg_page = -1;
+ }
+ return ret;
+}
+
+/* Automatic white balance control */
+static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
+{
+ int ret;
+
+ ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F);
+ if (!ret)
+ ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B);
+ return ret;
+}
+
+static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int reg, ret;
+
+ reg = cam_i2c_read(sd, VDO_CTL_REG(1));
+ if (reg < 0)
+ return reg;
+
+ reg &= 0x7C;
+ if (hflip)
+ reg |= 0x01;
+ if (vflip)
+ reg |= 0x02;
+
+ ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80);
+ if (!ret) {
+ info->hflip = hflip;
+ info->vflip = vflip;
+ }
+ return ret;
+}
+
+/* Configure resolution and color format */
+static int noon010_set_params(struct v4l2_subdev *sd)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int ret;
+
+ if (!info->curr_win)
+ return -EINVAL;
+
+ ret = cam_i2c_write(sd, VDO_CTL_REG(0), info->curr_win->vid_ctl1);
+
+ if (!ret && info->curr_fmt)
+ ret = cam_i2c_write(sd, ISP_CTL_REG(0),
+ info->curr_fmt->ispctl1_reg);
+ return ret;
+}
+
+/* Find nearest matching image pixel size. */
+static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int min_err = ~0;
+ int i = ARRAY_SIZE(noon010_sizes);
+ const struct noon010_frmsize *fsize = &noon010_sizes[0],
+ *match = NULL;
+
+ while (i--) {
+ int err = abs(fsize->width - mf->width)
+ + abs(fsize->height - mf->height);
+
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->width;
+ mf->height = match->height;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int power_enable(struct noon010_info *info)
+{
+ int ret;
+
+ if (info->power) {
+ v4l2_info(&info->sd, "%s: sensor is already on\n", __func__);
+ return 0;
+ }
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nreset)) {
+ msleep(50);
+ gpio_set_value(info->gpio_nreset, 1);
+ }
+ if (gpio_is_valid(info->gpio_nstby)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nstby, 1);
+ }
+ if (gpio_is_valid(info->gpio_nreset)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nreset, 0);
+ msleep(100);
+ gpio_set_value(info->gpio_nreset, 1);
+ msleep(20);
+ }
+ info->power = 1;
+
+ v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__);
+ return 0;
+}
+
+static int power_disable(struct noon010_info *info)
+{
+ int ret;
+
+ if (!info->power) {
+ v4l2_info(&info->sd, "%s: sensor is already off\n", __func__);
+ return 0;
+ }
+
+ ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ info->power = 0;
+
+ v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__);
+
+ return 0;
+}
+
+static int noon010_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
+ __func__, ctrl->id, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ return noon010_enable_autowhitebalance(sd, ctrl->val);
+ case V4L2_CID_BLUE_BALANCE:
+ return cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ case V4L2_CID_RED_BALANCE:
+ return cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int noon010_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (!code || index >= ARRAY_SIZE(noon010_formats))
+ return -EINVAL;
+
+ *code = noon010_formats[index].code;
+ return 0;
+}
+
+static int noon010_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int ret;
+
+ if (!mf)
+ return -EINVAL;
+
+ if (!info->curr_win || !info->curr_fmt) {
+ ret = noon010_set_params(sd);
+ if (ret)
+ return ret;
+ }
+
+ mf->width = info->curr_win->width;
+ mf->height = info->curr_win->height;
+ mf->code = info->curr_fmt->code;
+ mf->colorspace = info->curr_fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/* Return nearest media bus frame format. */
+static const struct noon010_format *try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i = ARRAY_SIZE(noon010_formats);
+
+ noon010_try_frame_size(mf);
+
+ while (i--)
+ if (mf->code == noon010_formats[i].code)
+ break;
+
+ mf->code = noon010_formats[i].code;
+
+ return &noon010_formats[i];
+}
+
+static int noon010_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ if (!sd || !mf)
+ return -EINVAL;
+
+ try_fmt(sd, mf);
+ return 0;
+}
+
+static int noon010_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct noon010_info *info = to_noon010(sd);
+
+ if (!sd || !mf)
+ return -EINVAL;
+
+ info->curr_fmt = try_fmt(sd, mf);
+
+ return noon010_set_params(sd);
+}
+
+static int noon010_base_config(struct v4l2_subdev *sd)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int ret;
+
+ ret = noon010_bulk_write_reg(sd, noon010_base_regs);
+ if (!ret) {
+ info->curr_fmt = &noon010_formats[0];
+ info->curr_win = &noon010_sizes[0];
+ ret = noon010_set_params(sd);
+ }
+ if (!ret)
+ ret = noon010_set_flip(sd, 1, 0);
+ if (!ret)
+ ret = noon010_power_ctrl(sd, false, false);
+
+ /* sync the handler and the registers state */
+ v4l2_ctrl_handler_setup(&to_noon010(sd)->hdl);
+ return ret;
+}
+
+static int noon010_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct noon010_info *info = to_noon010(sd);
+ const struct noon010pc30_platform_data *pdata = info->pdata;
+ int ret = 0;
+
+ if (WARN(pdata == NULL, "No platform data!\n"))
+ return -ENOMEM;
+
+ if (on) {
+ ret = power_enable(info);
+ if (ret)
+ return ret;
+ ret = noon010_base_config(sd);
+ } else {
+ noon010_power_ctrl(sd, false, true);
+ ret = power_disable(info);
+ info->curr_win = NULL;
+ info->curr_fmt = NULL;
+ }
+
+ return ret;
+}
+
+static int noon010_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip,
+ V4L2_IDENT_NOON010PC30, 0);
+}
+
+static int noon010_log_status(struct v4l2_subdev *sd)
+{
+ struct noon010_info *info = to_noon010(sd);
+
+ v4l2_ctrl_handler_log_status(&info->hdl, sd->name);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops noon010_ctrl_ops = {
+ .s_ctrl = noon010_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops noon010_core_ops = {
+ .g_chip_ident = noon010_g_chip_ident,
+ .s_power = noon010_s_power,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .log_status = noon010_log_status,
+};
+
+static const struct v4l2_subdev_video_ops noon010_video_ops = {
+ .g_mbus_fmt = noon010_g_fmt,
+ .s_mbus_fmt = noon010_s_fmt,
+ .try_mbus_fmt = noon010_try_fmt,
+ .enum_mbus_fmt = noon010_enum_fmt,
+};
+
+static const struct v4l2_subdev_ops noon010_ops = {
+ .core = &noon010_core_ops,
+ .video = &noon010_video_ops,
+};
+
+/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */
+static int noon010_detect(struct i2c_client *client, struct noon010_info *info)
+{
+ int ret;
+
+ ret = power_enable(info);
+ if (ret)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG);
+ if (ret < 0)
+ dev_err(&client->dev, "I2C read failed: 0x%X\n", ret);
+
+ power_disable(info);
+
+ return ret == NOON010PC30_ID ? 0 : -ENODEV;
+}
+
+static int noon010_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct noon010_info *info;
+ struct v4l2_subdev *sd;
+ const struct noon010pc30_platform_data *pdata
+ = client->dev.platform_data;
+ int ret;
+ int i;
+
+ if (!pdata) {
+ dev_err(&client->dev, "No platform data!\n");
+ return -EIO;
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ sd = &info->sd;
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+ v4l2_i2c_subdev_init(sd, client, &noon010_ops);
+
+ v4l2_ctrl_handler_init(&info->hdl, 3);
+
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64);
+
+ sd->ctrl_handler = &info->hdl;
+
+ ret = info->hdl.error;
+ if (ret)
+ goto np_err;
+
+ info->pdata = client->dev.platform_data;
+ info->i2c_reg_page = -1;
+ info->gpio_nreset = -EINVAL;
+ info->gpio_nstby = -EINVAL;
+
+ if (gpio_is_valid(pdata->gpio_nreset)) {
+ ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST");
+ if (ret) {
+ dev_err(&client->dev, "GPIO request error: %d\n", ret);
+ goto np_err;
+ }
+ info->gpio_nreset = pdata->gpio_nreset;
+ gpio_direction_output(info->gpio_nreset, 0);
+ gpio_export(info->gpio_nreset, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_nstby)) {
+ ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY");
+ if (ret) {
+ dev_err(&client->dev, "GPIO request error: %d\n", ret);
+ goto np_gpio_err;
+ }
+ info->gpio_nstby = pdata->gpio_nstby;
+ gpio_direction_output(info->gpio_nstby, 0);
+ gpio_export(info->gpio_nstby, 0);
+ }
+
+ for (i = 0; i < NOON010_NUM_SUPPLIES; i++)
+ info->supply[i].supply = noon010_supply_name[i];
+
+ ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES,
+ info->supply);
+ if (ret)
+ goto np_reg_err;
+
+ ret = noon010_detect(client, info);
+ if (!ret)
+ return 0;
+
+ /* the sensor detection failed */
+ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
+np_reg_err:
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+np_gpio_err:
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+np_err:
+ v4l2_ctrl_handler_free(&info->hdl);
+ v4l2_device_unregister_subdev(sd);
+ kfree(info);
+ return ret;
+}
+
+static int noon010_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct noon010_info *info = to_noon010(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&info->hdl);
+
+ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id noon010_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, noon010_id);
+
+
+static struct i2c_driver noon010_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME
+ },
+ .probe = noon010_probe,
+ .remove = noon010_remove,
+ .id_table = noon010_id,
+};
+
+static int __init noon010_init(void)
+{
+ return i2c_add_driver(&noon010_i2c_driver);
+}
+
+static void __exit noon010_exit(void)
+{
+ i2c_del_driver(&noon010_i2c_driver);
+}
+
+module_init(noon010_init);
+module_exit(noon010_exit);
+
+MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index 0a2fb2bfdbfb..eab31cbd68eb 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -811,8 +811,8 @@ static irqreturn_t cam_isr(int irq, void *data)
spin_lock_irqsave(&pcdev->lock, flags);
if (WARN_ON(!buf)) {
- dev_warn(dev, "%s: unhandled camera interrupt, status == "
- "%#x\n", __func__, it_status);
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
suspend_capture(pcdev);
disable_capture(pcdev);
goto out;
@@ -1088,15 +1088,15 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd,
xlate->host_fmt = &omap1_cam_formats[code];
xlate->code = code;
xlate++;
- dev_dbg(dev, "%s: providing format %s "
- "as byte swapped code #%d\n", __func__,
- omap1_cam_formats[code].name, code);
+ dev_dbg(dev,
+ "%s: providing format %s as byte swapped code #%d\n",
+ __func__, omap1_cam_formats[code].name, code);
}
default:
if (xlate)
- dev_dbg(dev, "%s: providing format %s "
- "in pass-through mode\n", __func__,
- fmt->name);
+ dev_dbg(dev,
+ "%s: providing format %s in pass-through mode\n",
+ __func__, fmt->name);
}
formats++;
if (xlate) {
@@ -1139,29 +1139,29 @@ static int dma_align(int *width, int *height,
return 1;
}
-#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \
-({ \
- struct soc_camera_sense sense = { \
- .master_clock = pcdev->camexclk, \
- .pixel_clock_max = 0, \
- }; \
- int __ret; \
- \
- if (pcdev->pdata) \
- sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
- icd->sense = &sense; \
- __ret = v4l2_subdev_call(sd, video, function, ##args); \
- icd->sense = NULL; \
- \
- if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
- if (sense.pixel_clock > sense.pixel_clock_max) { \
- dev_err(dev, "%s: pixel clock %lu " \
- "set by the camera too high!\n", \
- __func__, sense.pixel_clock); \
- __ret = -EINVAL; \
- } \
- } \
- __ret; \
+#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \
+({ \
+ struct soc_camera_sense sense = { \
+ .master_clock = pcdev->camexclk, \
+ .pixel_clock_max = 0, \
+ }; \
+ int __ret; \
+ \
+ if (pcdev->pdata) \
+ sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+ icd->sense = &sense; \
+ __ret = v4l2_subdev_call(sd, video, function, ##args); \
+ icd->sense = NULL; \
+ \
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
+ if (sense.pixel_clock > sense.pixel_clock_max) { \
+ dev_err(dev, \
+ "%s: pixel clock %lu set by the camera too high!\n", \
+ __func__, sense.pixel_clock); \
+ __ret = -EINVAL; \
+ } \
+ } \
+ __ret; \
})
static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev,
@@ -1664,10 +1664,10 @@ static int __exit omap1_cam_remove(struct platform_device *pdev)
res = pcdev->res;
release_mem_region(res->start, resource_size(res));
- kfree(pcdev);
-
clk_put(pcdev->clk);
+ kfree(pcdev);
+
dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
return 0;
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index 017552762902..f6626e87dbc5 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -36,6 +36,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/sched.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
new file mode 100644
index 000000000000..4d4ee4faca69
--- /dev/null
+++ b/drivers/media/video/ov9740.c
@@ -0,0 +1,1009 @@
+/*
+ * OmniVision OV9740 Camera Driver
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * Based on ov9640 camera driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
+
+/* General Status Registers */
+#define OV9740_MODEL_ID_HI 0x0000
+#define OV9740_MODEL_ID_LO 0x0001
+#define OV9740_REVISION_NUMBER 0x0002
+#define OV9740_MANUFACTURER_ID 0x0003
+#define OV9740_SMIA_VERSION 0x0004
+
+/* General Setup Registers */
+#define OV9740_MODE_SELECT 0x0100
+#define OV9740_IMAGE_ORT 0x0101
+#define OV9740_SOFTWARE_RESET 0x0103
+#define OV9740_GRP_PARAM_HOLD 0x0104
+#define OV9740_MSK_CORRUP_FM 0x0105
+
+/* Timing Setting */
+#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */
+#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */
+#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */
+#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */
+#define OV9740_X_ADDR_START_HI 0x0344
+#define OV9740_X_ADDR_START_LO 0x0345
+#define OV9740_Y_ADDR_START_HI 0x0346
+#define OV9740_Y_ADDR_START_LO 0x0347
+#define OV9740_X_ADDR_END_HI 0x0348
+#define OV9740_X_ADDR_END_LO 0x0349
+#define OV9740_Y_ADDR_END_HI 0x034A
+#define OV9740_Y_ADDR_END_LO 0x034B
+#define OV9740_X_OUTPUT_SIZE_HI 0x034C
+#define OV9740_X_OUTPUT_SIZE_LO 0x034D
+#define OV9740_Y_OUTPUT_SIZE_HI 0x034E
+#define OV9740_Y_OUTPUT_SIZE_LO 0x034F
+
+/* IO Control Registers */
+#define OV9740_IO_CREL00 0x3002
+#define OV9740_IO_CREL01 0x3004
+#define OV9740_IO_CREL02 0x3005
+#define OV9740_IO_OUTPUT_SEL01 0x3026
+#define OV9740_IO_OUTPUT_SEL02 0x3027
+
+/* AWB Registers */
+#define OV9740_AWB_MANUAL_CTRL 0x3406
+
+/* Analog Control Registers */
+#define OV9740_ANALOG_CTRL01 0x3601
+#define OV9740_ANALOG_CTRL02 0x3602
+#define OV9740_ANALOG_CTRL03 0x3603
+#define OV9740_ANALOG_CTRL04 0x3604
+#define OV9740_ANALOG_CTRL10 0x3610
+#define OV9740_ANALOG_CTRL12 0x3612
+#define OV9740_ANALOG_CTRL20 0x3620
+#define OV9740_ANALOG_CTRL21 0x3621
+#define OV9740_ANALOG_CTRL22 0x3622
+#define OV9740_ANALOG_CTRL30 0x3630
+#define OV9740_ANALOG_CTRL31 0x3631
+#define OV9740_ANALOG_CTRL32 0x3632
+#define OV9740_ANALOG_CTRL33 0x3633
+
+/* Sensor Control */
+#define OV9740_SENSOR_CTRL03 0x3703
+#define OV9740_SENSOR_CTRL04 0x3704
+#define OV9740_SENSOR_CTRL05 0x3705
+#define OV9740_SENSOR_CTRL07 0x3707
+
+/* Timing Control */
+#define OV9740_TIMING_CTRL17 0x3817
+#define OV9740_TIMING_CTRL19 0x3819
+#define OV9740_TIMING_CTRL33 0x3833
+#define OV9740_TIMING_CTRL35 0x3835
+
+/* Banding Filter */
+#define OV9740_AEC_MAXEXPO_60_H 0x3A02
+#define OV9740_AEC_MAXEXPO_60_L 0x3A03
+#define OV9740_AEC_B50_STEP_HI 0x3A08
+#define OV9740_AEC_B50_STEP_LO 0x3A09
+#define OV9740_AEC_B60_STEP_HI 0x3A0A
+#define OV9740_AEC_B60_STEP_LO 0x3A0B
+#define OV9740_AEC_CTRL0D 0x3A0D
+#define OV9740_AEC_CTRL0E 0x3A0E
+#define OV9740_AEC_MAXEXPO_50_H 0x3A14
+#define OV9740_AEC_MAXEXPO_50_L 0x3A15
+
+/* AEC/AGC Control */
+#define OV9740_AEC_ENABLE 0x3503
+#define OV9740_GAIN_CEILING_01 0x3A18
+#define OV9740_GAIN_CEILING_02 0x3A19
+#define OV9740_AEC_HI_THRESHOLD 0x3A11
+#define OV9740_AEC_3A1A 0x3A1A
+#define OV9740_AEC_CTRL1B_WPT2 0x3A1B
+#define OV9740_AEC_CTRL0F_WPT 0x3A0F
+#define OV9740_AEC_CTRL10_BPT 0x3A10
+#define OV9740_AEC_CTRL1E_BPT2 0x3A1E
+#define OV9740_AEC_LO_THRESHOLD 0x3A1F
+
+/* BLC Control */
+#define OV9740_BLC_AUTO_ENABLE 0x4002
+#define OV9740_BLC_MODE 0x4005
+
+/* VFIFO */
+#define OV9740_VFIFO_READ_START_HI 0x4608
+#define OV9740_VFIFO_READ_START_LO 0x4609
+
+/* DVP Control */
+#define OV9740_DVP_VSYNC_CTRL02 0x4702
+#define OV9740_DVP_VSYNC_MODE 0x4704
+#define OV9740_DVP_VSYNC_CTRL06 0x4706
+
+/* PLL Setting */
+#define OV9740_PLL_MODE_CTRL01 0x3104
+#define OV9740_PRE_PLL_CLK_DIV 0x0305
+#define OV9740_PLL_MULTIPLIER 0x0307
+#define OV9740_VT_SYS_CLK_DIV 0x0303
+#define OV9740_VT_PIX_CLK_DIV 0x0301
+#define OV9740_PLL_CTRL3010 0x3010
+#define OV9740_VFIFO_CTRL00 0x460E
+
+/* ISP Control */
+#define OV9740_ISP_CTRL00 0x5000
+#define OV9740_ISP_CTRL01 0x5001
+#define OV9740_ISP_CTRL03 0x5003
+#define OV9740_ISP_CTRL05 0x5005
+#define OV9740_ISP_CTRL12 0x5012
+#define OV9740_ISP_CTRL19 0x5019
+#define OV9740_ISP_CTRL1A 0x501A
+#define OV9740_ISP_CTRL1E 0x501E
+#define OV9740_ISP_CTRL1F 0x501F
+#define OV9740_ISP_CTRL20 0x5020
+#define OV9740_ISP_CTRL21 0x5021
+
+/* AWB */
+#define OV9740_AWB_CTRL00 0x5180
+#define OV9740_AWB_CTRL01 0x5181
+#define OV9740_AWB_CTRL02 0x5182
+#define OV9740_AWB_CTRL03 0x5183
+#define OV9740_AWB_ADV_CTRL01 0x5184
+#define OV9740_AWB_ADV_CTRL02 0x5185
+#define OV9740_AWB_ADV_CTRL03 0x5186
+#define OV9740_AWB_ADV_CTRL04 0x5187
+#define OV9740_AWB_ADV_CTRL05 0x5188
+#define OV9740_AWB_ADV_CTRL06 0x5189
+#define OV9740_AWB_ADV_CTRL07 0x518A
+#define OV9740_AWB_ADV_CTRL08 0x518B
+#define OV9740_AWB_ADV_CTRL09 0x518C
+#define OV9740_AWB_ADV_CTRL10 0x518D
+#define OV9740_AWB_ADV_CTRL11 0x518E
+#define OV9740_AWB_CTRL0F 0x518F
+#define OV9740_AWB_CTRL10 0x5190
+#define OV9740_AWB_CTRL11 0x5191
+#define OV9740_AWB_CTRL12 0x5192
+#define OV9740_AWB_CTRL13 0x5193
+#define OV9740_AWB_CTRL14 0x5194
+
+/* MIPI Control */
+#define OV9740_MIPI_CTRL00 0x4800
+#define OV9740_MIPI_3837 0x3837
+#define OV9740_MIPI_CTRL01 0x4801
+#define OV9740_MIPI_CTRL03 0x4803
+#define OV9740_MIPI_CTRL05 0x4805
+#define OV9740_VFIFO_RD_CTRL 0x4601
+#define OV9740_MIPI_CTRL_3012 0x3012
+#define OV9740_SC_CMMM_MIPI_CTR 0x3014
+
+/* supported resolutions */
+enum {
+ OV9740_VGA,
+ OV9740_720P,
+};
+
+struct ov9740_resolution {
+ unsigned int width;
+ unsigned int height;
+};
+
+static struct ov9740_resolution ov9740_resolutions[] = {
+ [OV9740_VGA] = {
+ .width = 640,
+ .height = 480,
+ },
+ [OV9740_720P] = {
+ .width = 1280,
+ .height = 720,
+ },
+};
+
+/* Misc. structures */
+struct ov9740_reg {
+ u16 reg;
+ u8 val;
+};
+
+struct ov9740_priv {
+ struct v4l2_subdev subdev;
+
+ int ident;
+ u16 model;
+ u8 revision;
+ u8 manid;
+ u8 smiaver;
+
+ bool flag_vflip;
+ bool flag_hflip;
+};
+
+static const struct ov9740_reg ov9740_defaults[] = {
+ /* Banding Filter */
+ { OV9740_AEC_B50_STEP_HI, 0x00 },
+ { OV9740_AEC_B50_STEP_LO, 0xe8 },
+ { OV9740_AEC_CTRL0E, 0x03 },
+ { OV9740_AEC_MAXEXPO_50_H, 0x15 },
+ { OV9740_AEC_MAXEXPO_50_L, 0xc6 },
+ { OV9740_AEC_B60_STEP_HI, 0x00 },
+ { OV9740_AEC_B60_STEP_LO, 0xc0 },
+ { OV9740_AEC_CTRL0D, 0x04 },
+ { OV9740_AEC_MAXEXPO_60_H, 0x18 },
+ { OV9740_AEC_MAXEXPO_60_L, 0x20 },
+
+ /* LC */
+ { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 },
+ { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc },
+
+ /* Un-documented OV9740 registers */
+ { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 },
+ { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c },
+ { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580A, 0x0e }, { 0x580B, 0x16 },
+ { 0x580C, 0x06 }, { 0x580D, 0x02 }, { 0x580E, 0x00 }, { 0x580F, 0x00 },
+ { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 },
+ { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 },
+ { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581A, 0x07 }, { 0x581B, 0x08 },
+ { 0x581C, 0x0b }, { 0x581D, 0x14 }, { 0x581E, 0x28 }, { 0x581F, 0x23 },
+ { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a },
+ { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f },
+ { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582A, 0x8f }, { 0x582B, 0x9e },
+ { 0x582C, 0x8f }, { 0x582D, 0x9f }, { 0x582E, 0x4f }, { 0x582F, 0x87 },
+ { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f },
+ { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf },
+ { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583A, 0x9f }, { 0x583B, 0x7f },
+ { 0x583C, 0x5f },
+
+ /* Y Gamma */
+ { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e },
+ { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 },
+ { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548A, 0xa4 }, { 0x548B, 0xb1 },
+ { 0x548C, 0xc6 }, { 0x548D, 0xd8 }, { 0x548E, 0xe9 },
+
+ /* UV Gamma */
+ { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 },
+ { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 },
+ { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549A, 0x02 }, { 0x549B, 0xeb },
+ { 0x549C, 0x02 }, { 0x549D, 0xa0 }, { 0x549E, 0x02 }, { 0x549F, 0x67 },
+ { 0x54A0, 0x02 }, { 0x54A1, 0x3b }, { 0x54A2, 0x02 }, { 0x54A3, 0x18 },
+ { 0x54A4, 0x01 }, { 0x54A5, 0xe7 }, { 0x54A6, 0x01 }, { 0x54A7, 0xc3 },
+ { 0x54A8, 0x01 }, { 0x54A9, 0x94 }, { 0x54AA, 0x01 }, { 0x54AB, 0x72 },
+ { 0x54AC, 0x01 }, { 0x54AD, 0x57 },
+
+ /* AWB */
+ { OV9740_AWB_CTRL00, 0xf0 },
+ { OV9740_AWB_CTRL01, 0x00 },
+ { OV9740_AWB_CTRL02, 0x41 },
+ { OV9740_AWB_CTRL03, 0x42 },
+ { OV9740_AWB_ADV_CTRL01, 0x8a },
+ { OV9740_AWB_ADV_CTRL02, 0x61 },
+ { OV9740_AWB_ADV_CTRL03, 0xce },
+ { OV9740_AWB_ADV_CTRL04, 0xa8 },
+ { OV9740_AWB_ADV_CTRL05, 0x17 },
+ { OV9740_AWB_ADV_CTRL06, 0x1f },
+ { OV9740_AWB_ADV_CTRL07, 0x27 },
+ { OV9740_AWB_ADV_CTRL08, 0x41 },
+ { OV9740_AWB_ADV_CTRL09, 0x34 },
+ { OV9740_AWB_ADV_CTRL10, 0xf0 },
+ { OV9740_AWB_ADV_CTRL11, 0x10 },
+ { OV9740_AWB_CTRL0F, 0xff },
+ { OV9740_AWB_CTRL10, 0x00 },
+ { OV9740_AWB_CTRL11, 0xff },
+ { OV9740_AWB_CTRL12, 0x00 },
+ { OV9740_AWB_CTRL13, 0xff },
+ { OV9740_AWB_CTRL14, 0x00 },
+
+ /* CIP */
+ { 0x530D, 0x12 },
+
+ /* CMX */
+ { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 },
+ { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 },
+ { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538A, 0x00 }, { 0x538B, 0x20 },
+ { 0x538C, 0x00 }, { 0x538D, 0x00 }, { 0x538E, 0x00 }, { 0x538F, 0x16 },
+ { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 },
+ { 0x5394, 0x18 },
+
+ /* 50/60 Detection */
+ { 0x3C0A, 0x9c }, { 0x3C0B, 0x3f },
+
+ /* Output Select */
+ { OV9740_IO_OUTPUT_SEL01, 0x00 },
+ { OV9740_IO_OUTPUT_SEL02, 0x00 },
+ { OV9740_IO_CREL00, 0x00 },
+ { OV9740_IO_CREL01, 0x00 },
+ { OV9740_IO_CREL02, 0x00 },
+
+ /* AWB Control */
+ { OV9740_AWB_MANUAL_CTRL, 0x00 },
+
+ /* Analog Control */
+ { OV9740_ANALOG_CTRL03, 0xaa },
+ { OV9740_ANALOG_CTRL32, 0x2f },
+ { OV9740_ANALOG_CTRL20, 0x66 },
+ { OV9740_ANALOG_CTRL21, 0xc0 },
+ { OV9740_ANALOG_CTRL31, 0x52 },
+ { OV9740_ANALOG_CTRL33, 0x50 },
+ { OV9740_ANALOG_CTRL30, 0xca },
+ { OV9740_ANALOG_CTRL04, 0x0c },
+ { OV9740_ANALOG_CTRL01, 0x40 },
+ { OV9740_ANALOG_CTRL02, 0x16 },
+ { OV9740_ANALOG_CTRL10, 0xa1 },
+ { OV9740_ANALOG_CTRL12, 0x24 },
+ { OV9740_ANALOG_CTRL22, 0x9f },
+
+ /* Sensor Control */
+ { OV9740_SENSOR_CTRL03, 0x42 },
+ { OV9740_SENSOR_CTRL04, 0x10 },
+ { OV9740_SENSOR_CTRL05, 0x45 },
+ { OV9740_SENSOR_CTRL07, 0x14 },
+
+ /* Timing Control */
+ { OV9740_TIMING_CTRL33, 0x04 },
+ { OV9740_TIMING_CTRL35, 0x02 },
+ { OV9740_TIMING_CTRL19, 0x6e },
+ { OV9740_TIMING_CTRL17, 0x94 },
+
+ /* AEC/AGC Control */
+ { OV9740_AEC_ENABLE, 0x10 },
+ { OV9740_GAIN_CEILING_01, 0x00 },
+ { OV9740_GAIN_CEILING_02, 0x7f },
+ { OV9740_AEC_HI_THRESHOLD, 0xa0 },
+ { OV9740_AEC_3A1A, 0x05 },
+ { OV9740_AEC_CTRL1B_WPT2, 0x50 },
+ { OV9740_AEC_CTRL0F_WPT, 0x50 },
+ { OV9740_AEC_CTRL10_BPT, 0x4c },
+ { OV9740_AEC_CTRL1E_BPT2, 0x4c },
+ { OV9740_AEC_LO_THRESHOLD, 0x26 },
+
+ /* BLC Control */
+ { OV9740_BLC_AUTO_ENABLE, 0x45 },
+ { OV9740_BLC_MODE, 0x18 },
+
+ /* DVP Control */
+ { OV9740_DVP_VSYNC_CTRL02, 0x04 },
+ { OV9740_DVP_VSYNC_MODE, 0x00 },
+ { OV9740_DVP_VSYNC_CTRL06, 0x08 },
+
+ /* PLL Setting */
+ { OV9740_PLL_MODE_CTRL01, 0x20 },
+ { OV9740_PRE_PLL_CLK_DIV, 0x03 },
+ { OV9740_PLL_MULTIPLIER, 0x4c },
+ { OV9740_VT_SYS_CLK_DIV, 0x01 },
+ { OV9740_VT_PIX_CLK_DIV, 0x08 },
+ { OV9740_PLL_CTRL3010, 0x01 },
+ { OV9740_VFIFO_CTRL00, 0x82 },
+
+ /* Timing Setting */
+ /* VTS */
+ { OV9740_FRM_LENGTH_LN_HI, 0x03 },
+ { OV9740_FRM_LENGTH_LN_LO, 0x07 },
+ /* HTS */
+ { OV9740_LN_LENGTH_PCK_HI, 0x06 },
+ { OV9740_LN_LENGTH_PCK_LO, 0x62 },
+
+ /* MIPI Control */
+ { OV9740_MIPI_CTRL00, 0x44 },
+ { OV9740_MIPI_3837, 0x01 },
+ { OV9740_MIPI_CTRL01, 0x0f },
+ { OV9740_MIPI_CTRL03, 0x05 },
+ { OV9740_MIPI_CTRL05, 0x10 },
+ { OV9740_VFIFO_RD_CTRL, 0x16 },
+ { OV9740_MIPI_CTRL_3012, 0x70 },
+ { OV9740_SC_CMMM_MIPI_CTR, 0x01 },
+};
+
+static const struct ov9740_reg ov9740_regs_vga[] = {
+ { OV9740_X_ADDR_START_HI, 0x00 },
+ { OV9740_X_ADDR_START_LO, 0xa0 },
+ { OV9740_Y_ADDR_START_HI, 0x00 },
+ { OV9740_Y_ADDR_START_LO, 0x00 },
+ { OV9740_X_ADDR_END_HI, 0x04 },
+ { OV9740_X_ADDR_END_LO, 0x63 },
+ { OV9740_Y_ADDR_END_HI, 0x02 },
+ { OV9740_Y_ADDR_END_LO, 0xd3 },
+ { OV9740_X_OUTPUT_SIZE_HI, 0x02 },
+ { OV9740_X_OUTPUT_SIZE_LO, 0x80 },
+ { OV9740_Y_OUTPUT_SIZE_HI, 0x01 },
+ { OV9740_Y_OUTPUT_SIZE_LO, 0xe0 },
+ { OV9740_ISP_CTRL1E, 0x03 },
+ { OV9740_ISP_CTRL1F, 0xc0 },
+ { OV9740_ISP_CTRL20, 0x02 },
+ { OV9740_ISP_CTRL21, 0xd0 },
+ { OV9740_VFIFO_READ_START_HI, 0x01 },
+ { OV9740_VFIFO_READ_START_LO, 0x40 },
+ { OV9740_ISP_CTRL00, 0xff },
+ { OV9740_ISP_CTRL01, 0xff },
+ { OV9740_ISP_CTRL03, 0xff },
+};
+
+static const struct ov9740_reg ov9740_regs_720p[] = {
+ { OV9740_X_ADDR_START_HI, 0x00 },
+ { OV9740_X_ADDR_START_LO, 0x00 },
+ { OV9740_Y_ADDR_START_HI, 0x00 },
+ { OV9740_Y_ADDR_START_LO, 0x00 },
+ { OV9740_X_ADDR_END_HI, 0x05 },
+ { OV9740_X_ADDR_END_LO, 0x03 },
+ { OV9740_Y_ADDR_END_HI, 0x02 },
+ { OV9740_Y_ADDR_END_LO, 0xd3 },
+ { OV9740_X_OUTPUT_SIZE_HI, 0x05 },
+ { OV9740_X_OUTPUT_SIZE_LO, 0x00 },
+ { OV9740_Y_OUTPUT_SIZE_HI, 0x02 },
+ { OV9740_Y_OUTPUT_SIZE_LO, 0xd0 },
+ { OV9740_ISP_CTRL1E, 0x05 },
+ { OV9740_ISP_CTRL1F, 0x00 },
+ { OV9740_ISP_CTRL20, 0x02 },
+ { OV9740_ISP_CTRL21, 0xd0 },
+ { OV9740_VFIFO_READ_START_HI, 0x02 },
+ { OV9740_VFIFO_READ_START_LO, 0x30 },
+ { OV9740_ISP_CTRL00, 0xff },
+ { OV9740_ISP_CTRL01, 0xef },
+ { OV9740_ISP_CTRL03, 0xff },
+};
+
+static enum v4l2_mbus_pixelcode ov9740_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+};
+
+static const struct v4l2_queryctrl ov9740_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Horizontally",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+/* read a register */
+static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = (u8 *)&reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = val,
+ },
+ };
+
+ reg = swab16(reg);
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* write a register */
+static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+ struct i2c_msg msg;
+ struct {
+ u16 reg;
+ u8 val;
+ } __packed buf;
+ int ret;
+
+ reg = swab16(reg);
+
+ buf.reg = reg;
+ buf.val = val;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = (u8 *)&buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
+{
+ u8 val;
+ int ret;
+
+ ret = ov9740_reg_read(client, reg, &val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register %02x failed!\n", reg);
+ return ret;
+ }
+
+ val |= set;
+ val &= ~unset;
+
+ ret = ov9740_reg_write(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register %02x failed!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov9740_reg_write_array(struct i2c_client *client,
+ const struct ov9740_reg *regarray,
+ int regarraylen)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < regarraylen; i++) {
+ ret = ov9740_reg_write(client,
+ regarray[i].reg, regarray[i].val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ int ret;
+
+ /* Program orientation register. */
+ if (priv->flag_vflip)
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0);
+ else
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2);
+ if (ret < 0)
+ return ret;
+
+ if (priv->flag_hflip)
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0);
+ else
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1);
+ if (ret < 0)
+ return ret;
+
+ if (enable) {
+ dev_dbg(&client->dev, "Enabling Streaming\n");
+ /* Start Streaming */
+ ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01);
+
+ } else {
+ dev_dbg(&client->dev, "Disabling Streaming\n");
+ /* Software Reset */
+ ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01);
+ if (!ret)
+ /* Setting Streaming to Standby */
+ ret = ov9740_reg_write(client, OV9740_MODE_SELECT,
+ 0x00);
+ }
+
+ return ret;
+}
+
+/* Alter bus settings on camera side */
+static int ov9740_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+/* Request bus settings on camera side */
+static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/* Get status of additional camera capabilities */
+static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ ctrl->value = priv->flag_vflip;
+ break;
+ case V4L2_CID_HFLIP:
+ ctrl->value = priv->flag_hflip;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ priv->flag_vflip = ctrl->value;
+ break;
+ case V4L2_CID_HFLIP:
+ priv->flag_hflip = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Get chip identification */
+static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ id->ident = priv->ident;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9740_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 2;
+
+ ret = ov9740_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov9740_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9740_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* select nearest higher resolution for capture */
+static void ov9740_res_roundup(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++)
+ if ((ov9740_resolutions[i].width >= *width) &&
+ (ov9740_resolutions[i].height >= *height)) {
+ *width = ov9740_resolutions[i].width;
+ *height = ov9740_resolutions[i].height;
+ return;
+ }
+
+ *width = ov9740_resolutions[OV9740_720P].width;
+ *height = ov9740_resolutions[OV9740_720P].height;
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9740_set_res(struct i2c_client *client, u32 width)
+{
+ int ret;
+
+ /* select register configuration for given resolution */
+ if (width == ov9740_resolutions[OV9740_VGA].width) {
+ dev_dbg(&client->dev, "Setting image size to 640x480\n");
+ ret = ov9740_reg_write_array(client, ov9740_regs_vga,
+ ARRAY_SIZE(ov9740_regs_vga));
+ } else if (width == ov9740_resolutions[OV9740_720P].width) {
+ dev_dbg(&client->dev, "Setting image size to 1280x720\n");
+ ret = ov9740_reg_write_array(client, ov9740_regs_720p,
+ ARRAY_SIZE(ov9740_regs_720p));
+ } else {
+ dev_err(&client->dev, "Failed to select resolution!\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/* set the format we will capture in */
+static int ov9740_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
+ int ret;
+
+ ov9740_res_roundup(&mf->width, &mf->height);
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = ov9740_reg_write_array(client, ov9740_defaults,
+ ARRAY_SIZE(ov9740_defaults));
+ if (ret < 0)
+ return ret;
+
+ ret = ov9740_set_res(client, mf->width);
+ if (ret < 0)
+ return ret;
+
+ mf->code = code;
+ mf->colorspace = cspace;
+
+ return ret;
+}
+
+static int ov9740_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ ov9740_res_roundup(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov9740_codes))
+ return -EINVAL;
+
+ *code = ov9740_codes[index];
+
+ return 0;
+}
+
+static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = ov9740_resolutions[OV9740_720P].width;
+ a->bounds.height = ov9740_resolutions[OV9740_720P].height;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = ov9740_resolutions[OV9740_720P].width;
+ a->c.height = ov9740_resolutions[OV9740_720P].height;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov9740_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ u8 modelhi, modello;
+ int ret;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
+ dev_err(&client->dev, "Parent missing or invalid!\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello);
+ if (ret < 0)
+ goto err;
+
+ priv->model = (modelhi << 8) | modello;
+
+ ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver);
+ if (ret < 0)
+ goto err;
+
+ if (priv->model != 0x9740) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ priv->ident = V4L2_IDENT_OV9740;
+
+ dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, "
+ "Manufacturer 0x%02x, SMIA Version 0x%02x\n",
+ priv->model, priv->revision, priv->manid, priv->smiaver);
+
+err:
+ return ret;
+}
+
+static struct soc_camera_ops ov9740_ops = {
+ .set_bus_param = ov9740_set_bus_param,
+ .query_bus_param = ov9740_query_bus_param,
+ .controls = ov9740_controls,
+ .num_controls = ARRAY_SIZE(ov9740_controls),
+};
+
+static struct v4l2_subdev_core_ops ov9740_core_ops = {
+ .g_ctrl = ov9740_g_ctrl,
+ .s_ctrl = ov9740_s_ctrl,
+ .g_chip_ident = ov9740_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9740_get_register,
+ .s_register = ov9740_set_register,
+#endif
+
+};
+
+static struct v4l2_subdev_video_ops ov9740_video_ops = {
+ .s_stream = ov9740_s_stream,
+ .s_mbus_fmt = ov9740_s_fmt,
+ .try_mbus_fmt = ov9740_try_fmt,
+ .enum_mbus_fmt = ov9740_enum_fmt,
+ .cropcap = ov9740_cropcap,
+ .g_crop = ov9740_g_crop,
+};
+
+static struct v4l2_subdev_ops ov9740_subdev_ops = {
+ .core = &ov9740_core_ops,
+ .video = &ov9740_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9740_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9740_priv *priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl;
+ int ret;
+
+ if (!icd) {
+ dev_err(&client->dev, "Missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev, "Failed to allocate private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
+
+ icd->ops = &ov9740_ops;
+
+ ret = ov9740_video_probe(icd, client);
+ if (ret < 0) {
+ icd->ops = NULL;
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9740_remove(struct i2c_client *client)
+{
+ struct ov9740_priv *priv = i2c_get_clientdata(client);
+
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov9740_id[] = {
+ { "ov9740", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9740_id);
+
+static struct i2c_driver ov9740_i2c_driver = {
+ .driver = {
+ .name = "ov9740",
+ },
+ .probe = ov9740_probe,
+ .remove = ov9740_remove,
+ .id_table = ov9740_id,
+};
+
+static int __init ov9740_module_init(void)
+{
+ return i2c_add_driver(&ov9740_i2c_driver);
+}
+
+static void __exit ov9740_module_exit(void)
+{
+ i2c_del_driver(&ov9740_i2c_driver);
+}
+
+module_init(ov9740_module_init);
+module_exit(ov9740_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
+MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 66ad516bdfd9..d33dd61de263 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -499,31 +499,35 @@ static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
return 0;
}
-static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val)
+static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
{
struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
- int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ int stat, bleftend, cleft;
+
+ stat = pvr2_hdw_check_cropcap(cptr->hdw);
if (stat != 0) {
return stat;
}
- *val = 0;
- if (cap->bounds.width > cptr->hdw->cropl_val) {
- *val = cap->bounds.width - cptr->hdw->cropl_val;
- }
+ bleftend = cap->bounds.left+cap->bounds.width;
+ cleft = cptr->hdw->cropl_val;
+
+ *width = cleft < bleftend ? bleftend-cleft : 0;
return 0;
}
-static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val)
+static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
{
struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
- int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ int stat, btopend, ctop;
+
+ stat = pvr2_hdw_check_cropcap(cptr->hdw);
if (stat != 0) {
return stat;
}
- *val = 0;
- if (cap->bounds.height > cptr->hdw->cropt_val) {
- *val = cap->bounds.height - cptr->hdw->cropt_val;
- }
+ btopend = cap->bounds.top+cap->bounds.height;
+ ctop = cptr->hdw->cropt_val;
+
+ *height = ctop < btopend ? btopend-ctop : 0;
return 0;
}
@@ -1114,6 +1118,7 @@ static const struct pvr2_ctl_info control_defs[] = {
.internal_id = PVR2_CID_CROPW,
.default_value = 720,
DEFREF(cropw),
+ DEFINT(0, 864),
.get_max_value = ctrl_cropw_max_get,
.get_def_value = ctrl_get_cropcapdw,
}, {
@@ -1122,6 +1127,7 @@ static const struct pvr2_ctl_info control_defs[] = {
.internal_id = PVR2_CID_CROPH,
.default_value = 480,
DEFREF(croph),
+ DEFINT(0, 576),
.get_max_value = ctrl_croph_max_get,
.get_def_value = ctrl_get_cropcapdh,
}, {
@@ -2027,6 +2033,8 @@ static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
hdw->decoder_client_id);
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
vbi, s_sliced_fmt, &fmt.fmt.sliced);
}
@@ -3165,6 +3173,19 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
struct pvr2_ctrl *cptr;
int disruptive_change;
+ if (hdw->input_dirty && hdw->state_pathway_ok &&
+ (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
+ PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
+ hdw->pathway_state)) {
+ /* Change of mode being asked for... */
+ hdw->state_pathway_ok = 0;
+ trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Can't commit anything until pathway is ok. */
+ return 0;
+ }
+
/* Handle some required side effects when the video standard is
changed.... */
if (hdw->std_dirty) {
@@ -3199,18 +3220,6 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
}
}
- if (hdw->input_dirty && hdw->state_pathway_ok &&
- (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
- PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
- hdw->pathway_state)) {
- /* Change of mode being asked for... */
- hdw->state_pathway_ok = 0;
- trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
- }
- if (!hdw->state_pathway_ok) {
- /* Can't commit anything until pathway is ok. */
- return 0;
- }
/* The broadcast decoder can only scale down, so if
* res_*_dirty && crop window < output format ==> enlarge crop.
*
@@ -5159,8 +5168,7 @@ void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
using v4l2-subdev - therefore we can't support that AT ALL right
now. (Of course, no sub-drivers seem to implement it either.
But now it's a a chicken and egg problem...) */
- v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner,
- &hdw->tuner_signal_info);
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll"
" type=%u strength=%u audio=0x%x cap=0x%x"
" low=%u hi=%u",
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 281806b2df62..6ef1335b2858 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -324,36 +324,45 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
}
sfp->item_last = cip;
+ sysfs_attr_init(&cip->attr_name.attr);
cip->attr_name.attr.name = "name";
cip->attr_name.attr.mode = S_IRUGO;
cip->attr_name.show = show_name;
+ sysfs_attr_init(&cip->attr_type.attr);
cip->attr_type.attr.name = "type";
cip->attr_type.attr.mode = S_IRUGO;
cip->attr_type.show = show_type;
+ sysfs_attr_init(&cip->attr_min.attr);
cip->attr_min.attr.name = "min_val";
cip->attr_min.attr.mode = S_IRUGO;
cip->attr_min.show = show_min;
+ sysfs_attr_init(&cip->attr_max.attr);
cip->attr_max.attr.name = "max_val";
cip->attr_max.attr.mode = S_IRUGO;
cip->attr_max.show = show_max;
+ sysfs_attr_init(&cip->attr_def.attr);
cip->attr_def.attr.name = "def_val";
cip->attr_def.attr.mode = S_IRUGO;
cip->attr_def.show = show_def;
+ sysfs_attr_init(&cip->attr_val.attr);
cip->attr_val.attr.name = "cur_val";
cip->attr_val.attr.mode = S_IRUGO;
+ sysfs_attr_init(&cip->attr_custom.attr);
cip->attr_custom.attr.name = "custom_val";
cip->attr_custom.attr.mode = S_IRUGO;
+ sysfs_attr_init(&cip->attr_enum.attr);
cip->attr_enum.attr.name = "enum_val";
cip->attr_enum.attr.mode = S_IRUGO;
cip->attr_enum.show = show_enum;
+ sysfs_attr_init(&cip->attr_bits.attr);
cip->attr_bits.attr.name = "bit_val";
cip->attr_bits.attr.mode = S_IRUGO;
cip->attr_bits.show = show_bits;
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index bd1519a4ecb4..780af5f81642 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -151,8 +151,6 @@ static int pwc_video_close(struct file *file);
static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
-static long pwc_video_ioctl(struct file *file,
- unsigned int ioctlnr, unsigned long arg);
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
static const struct v4l2_file_operations pwc_fops = {
@@ -162,7 +160,7 @@ static const struct v4l2_file_operations pwc_fops = {
.read = pwc_video_read,
.poll = pwc_video_poll,
.mmap = pwc_video_mmap,
- .unlocked_ioctl = pwc_video_ioctl,
+ .unlocked_ioctl = video_ioctl2,
};
static struct video_device pwc_template = {
.name = "Philips Webcam", /* Filled in later */
@@ -1098,7 +1096,6 @@ static int pwc_video_open(struct file *file)
return -EBUSY;
}
- mutex_lock(&pdev->modlock);
pwc_construct(pdev); /* set min/max sizes correct */
if (!pdev->usb_init) {
PWC_DEBUG_OPEN("Doing first time initialization.\n");
@@ -1130,7 +1127,6 @@ static int pwc_video_open(struct file *file)
if (i < 0) {
PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n");
pwc_free_buffers(pdev);
- mutex_unlock(&pdev->modlock);
return i;
}
@@ -1171,7 +1167,6 @@ static int pwc_video_open(struct file *file)
if (i) {
PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n");
pwc_free_buffers(pdev);
- mutex_unlock(&pdev->modlock);
return i;
}
@@ -1181,7 +1176,6 @@ static int pwc_video_open(struct file *file)
pdev->vopen++;
file->private_data = vdev;
- mutex_unlock(&pdev->modlock);
PWC_DEBUG_OPEN("<< video_open() returns 0.\n");
return 0;
}
@@ -1210,7 +1204,6 @@ static int pwc_video_close(struct file *file)
PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
pdev = video_get_drvdata(vdev);
- mutex_lock(&pdev->modlock);
if (pdev->vopen == 0)
PWC_DEBUG_MODULE("video_close() called on closed device?\n");
@@ -1248,7 +1241,6 @@ static int pwc_video_close(struct file *file)
if (device_hint[hint].pdev == pdev)
device_hint[hint].pdev = NULL;
}
- mutex_unlock(&pdev->modlock);
return 0;
}
@@ -1283,7 +1275,6 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
if (pdev == NULL)
return -EFAULT;
- mutex_lock(&pdev->modlock);
if (pdev->error_status) {
rv = -pdev->error_status; /* Something happened, report what. */
goto err_out;
@@ -1318,8 +1309,10 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
rv = -ERESTARTSYS;
goto err_out;
}
+ mutex_unlock(&pdev->modlock);
schedule();
set_current_state(TASK_INTERRUPTIBLE);
+ mutex_lock(&pdev->modlock);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
@@ -1352,10 +1345,8 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
pdev->image_read_pos = 0;
pwc_next_image(pdev);
}
- mutex_unlock(&pdev->modlock);
return count;
err_out:
- mutex_unlock(&pdev->modlock);
return rv;
}
@@ -1372,9 +1363,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
return -EFAULT;
/* Start the stream (if not already started) */
- mutex_lock(&pdev->modlock);
ret = pwc_isoc_init(pdev);
- mutex_unlock(&pdev->modlock);
if (ret)
return ret;
@@ -1387,25 +1376,6 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
return 0;
}
-static long pwc_video_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct video_device *vdev = file->private_data;
- struct pwc_device *pdev;
- long r = -ENODEV;
-
- if (!vdev)
- goto out;
- pdev = video_get_drvdata(vdev);
-
- mutex_lock(&pdev->modlock);
- if (!pdev->unplugged)
- r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl);
- mutex_unlock(&pdev->modlock);
-out:
- return r;
-}
-
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vdev = file->private_data;
@@ -1754,6 +1724,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
}
memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
pdev->vdev->parent = &intf->dev;
+ pdev->vdev->lock = &pdev->modlock;
+ pdev->vdev->ioctl_ops = &pwc_ioctl_ops;
strcpy(pdev->vdev->name, name);
video_set_drvdata(pdev->vdev, pdev);
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 8ca4d22b4384..68a5313a52d5 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -341,604 +341,554 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
}
-long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
- struct pwc_device *pdev;
- DECLARE_WAITQUEUE(wait, current);
-
- if (vdev == NULL)
- return -EFAULT;
- pdev = video_get_drvdata(vdev);
- if (pdev == NULL)
- return -EFAULT;
+ struct pwc_device *pdev = video_drvdata(file);
+
+ strcpy(cap->driver, PWC_NAME);
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->version = PWC_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
-#ifdef CONFIG_USB_PWC_DEBUG
- if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) {
- v4l_printk_ioctl(cmd);
- printk("\n");
- }
-#endif
-
-
- switch (cmd) {
- /* V4L2 Layer */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\
- "try to use the v4l2 layer\n");
- strcpy(cap->driver,PWC_NAME);
- strlcpy(cap->card, vdev->name, sizeof(cap->card));
- usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info));
- cap->version = PWC_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- return 0;
- }
+static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ if (i->index) /* Only one INPUT is supported */
+ return -EINVAL;
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
+ strcpy(i->name, "usb");
+ return 0;
+}
- if ( i->index ) /* Only one INPUT is supported */
- return -EINVAL;
+static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
- memset(i, 0, sizeof(struct v4l2_input));
- strcpy(i->name, "usb");
- return 0;
- }
+static int pwc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = 0; /* Only one INPUT is supported */
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *i = arg;
+static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+{
+ int i;
- if ( *i ) { /* Only one INPUT is supported */
- PWC_DEBUG_IOCTL("Only one input source is"\
- " supported with this webcam.\n");
- return -EINVAL;
- }
+ for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) {
+ if (pwc_controls[i].id == c->id) {
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
+ memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl));
return 0;
}
+ }
+ return -EINVAL;
+}
- /* TODO: */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *c = arg;
- int i;
-
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id);
- for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) {
- if (pwc_controls[i].id == c->id) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
- memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl));
- return 0;
- }
- }
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n");
+static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int ret;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = pwc_get_brightness(pdev);
+ if (c->value < 0)
return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *c = arg;
- int ret;
-
- switch (c->id)
- {
- case V4L2_CID_BRIGHTNESS:
- c->value = pwc_get_brightness(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_CONTRAST:
- c->value = pwc_get_contrast(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_SATURATION:
- ret = pwc_get_saturation(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAMMA:
- c->value = pwc_get_gamma(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- ret = pwc_get_red_gain(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- ret = pwc_get_blue_gain(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = pwc_get_awb(pdev);
- if (ret<0)
- return -EINVAL;
- c->value = (ret == PWC_WB_MANUAL)?0:1;
- return 0;
- case V4L2_CID_GAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTOGAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value = (c->value < 0)?1:0;
- return 0;
- case V4L2_CID_EXPOSURE:
- ret = pwc_get_shutter_speed(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_get_colour_mode(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value=(c->value == -1?1:0);
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 10;
- return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_get_backlight(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_get_flicker(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value=(c->value?1:0);
- return 0;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_get_dynamic_noise(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
-
- case V4L2_CID_PRIVATE_SAVE_USER:
- case V4L2_CID_PRIVATE_RESTORE_USER:
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- return -EINVAL;
- }
+ return 0;
+ case V4L2_CID_CONTRAST:
+ c->value = pwc_get_contrast(pdev);
+ if (c->value < 0)
return -EINVAL;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *c = arg;
- int ret;
-
- switch (c->id)
- {
- case V4L2_CID_BRIGHTNESS:
- c->value <<= 9;
- ret = pwc_set_brightness(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_CONTRAST:
- c->value <<= 10;
- ret = pwc_set_contrast(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_SATURATION:
- ret = pwc_set_saturation(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAMMA:
- c->value <<= 11;
- ret = pwc_set_gamma(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- c->value <<= 8;
- ret = pwc_set_red_gain(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- c->value <<= 8;
- ret = pwc_set_blue_gain(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO;
- ret = pwc_set_awb(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_EXPOSURE:
- c->value <<= 8;
- ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_AUTOGAIN:
- /* autogain off means nothing without a gain */
- if (c->value == 0)
- return 0;
- ret = pwc_set_agc(pdev, c->value, 0);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAIN:
- c->value <<= 8;
- ret = pwc_set_agc(pdev, 0, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_SAVE_USER:
- if (pwc_save_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_USER:
- if (pwc_restore_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- if (pwc_restore_factory(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_set_colour_mode(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- c->value=(c->value == 1)?-1:0;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- c->value <<= 10;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_set_backlight(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_set_flicker(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_set_dynamic_noise(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
-
- }
+ return 0;
+ case V4L2_CID_SATURATION:
+ ret = pwc_get_saturation(pdev, &c->value);
+ if (ret < 0)
return -EINVAL;
- }
+ return 0;
+ case V4L2_CID_GAMMA:
+ c->value = pwc_get_gamma(pdev);
+ if (c->value < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ ret = pwc_get_red_gain(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value >>= 8;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = pwc_get_blue_gain(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value >>= 8;
+ return 0;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = pwc_get_awb(pdev);
+ if (ret < 0)
+ return -EINVAL;
+ c->value = (ret == PWC_WB_MANUAL) ? 0 : 1;
+ return 0;
+ case V4L2_CID_GAIN:
+ ret = pwc_get_agc(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value >>= 8;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ ret = pwc_get_agc(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value = (c->value < 0) ? 1 : 0;
+ return 0;
+ case V4L2_CID_EXPOSURE:
+ ret = pwc_get_shutter_speed(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_COLOUR_MODE:
+ ret = pwc_get_colour_mode(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_AUTOCONTOUR:
+ ret = pwc_get_contour(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value = (c->value == -1 ? 1 : 0);
+ return 0;
+ case V4L2_CID_PRIVATE_CONTOUR:
+ ret = pwc_get_contour(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value >>= 10;
+ return 0;
+ case V4L2_CID_PRIVATE_BACKLIGHT:
+ ret = pwc_get_backlight(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_FLICKERLESS:
+ ret = pwc_get_flicker(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ c->value = (c->value ? 1 : 0);
+ return 0;
+ case V4L2_CID_PRIVATE_NOISE_REDUCTION:
+ ret = pwc_get_dynamic_noise(pdev, &c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- int index;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* We only support two format: the raw format, and YUV */
- index = f->index;
- memset(f,0,sizeof(struct v4l2_fmtdesc));
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->index = index;
- switch(index)
- {
- case 0:
- /* RAW format */
- f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2;
- f->flags = V4L2_FMT_FLAG_COMPRESSED;
- strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description));
- break;
- case 1:
- f->pixelformat = V4L2_PIX_FMT_YUV420;
- strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description));
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
+ case V4L2_CID_PRIVATE_SAVE_USER:
+ case V4L2_CID_PRIVATE_RESTORE_USER:
+ case V4L2_CID_PRIVATE_RESTORE_FACTORY:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
+static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int ret;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value <<= 9;
+ ret = pwc_set_brightness(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_CONTRAST:
+ c->value <<= 10;
+ ret = pwc_set_contrast(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_SATURATION:
+ ret = pwc_set_saturation(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_GAMMA:
+ c->value <<= 11;
+ ret = pwc_set_gamma(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ c->value <<= 8;
+ ret = pwc_set_red_gain(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ c->value <<= 8;
+ ret = pwc_set_blue_gain(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO;
+ ret = pwc_set_awb(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_EXPOSURE:
+ c->value <<= 8;
+ ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ /* autogain off means nothing without a gain */
+ if (c->value == 0)
+ return 0;
+ ret = pwc_set_agc(pdev, c->value, 0);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_GAIN:
+ c->value <<= 8;
+ ret = pwc_set_agc(pdev, 0, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_SAVE_USER:
+ if (pwc_save_user(pdev))
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_RESTORE_USER:
+ if (pwc_restore_user(pdev))
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_RESTORE_FACTORY:
+ if (pwc_restore_factory(pdev))
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_COLOUR_MODE:
+ ret = pwc_set_colour_mode(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_AUTOCONTOUR:
+ c->value = (c->value == 1) ? -1 : 0;
+ ret = pwc_set_contour(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_CONTOUR:
+ c->value <<= 10;
+ ret = pwc_set_contour(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_BACKLIGHT:
+ ret = pwc_set_backlight(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ case V4L2_CID_PRIVATE_FLICKERLESS:
+ ret = pwc_set_flicker(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ case V4L2_CID_PRIVATE_NOISE_REDUCTION:
+ ret = pwc_set_dynamic_noise(pdev, c->value);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y);
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ }
+ return -EINVAL;
+}
- pwc_vidioc_fill_fmt(pdev, f);
+static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ /* We only support two format: the raw format, and YUV */
+ switch (f->index) {
+ case 0:
+ /* RAW format */
+ f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description));
+ break;
+ case 1:
+ f->pixelformat = V4L2_PIX_FMT_YUV420;
+ strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
- return 0;
- }
+static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
- case VIDIOC_TRY_FMT:
- return pwc_vidioc_try_fmt(pdev, arg);
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
+ pdev->image.x, pdev->image.y);
+ pwc_vidioc_fill_fmt(pdev, f);
+ return 0;
+}
- case VIDIOC_S_FMT:
- return pwc_vidioc_set_fmt(pdev, arg);
+static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
- case VIDIOC_G_STD:
- {
- v4l2_std_id *std = arg;
- *std = V4L2_STD_UNKNOWN;
- return 0;
- }
+ return pwc_vidioc_try_fmt(pdev, f);
+}
- case VIDIOC_S_STD:
- {
- v4l2_std_id *std = arg;
- if (*std != V4L2_STD_UNKNOWN)
- return -EINVAL;
- return 0;
- }
+static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *std = arg;
- if (std->index != 0)
- return -EINVAL;
- std->id = V4L2_STD_UNKNOWN;
- strlcpy(std->name, "webcam", sizeof(std->name));
- return 0;
- }
+ return pwc_vidioc_set_fmt(pdev, f);
+}
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
- int nbuffers;
+static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+ int nbuffers;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count);
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
+ if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
- nbuffers = rb->count;
- if (nbuffers < 2)
- nbuffers = 2;
- else if (nbuffers > pwc_mbufs)
- nbuffers = pwc_mbufs;
- /* Force to use our # of buffers */
- rb->count = pwc_mbufs;
- return 0;
- }
+ nbuffers = rb->count;
+ if (nbuffers < 2)
+ nbuffers = 2;
+ else if (nbuffers > pwc_mbufs)
+ nbuffers = pwc_mbufs;
+ /* Force to use our # of buffers */
+ rb->count = pwc_mbufs;
+ return 0;
+}
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *buf = arg;
- int index;
+static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int index;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
- return -EINVAL;
- }
- if (buf->memory != V4L2_MEMORY_MMAP) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n");
- return -EINVAL;
- }
- index = buf->index;
- if (index < 0 || index >= pwc_mbufs) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
- return -EINVAL;
- }
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
+ return -EINVAL;
+ }
+ index = buf->index;
+ if (index < 0 || index >= pwc_mbufs) {
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
+ return -EINVAL;
+ }
- memset(buf, 0, sizeof(struct v4l2_buffer));
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->index = index;
- buf->m.offset = index * pdev->len_per_image;
- if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->field = V4L2_FIELD_NONE;
- buf->memory = V4L2_MEMORY_MMAP;
- //buf->flags = V4L2_BUF_FLAG_MAPPED;
- buf->length = pdev->len_per_image;
-
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused);
+ buf->m.offset = index * pdev->len_per_image;
+ if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
+ buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
+ else
+ buf->bytesused = pdev->view.size;
+ buf->field = V4L2_FIELD_NONE;
+ buf->memory = V4L2_MEMORY_MMAP;
+ /*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
+ buf->length = pdev->len_per_image;
- return 0;
- }
+ PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index);
+ PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
+ PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *buf = arg;
+ return 0;
+}
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (buf->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
- if (buf->index >= pwc_mbufs)
- return -EINVAL;
+static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index);
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (buf->index >= pwc_mbufs)
+ return -EINVAL;
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ buf->flags &= ~V4L2_BUF_FLAG_DONE;
- return 0;
- }
+ return 0;
+}
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *buf = arg;
- int ret;
+static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
- /* Add ourselves to the frame wait-queue.
-
- FIXME: needs auditing for safety.
- QUESTION: In what respect? I think that using the
- frameq is safe now.
- */
- add_wait_queue(&pdev->frameq, &wait);
- while (pdev->full_frames == NULL) {
- if (pdev->error_status) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -pdev->error_status;
- }
+ add_wait_queue(&pdev->frameq, &wait);
+ while (pdev->full_frames == NULL) {
+ if (pdev->error_status) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -pdev->error_status;
+ }
- if (signal_pending(current)) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -ERESTARTSYS;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
+ if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
+ return -ERESTARTSYS;
+ }
+ mutex_unlock(&pdev->modlock);
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ mutex_lock(&pdev->modlock);
+ }
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
- /* Decompress data in pdev->images[pdev->fill_image] */
- ret = pwc_handle_frame(pdev);
- if (ret)
- return -EFAULT;
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
-
- buf->index = pdev->fill_image;
- if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->flags = V4L2_BUF_FLAG_MAPPED;
- buf->field = V4L2_FIELD_NONE;
- do_gettimeofday(&buf->timestamp);
- buf->sequence = 0;
- buf->memory = V4L2_MEMORY_MMAP;
- buf->m.offset = pdev->fill_image * pdev->len_per_image;
- buf->length = pdev->len_per_image;
- pwc_next_image(pdev);
-
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
- return 0;
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
+ /* Decompress data in pdev->images[pdev->fill_image] */
+ ret = pwc_handle_frame(pdev);
+ if (ret)
+ return -EFAULT;
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
+
+ buf->index = pdev->fill_image;
+ if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
+ buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
+ else
+ buf->bytesused = pdev->view.size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ buf->field = V4L2_FIELD_NONE;
+ do_gettimeofday(&buf->timestamp);
+ buf->sequence = 0;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = pdev->fill_image * pdev->len_per_image;
+ buf->length = pdev->len_per_image;
+ pwc_next_image(pdev);
+
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
+ PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
+ return 0;
- }
+}
- case VIDIOC_STREAMON:
- {
- return pwc_isoc_init(pdev);
- }
+static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct pwc_device *pdev = video_drvdata(file);
- case VIDIOC_STREAMOFF:
- {
- pwc_isoc_cleanup(pdev);
- return 0;
- }
+ return pwc_isoc_init(pdev);
+}
- case VIDIOC_ENUM_FRAMESIZES:
- {
- struct v4l2_frmsizeenum *fsize = arg;
- unsigned int i = 0, index = fsize->index;
-
- if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
- for (i = 0; i < PSZ_MAX; i++) {
- if (pdev->image_mask & (1UL << i)) {
- if (!index--) {
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = pwc_image_sizes[i].x;
- fsize->discrete.height = pwc_image_sizes[i].y;
- return 0;
- }
- }
- }
- } else if (fsize->index == 0 &&
- ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
- (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = pdev->abs_max.x;
- fsize->discrete.height = pdev->abs_max.y;
- return 0;
- }
- return -EINVAL;
- }
+static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct pwc_device *pdev = video_drvdata(file);
- case VIDIOC_ENUM_FRAMEINTERVALS:
- {
- struct v4l2_frmivalenum *fival = arg;
- int size = -1;
- unsigned int i;
-
- for (i = 0; i < PSZ_MAX; i++) {
- if (pwc_image_sizes[i].x == fival->width &&
- pwc_image_sizes[i].y == fival->height) {
- size = i;
- break;
+ pwc_isoc_cleanup(pdev);
+ return 0;
+}
+
+static int pwc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ unsigned int i = 0, index = fsize->index;
+
+ if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pdev->image_mask & (1UL << i)) {
+ if (!index--) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = pwc_image_sizes[i].x;
+ fsize->discrete.height = pwc_image_sizes[i].y;
+ return 0;
}
}
+ }
+ } else if (fsize->index == 0 &&
+ ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
+ (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = pdev->abs_max.x;
+ fsize->discrete.height = pdev->abs_max.y;
+ return 0;
+ }
+ return -EINVAL;
+}
- /* TODO: Support raw format */
- if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
- return -EINVAL;
- }
+static int pwc_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int size = -1;
+ unsigned int i;
+
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pwc_image_sizes[i].x == fival->width &&
+ pwc_image_sizes[i].y == fival->height) {
+ size = i;
+ break;
+ }
+ }
- i = pwc_get_fps(pdev, fival->index, size);
- if (!i)
- return -EINVAL;
+ /* TODO: Support raw format */
+ if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
+ return -EINVAL;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete.numerator = 1;
- fival->discrete.denominator = i;
+ i = pwc_get_fps(pdev, fival->index, size);
+ if (!i)
+ return -EINVAL;
- return 0;
- }
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = i;
- default:
- return pwc_ioctl(pdev, cmd, arg);
- } /* ..switch */
return 0;
}
+static long pwc_default(struct file *file, void *fh, int cmd, void *arg)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ return pwc_ioctl(pdev, cmd, arg);
+}
+
+const struct v4l2_ioctl_ops pwc_ioctl_ops = {
+ .vidioc_querycap = pwc_querycap,
+ .vidioc_enum_input = pwc_enum_input,
+ .vidioc_g_input = pwc_g_input,
+ .vidioc_s_input = pwc_s_input,
+ .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap,
+ .vidioc_queryctrl = pwc_queryctrl,
+ .vidioc_g_ctrl = pwc_g_ctrl,
+ .vidioc_s_ctrl = pwc_s_ctrl,
+ .vidioc_reqbufs = pwc_reqbufs,
+ .vidioc_querybuf = pwc_querybuf,
+ .vidioc_qbuf = pwc_qbuf,
+ .vidioc_dqbuf = pwc_dqbuf,
+ .vidioc_streamon = pwc_streamon,
+ .vidioc_streamoff = pwc_streamoff,
+ .vidioc_enum_framesizes = pwc_enum_framesizes,
+ .vidioc_enum_frameintervals = pwc_enum_frameintervals,
+ .vidioc_default = pwc_default,
+};
+
+
/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 16bbc6df9b07..e947766337d6 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -339,8 +339,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power);
/* Private ioctl()s; see pwc-ioctl.h */
extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
-/** Functions in pwc-v4l.c */
-extern long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg);
+extern const struct v4l2_ioctl_ops pwc_ioctl_ops;
/** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 2f500809f53d..5159cc8a0e1c 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -27,13 +27,13 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
-#include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
#include "fimc-core.h"
static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *isp_info)
+ struct s5p_fimc_isp_info *isp_info)
{
struct i2c_adapter *i2c_adap;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
@@ -86,8 +86,8 @@ static void fimc_subdev_unregister(struct fimc_dev *fimc)
static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
{
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct s3c_platform_fimc *pdata = fimc->pdata;
- struct s3c_fimc_isp_info *isp_info;
+ struct s5p_platform_fimc *pdata = fimc->pdata;
+ struct s5p_fimc_isp_info *isp_info;
struct v4l2_subdev *sd;
int i;
@@ -113,26 +113,43 @@ static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
return -ENODEV;
}
-static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index)
+static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index)
{
- struct s3c_fimc_isp_info *isp_info;
+ struct s5p_fimc_isp_info *isp_info;
int ret;
+ if (index >= FIMC_MAX_CAMIF_CLIENTS)
+ return -EINVAL;
+
+ isp_info = fimc->pdata->isp_info[index];
+ if (!isp_info)
+ return -EINVAL;
+
+ if (isp_info->clk_frequency)
+ clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency);
+
+ ret = clk_enable(fimc->clock[CLK_CAM]);
+ if (ret)
+ return ret;
+
ret = fimc_subdev_attach(fimc, index);
if (ret)
return ret;
- isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index];
ret = fimc_hw_set_camera_polarity(fimc, isp_info);
- if (!ret) {
- ret = v4l2_subdev_call(fimc->vid_cap.sd, core,
- s_power, 1);
- if (!ret)
- return ret;
- }
+ if (ret)
+ return ret;
+ ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1);
+ if (!ret)
+ return ret;
+
+ /* enabling power failed so unregister subdev */
fimc_subdev_unregister(fimc);
- err("ISP initialization failed: %d", ret);
+
+ v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n",
+ ret);
+
return ret;
}
@@ -141,7 +158,7 @@ static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index)
* Locking: The caller holds fimc->slock spinlock.
*/
int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
- struct fimc_vid_buffer *fimc_vb)
+ struct fimc_vid_buffer *fimc_vb)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_ctx *ctx = cap->ctx;
@@ -149,7 +166,7 @@ int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
BUG_ON(!fimc || !fimc_vb);
- ret = fimc_prepare_addr(ctx, fimc_vb, &ctx->d_frame,
+ ret = fimc_prepare_addr(ctx, &fimc_vb->vb, &ctx->d_frame,
&fimc_vb->paddr);
if (ret)
return ret;
@@ -174,7 +191,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
{
unsigned long flags;
struct fimc_vid_cap *cap;
- int ret;
+ struct fimc_vid_buffer *buf;
cap = &fimc->vid_cap;
@@ -187,24 +204,213 @@ static int fimc_stop_capture(struct fimc_dev *fimc)
spin_unlock_irqrestore(&fimc->slock, flags);
wait_event_timeout(fimc->irq_queue,
- test_bit(ST_CAPT_SHUT, &fimc->state),
+ !test_bit(ST_CAPT_SHUT, &fimc->state),
FIMC_SHUTDOWN_TIMEOUT);
- ret = v4l2_subdev_call(cap->sd, video, s_stream, 0);
- if (ret)
- v4l2_err(&fimc->vid_cap.v4l2_dev, "s_stream(0) failed\n");
+ v4l2_subdev_call(cap->sd, video, s_stream, 0);
spin_lock_irqsave(&fimc->slock, flags);
fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
1 << ST_CAPT_STREAM);
fimc->vid_cap.active_buf_cnt = 0;
+
+ /* Release buffers that were enqueued in the driver by videobuf2. */
+ while (!list_empty(&cap->pending_buf_q)) {
+ buf = pending_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ while (!list_empty(&cap->active_buf_q)) {
+ buf = active_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
spin_unlock_irqrestore(&fimc->slock, flags);
dbg("state: 0x%lx", fimc->state);
return 0;
}
+static int start_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct s5p_fimc_isp_info *isp_info;
+ int ret;
+
+ ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = fimc_prepare_config(ctx, ctx->state);
+ if (ret)
+ return ret;
+
+ isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index];
+ fimc_hw_set_camera_type(fimc, isp_info);
+ fimc_hw_set_camera_source(fimc, isp_info);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
+ if (ctx->state & FIMC_PARAMS) {
+ ret = fimc_set_scaler_info(ctx);
+ if (ret) {
+ err("Scaler setup error");
+ return ret;
+ }
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx);
+ }
+
+ fimc_hw_set_output_path(ctx);
+ fimc_hw_set_out_dma(ctx);
+
+ INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q);
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ fimc->vid_cap.active_buf_cnt = 0;
+ fimc->vid_cap.frame_count = 0;
+ fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc);
+
+ set_bit(ST_CAPT_PEND, &fimc->state);
+
+ return 0;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ return fimc_stop_capture(fimc);
+}
+
+static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
+{
+ if (!fr || plane >= fr->fmt->memplanes)
+ return 0;
+
+ dbg("%s: w: %d. h: %d. depth[%d]: %d",
+ __func__, fr->width, fr->height, plane, fr->fmt->depth[plane]);
+
+ return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
+
+}
+
+static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned long sizes[],
+ void *allocators[])
+{
+ struct fimc_ctx *ctx = vq->drv_priv;
+ struct fimc_fmt *fmt = ctx->d_frame.fmt;
+ int i;
+
+ if (!fmt)
+ return -EINVAL;
+
+ *num_planes = fmt->memplanes;
+
+ dbg("%s, buffer count=%d, plane count=%d",
+ __func__, *num_buffers, *num_planes);
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ sizes[i] = get_plane_size(&ctx->d_frame, i);
+ dbg("plane: %u, plane_size: %lu", i, sizes[i]);
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ /* TODO: */
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_ctx *ctx = vq->drv_priv;
+ struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ int i;
+
+ if (!ctx->d_frame.fmt || vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
+ unsigned long size = get_plane_size(&ctx->d_frame, i);
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_vid_buffer *buf
+ = container_of(vb, struct fimc_vid_buffer, vb);
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_vid_cap_buf_queue(fimc, buf);
+
+ dbg("active_buf_cnt: %d", fimc->vid_cap.active_buf_cnt);
+
+ if (vid_cap->active_buf_cnt >= vid_cap->reqbufs_count ||
+ vid_cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) {
+ if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
+ dbg("");
+ }
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->fimc_dev->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->fimc_dev->lock);
+}
+
+static struct vb2_ops fimc_capture_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_init = buffer_init,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
@@ -216,44 +422,36 @@ static int fimc_capture_open(struct file *file)
if (fimc_m2m_active(fimc))
return -EBUSY;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_isp_subdev_init(fimc, -1);
+ ret = fimc_isp_subdev_init(fimc, 0);
if (ret) {
fimc->vid_cap.refcnt--;
- ret = -EIO;
+ return -EIO;
}
}
file->private_data = fimc->vid_cap.ctx;
- mutex_unlock(&fimc->lock);
- return ret;
+ return 0;
}
static int fimc_capture_close(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) {
fimc_stop_capture(fimc);
-
- videobuf_stop(&fimc->vid_cap.vbq);
- videobuf_mmap_free(&fimc->vid_cap.vbq);
+ vb2_queue_release(&fimc->vid_cap.vbq);
v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n");
+
v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
+ clk_disable(fimc->clock[CLK_CAM]);
fimc_subdev_unregister(fimc);
}
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -262,32 +460,16 @@ static unsigned int fimc_capture_poll(struct file *file,
{
struct fimc_ctx *ctx = file->private_data;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- int ret;
- if (mutex_lock_interruptible(&fimc->lock))
- return POLLERR;
-
- ret = videobuf_poll_stream(file, &cap->vbq, wait);
- mutex_unlock(&fimc->lock);
-
- return ret;
+ return vb2_poll(&fimc->vid_cap.vbq, file, wait);
}
static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
{
struct fimc_ctx *ctx = file->private_data;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
- ret = videobuf_mmap_mapper(&cap->vbq, vma);
- mutex_unlock(&fimc->lock);
-
- return ret;
+ return vb2_mmap(&fimc->vid_cap.vbq, vma);
}
/* video device file operations */
@@ -310,7 +492,8 @@ static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
@@ -351,57 +534,52 @@ static int sync_capture_fmt(struct fimc_ctx *ctx)
return 0;
}
-static int fimc_cap_s_fmt(struct file *file, void *priv,
- struct v4l2_format *f)
+static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *frame;
- struct v4l2_pix_format *pix;
+ struct v4l2_pix_format_mplane *pix;
int ret;
+ int i;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- ret = fimc_vidioc_try_fmt(file, priv, f);
+ ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
if (ret)
return ret;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- if (fimc_capture_active(fimc)) {
- ret = -EBUSY;
- goto sf_unlock;
- }
+ if (vb2_is_streaming(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
+ return -EBUSY;
frame = &ctx->d_frame;
- pix = &f->fmt.pix;
+ pix = &f->fmt.pix_mp;
frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM);
if (!frame->fmt) {
err("fimc target format not found\n");
- ret = -EINVAL;
- goto sf_unlock;
+ return -EINVAL;
}
+ for (i = 0; i < frame->fmt->colplanes; i++)
+ frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
+
/* Output DMA frame pixel size and offsets. */
- frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
+ frame->f_width = pix->plane_fmt[0].bytesperline * 8
+ / frame->fmt->depth[0];
frame->f_height = pix->height;
frame->width = pix->width;
frame->height = pix->height;
frame->o_width = pix->width;
frame->o_height = pix->height;
- frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
frame->offs_h = 0;
frame->offs_v = 0;
- ret = sync_capture_fmt(ctx);
-
ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT);
-sf_unlock:
- mutex_unlock(&fimc->lock);
+ ret = sync_capture_fmt(ctx);
return ret;
}
@@ -409,8 +587,8 @@ static int fimc_cap_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
struct fimc_ctx *ctx = priv;
- struct s3c_platform_fimc *pldata = ctx->fimc_dev->pdata;
- struct s3c_fimc_isp_info *isp_info;
+ struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata;
+ struct s5p_fimc_isp_info *isp_info;
if (i->index >= FIMC_MAX_CAMIF_CLIENTS)
return -EINVAL;
@@ -429,34 +607,27 @@ static int fimc_cap_s_input(struct file *file, void *priv,
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct s3c_platform_fimc *pdata = fimc->pdata;
- int ret;
+ struct s5p_platform_fimc *pdata = fimc->pdata;
if (fimc_capture_active(ctx->fimc_dev))
return -EBUSY;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i])
+ return -EINVAL;
- if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) {
- ret = -EINVAL;
- goto si_unlock;
- }
if (fimc->vid_cap.sd) {
- ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
+ int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
if (ret)
err("s_power failed: %d", ret);
+
+ clk_disable(fimc->clock[CLK_CAM]);
}
/* Release the attached sensor subdevice. */
fimc_subdev_unregister(fimc);
- ret = fimc_isp_subdev_init(fimc, i);
-
-si_unlock:
- mutex_unlock(&fimc->lock);
- return ret;
+ return fimc_isp_subdev_init(fimc, i);
}
static int fimc_cap_g_input(struct file *file, void *priv,
@@ -470,66 +641,20 @@ static int fimc_cap_g_input(struct file *file, void *priv,
}
static int fimc_cap_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
+ enum v4l2_buf_type type)
{
- struct s3c_fimc_isp_info *isp_info;
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = -EBUSY;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
if (fimc_capture_active(fimc) || !fimc->vid_cap.sd)
- goto s_unlock;
+ return -EBUSY;
if (!(ctx->state & FIMC_DST_FMT)) {
v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n");
- ret = -EINVAL;
- goto s_unlock;
- }
-
- ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD)
- goto s_unlock;
-
- ret = fimc_prepare_config(ctx, ctx->state);
- if (ret)
- goto s_unlock;
-
- isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index];
- fimc_hw_set_camera_type(fimc, isp_info);
- fimc_hw_set_camera_source(fimc, isp_info);
- fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
-
- if (ctx->state & FIMC_PARAMS) {
- ret = fimc_set_scaler_info(ctx);
- if (ret) {
- err("Scaler setup error");
- goto s_unlock;
- }
- fimc_hw_set_input_path(ctx);
- fimc_hw_set_scaler(ctx);
- fimc_hw_set_target_format(ctx);
- fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx);
+ return -EINVAL;
}
- fimc_hw_set_output_path(ctx);
- fimc_hw_set_out_dma(ctx);
-
- INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q);
- INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
- fimc->vid_cap.active_buf_cnt = 0;
- fimc->vid_cap.frame_count = 0;
- fimc->vid_cap.buf_index = fimc_hw_get_frame_index(fimc);
-
- set_bit(ST_CAPT_PEND, &fimc->state);
- ret = videobuf_streamon(&fimc->vid_cap.vbq);
-
-s_unlock:
- mutex_unlock(&fimc->lock);
- return ret;
+ return vb2_streamon(&fimc->vid_cap.vbq, type);
}
static int fimc_cap_streamoff(struct file *file, void *priv,
@@ -537,46 +662,22 @@ static int fimc_cap_streamoff(struct file *file, void *priv,
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&fimc->slock, flags);
- if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) {
- spin_unlock_irqrestore(&fimc->slock, flags);
- dbg("state: 0x%lx", fimc->state);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&fimc->slock, flags);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- fimc_stop_capture(fimc);
- ret = videobuf_streamoff(&cap->vbq);
- mutex_unlock(&fimc->lock);
- return ret;
+ return vb2_streamoff(&fimc->vid_cap.vbq, type);
}
static int fimc_cap_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
+ struct v4l2_requestbuffers *reqbufs)
{
struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
int ret;
- if (fimc_capture_active(ctx->fimc_dev))
- return -EBUSY;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
- ret = videobuf_reqbufs(&cap->vbq, reqbufs);
+ ret = vb2_reqbufs(&cap->vbq, reqbufs);
if (!ret)
cap->reqbufs_count = reqbufs->count;
- mutex_unlock(&fimc->lock);
return ret;
}
@@ -586,43 +687,23 @@ static int fimc_cap_querybuf(struct file *file, void *priv,
struct fimc_ctx *ctx = priv;
struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
- if (fimc_capture_active(ctx->fimc_dev))
- return -EBUSY;
-
- return videobuf_querybuf(&cap->vbq, buf);
+ return vb2_querybuf(&cap->vbq, buf);
}
static int fimc_cap_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = videobuf_qbuf(&cap->vbq, buf);
-
- mutex_unlock(&fimc->lock);
- return ret;
+ struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+ return vb2_qbuf(&cap->vbq, buf);
}
static int fimc_cap_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
struct fimc_ctx *ctx = priv;
- int ret;
-
- if (mutex_lock_interruptible(&ctx->fimc_dev->lock))
- return -ERESTARTSYS;
-
- ret = videobuf_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf,
+ return vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf,
file->f_flags & O_NONBLOCK);
-
- mutex_unlock(&ctx->fimc_dev->lock);
- return ret;
}
static int fimc_cap_s_ctrl(struct file *file, void *priv,
@@ -631,9 +712,6 @@ static int fimc_cap_s_ctrl(struct file *file, void *priv,
struct fimc_ctx *ctx = priv;
int ret = -EINVAL;
- if (mutex_lock_interruptible(&ctx->fimc_dev->lock))
- return -ERESTARTSYS;
-
/* Allow any controls but 90/270 rotation while streaming */
if (!fimc_capture_active(ctx->fimc_dev) ||
ctrl->id != V4L2_CID_ROTATE ||
@@ -648,8 +726,6 @@ static int fimc_cap_s_ctrl(struct file *file, void *priv,
if (ret == -EINVAL)
ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
core, s_ctrl, ctrl);
-
- mutex_unlock(&ctx->fimc_dev->lock);
return ret;
}
@@ -658,22 +734,18 @@ static int fimc_cap_cropcap(struct file *file, void *fh,
{
struct fimc_frame *f;
struct fimc_ctx *ctx = fh;
- struct fimc_dev *fimc = ctx->fimc_dev;
- if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
f = &ctx->s_frame;
+
cr->bounds.left = 0;
cr->bounds.top = 0;
cr->bounds.width = f->o_width;
cr->bounds.height = f->o_height;
cr->defrect = cr->bounds;
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -681,19 +753,14 @@ static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
struct fimc_frame *f;
struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
-
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
f = &ctx->s_frame;
+
cr->c.left = f->offs_h;
cr->c.top = f->offs_v;
cr->c.width = f->width;
cr->c.height = f->height;
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -712,41 +779,38 @@ static int fimc_cap_s_crop(struct file *file, void *fh,
if (ret)
return ret;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
if (!(ctx->state & FIMC_DST_FMT)) {
v4l2_err(&fimc->vid_cap.v4l2_dev,
"Capture color format not set\n");
- goto sc_unlock;
+ return -EINVAL; /* TODO: make sure this is the right value */
}
f = &ctx->s_frame;
/* Check for the pixel scaling ratio when cropping input image. */
- ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
+ ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
+ ctx->d_frame.width, ctx->d_frame.height,
+ ctx->rotation);
if (ret) {
v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range");
- } else {
- ret = 0;
- f->offs_h = cr->c.left;
- f->offs_v = cr->c.top;
- f->width = cr->c.width;
- f->height = cr->c.height;
+ return ret;
}
-sc_unlock:
- mutex_unlock(&fimc->lock);
- return ret;
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+
+ return 0;
}
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_querycap = fimc_vidioc_querycap_capture,
- .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt,
- .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt,
- .vidioc_s_fmt_vid_cap = fimc_cap_s_fmt,
- .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
.vidioc_reqbufs = fimc_cap_reqbufs,
.vidioc_querybuf = fimc_cap_querybuf,
@@ -770,6 +834,7 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_g_input = fimc_cap_g_input,
};
+/* fimc->lock must be already initialized */
int fimc_register_capture_device(struct fimc_dev *fimc)
{
struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev;
@@ -777,6 +842,8 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
struct fimc_vid_cap *vid_cap;
struct fimc_ctx *ctx;
struct v4l2_format f;
+ struct fimc_frame *fr;
+ struct vb2_queue *q;
int ret;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
@@ -788,8 +855,12 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
ctx->out_path = FIMC_DMA;
ctx->state = FIMC_CTX_CAP;
- f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
- ctx->d_frame.fmt = find_format(&f, FMT_FLAGS_M2M);
+ /* Default format of the output frames */
+ f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
+ fr = &ctx->d_frame;
+ fr->fmt = find_format(&f, FMT_FLAGS_M2M);
+ fr->width = fr->f_width = fr->o_width = 640;
+ fr->height = fr->f_height = fr->o_height = 480;
if (!v4l2_dev->name[0])
snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
@@ -812,6 +883,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vfd->ioctl_ops = &fimc_capture_ioctl_ops;
vfd->minor = -1;
vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
video_set_drvdata(vfd, fimc);
vid_cap = &fimc->vid_cap;
@@ -819,7 +891,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
vid_cap->active_buf_cnt = 0;
vid_cap->reqbufs_count = 0;
vid_cap->refcnt = 0;
- /* The default color format for image sensor. */
+ /* Default color format for image sensor */
vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
INIT_LIST_HEAD(&vid_cap->pending_buf_q);
@@ -827,10 +899,16 @@ int fimc_register_capture_device(struct fimc_dev *fimc)
spin_lock_init(&ctx->slock);
vid_cap->ctx = ctx;
- videobuf_queue_dma_contig_init(&vid_cap->vbq, &fimc_qops,
- vid_cap->v4l2_dev.dev, &fimc->irqlock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct fimc_vid_buffer), (void *)ctx, NULL);
+ q = &fimc->vid_cap.vbq;
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fimc->vid_cap.ctx;
+ q->ops = &fimc_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct fimc_vid_buffer);
+
+ vb2_queue_init(q);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret) {
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 817aa66627f6..cd8a3003f1aa 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -25,114 +25,141 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <media/v4l2-ioctl.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
#include "fimc-core.h"
-static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" };
+static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
+ "sclk_fimc", "fimc", "sclk_cam"
+};
static struct fimc_fmt fimc_formats[] = {
{
- .name = "RGB565",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .depth = 16,
- .color = S5P_FIMC_RGB565,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE,
- .flags = FMT_FLAGS_M2M,
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = { 16 },
+ .color = S5P_FIMC_RGB565,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "BGR666",
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .depth = { 32 },
+ .color = S5P_FIMC_RGB666,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "XRGB-8-8-8-8, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = { 32 },
+ .color = S5P_FIMC_RGB888,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "BGR666",
- .fourcc = V4L2_PIX_FMT_BGR666,
- .depth = 32,
- .color = S5P_FIMC_RGB666,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
- .name = "XRGB-8-8-8-8, 32 bpp",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- .color = S5P_FIMC_RGB888,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = S5P_FIMC_CBYCRY422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .color = S5P_FIMC_YCBYCR422,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
- .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = { 16 },
+ .color = S5P_FIMC_CRYCBY422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
- .name = "YUV 4:2:2 packed, CbYCrY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .color = S5P_FIMC_CBYCRY422,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
- .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCRYCB422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
- .name = "YUV 4:2:2 packed, CrYCbY",
- .fourcc = V4L2_PIX_FMT_VYUY,
- .depth = 16,
- .color = S5P_FIMC_CRYCBY422,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
- .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:2 packed, YCrYCb",
- .fourcc = V4L2_PIX_FMT_YVYU,
- .depth = 16,
- .color = S5P_FIMC_YCRYCB422,
- .buff_cnt = 1,
- .planes_cnt = 1,
- .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
- .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:2 planar, Y/Cb/Cr",
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .depth = 12,
- .color = S5P_FIMC_YCBCR422,
- .buff_cnt = 1,
- .planes_cnt = 3,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCRYCB422,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:2 planar, Y/CbCr",
- .fourcc = V4L2_PIX_FMT_NV16,
- .depth = 16,
- .color = S5P_FIMC_YCBCR422,
- .buff_cnt = 1,
- .planes_cnt = 2,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBCR420,
+ .memplanes = 1,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:2 planar, Y/CrCb",
- .fourcc = V4L2_PIX_FMT_NV61,
- .depth = 16,
- .color = S5P_FIMC_RGB565,
- .buff_cnt = 1,
- .planes_cnt = 2,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBCR420,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:0 planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .color = S5P_FIMC_YCBCR420,
- .buff_cnt = 1,
- .planes_cnt = 3,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 4 },
+ .memplanes = 2,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
}, {
- .name = "YUV 4:2:0 planar, Y/CbCr",
- .fourcc = V4L2_PIX_FMT_NV12,
- .depth = 12,
- .color = S5P_FIMC_YCBCR420,
- .buff_cnt = 1,
- .planes_cnt = 2,
- .flags = FMT_FLAGS_M2M,
+ .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 2, 2 },
+ .memplanes = 3,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 4 },
+ .memplanes = 2,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
},
};
@@ -173,24 +200,21 @@ static struct v4l2_queryctrl *get_ctrl(int id)
return NULL;
}
-int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
+int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot)
{
- if (r->width > f->width) {
- if (f->width > (r->width * SCALER_MAX_HRATIO))
- return -EINVAL;
- } else {
- if ((f->width * SCALER_MAX_HRATIO) < r->width)
- return -EINVAL;
- }
+ int tx, ty;
- if (r->height > f->height) {
- if (f->height > (r->height * SCALER_MAX_VRATIO))
- return -EINVAL;
+ if (rot == 90 || rot == 270) {
+ ty = dw;
+ tx = dh;
} else {
- if ((f->height * SCALER_MAX_VRATIO) < r->height)
- return -EINVAL;
+ tx = dw;
+ ty = dh;
}
+ if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty))
+ return -EINVAL;
+
return 0;
}
@@ -221,6 +245,7 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *s_frame = &ctx->s_frame;
struct fimc_frame *d_frame = &ctx->d_frame;
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
int tx, ty, sx, sy;
int ret;
@@ -259,8 +284,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
sc->pre_dst_width = sx / sc->pre_hratio;
sc->pre_dst_height = sy / sc->pre_vratio;
- sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
- sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+ if (variant->has_mainscaler_ext) {
+ sc->main_hratio = (sx << 14) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 14) / (ty << sc->vfactor);
+ } else {
+ sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+ }
sc->scaleup_h = (tx >= sx) ? 1 : 0;
sc->scaleup_v = (ty >= sy) ? 1 : 0;
@@ -276,6 +307,23 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
return 0;
}
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ if (!fimc_m2m_pending(fimc))
+ return 0;
+
+ set_bit(ST_M2M_SHUT, &fimc->state);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_M2M_SHUT, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ return 0;
+}
+
static void fimc_capture_handler(struct fimc_dev *fimc)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
@@ -283,7 +331,7 @@ static void fimc_capture_handler(struct fimc_dev *fimc)
if (!list_empty(&cap->active_buf_q)) {
v_buf = active_queue_pop(cap);
- fimc_buf_finish(fimc, v_buf);
+ vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
@@ -300,10 +348,6 @@ static void fimc_capture_handler(struct fimc_dev *fimc)
dbg("hw ptr: %d, sw ptr: %d",
fimc_hw_get_frame_index(fimc), cap->buf_index);
- spin_lock(&fimc->irqlock);
- v_buf->vb.state = VIDEOBUF_ACTIVE;
- spin_unlock(&fimc->irqlock);
-
/* Move the buffer to the capture active queue */
active_queue_add(cap, v_buf);
@@ -324,8 +368,6 @@ static void fimc_capture_handler(struct fimc_dev *fimc)
static irqreturn_t fimc_isr(int irq, void *priv)
{
- struct fimc_vid_buffer *src_buf, *dst_buf;
- struct fimc_ctx *ctx;
struct fimc_dev *fimc = priv;
BUG_ON(!fimc);
@@ -333,18 +375,21 @@ static irqreturn_t fimc_isr(int irq, void *priv)
spin_lock(&fimc->slock);
- if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
- ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+ if (test_and_clear_bit(ST_M2M_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ goto isr_unlock;
+ } else if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ struct vb2_buffer *src_vb, *dst_vb;
+ struct fimc_ctx *ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+
if (!ctx || !ctx->m2m_ctx)
goto isr_unlock;
- src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
- if (src_buf && dst_buf) {
- spin_lock(&fimc->irqlock);
- src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
- wake_up(&src_buf->vb.done);
- wake_up(&dst_buf->vb.done);
- spin_unlock(&fimc->irqlock);
+
+ src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ if (src_vb && dst_vb) {
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
}
goto isr_unlock;
@@ -364,25 +409,25 @@ isr_unlock:
return IRQ_HANDLED;
}
-/* The color format (planes_cnt, buff_cnt) must be already configured. */
-int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
+/* The color format (colplanes, memplanes) must be already configured. */
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
struct fimc_frame *frame, struct fimc_addr *paddr)
{
int ret = 0;
u32 pix_size;
- if (buf == NULL || frame == NULL)
+ if (vb == NULL || frame == NULL)
return -EINVAL;
pix_size = frame->width * frame->height;
- dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d",
- frame->fmt->buff_cnt, frame->fmt->planes_cnt,
- frame->size, pix_size);
+ dbg("memplanes= %d, colplanes= %d, pix_size= %d",
+ frame->fmt->memplanes, frame->fmt->colplanes, pix_size);
- if (frame->fmt->buff_cnt == 1) {
- paddr->y = videobuf_to_dma_contig(&buf->vb);
- switch (frame->fmt->planes_cnt) {
+ paddr->y = vb2_dma_contig_plane_paddr(vb, 0);
+
+ if (frame->fmt->memplanes == 1) {
+ switch (frame->fmt->colplanes) {
case 1:
paddr->cb = 0;
paddr->cr = 0;
@@ -405,6 +450,12 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
default:
return -EINVAL;
}
+ } else {
+ if (frame->fmt->memplanes >= 2)
+ paddr->cb = vb2_dma_contig_plane_paddr(vb, 1);
+
+ if (frame->fmt->memplanes == 3)
+ paddr->cr = vb2_dma_contig_plane_paddr(vb, 2);
}
dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
@@ -423,34 +474,34 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx)
/* Set order for 1 plane input formats. */
switch (ctx->s_frame.fmt->color) {
case S5P_FIMC_YCRYCB422:
- ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY;
break;
case S5P_FIMC_CBYCRY422:
- ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB;
break;
case S5P_FIMC_CRYCBY422:
- ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR;
break;
case S5P_FIMC_YCBYCR422:
default:
- ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY;
break;
}
dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
switch (ctx->d_frame.fmt->color) {
case S5P_FIMC_YCRYCB422:
- ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY;
break;
case S5P_FIMC_CBYCRY422:
- ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB;
break;
case S5P_FIMC_CRYCBY422:
- ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR;
break;
case S5P_FIMC_YCBYCR422:
default:
- ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY;
break;
}
dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
@@ -459,10 +510,14 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx)
static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
{
struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ u32 i, depth = 0;
+
+ for (i = 0; i < f->fmt->colplanes; i++)
+ depth += f->fmt->depth[i];
f->dma_offset.y_h = f->offs_h;
if (!variant->pix_hoff)
- f->dma_offset.y_h *= (f->fmt->depth >> 3);
+ f->dma_offset.y_h *= (depth >> 3);
f->dma_offset.y_v = f->offs_v;
@@ -473,7 +528,7 @@ static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
f->dma_offset.cr_v = f->offs_v;
if (!variant->pix_hoff) {
- if (f->fmt->planes_cnt == 3) {
+ if (f->fmt->colplanes == 3) {
f->dma_offset.cb_h >>= 1;
f->dma_offset.cr_h >>= 1;
}
@@ -499,7 +554,7 @@ static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
{
struct fimc_frame *s_frame, *d_frame;
- struct fimc_vid_buffer *buf = NULL;
+ struct vb2_buffer *vb = NULL;
int ret = 0;
s_frame = &ctx->s_frame;
@@ -522,15 +577,15 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
ctx->scaler.enabled = 1;
if (flags & FIMC_SRC_ADDR) {
- buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, buf, s_frame, &s_frame->paddr);
+ vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
if (ret)
return ret;
}
if (flags & FIMC_DST_ADDR) {
- buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, buf, d_frame, &d_frame->paddr);
+ vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr);
}
return ret;
@@ -572,7 +627,9 @@ static void fimc_dma_run(void *priv)
err("Scaler setup error");
goto dma_unlock;
}
- fimc_hw_set_scaler(ctx);
+
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
fimc_hw_set_target_format(ctx);
fimc_hw_set_rotation(ctx);
fimc_hw_set_effect(ctx);
@@ -587,7 +644,8 @@ static void fimc_dma_run(void *priv)
fimc_activate_capture(ctx);
- ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
+ ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
+ FIMC_SRC_FMT | FIMC_DST_FMT);
fimc_hw_activate_input_dma(fimc, true);
dma_unlock:
@@ -596,109 +654,94 @@ dma_unlock:
static void fimc_job_abort(void *priv)
{
- /* Nothing done in job_abort. */
-}
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
-static void fimc_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
+ if (!fimc_m2m_pending(fimc))
+ return;
+
+ set_bit(ST_M2M_SHUT, &fimc->state);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_M2M_SHUT, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
}
-static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned long sizes[],
+ void *allocators[])
{
- struct fimc_ctx *ctx = vq->priv_data;
- struct fimc_frame *frame;
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ struct fimc_frame *f;
+ int i;
- frame = ctx_get_frame(ctx, vq->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
+ f = ctx_get_frame(ctx, vq->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ /*
+ * Return number of non-contigous planes (plane buffers)
+ * depending on the configured color format.
+ */
+ if (f->fmt)
+ *num_planes = f->fmt->memplanes;
+
+ for (i = 0; i < f->fmt->memplanes; i++) {
+ sizes[i] = (f->width * f->height * f->fmt->depth[i]) >> 3;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+
+ if (*num_buffers == 0)
+ *num_buffers = 1;
- *size = (frame->width * frame->height * frame->fmt->depth) >> 3;
- if (0 == *count)
- *count = 1;
return 0;
}
-static int fimc_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
+static int fimc_buf_prepare(struct vb2_buffer *vb)
{
- struct fimc_ctx *ctx = vq->priv_data;
- struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct fimc_frame *frame;
- int ret;
+ int i;
- frame = ctx_get_frame(ctx, vq->type);
+ frame = ctx_get_frame(ctx, vb->vb2_queue->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
- if (vb->baddr) {
- if (vb->bsize < frame->size) {
- v4l2_err(v4l2_dev,
- "User-provided buffer too small (%d < %d)\n",
- vb->bsize, frame->size);
- WARN_ON(1);
- return -EINVAL;
- }
- } else if (vb->state != VIDEOBUF_NEEDS_INIT
- && vb->bsize < frame->size) {
- return -EINVAL;
- }
-
- vb->width = frame->width;
- vb->height = frame->height;
- vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
- vb->size = frame->size;
- vb->field = field;
-
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret) {
- v4l2_err(v4l2_dev, "Iolock failed\n");
- fimc_buf_release(vq, vb);
- return ret;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
+ for (i = 0; i < frame->fmt->memplanes; i++)
+ vb2_set_plane_payload(vb, i, frame->payload[i]);
return 0;
}
-static void fimc_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void fimc_buf_queue(struct vb2_buffer *vb)
{
- struct fimc_ctx *ctx = vq->priv_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- unsigned long flags;
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
- if ((ctx->state & FIMC_CTX_M2M) && ctx->m2m_ctx) {
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
- } else if (ctx->state & FIMC_CTX_CAP) {
- spin_lock_irqsave(&fimc->slock, flags);
- fimc_vid_cap_buf_queue(fimc, (struct fimc_vid_buffer *)vb);
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
- dbg("fimc->cap.active_buf_cnt: %d",
- fimc->vid_cap.active_buf_cnt);
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->fimc_dev->lock);
+}
- if (cap->active_buf_cnt >= cap->reqbufs_count ||
- cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) {
- if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state))
- fimc_activate_capture(ctx);
- }
- spin_unlock_irqrestore(&fimc->slock, flags);
- }
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->fimc_dev->lock);
}
-struct videobuf_queue_ops fimc_qops = {
- .buf_setup = fimc_buf_setup,
- .buf_prepare = fimc_buf_prepare,
- .buf_queue = fimc_buf_queue,
- .buf_release = fimc_buf_release,
+struct vb2_ops fimc_qops = {
+ .queue_setup = fimc_queue_setup,
+ .buf_prepare = fimc_buf_prepare,
+ .buf_queue = fimc_buf_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .stop_streaming = stop_streaming,
};
static int fimc_m2m_querycap(struct file *file, void *priv,
@@ -712,12 +755,13 @@ static int fimc_m2m_querycap(struct file *file, void *priv,
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
cap->capabilities = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
-int fimc_vidioc_enum_fmt(struct file *file, void *priv,
+int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct fimc_fmt *fmt;
@@ -732,25 +776,21 @@ int fimc_vidioc_enum_fmt(struct file *file, void *priv,
return 0;
}
-int fimc_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *frame;
frame = ctx_get_frame(ctx, f->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
f->fmt.pix.width = frame->width;
f->fmt.pix.height = frame->height;
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.pixelformat = frame->fmt->fourcc;
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -785,42 +825,40 @@ struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
}
-int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
struct samsung_fimc_variant *variant = fimc->variant;
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct fimc_fmt *fmt;
u32 max_width, mod_x, mod_y, mask;
- int ret = -EINVAL, is_output = 0;
+ int i, is_output = 0;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (ctx->state & FIMC_CTX_CAP)
return -EINVAL;
is_output = 1;
- } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
return -EINVAL;
}
- dbg("w: %d, h: %d, bpl: %d",
- pix->width, pix->height, pix->bytesperline);
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ dbg("w: %d, h: %d", pix->width, pix->height);
mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM;
fmt = find_format(f, mask);
if (!fmt) {
v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n",
pix->pixelformat);
- goto tf_out;
+ return -EINVAL;
}
if (pix->field == V4L2_FIELD_ANY)
pix->field = V4L2_FIELD_NONE;
else if (V4L2_FIELD_NONE != pix->field)
- goto tf_out;
+ return -EINVAL;
if (is_output) {
max_width = variant->pix_limit->scaler_dis_w;
@@ -834,7 +872,7 @@ int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mod_x = 6; /* 64 x 32 pixels tile */
mod_y = 5;
} else {
- if (fimc->id == 1 && fimc->variant->pix_hoff)
+ if (fimc->id == 1 && variant->pix_hoff)
mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
else
mod_y = mod_x;
@@ -845,74 +883,73 @@ int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
v4l_bound_align_image(&pix->width, 16, max_width, mod_x,
&pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
- if (pix->bytesperline == 0 ||
- (pix->bytesperline * 8 / fmt->depth) > pix->width)
- pix->bytesperline = (pix->width * fmt->depth) >> 3;
+ pix->num_planes = fmt->memplanes;
- if (pix->sizeimage == 0)
- pix->sizeimage = pix->height * pix->bytesperline;
+ for (i = 0; i < pix->num_planes; ++i) {
+ int bpl = pix->plane_fmt[i].bytesperline;
- dbg("w: %d, h: %d, bpl: %d, depth: %d",
- pix->width, pix->height, pix->bytesperline, fmt->depth);
+ dbg("[%d] bpl: %d, depth: %d, w: %d, h: %d",
+ i, bpl, fmt->depth[i], pix->width, pix->height);
- ret = 0;
+ if (!bpl || (bpl * 8 / fmt->depth[i]) > pix->width)
+ bpl = (pix->width * fmt->depth[0]) >> 3;
-tf_out:
- mutex_unlock(&fimc->lock);
- return ret;
+ if (!pix->plane_fmt[i].sizeimage)
+ pix->plane_fmt[i].sizeimage = pix->height * bpl;
+
+ pix->plane_fmt[i].bytesperline = bpl;
+
+ dbg("[%d]: bpl: %d, sizeimage: %d",
+ i, pix->plane_fmt[i].bytesperline,
+ pix->plane_fmt[i].sizeimage);
+ }
+
+ return 0;
}
-static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_device *v4l2_dev = &fimc->m2m.v4l2_dev;
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
struct fimc_frame *frame;
- struct v4l2_pix_format *pix;
+ struct v4l2_pix_format_mplane *pix;
unsigned long flags;
- int ret = 0;
+ int i, ret = 0;
+ u32 tmp;
- ret = fimc_vidioc_try_fmt(file, priv, f);
+ ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
if (ret)
return ret;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- mutex_lock(&vq->vb_lock);
- if (videobuf_queue_is_busy(vq)) {
- v4l2_err(v4l2_dev, "%s: queue (%d) busy\n", __func__, f->type);
- ret = -EBUSY;
- goto sf_out;
+ if (vb2_is_streaming(vq)) {
+ v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type);
+ return -EBUSY;
}
- spin_lock_irqsave(&ctx->slock, flags);
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
frame = &ctx->s_frame;
- ctx->state |= FIMC_SRC_FMT;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
frame = &ctx->d_frame;
- ctx->state |= FIMC_DST_FMT;
} else {
- spin_unlock_irqrestore(&ctx->slock, flags);
- v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ v4l2_err(&fimc->m2m.v4l2_dev,
"Wrong buffer/video queue type (%d)\n", f->type);
- ret = -EINVAL;
- goto sf_out;
+ return -EINVAL;
}
- spin_unlock_irqrestore(&ctx->slock, flags);
- pix = &f->fmt.pix;
+ pix = &f->fmt.pix_mp;
frame->fmt = find_format(f, FMT_FLAGS_M2M);
- if (!frame->fmt) {
- ret = -EINVAL;
- goto sf_out;
- }
+ if (!frame->fmt)
+ return -EINVAL;
+
+ for (i = 0; i < frame->fmt->colplanes; i++)
+ frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
- frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
+ frame->f_width = pix->plane_fmt[0].bytesperline * 8 /
+ frame->fmt->depth[0];
frame->f_height = pix->height;
frame->width = pix->width;
frame->height = pix->height;
@@ -920,19 +957,15 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frame->o_height = pix->height;
frame->offs_h = 0;
frame->offs_v = 0;
- frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
- vq->field = pix->field;
spin_lock_irqsave(&ctx->slock, flags);
- ctx->state |= FIMC_PARAMS;
+ tmp = (frame == &ctx->d_frame) ? FIMC_DST_FMT : FIMC_SRC_FMT;
+ ctx->state |= FIMC_PARAMS | tmp;
spin_unlock_irqrestore(&ctx->slock, flags);
dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
-sf_out:
- mutex_unlock(&vq->vb_lock);
- mutex_unlock(&fimc->lock);
- return ret;
+ return 0;
}
static int fimc_m2m_reqbufs(struct file *file, void *priv,
@@ -968,6 +1001,15 @@ static int fimc_m2m_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_ctx *ctx = priv;
+
+ /* The source and target color format need to be set */
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (~ctx->state & FIMC_SRC_FMT)
+ return -EINVAL;
+ } else if (~ctx->state & FIMC_DST_FMT) {
+ return -EINVAL;
+ }
+
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
@@ -992,11 +1034,8 @@ int fimc_vidioc_queryctrl(struct file *file, void *priv,
}
if (ctx->state & FIMC_CTX_CAP) {
- if (mutex_lock_interruptible(&ctx->fimc_dev->lock))
- return -ERESTARTSYS;
- ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
+ return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
core, queryctrl, qc);
- mutex_unlock(&ctx->fimc_dev->lock);
}
return ret;
}
@@ -1006,10 +1045,6 @@ int fimc_vidioc_g_ctrl(struct file *file, void *priv,
{
struct fimc_ctx *ctx = priv;
struct fimc_dev *fimc = ctx->fimc_dev;
- int ret = 0;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
switch (ctrl->id) {
case V4L2_CID_HFLIP:
@@ -1023,18 +1058,17 @@ int fimc_vidioc_g_ctrl(struct file *file, void *priv,
break;
default:
if (ctx->state & FIMC_CTX_CAP) {
- ret = v4l2_subdev_call(fimc->vid_cap.sd, core,
- g_ctrl, ctrl);
+ return v4l2_subdev_call(fimc->vid_cap.sd, core,
+ g_ctrl, ctrl);
} else {
v4l2_err(&fimc->m2m.v4l2_dev,
"Invalid control\n");
- ret = -EINVAL;
+ return -EINVAL;
}
}
dbg("ctrl->value= %d", ctrl->value);
- mutex_unlock(&fimc->lock);
- return ret;
+ return 0;
}
int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
@@ -1059,13 +1093,7 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
struct fimc_dev *fimc = ctx->fimc_dev;
unsigned long flags;
-
- if (ctx->rotation != 0 &&
- (ctrl->id == V4L2_CID_HFLIP || ctrl->id == V4L2_CID_VFLIP)) {
- v4l2_err(&fimc->m2m.v4l2_dev,
- "Simultaneous flip and rotation is not supported\n");
- return -EINVAL;
- }
+ int ret = 0;
spin_lock_irqsave(&ctx->slock, flags);
@@ -1085,6 +1113,20 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
break;
case V4L2_CID_ROTATE:
+ if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) {
+ ret = fimc_check_scaler_ratio(ctx->s_frame.width,
+ ctx->s_frame.height,
+ ctx->d_frame.width,
+ ctx->d_frame.height,
+ ctrl->value);
+ if (ret) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "Out of scaler range");
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return -EINVAL;
+ }
+ }
+
/* Check for the output rotator availability */
if ((ctrl->value == 90 || ctrl->value == 270) &&
(ctx->in_path == FIMC_DMA && !variant->has_out_rot)) {
@@ -1107,7 +1149,7 @@ int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
}
static int fimc_m2m_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+ struct v4l2_control *ctrl)
{
struct fimc_ctx *ctx = priv;
int ret = 0;
@@ -1125,22 +1167,17 @@ static int fimc_m2m_cropcap(struct file *file, void *fh,
{
struct fimc_frame *frame;
struct fimc_ctx *ctx = fh;
- struct fimc_dev *fimc = ctx->fimc_dev;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
cr->bounds.left = 0;
cr->bounds.top = 0;
cr->bounds.width = frame->f_width;
cr->bounds.height = frame->f_height;
cr->defrect = cr->bounds;
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -1148,21 +1185,16 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
struct fimc_frame *frame;
struct fimc_ctx *ctx = file->private_data;
- struct fimc_dev *fimc = ctx->fimc_dev;
frame = ctx_get_frame(ctx, cr->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
cr->c.left = frame->offs_h;
cr->c.top = frame->offs_v;
cr->c.width = frame->width;
cr->c.height = frame->height;
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -1170,7 +1202,8 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
{
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
- u32 min_size, halign;
+ u32 min_size, halign, depth = 0;
+ int i;
if (cr->c.top < 0 || cr->c.left < 0) {
v4l2_err(&fimc->m2m.v4l2_dev,
@@ -1178,9 +1211,9 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
return -EINVAL;
}
- if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
f = (ctx->state & FIMC_CTX_CAP) ? &ctx->s_frame : &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
ctx->state & FIMC_CTX_M2M)
f = &ctx->s_frame;
else
@@ -1200,10 +1233,13 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
halign = 4;
}
+ for (i = 0; i < f->fmt->colplanes; i++)
+ depth += f->fmt->depth[i];
+
v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
ffs(min_size) - 1,
&cr->c.height, min_size, f->o_height,
- halign, 64/(ALIGN(f->fmt->depth, 8)));
+ halign, 64/(ALIGN(depth, 8)));
/* adjust left/top if cropping rectangle is out of bounds */
if (cr->c.left + cr->c.width > f->o_width)
@@ -1235,25 +1271,31 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
if (ret)
return ret;
- f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+ f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
&ctx->s_frame : &ctx->d_frame;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
spin_lock_irqsave(&ctx->slock, flags);
- if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) {
- /* Check to see if scaling ratio is within supported range */
- if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
- else
- ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame);
+ /* Check to see if scaling ratio is within supported range */
+ if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) {
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
+ ctx->d_frame.width,
+ ctx->d_frame.height,
+ ctx->rotation);
+ } else {
+ ret = fimc_check_scaler_ratio(ctx->s_frame.width,
+ ctx->s_frame.height,
+ cr->c.width, cr->c.height,
+ ctx->rotation);
+ }
+
if (ret) {
v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range");
- ret = -EINVAL;
- goto scr_unlock;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return -EINVAL;
}
}
+
ctx->state |= FIMC_PARAMS;
f->offs_h = cr->c.left;
@@ -1261,26 +1303,24 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
f->width = cr->c.width;
f->height = cr->c.height;
-scr_unlock:
spin_unlock_irqrestore(&ctx->slock, flags);
- mutex_unlock(&fimc->lock);
return 0;
}
static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_querycap = fimc_m2m_querycap,
- .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt,
- .vidioc_enum_fmt_vid_out = fimc_vidioc_enum_fmt,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane,
- .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt,
- .vidioc_g_fmt_vid_out = fimc_vidioc_g_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane,
- .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt,
- .vidioc_try_fmt_vid_out = fimc_vidioc_try_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane,
- .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt,
- .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
+ .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
.vidioc_reqbufs = fimc_m2m_reqbufs,
.vidioc_querybuf = fimc_m2m_querybuf,
@@ -1301,26 +1341,39 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
};
-static void queue_init(void *priv, struct videobuf_queue *vq,
- enum v4l2_buf_type type)
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
{
struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &fimc_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- videobuf_queue_dma_contig_init(vq, &fimc_qops,
- &fimc->pdev->dev,
- &fimc->irqlock, type, V4L2_FIELD_NONE,
- sizeof(struct fimc_vid_buffer), priv, NULL);
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &fimc_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ return vb2_queue_init(dst_vq);
}
static int fimc_m2m_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_ctx *ctx = NULL;
- int err = 0;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
dbg("pid: %d, state: 0x%lx, refcnt: %d",
task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
@@ -1329,19 +1382,15 @@ static int fimc_m2m_open(struct file *file)
* Return if the corresponding video capture node
* is already opened.
*/
- if (fimc->vid_cap.refcnt > 0) {
- err = -EBUSY;
- goto err_unlock;
- }
+ if (fimc->vid_cap.refcnt > 0)
+ return -EBUSY;
fimc->m2m.refcnt++;
set_bit(ST_OUTDMA_RUN, &fimc->state);
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx) {
- err = -ENOMEM;
- goto err_unlock;
- }
+ if (!ctx)
+ return -ENOMEM;
file->private_data = ctx;
ctx->fimc_dev = fimc;
@@ -1355,15 +1404,14 @@ static int fimc_m2m_open(struct file *file)
ctx->out_path = FIMC_DMA;
spin_lock_init(&ctx->slock);
- ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init);
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- err = PTR_ERR(ctx->m2m_ctx);
+ int err = PTR_ERR(ctx->m2m_ctx);
kfree(ctx);
+ return err;
}
-err_unlock:
- mutex_unlock(&fimc->lock);
- return err;
+ return 0;
}
static int fimc_m2m_release(struct file *file)
@@ -1371,8 +1419,6 @@ static int fimc_m2m_release(struct file *file)
struct fimc_ctx *ctx = file->private_data;
struct fimc_dev *fimc = ctx->fimc_dev;
- mutex_lock(&fimc->lock);
-
dbg("pid: %d, state: 0x%lx, refcnt= %d",
task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
@@ -1381,7 +1427,6 @@ static int fimc_m2m_release(struct file *file)
if (--fimc->m2m.refcnt <= 0)
clear_bit(ST_OUTDMA_RUN, &fimc->state);
- mutex_unlock(&fimc->lock);
return 0;
}
@@ -1415,7 +1460,6 @@ static struct v4l2_m2m_ops m2m_ops = {
.job_abort = fimc_job_abort,
};
-
static int fimc_register_m2m_device(struct fimc_dev *fimc)
{
struct video_device *vfd;
@@ -1448,6 +1492,7 @@ static int fimc_register_m2m_device(struct fimc_dev *fimc)
vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
vfd->minor = -1;
vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
@@ -1496,7 +1541,7 @@ static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
static void fimc_clk_release(struct fimc_dev *fimc)
{
int i;
- for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+ for (i = 0; i < fimc->num_clocks; i++) {
if (fimc->clock[i]) {
clk_disable(fimc->clock[i]);
clk_put(fimc->clock[i]);
@@ -1507,15 +1552,16 @@ static void fimc_clk_release(struct fimc_dev *fimc)
static int fimc_clk_get(struct fimc_dev *fimc)
{
int i;
- for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
- fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]);
- if (IS_ERR(fimc->clock[i])) {
- dev_err(&fimc->pdev->dev,
- "failed to get fimc clock: %s\n",
- fimc_clock_name[i]);
- return -ENXIO;
+ for (i = 0; i < fimc->num_clocks; i++) {
+ fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
+
+ if (!IS_ERR_OR_NULL(fimc->clock[i])) {
+ clk_enable(fimc->clock[i]);
+ continue;
}
- clk_enable(fimc->clock[i]);
+ dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
+ fimc_clocks[i]);
+ return -ENXIO;
}
return 0;
}
@@ -1526,6 +1572,7 @@ static int fimc_probe(struct platform_device *pdev)
struct resource *res;
struct samsung_fimc_driverdata *drv_data;
int ret = 0;
+ int cap_input_index = -1;
dev_dbg(&pdev->dev, "%s():\n", __func__);
@@ -1548,7 +1595,6 @@ static int fimc_probe(struct platform_device *pdev)
fimc->pdata = pdev->dev.platform_data;
fimc->state = ST_IDLE;
- spin_lock_init(&fimc->irqlock);
init_waitqueue_head(&fimc->irq_queue);
spin_lock_init(&fimc->slock);
@@ -1576,10 +1622,26 @@ static int fimc_probe(struct platform_device *pdev)
goto err_req_region;
}
+ fimc->num_clocks = MAX_FIMC_CLOCKS - 1;
+ /*
+ * Check if vide capture node needs to be registered for this device
+ * instance.
+ */
+ if (fimc->pdata) {
+ int i;
+ for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i)
+ if (fimc->pdata->isp_info[i])
+ break;
+ if (i < FIMC_MAX_CAMIF_CLIENTS) {
+ cap_input_index = i;
+ fimc->num_clocks++;
+ }
+ }
+
ret = fimc_clk_get(fimc);
if (ret)
goto err_regs_unmap;
- clk_set_rate(fimc->clock[0], drv_data->lclk_frequency);
+ clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -1597,24 +1659,24 @@ static int fimc_probe(struct platform_device *pdev)
goto err_clk;
}
+ /* Initialize contiguous memory allocator */
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev);
+ if (IS_ERR(fimc->alloc_ctx)) {
+ ret = PTR_ERR(fimc->alloc_ctx);
+ goto err_irq;
+ }
+
ret = fimc_register_m2m_device(fimc);
if (ret)
goto err_irq;
/* At least one camera sensor is required to register capture node */
- if (fimc->pdata) {
- int i;
- for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i)
- if (fimc->pdata->isp_info[i])
- break;
-
- if (i < FIMC_MAX_CAMIF_CLIENTS) {
- ret = fimc_register_capture_device(fimc);
- if (ret)
- goto err_m2m;
- }
+ if (cap_input_index >= 0) {
+ ret = fimc_register_capture_device(fimc);
+ if (ret)
+ goto err_m2m;
+ clk_disable(fimc->clock[CLK_CAM]);
}
-
/*
* Exclude the additional output DMA address registers by masking
* them out on HW revisions that provide extended capabilites.
@@ -1656,6 +1718,9 @@ static int __devexit fimc_remove(struct platform_device *pdev)
fimc_unregister_capture_device(fimc);
fimc_clk_release(fimc);
+
+ vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+
iounmap(fimc->regs);
release_resource(fimc->regs_res);
kfree(fimc->regs_res);
@@ -1726,6 +1791,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
+ .has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 1,
@@ -1747,6 +1813,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cistatus2 = 1,
+ .has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 1,
@@ -1757,6 +1824,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
.pix_hoff = 1,
.has_cistatus2 = 1,
+ .has_mainscaler_ext = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 1,
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 4f047d35f8ad..4829a2515076 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -16,11 +16,12 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/videodev2.h>
-#include <media/videobuf-core.h>
+#include <linux/io.h>
+#include <media/videobuf2-core.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
-#include <media/s3c_fimc.h>
+#include <media/s5p_fimc.h>
#include "regs-fimc.h"
@@ -36,7 +37,7 @@
/* Time to wait for next frame VSYNC interrupt while stopping operation. */
#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
-#define NUM_FIMC_CLOCKS 2
+#define MAX_FIMC_CLOCKS 3
#define MODULE_NAME "s5p-fimc"
#define FIMC_MAX_DEVS 4
#define FIMC_MAX_OUT_BUFS 4
@@ -44,12 +45,19 @@
#define SCALER_MAX_VRATIO 64
#define DMA_MIN_SIZE 8
-/* FIMC device state flags */
+/* indices to the clocks array */
+enum {
+ CLK_BUS,
+ CLK_GATE,
+ CLK_CAM,
+};
+
enum fimc_dev_flags {
/* for m2m node */
ST_IDLE,
ST_OUTDMA_RUN,
ST_M2M_PEND,
+ ST_M2M_SHUT,
/* for capture node */
ST_CAPT_PEND,
ST_CAPT_RUN,
@@ -70,13 +78,6 @@ enum fimc_dev_flags {
#define fimc_capture_streaming(dev) \
test_bit(ST_CAPT_STREAM, &(dev)->state)
-#define fimc_buf_finish(dev, vid_buf) do { \
- spin_lock(&(dev)->irqlock); \
- (vid_buf)->vb.state = VIDEOBUF_DONE; \
- spin_unlock(&(dev)->irqlock); \
- wake_up(&(vid_buf)->vb.done); \
-} while (0)
-
enum fimc_datapath {
FIMC_CAMERA,
FIMC_DMA,
@@ -90,7 +91,6 @@ enum fimc_color_fmt {
S5P_FIMC_RGB888,
S5P_FIMC_RGB30_LOCAL,
S5P_FIMC_YCBCR420 = 0x20,
- S5P_FIMC_YCBCR422,
S5P_FIMC_YCBYCR422,
S5P_FIMC_YCRYCB422,
S5P_FIMC_CBYCRY422,
@@ -100,18 +100,6 @@ enum fimc_color_fmt {
#define fimc_fmt_is_rgb(x) ((x) & 0x10)
-/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
-#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY
-#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB
-#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY
-#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR
-
-/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */
-#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY
-#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB
-#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY
-#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR
-
/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
@@ -157,18 +145,18 @@ enum fimc_color_fmt {
* @name: format description
* @fourcc: the fourcc code for this format, 0 if not applicable
* @color: the corresponding fimc_color_fmt
- * @depth: driver's private 'number of bits per pixel'
- * @buff_cnt: number of physically non-contiguous data planes
- * @planes_cnt: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @memplanes: number of physically non-contiguous data planes
+ * @colplanes: number of physically contiguous data planes
*/
struct fimc_fmt {
enum v4l2_mbus_pixelcode mbus_code;
char *name;
u32 fourcc;
u32 color;
- u16 buff_cnt;
- u16 planes_cnt;
- u16 depth;
+ u16 memplanes;
+ u16 colplanes;
+ u8 depth[VIDEO_MAX_PLANES];
u16 flags;
#define FMT_FLAGS_CAM (1 << 0)
#define FMT_FLAGS_M2M (1 << 1)
@@ -260,7 +248,8 @@ struct fimc_addr {
* @index: buffer index for the output DMA engine
*/
struct fimc_vid_buffer {
- struct videobuf_buffer vb;
+ struct vb2_buffer vb;
+ struct list_head list;
struct fimc_addr paddr;
int index;
};
@@ -277,7 +266,7 @@ struct fimc_vid_buffer {
* @height: image pixel weight
* @paddr: image frame buffer physical addresses
* @buf_cnt: number of buffers depending on a color format
- * @size: image size in bytes
+ * @payload: image size in bytes (w x h x bpp)
* @color: color format
* @dma_offset: DMA offset in bytes
*/
@@ -290,7 +279,7 @@ struct fimc_frame {
u32 offs_v;
u32 width;
u32 height;
- u32 size;
+ unsigned long payload[VIDEO_MAX_PLANES];
struct fimc_addr paddr;
struct fimc_dma_offset dma_offset;
struct fimc_fmt *fmt;
@@ -331,13 +320,14 @@ struct fimc_m2m_device {
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
+ struct vb2_alloc_ctx *alloc_ctx;
struct video_device *vfd;
struct v4l2_device v4l2_dev;
- struct v4l2_subdev *sd;
+ struct v4l2_subdev *sd;;
struct v4l2_mbus_framefmt fmt;
struct list_head pending_buf_q;
struct list_head active_buf_q;
- struct videobuf_queue vbq;
+ struct vb2_queue vbq;
int active_buf_cnt;
int buf_index;
unsigned int frame_count;
@@ -372,6 +362,8 @@ struct fimc_pix_limit {
* @has_inp_rot: set if has input rotator
* @has_out_rot: set if has output rotator
* @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
+ * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
+ * are present in this IP revision
* @pix_limit: pixel size constraints for the scaler
* @min_inp_pixsize: minimum input pixel size
* @min_out_pixsize: minimum output pixel size
@@ -383,6 +375,7 @@ struct samsung_fimc_variant {
unsigned int has_inp_rot:1;
unsigned int has_out_rot:1;
unsigned int has_cistatus2:1;
+ unsigned int has_mainscaler_ext:1;
struct fimc_pix_limit *pix_limit;
u16 min_inp_pixsize;
u16 min_out_pixsize;
@@ -412,12 +405,12 @@ struct fimc_ctx;
* @lock: the mutex protecting this data structure
* @pdev: pointer to the FIMC platform device
* @pdata: pointer to the device platform data
- * @id: FIMC device index (0..2)
+ * @id: FIMC device index (0..FIMC_MAX_DEVS)
+ * @num_clocks: the number of clocks managed by this device instance
* @clock[]: the clocks required for FIMC operation
* @regs: the mapped hardware registers
* @regs_res: the resource claimed for IO registers
* @irq: interrupt number of the FIMC subdevice
- * @irqlock: spinlock protecting videobuffer queue
* @irq_queue:
* @m2m: memory-to-memory V4L2 device information
* @vid_cap: camera capture device information
@@ -427,18 +420,19 @@ struct fimc_dev {
spinlock_t slock;
struct mutex lock;
struct platform_device *pdev;
- struct s3c_platform_fimc *pdata;
+ struct s5p_platform_fimc *pdata;
struct samsung_fimc_variant *variant;
- int id;
- struct clk *clock[NUM_FIMC_CLOCKS];
+ u16 id;
+ u16 num_clocks;
+ struct clk *clock[MAX_FIMC_CLOCKS];
void __iomem *regs;
struct resource *regs_res;
int irq;
- spinlock_t irqlock;
wait_queue_head_t irq_queue;
struct fimc_m2m_device m2m;
struct fimc_vid_cap vid_cap;
unsigned long state;
+ struct vb2_alloc_ctx *alloc_ctx;
};
/**
@@ -482,11 +476,9 @@ struct fimc_ctx {
struct v4l2_m2m_ctx *m2m_ctx;
};
-extern struct videobuf_queue_ops fimc_qops;
-
static inline int tiled_fmt(struct fimc_fmt *fmt)
{
- return 0;
+ return fmt->fourcc == V4L2_PIX_FMT_NV12MT;
}
static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
@@ -542,12 +534,12 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
{
struct fimc_frame *frame;
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
if (ctx->state & FIMC_CTX_M2M)
frame = &ctx->s_frame;
else
return ERR_PTR(-EINVAL);
- } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
frame = &ctx->d_frame;
} else {
v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
@@ -581,7 +573,8 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx);
void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
-void fimc_hw_set_scaler(struct fimc_ctx *ctx);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
void fimc_hw_en_capture(struct fimc_ctx *ctx);
void fimc_hw_set_effect(struct fimc_ctx *ctx);
void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
@@ -589,23 +582,23 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx);
void fimc_hw_set_output_path(struct fimc_ctx *ctx);
void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
- int index);
+ int index);
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam);
+ struct s5p_fimc_isp_info *cam);
int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam);
+ struct s5p_fimc_isp_info *cam);
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam);
+ struct s5p_fimc_isp_info *cam);
/* -----------------------------------------------------*/
/* fimc-core.c */
-int fimc_vidioc_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *f);
-int fimc_vidioc_g_fmt(struct file *file, void *priv,
- struct v4l2_format *f);
-int fimc_vidioc_try_fmt(struct file *file, void *priv,
- struct v4l2_format *f);
+int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f);
+int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f);
+int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f);
int fimc_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc);
int fimc_vidioc_g_ctrl(struct file *file, void *priv,
@@ -619,10 +612,10 @@ struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask);
struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
unsigned int mask);
-int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f);
+int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot);
int fimc_set_scaler_info(struct fimc_ctx *ctx);
int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
-int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
struct fimc_frame *frame, struct fimc_addr *paddr);
/* -----------------------------------------------------*/
@@ -649,28 +642,27 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
}
/*
- * Add video buffer to the active buffers queue.
- * The caller holds irqlock spinlock.
+ * Add buf to the capture active buffers queue.
+ * Locking: Need to be called with fimc_dev::slock held.
*/
static inline void active_queue_add(struct fimc_vid_cap *vid_cap,
- struct fimc_vid_buffer *buf)
+ struct fimc_vid_buffer *buf)
{
- buf->vb.state = VIDEOBUF_ACTIVE;
- list_add_tail(&buf->vb.queue, &vid_cap->active_buf_q);
+ list_add_tail(&buf->list, &vid_cap->active_buf_q);
vid_cap->active_buf_cnt++;
}
/*
* Pop a video buffer from the capture active buffers queue
- * Locking: Need to be called with dev->slock held.
+ * Locking: Need to be called with fimc_dev::slock held.
*/
static inline struct fimc_vid_buffer *
active_queue_pop(struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->active_buf_q.next,
- struct fimc_vid_buffer, vb.queue);
- list_del(&buf->vb.queue);
+ struct fimc_vid_buffer, list);
+ list_del(&buf->list);
vid_cap->active_buf_cnt--;
return buf;
}
@@ -679,8 +671,7 @@ active_queue_pop(struct fimc_vid_cap *vid_cap)
static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
struct fimc_vid_buffer *buf)
{
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vid_cap->pending_buf_q);
+ list_add_tail(&buf->list, &vid_cap->pending_buf_q);
}
/* Add video buffer to the capture pending buffers queue */
@@ -689,10 +680,9 @@ pending_queue_pop(struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->pending_buf_q.next,
- struct fimc_vid_buffer, vb.queue);
- list_del(&buf->vb.queue);
+ struct fimc_vid_buffer, list);
+ list_del(&buf->list);
return buf;
}
-
#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 511631a2e5c3..10684aef5b2d 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -13,7 +13,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/map.h>
-#include <media/s3c_fimc.h>
+#include <media/s5p_fimc.h>
#include "fimc-core.h"
@@ -37,11 +37,11 @@ void fimc_hw_reset(struct fimc_dev *dev)
writel(cfg, dev->regs + S5P_CIGCTRL);
}
-static u32 fimc_hw_get_in_flip(u32 ctx_flip)
+static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_MSCTRL_FLIP_NORMAL;
- switch (ctx_flip) {
+ switch (ctx->flip) {
case FLIP_X_AXIS:
flip = S5P_MSCTRL_FLIP_X_MIRROR;
break;
@@ -51,16 +51,20 @@ static u32 fimc_hw_get_in_flip(u32 ctx_flip)
case FLIP_XY_AXIS:
flip = S5P_MSCTRL_FLIP_180;
break;
+ default:
+ break;
}
+ if (ctx->rotation <= 90)
+ return flip;
- return flip;
+ return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180;
}
-static u32 fimc_hw_get_target_flip(u32 ctx_flip)
+static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
{
u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
- switch (ctx_flip) {
+ switch (ctx->flip) {
case FLIP_X_AXIS:
flip = S5P_CITRGFMT_FLIP_X_MIRROR;
break;
@@ -70,11 +74,13 @@ static u32 fimc_hw_get_target_flip(u32 ctx_flip)
case FLIP_XY_AXIS:
flip = S5P_CITRGFMT_FLIP_180;
break;
- case FLIP_NONE:
+ default:
break;
-
}
- return flip;
+ if (ctx->rotation <= 90)
+ return flip;
+
+ return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180;
}
void fimc_hw_set_rotation(struct fimc_ctx *ctx)
@@ -84,10 +90,7 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx)
cfg = readl(dev->regs + S5P_CITRGFMT);
cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 |
- S5P_CITRGFMT_FLIP_180);
-
- flip = readl(dev->regs + S5P_MSCTRL);
- flip &= ~S5P_MSCTRL_FLIP_MASK;
+ S5P_CITRGFMT_FLIP_180);
/*
* The input and output rotator cannot work simultaneously.
@@ -95,26 +98,22 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx)
* in direct fifo output mode.
*/
if (ctx->rotation == 90 || ctx->rotation == 270) {
- if (ctx->out_path == FIMC_LCDFIFO) {
- cfg |= S5P_CITRGFMT_INROT90;
- if (ctx->rotation == 270)
- flip |= S5P_MSCTRL_FLIP_180;
- } else {
- cfg |= S5P_CITRGFMT_OUTROT90;
- if (ctx->rotation == 270)
- cfg |= S5P_CITRGFMT_FLIP_180;
- }
- } else if (ctx->rotation == 180) {
if (ctx->out_path == FIMC_LCDFIFO)
- flip |= S5P_MSCTRL_FLIP_180;
+ cfg |= S5P_CITRGFMT_INROT90;
else
- cfg |= S5P_CITRGFMT_FLIP_180;
+ cfg |= S5P_CITRGFMT_OUTROT90;
}
- if (ctx->rotation == 180 || ctx->rotation == 270)
- writel(flip, dev->regs + S5P_MSCTRL);
- cfg |= fimc_hw_get_target_flip(ctx->flip);
- writel(cfg, dev->regs + S5P_CITRGFMT);
+ if (ctx->out_path == FIMC_DMA) {
+ cfg |= fimc_hw_get_target_flip(ctx);
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+ } else {
+ /* LCD FIFO path */
+ flip = readl(dev->regs + S5P_MSCTRL);
+ flip &= ~S5P_MSCTRL_FLIP_MASK;
+ flip |= fimc_hw_get_in_flip(ctx);
+ writel(flip, dev->regs + S5P_MSCTRL);
+ }
}
void fimc_hw_set_target_format(struct fimc_ctx *ctx)
@@ -131,19 +130,14 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx)
S5P_CITRGFMT_VSIZE_MASK);
switch (frame->fmt->color) {
- case S5P_FIMC_RGB565:
- case S5P_FIMC_RGB666:
- case S5P_FIMC_RGB888:
+ case S5P_FIMC_RGB565...S5P_FIMC_RGB888:
cfg |= S5P_CITRGFMT_RGB;
break;
case S5P_FIMC_YCBCR420:
cfg |= S5P_CITRGFMT_YCBCR420;
break;
- case S5P_FIMC_YCBYCR422:
- case S5P_FIMC_YCRYCB422:
- case S5P_FIMC_CBYCRY422:
- case S5P_FIMC_CRYCBY422:
- if (frame->fmt->planes_cnt == 1)
+ case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ if (frame->fmt->colplanes == 1)
cfg |= S5P_CITRGFMT_YCBCR422_1P;
else
cfg |= S5P_CITRGFMT_YCBCR422;
@@ -219,11 +213,11 @@ void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
S5P_CIOCTRL_YCBCR_PLANE_MASK);
- if (frame->fmt->planes_cnt == 1)
+ if (frame->fmt->colplanes == 1)
cfg |= ctx->out_order_1p;
- else if (frame->fmt->planes_cnt == 2)
+ else if (frame->fmt->colplanes == 2)
cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
- else if (frame->fmt->planes_cnt == 3)
+ else if (frame->fmt->colplanes == 3)
cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
writel(cfg, dev->regs + S5P_CIOCTRL);
@@ -249,7 +243,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
writel(cfg, dev->regs + S5P_CIOCTRL);
}
-static void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_scaler *sc = &ctx->scaler;
@@ -267,7 +261,7 @@ static void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
writel(cfg, dev->regs + S5P_CISCPREDST);
}
-void fimc_hw_set_scaler(struct fimc_ctx *ctx)
+static void fimc_hw_set_scaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_scaler *sc = &ctx->scaler;
@@ -275,8 +269,6 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx)
struct fimc_frame *dst_frame = &ctx->d_frame;
u32 cfg = 0;
- fimc_hw_set_prescaler(ctx);
-
if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
@@ -316,13 +308,42 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx)
cfg |= S5P_CISCCTRL_INTERLACE;
}
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_mainscaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = dev->variant;
+ struct fimc_scaler *sc = &ctx->scaler;
+ u32 cfg;
+
dbg("main_hratio= 0x%X main_vratio= 0x%X",
sc->main_hratio, sc->main_vratio);
- cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
- cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
+ fimc_hw_set_scaler(ctx);
- writel(cfg, dev->regs + S5P_CISCCTRL);
+ cfg = readl(dev->regs + S5P_CISCCTRL);
+
+ if (variant->has_mainscaler_ext) {
+ cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK);
+ cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+
+ cfg = readl(dev->regs + S5P_CIEXTEN);
+
+ cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK |
+ S5P_CIEXTEN_MHRATIO_EXT_MASK);
+ cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio);
+ cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CIEXTEN);
+ } else {
+ cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK);
+ cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+ }
}
void fimc_hw_en_capture(struct fimc_ctx *ctx)
@@ -410,41 +431,37 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
/* Set the input DMA to process single frame only. */
cfg = readl(dev->regs + S5P_MSCTRL);
- cfg &= ~(S5P_MSCTRL_FLIP_MASK
- | S5P_MSCTRL_INFORMAT_MASK
+ cfg &= ~(S5P_MSCTRL_INFORMAT_MASK
| S5P_MSCTRL_IN_BURST_COUNT_MASK
| S5P_MSCTRL_INPUT_MASK
| S5P_MSCTRL_C_INT_IN_MASK
| S5P_MSCTRL_2P_IN_ORDER_MASK);
- cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
+ cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4)
+ | S5P_MSCTRL_INPUT_MEMORY
+ | S5P_MSCTRL_FIFO_CTRL_FULL);
switch (frame->fmt->color) {
- case S5P_FIMC_RGB565:
- case S5P_FIMC_RGB666:
- case S5P_FIMC_RGB888:
+ case S5P_FIMC_RGB565...S5P_FIMC_RGB888:
cfg |= S5P_MSCTRL_INFORMAT_RGB;
break;
case S5P_FIMC_YCBCR420:
cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
- if (frame->fmt->planes_cnt == 2)
+ if (frame->fmt->colplanes == 2)
cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
else
cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
break;
- case S5P_FIMC_YCBYCR422:
- case S5P_FIMC_YCRYCB422:
- case S5P_FIMC_CBYCRY422:
- case S5P_FIMC_CRYCBY422:
- if (frame->fmt->planes_cnt == 1) {
+ case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ if (frame->fmt->colplanes == 1) {
cfg |= ctx->in_order_1p
| S5P_MSCTRL_INFORMAT_YCBCR422_1P;
} else {
cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
- if (frame->fmt->planes_cnt == 2)
+ if (frame->fmt->colplanes == 2)
cfg |= ctx->in_order_2p
| S5P_MSCTRL_C_INT_IN_2PLANE;
else
@@ -455,13 +472,6 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
break;
}
- /*
- * Input DMA flip mode (and rotation).
- * Do not allow simultaneous rotation and flipping.
- */
- if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
- cfg |= fimc_hw_get_in_flip(ctx->flip);
-
writel(cfg, dev->regs + S5P_MSCTRL);
/* Input/output DMA linear/tiled mode. */
@@ -532,7 +542,7 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev,
}
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam)
+ struct s5p_fimc_isp_info *cam)
{
u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
@@ -557,41 +567,46 @@ int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
}
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam)
+ struct s5p_fimc_isp_info *cam)
{
struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
u32 cfg = 0;
+ u32 bus_width;
+ int i;
+
+ static const struct {
+ u32 pixelcode;
+ u32 cisrcfmt;
+ u16 bus_width;
+ } pix_desc[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 },
+ { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 },
+ { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 },
+ { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 },
+ /* TODO: Add pixel codes for 16-bit bus width */
+ };
if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
+ for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
+ if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) {
+ cfg = pix_desc[i].cisrcfmt;
+ bus_width = pix_desc[i].bus_width;
+ break;
+ }
+ }
- switch (fimc->vid_cap.fmt.code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
- cfg = S5P_CISRCFMT_ORDER422_YCBYCR;
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- cfg = S5P_CISRCFMT_ORDER422_YCRYCB;
- break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
- cfg = S5P_CISRCFMT_ORDER422_CRYCBY;
- break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- cfg = S5P_CISRCFMT_ORDER422_CBYCRY;
- break;
- default:
- err("camera image format not supported: %d",
- fimc->vid_cap.fmt.code);
+ if (i == ARRAY_SIZE(pix_desc)) {
+ v4l2_err(&fimc->vid_cap.v4l2_dev,
+ "Camera color format not supported: %d\n",
+ fimc->vid_cap.fmt.code);
return -EINVAL;
}
if (cam->bus_type == FIMC_ITU_601) {
- if (cam->bus_width == 8) {
+ if (bus_width == 8)
cfg |= S5P_CISRCFMT_ITU601_8BIT;
- } else if (cam->bus_width == 16) {
+ else if (bus_width == 16)
cfg |= S5P_CISRCFMT_ITU601_16BIT;
- } else {
- err("invalid bus width: %d", cam->bus_width);
- return -EINVAL;
- }
} /* else defaults to ITU-R BT.656 8-bit */
}
@@ -624,7 +639,7 @@ int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
}
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
- struct s3c_fimc_isp_info *cam)
+ struct s5p_fimc_isp_info *cam)
{
u32 cfg, tmp;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
index 57e33f84fcfa..0fea3e635d76 100644
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -98,8 +98,8 @@
#define S5P_CIOCTRL 0x4c
#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
-#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0)
-#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0)
+#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0)
+#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0)
#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
@@ -139,8 +139,12 @@
#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
#define S5P_CISCCTRL_RGB_EXT (1 << 10)
#define S5P_CISCCTRL_ONE2ONE (1 << 9)
-#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16)
-#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0)
+#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16)
+#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0)
+#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16)
+#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0)
+#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16)
+#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0)
/* Target area */
#define S5P_CITAREA 0x5c
@@ -210,7 +214,7 @@
/* Input DMA control */
#define S5P_MSCTRL 0xfc
-#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24)
+#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24)
#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
@@ -222,11 +226,12 @@
#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
#define S5P_MSCTRL_FLIP_180 (3 << 13)
+#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12)
#define S5P_MSCTRL_ORDER422_SHIFT 4
-#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4)
-#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4)
-#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4)
-#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4)
+#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4)
+#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4)
+#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4)
+#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4)
#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
@@ -237,7 +242,7 @@
#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
#define S5P_MSCTRL_ENVID (1 << 0)
-#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24)
+#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24)
/* Output DMA Y/Cb/Cr offset */
#define S5P_CIOYOFF 0x168
@@ -263,6 +268,10 @@
/* Real output DMA image size (extension register) */
#define S5P_CIEXTEN 0x188
+#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10)
+#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f)
+#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10)
+#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f
#define S5P_CIDMAPARAM 0x18c
#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
index 7913f93979b8..99664205ef4e 100644
--- a/drivers/media/video/saa7110.c
+++ b/drivers/media/video/saa7110.c
@@ -36,6 +36,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
MODULE_AUTHOR("Pauline Middelink");
@@ -53,15 +54,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa7110 {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
u8 reg[SAA7110_NR_REG];
v4l2_std_id norm;
int input;
int enable;
- int bright;
- int contrast;
- int hue;
- int sat;
wait_queue_head_t wq;
};
@@ -71,6 +69,11 @@ static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd)
return container_of(sd, struct saa7110, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa7110, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
/* I2C support functions */
/* ----------------------------------------------------------------------- */
@@ -326,73 +329,22 @@ static int saa7110_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int saa7110_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int saa7110_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct saa7110 *decoder = to_saa7110(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = decoder->bright;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = decoder->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = decoder->sat;
- break;
- case V4L2_CID_HUE:
- ctrl->value = decoder->hue;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int saa7110_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct saa7110 *decoder = to_saa7110(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if (decoder->bright != ctrl->value) {
- decoder->bright = ctrl->value;
- saa7110_write(sd, 0x19, decoder->bright);
- }
+ saa7110_write(sd, 0x19, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- if (decoder->contrast != ctrl->value) {
- decoder->contrast = ctrl->value;
- saa7110_write(sd, 0x13, decoder->contrast);
- }
+ saa7110_write(sd, 0x13, ctrl->val);
break;
case V4L2_CID_SATURATION:
- if (decoder->sat != ctrl->value) {
- decoder->sat = ctrl->value;
- saa7110_write(sd, 0x12, decoder->sat);
- }
+ saa7110_write(sd, 0x12, ctrl->val);
break;
case V4L2_CID_HUE:
- if (decoder->hue != ctrl->value) {
- decoder->hue = ctrl->value;
- saa7110_write(sd, 0x07, decoder->hue);
- }
+ saa7110_write(sd, 0x07, ctrl->val);
break;
default:
return -EINVAL;
@@ -409,11 +361,19 @@ static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa7110_ctrl_ops = {
+ .s_ctrl = saa7110_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa7110_core_ops = {
.g_chip_ident = saa7110_g_chip_ident,
- .g_ctrl = saa7110_g_ctrl,
- .s_ctrl = saa7110_s_ctrl,
- .queryctrl = saa7110_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = saa7110_s_std,
};
@@ -454,10 +414,25 @@ static int saa7110_probe(struct i2c_client *client,
decoder->norm = V4L2_STD_PAL;
decoder->input = 0;
decoder->enable = 1;
- decoder->bright = 32768;
- decoder->contrast = 32768;
- decoder->hue = 32768;
- decoder->sat = 32768;
+ v4l2_ctrl_handler_init(&decoder->hdl, 2);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
init_waitqueue_head(&decoder->wq);
rv = saa7110_write_block(sd, initseq, sizeof(initseq));
@@ -490,9 +465,11 @@ static int saa7110_probe(struct i2c_client *client,
static int saa7110_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct saa7110 *decoder = to_saa7110(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_saa7110(sd));
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index deb8fcf4aa49..61c6007c8ea6 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -3620,6 +3620,38 @@ struct saa7134_board saa7134_boards[] = {
.amux = 0,
},
},
+ [SAA7134_BOARD_ENCORE_ENLTV_FM3] = {
+ .name = "Encore ENLTV-FM 3",
+ .audio_clock = 0x02187de7,
+ .tuner_type = TUNER_TENA_TNF_5337,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x60,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x43000,
+ },
+ },
[SAA7134_BOARD_CINERGY_HT_PCI] = {
.name = "Terratec Cinergy HT PCI",
.audio_clock = 0x00187de7,
@@ -6387,6 +6419,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53,
}, {
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1a7f,
+ .subdevice = 0x2108,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x153b,
.subdevice = 0x1175,
@@ -7102,6 +7140,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_ENCORE_ENLTV:
case SAA7134_BOARD_ENCORE_ENLTV_FM:
case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM3:
case SAA7134_BOARD_10MOONSTVMASTER3:
case SAA7134_BOARD_BEHOLD_401:
case SAA7134_BOARD_BEHOLD_403:
@@ -7294,9 +7333,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
static void saa7134_tuner_setup(struct saa7134_dev *dev)
{
struct tuner_setup tun_setup;
- unsigned int mode_mask = T_RADIO |
- T_ANALOG_TV |
- T_DIGITAL_TV;
+ unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
memset(&tun_setup, 0, sizeof(tun_setup));
tun_setup.tuner_callback = saa7134_tuner_callback;
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 6abeecff6da7..41f836fc93ec 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -752,19 +752,28 @@ static int saa7134_hwfini(struct saa7134_dev *dev)
return 0;
}
-static void __devinit must_configure_manually(void)
+static void __devinit must_configure_manually(int has_eeprom)
{
unsigned int i,p;
- printk(KERN_WARNING
- "saa7134: <rant>\n"
- "saa7134: Congratulations! Your TV card vendor saved a few\n"
- "saa7134: cents for a eeprom, thus your pci board has no\n"
- "saa7134: subsystem ID and I can't identify it automatically\n"
- "saa7134: </rant>\n"
- "saa7134: I feel better now. Ok, here are the good news:\n"
- "saa7134: You can use the card=<nr> insmod option to specify\n"
- "saa7134: which board do you have. The list:\n");
+ if (!has_eeprom)
+ printk(KERN_WARNING
+ "saa7134: <rant>\n"
+ "saa7134: Congratulations! Your TV card vendor saved a few\n"
+ "saa7134: cents for a eeprom, thus your pci board has no\n"
+ "saa7134: subsystem ID and I can't identify it automatically\n"
+ "saa7134: </rant>\n"
+ "saa7134: I feel better now. Ok, here are the good news:\n"
+ "saa7134: You can use the card=<nr> insmod option to specify\n"
+ "saa7134: which board do you have. The list:\n");
+ else
+ printk(KERN_WARNING
+ "saa7134: Board is currently unknown. You might try to use the card=<nr>\n"
+ "saa7134: insmod option to specify which board do you have, but this is\n"
+ "saa7134: somewhat risky, as might damage your card. It is better to ask\n"
+ "saa7134: for support at linux-media@vger.kernel.org.\n"
+ "saa7134: The supported cards are:\n");
+
for (i = 0; i < saa7134_bcount; i++) {
printk(KERN_WARNING "saa7134: card=%d -> %-40.40s",
i,saa7134_boards[i].name);
@@ -936,8 +945,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
if (card[dev->nr] >= 0 &&
card[dev->nr] < saa7134_bcount)
dev->board = card[dev->nr];
- if (SAA7134_BOARD_NOAUTO == dev->board) {
- must_configure_manually();
+ if (SAA7134_BOARD_UNKNOWN == dev->board)
+ must_configure_manually(0);
+ else if (SAA7134_BOARD_NOAUTO == dev->board) {
+ must_configure_manually(1);
dev->board = SAA7134_BOARD_UNKNOWN;
}
dev->autodetected = card[dev->nr] != dev->board;
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 6b8459c7728e..18294db38a01 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -373,6 +373,10 @@ static int empress_queryctrl(struct file *file, void *priv,
static const u32 mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_STREAM_PID_PMT,
+ V4L2_CID_MPEG_STREAM_PID_AUDIO,
+ V4L2_CID_MPEG_STREAM_PID_VIDEO,
+ V4L2_CID_MPEG_STREAM_PID_PCR,
V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
V4L2_CID_MPEG_AUDIO_ENCODING,
V4L2_CID_MPEG_AUDIO_L2_BITRATE,
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index dc646e65edb7..c9eff0336aa6 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -681,6 +681,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
polling = 50; // ms
break;
case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM3:
ir_codes = RC_MAP_ENCORE_ENLTV_FM53;
mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
mask_keyup = 0x0040000;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 5b0a347b0b8f..f96cd5d761f9 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -327,6 +327,7 @@ struct saa7134_card_ir {
#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181
#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182
#define SAA7134_BOARD_VIDEOMATE_M1F 183
+#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
index bd86d970f4c2..8a98ab68239e 100644
--- a/drivers/media/video/saa7164/saa7164-api.c
+++ b/drivers/media/video/saa7164/saa7164-api.c
@@ -743,7 +743,7 @@ int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
int saa7164_api_initialize_dif(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
- struct saa7164_port *p = 0;
+ struct saa7164_port *p = NULL;
int ret = -EINVAL;
u32 std = 0;
@@ -926,9 +926,9 @@ int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
{
- struct saa7164_port *tsport = 0;
- struct saa7164_port *encport = 0;
- struct saa7164_port *vbiport = 0;
+ struct saa7164_port *tsport = NULL;
+ struct saa7164_port *encport = NULL;
+ struct saa7164_port *vbiport = NULL;
u32 idx, next_offset;
int i;
struct tmComResDescrHeader *hdr, *t;
@@ -1340,7 +1340,7 @@ int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
/* Allocate enough storage for all of the descs */
buf = kzalloc(buflen, GFP_KERNEL);
- if (buf == NULL)
+ if (!buf)
return SAA_ERR_NO_RESOURCES;
/* Retrieve them */
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c
index ddd25211c9e8..66696fa8341d 100644
--- a/drivers/media/video/saa7164/saa7164-buffer.c
+++ b/drivers/media/video/saa7164/saa7164-buffer.c
@@ -93,7 +93,7 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port,
u32 len)
{
struct tmHWStreamParameters *params = &port->hw_streamingparams;
- struct saa7164_buffer *buf = 0;
+ struct saa7164_buffer *buf = NULL;
struct saa7164_dev *dev = port->dev;
int i;
@@ -103,7 +103,7 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port,
}
buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL);
- if (buf == NULL) {
+ if (!buf) {
log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__);
goto ret;
}
@@ -157,7 +157,7 @@ fail2:
fail1:
kfree(buf);
- buf = 0;
+ buf = NULL;
ret:
return buf;
}
@@ -289,14 +289,14 @@ struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev,
struct saa7164_user_buffer *buf;
buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL);
- if (buf == 0)
- return 0;
+ if (!buf)
+ return NULL;
buf->data = kzalloc(len, GFP_KERNEL);
- if (buf->data == 0) {
+ if (!buf->data) {
kfree(buf);
- return 0;
+ return NULL;
}
buf->actual_size = len;
@@ -315,7 +315,7 @@ void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf)
return;
kfree(buf->data);
- buf->data = 0;
+ buf->data = NULL;
kfree(buf);
}
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c
index b2b0d97101d0..466e1b02f91f 100644
--- a/drivers/media/video/saa7164/saa7164-bus.c
+++ b/drivers/media/video/saa7164/saa7164-bus.c
@@ -158,7 +158,7 @@ int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
return SAA_ERR_BAD_PARAMETER;
}
- if ((msg->size > 0) && (buf == 0)) {
+ if ((msg->size > 0) && (buf == NULL)) {
printk(KERN_ERR "%s() Missing message buffer\n", __func__);
return SAA_ERR_BAD_PARAMETER;
}
@@ -315,7 +315,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
saa7164_bus_verify(dev);
- if (msg == 0)
+ if (msg == NULL)
return ret;
if (msg->size > dev->bus.m_wMaxReqSize) {
@@ -324,7 +324,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
return ret;
}
- if ((peekonly == 0) && (msg->size > 0) && (buf == 0)) {
+ if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
printk(KERN_ERR
"%s() Missing msg buf, size should be %d bytes\n",
__func__, msg->size);
@@ -392,7 +392,7 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
saa7164_bus_dumpmsg(dev, msg, buf);
- saa7164_bus_dumpmsg(dev, &msg_tmp, 0);
+ saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
ret = SAA_ERR_INVALID_COMMAND;
goto out;
}
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c
index a97ae17b36c2..6a4c217ed3a7 100644
--- a/drivers/media/video/saa7164/saa7164-cmd.c
+++ b/drivers/media/video/saa7164/saa7164-cmd.c
@@ -84,7 +84,7 @@ int saa7164_irq_dequeue(struct saa7164_dev *dev)
{
int ret = SAA_OK, i = 0;
u32 timeout;
- wait_queue_head_t *q = 0;
+ wait_queue_head_t *q = NULL;
u8 tmp[512];
dprintk(DBGLVL_CMD, "%s()\n", __func__);
@@ -137,7 +137,7 @@ int saa7164_cmd_dequeue(struct saa7164_dev *dev)
int loop = 1;
int ret;
u32 timeout;
- wait_queue_head_t *q = 0;
+ wait_queue_head_t *q = NULL;
u8 tmp[512];
dprintk(DBGLVL_CMD, "%s()\n", __func__);
@@ -261,7 +261,7 @@ out:
*/
int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
{
- wait_queue_head_t *q = 0;
+ wait_queue_head_t *q = NULL;
int ret = SAA_BUS_TIMEOUT;
unsigned long stamp;
int r;
@@ -357,7 +357,7 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
"sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id,
command, controlselector);
- if ((size == 0) || (buf == 0)) {
+ if ((size == 0) || (buf == NULL)) {
printk(KERN_ERR "%s() Invalid param\n", __func__);
return SAA_ERR_BAD_PARAMETER;
}
@@ -538,7 +538,7 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
/* Invalid */
dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__);
- ret = saa7164_bus_get(dev, presponse_t, 0, 0);
+ ret = saa7164_bus_get(dev, presponse_t, NULL, 0);
if (ret != SAA_OK) {
printk(KERN_ERR "get failed\n");
return ret;
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index 58af67f2278b..b813aec1e456 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -277,8 +277,8 @@ static void saa7164_histogram_print(struct saa7164_port *port,
static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
{
struct saa7164_dev *dev = port->dev;
- struct saa7164_buffer *buf = 0;
- struct saa7164_user_buffer *ubuf = 0;
+ struct saa7164_buffer *buf = NULL;
+ struct saa7164_user_buffer *ubuf = NULL;
struct list_head *c, *n;
int i = 0;
u8 __iomem *p;
@@ -649,7 +649,7 @@ static irqreturn_t saa7164_irq(int irq, void *dev_id)
u32 intid, intstat[INT_SIZE/4];
int i, handled = 0, bit;
- if (dev == 0) {
+ if (dev == NULL) {
printk(KERN_ERR "%s() No device specified\n", __func__);
handled = 0;
goto out;
@@ -945,7 +945,7 @@ static int get_resources(struct saa7164_dev *dev)
static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
{
- struct saa7164_port *port = 0;
+ struct saa7164_port *port = NULL;
if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
BUG();
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
index b305a01b3bde..f65eab63ca87 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -309,8 +309,8 @@ static int dvb_register(struct saa7164_port *port)
port->hw_streamingparams.pitch = 188;
port->hw_streamingparams.linethreshold = 0;
- port->hw_streamingparams.pagetablelistvirt = 0;
- port->hw_streamingparams.pagetablelistphys = 0;
+ port->hw_streamingparams.pagetablelistvirt = NULL;
+ port->hw_streamingparams.pagetablelistphys = NULL;
port->hw_streamingparams.numpagetables = 2 +
((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index 1838408cd5cb..f9d594698832 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -152,8 +152,8 @@ static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
/* Init and establish defaults */
params->bitspersample = 8;
params->linethreshold = 0;
- params->pagetablelistvirt = 0;
- params->pagetablelistphys = 0;
+ params->pagetablelistvirt = NULL;
+ params->pagetablelistphys = NULL;
params->numpagetableentries = port->hwcfg.buffercount;
/* Allocate the PCI resources, buffers (hard) */
@@ -1108,7 +1108,7 @@ static int fops_release(struct file *file)
struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
{
- struct saa7164_user_buffer *ubuf = 0;
+ struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
u32 crc;
@@ -1443,7 +1443,7 @@ int saa7164_encoder_register(struct saa7164_port *port)
port->v4l_device = saa7164_encoder_alloc(port,
dev->pci, &saa7164_mpeg_template, "mpeg");
- if (port->v4l_device == NULL) {
+ if (!port->v4l_device) {
printk(KERN_INFO "%s: can't allocate mpeg device\n",
dev->name);
result = -ENOMEM;
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c
index ebed6f786a23..b369300cce06 100644
--- a/drivers/media/video/saa7164/saa7164-fw.c
+++ b/drivers/media/video/saa7164/saa7164-fw.c
@@ -88,7 +88,7 @@ int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
"%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n",
__func__, src, srcsize, dlflags, dst, dstsize);
- if ((src == 0) || (dst == 0)) {
+ if ((src == NULL) || (dst == NULL)) {
ret = -EIO;
goto out;
}
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
index 8abbe6d661e4..9e5b01c29cf5 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -123,8 +123,8 @@ static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)
((params->numberoflines * params->pitch) / PAGE_SIZE);
params->bitspersample = 8;
params->linethreshold = 0;
- params->pagetablelistvirt = 0;
- params->pagetablelistphys = 0;
+ params->pagetablelistvirt = NULL;
+ params->pagetablelistphys = NULL;
params->numpagetableentries = port->hwcfg.buffercount;
/* Allocate the PCI resources, buffers (hard) */
@@ -1054,7 +1054,7 @@ static int fops_release(struct file *file)
struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
{
- struct saa7164_user_buffer *ubuf = 0;
+ struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
u32 crc;
@@ -1334,7 +1334,7 @@ int saa7164_vbi_register(struct saa7164_port *port)
port->v4l_device = saa7164_vbi_alloc(port,
dev->pci, &saa7164_vbi_template, "vbi");
- if (port->v4l_device == NULL) {
+ if (!port->v4l_device) {
printk(KERN_INFO "%s: can't allocate vbi device\n",
dev->name);
result = -ENOMEM;
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 954222bc3458..61f37012b4f8 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -38,7 +38,7 @@
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
#include <media/sh_mobile_ceu.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-mediabus.h>
#include <media/soc_mediabus.h>
@@ -87,7 +87,8 @@
/* per video frame buffer */
struct sh_mobile_ceu_buffer {
- struct videobuf_buffer vb; /* v4l buffer must be first */
+ struct vb2_buffer vb; /* v4l buffer must be first */
+ struct list_head queue;
enum v4l2_mbus_pixelcode code;
};
@@ -99,16 +100,17 @@ struct sh_mobile_ceu_dev {
void __iomem *base;
unsigned long video_limit;
- /* lock used to protect videobuf */
- spinlock_t lock;
+ spinlock_t lock; /* Protects video buffer lists */
struct list_head capture;
- struct videobuf_buffer *active;
+ struct vb2_buffer *active;
+ struct vb2_alloc_ctx *alloc_ctx;
struct sh_mobile_ceu_info *pdata;
u32 cflcr;
enum v4l2_field field;
+ int sequence;
unsigned int image_mode:1;
unsigned int is_16bit:1;
@@ -133,6 +135,11 @@ struct sh_mobile_ceu_cam {
enum v4l2_mbus_pixelcode code;
};
+static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct sh_mobile_ceu_buffer, vb);
+}
+
static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
{
unsigned long flags;
@@ -205,11 +212,11 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
/*
* Videobuf operations
*/
-static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
- unsigned int *count,
- unsigned int *size)
+static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned long sizes[], void *alloc_ctxs[])
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
@@ -218,39 +225,25 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
if (bytes_per_line < 0)
return bytes_per_line;
- *size = bytes_per_line * icd->user_height;
+ *num_planes = 1;
+
+ pcdev->sequence = 0;
+ sizes[0] = bytes_per_line * icd->user_height;
+ alloc_ctxs[0] = pcdev->alloc_ctx;
- if (0 == *count)
+ if (!*count)
*count = 2;
if (pcdev->video_limit) {
- if (PAGE_ALIGN(*size) * *count > pcdev->video_limit)
- *count = pcdev->video_limit / PAGE_ALIGN(*size);
+ if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit)
+ *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
}
- dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->dev.parent, "count=%d, size=%lu\n", *count, sizes[0]);
return 0;
}
-static void free_buffer(struct videobuf_queue *vq,
- struct sh_mobile_ceu_buffer *buf)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->dev.parent;
-
- dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
- &buf->vb, buf->vb.baddr, buf->vb.bsize);
-
- if (in_interrupt())
- BUG();
-
- videobuf_waiton(vq, &buf->vb, 0, 0);
- videobuf_dma_contig_free(vq, &buf->vb);
- dev_dbg(dev, "%s freed\n", __func__);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
@@ -309,7 +302,10 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
bottom2 = CDBCR;
}
- phys_addr_top = videobuf_to_dma_contig(pcdev->active);
+ /* mem_ops->cookie must not be NULL */
+ phys_addr_top = (dma_addr_t)icd->vb2_vidq.mem_ops->cookie(pcdev->
+ active->planes[0].mem_priv);
+
ceu_write(pcdev, top1, phys_addr_top);
if (V4L2_FIELD_NONE != pcdev->field) {
phys_addr_bottom = phys_addr_top + icd->user_width;
@@ -330,87 +326,67 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
}
}
- pcdev->active->state = VIDEOBUF_ACTIVE;
ceu_write(pcdev, CAPSR, 0x1); /* start capture */
return ret;
}
-static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
struct sh_mobile_ceu_buffer *buf;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- int ret;
+ unsigned long size;
if (bytes_per_line < 0)
return bytes_per_line;
- buf = container_of(vb, struct sh_mobile_ceu_buffer, vb);
+ buf = to_ceu_vb(vb);
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
/* Added list head initialization on alloc */
- WARN_ON(!list_empty(&vb->queue));
+ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
#ifdef DEBUG
/*
* This can be useful if you want to see if we actually fill
* the buffer with something
*/
- memset((void *)vb->baddr, 0xaa, vb->bsize);
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
#endif
BUG_ON(NULL == icd->current_fmt);
- if (buf->code != icd->current_fmt->code ||
- vb->width != icd->user_width ||
- vb->height != icd->user_height ||
- vb->field != field) {
- buf->code = icd->current_fmt->code;
- vb->width = icd->user_width;
- vb->height = icd->user_height;
- vb->field = field;
- vb->state = VIDEOBUF_NEEDS_INIT;
- }
+ size = icd->user_height * bytes_per_line;
- vb->size = vb->height * bytes_per_line;
- if (0 != vb->baddr && vb->bsize < vb->size) {
- ret = -EINVAL;
- goto out;
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->dev.parent, "Buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -ENOBUFS;
}
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret)
- goto fail;
- vb->state = VIDEOBUF_PREPARED;
- }
+ vb2_set_plane_payload(vb, 0, size);
return 0;
-fail:
- free_buffer(vq, buf);
-out:
- return ret;
}
-/* Called under spinlock_irqsave(&pcdev->lock, ...) */
-static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ unsigned long flags;
- dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &pcdev->capture);
+ spin_lock_irqsave(&pcdev->lock, flags);
+ list_add_tail(&buf->queue, &pcdev->capture);
if (!pcdev->active) {
/*
@@ -421,13 +397,14 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
pcdev->active = vb;
sh_mobile_ceu_capture(pcdev);
}
+ spin_unlock_irqrestore(&pcdev->lock, flags);
}
-static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
unsigned long flags;
@@ -439,53 +416,60 @@ static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
pcdev->active = NULL;
}
- if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
- !list_empty(&vb->queue)) {
- vb->state = VIDEOBUF_ERROR;
- list_del_init(&vb->queue);
- }
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
spin_unlock_irqrestore(&pcdev->lock, flags);
+}
- free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb));
+static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
+{
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
+ return 0;
}
-static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = {
- .buf_setup = sh_mobile_ceu_videobuf_setup,
- .buf_prepare = sh_mobile_ceu_videobuf_prepare,
- .buf_queue = sh_mobile_ceu_videobuf_queue,
- .buf_release = sh_mobile_ceu_videobuf_release,
+static struct vb2_ops sh_mobile_ceu_videobuf_ops = {
+ .queue_setup = sh_mobile_ceu_videobuf_setup,
+ .buf_prepare = sh_mobile_ceu_videobuf_prepare,
+ .buf_queue = sh_mobile_ceu_videobuf_queue,
+ .buf_cleanup = sh_mobile_ceu_videobuf_release,
+ .buf_init = sh_mobile_ceu_videobuf_init,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
};
static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
{
struct sh_mobile_ceu_dev *pcdev = data;
- struct videobuf_buffer *vb;
- unsigned long flags;
+ struct vb2_buffer *vb;
+ int ret;
- spin_lock_irqsave(&pcdev->lock, flags);
+ spin_lock(&pcdev->lock);
vb = pcdev->active;
if (!vb)
/* Stale interrupt from a released buffer */
goto out;
- list_del_init(&vb->queue);
+ list_del_init(&to_ceu_vb(vb)->queue);
if (!list_empty(&pcdev->capture))
- pcdev->active = list_entry(pcdev->capture.next,
- struct videobuf_buffer, queue);
+ pcdev->active = &list_entry(pcdev->capture.next,
+ struct sh_mobile_ceu_buffer, queue)->vb;
else
pcdev->active = NULL;
- vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ?
- VIDEOBUF_ERROR : VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
- vb->field_count++;
- wake_up(&vb->done);
+ ret = sh_mobile_ceu_capture(pcdev);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ if (!ret) {
+ vb->v4l2_buf.field = pcdev->field;
+ vb->v4l2_buf.sequence = pcdev->sequence++;
+ }
+ vb2_buffer_done(vb, ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
+ spin_unlock(&pcdev->lock);
return IRQ_HANDLED;
}
@@ -529,9 +513,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
/* make sure active buffer is canceled */
spin_lock_irqsave(&pcdev->lock, flags);
if (pcdev->active) {
- list_del(&pcdev->active->queue);
- pcdev->active->state = VIDEOBUF_ERROR;
- wake_up_all(&pcdev->active->done);
+ list_del_init(&to_ceu_vb(pcdev->active)->queue);
+ vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR);
pcdev->active = NULL;
}
spin_unlock_irqrestore(&pcdev->lock, flags);
@@ -686,6 +669,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
ceu_write(pcdev, CAPSR, capsr);
}
+/* Capture is not running, no interrupts, no locking needed */
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
@@ -1364,7 +1348,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
struct device *dev = icd->dev.parent;
struct v4l2_mbus_framefmt mf;
unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
- out_width, out_height, scale_h, scale_v;
+ out_width, out_height;
int interm_width, interm_height;
u32 capsr, cflcr;
int ret;
@@ -1422,10 +1406,6 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
scale_ceu_h = calc_scale(interm_width, &out_width);
scale_ceu_v = calc_scale(interm_height, &out_height);
- /* Calculate camera scales */
- scale_h = calc_generic_scale(cam_rect->width, out_width);
- scale_v = calc_generic_scale(cam_rect->height, out_height);
-
dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
/* Apply CEU scales. */
@@ -1437,8 +1417,8 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
icd->user_width = out_width;
icd->user_height = out_height;
- cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_h) & ~1;
- cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_v) & ~1;
+ cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
+ cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
/* 6. Use CEU cropping to crop to the new window. */
sh_mobile_ceu_set_rect(icd);
@@ -1449,7 +1429,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
icd->user_width, icd->user_height,
cam->ceu_left, cam->ceu_top);
- /* Restore capture */
+ /* Restore capture. The CE bit can be cleared by the hardware */
if (pcdev->active)
capsr |= 1;
capture_restore(pcdev, capsr);
@@ -1726,43 +1706,11 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
return ret;
}
-static int sh_mobile_ceu_reqbufs(struct soc_camera_device *icd,
- struct v4l2_requestbuffers *p)
-{
- int i;
-
- /*
- * This is for locking debugging only. I removed spinlocks and now I
- * check whether .prepare is ever called on a linked buffer, or whether
- * a dma IRQ can occur for an in-work or unlinked buffer. Until now
- * it hadn't triggered
- */
- for (i = 0; i < p->count; i++) {
- struct sh_mobile_ceu_buffer *buf;
-
- buf = container_of(icd->vb_vidq.bufs[i],
- struct sh_mobile_ceu_buffer, vb);
- INIT_LIST_HEAD(&buf->vb.queue);
- }
-
- return 0;
-}
-
static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
- struct sh_mobile_ceu_buffer *buf;
-
- buf = list_entry(icd->vb_vidq.stream.next,
- struct sh_mobile_ceu_buffer, vb.stream);
-
- poll_wait(file, &buf->vb.done, pt);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- return POLLIN|POLLRDNORM;
-
- return 0;
+ return vb2_poll(&icd->vb2_vidq, file, pt);
}
static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
@@ -1774,19 +1722,17 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
return 0;
}
-static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
- struct soc_camera_device *icd)
+static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- videobuf_queue_dma_contig_init(q,
- &sh_mobile_ceu_videobuf_ops,
- icd->dev.parent, &pcdev->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- pcdev->field,
- sizeof(struct sh_mobile_ceu_buffer),
- icd, &icd->video_lock);
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &sh_mobile_ceu_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
+
+ return vb2_queue_init(q);
}
static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
@@ -1850,11 +1796,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.try_fmt = sh_mobile_ceu_try_fmt,
.set_ctrl = sh_mobile_ceu_set_ctrl,
.get_ctrl = sh_mobile_ceu_get_ctrl,
- .reqbufs = sh_mobile_ceu_reqbufs,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
.set_bus_param = sh_mobile_ceu_set_bus_param,
- .init_videobuf = sh_mobile_ceu_init_videobuf,
+ .init_videobuf2 = sh_mobile_ceu_init_videobuf,
.controls = sh_mobile_ceu_controls,
.num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
};
@@ -2005,12 +1950,20 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
}
}
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto exit_module_put;
+ }
+
err = soc_camera_host_register(&pcdev->ici);
if (err)
- goto exit_module_put;
+ goto exit_free_ctx;
return 0;
+exit_free_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
exit_module_put:
if (csi2 && csi2->driver)
module_put(csi2->driver->owner);
@@ -2041,6 +1994,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
dma_release_declared_memory(&pdev->dev);
iounmap(pcdev->base);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
if (csi2 && csi2->driver)
module_put(csi2->driver->owner);
kfree(pcdev);
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 84984f64b234..ce56a1cdbf0a 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -1430,9 +1430,9 @@ static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
-static DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
-static DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
-static DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
+static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green);
+static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue);
+static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red);
static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL);
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index a66811b43710..fa80a4a914d4 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -34,6 +34,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
#include <media/soc_mediabus.h>
/* Default to VGA resolution */
@@ -191,6 +192,15 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
return v4l2_subdev_call(sd, core, s_std, *a);
}
+static int soc_camera_enum_fsizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+ return ici->ops->enum_fsizes(icd, fsize);
+}
+
static int soc_camera_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
@@ -203,11 +213,16 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
if (icd->streamer && icd->streamer != file)
return -EBUSY;
- ret = videobuf_reqbufs(&icd->vb_vidq, p);
- if (ret < 0)
- return ret;
+ if (ici->ops->init_videobuf) {
+ ret = videobuf_reqbufs(&icd->vb_vidq, p);
+ if (ret < 0)
+ return ret;
+
+ ret = ici->ops->reqbufs(icd, p);
+ } else {
+ ret = vb2_reqbufs(&icd->vb2_vidq, p);
+ }
- ret = ici->ops->reqbufs(icd, p);
if (!ret && !icd->streamer)
icd->streamer = file;
@@ -218,36 +233,48 @@ static int soc_camera_querybuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
- return videobuf_querybuf(&icd->vb_vidq, p);
+ if (ici->ops->init_videobuf)
+ return videobuf_querybuf(&icd->vb_vidq, p);
+ else
+ return vb2_querybuf(&icd->vb2_vidq, p);
}
static int soc_camera_qbuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
if (icd->streamer != file)
return -EBUSY;
- return videobuf_qbuf(&icd->vb_vidq, p);
+ if (ici->ops->init_videobuf)
+ return videobuf_qbuf(&icd->vb_vidq, p);
+ else
+ return vb2_qbuf(&icd->vb2_vidq, p);
}
static int soc_camera_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *p)
{
struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
if (icd->streamer != file)
return -EBUSY;
- return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ if (ici->ops->init_videobuf)
+ return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ else
+ return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK);
}
/* Always entered with .video_lock held */
@@ -363,8 +390,9 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
icd->user_width = pix->width;
icd->user_height = pix->height;
icd->colorspace = pix->colorspace;
- icd->vb_vidq.field =
- icd->field = pix->field;
+ icd->field = pix->field;
+ if (ici->ops->init_videobuf)
+ icd->vb_vidq.field = pix->field;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
@@ -444,7 +472,13 @@ static int soc_camera_open(struct file *file)
if (ret < 0)
goto esfmt;
- ici->ops->init_videobuf(&icd->vb_vidq, icd);
+ if (ici->ops->init_videobuf) {
+ ici->ops->init_videobuf(&icd->vb_vidq, icd);
+ } else {
+ ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd);
+ if (ret < 0)
+ goto einitvb;
+ }
}
file->private_data = icd;
@@ -456,6 +490,7 @@ static int soc_camera_open(struct file *file)
* First four errors are entered with the .video_lock held
* and use_count == 1
*/
+einitvb:
esfmt:
pm_runtime_disable(&icd->vdev->dev);
eresume:
@@ -482,6 +517,8 @@ static int soc_camera_close(struct file *file)
pm_runtime_disable(&icd->vdev->dev);
ici->ops->remove(icd);
+ if (ici->ops->init_videobuf2)
+ vb2_queue_release(&icd->vb2_vidq);
soc_camera_power_set(icd, icl, 0);
}
@@ -510,6 +547,7 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf,
static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
{
struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int err;
dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
@@ -517,7 +555,10 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
if (icd->streamer != file)
return -EBUSY;
- err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
+ if (ici->ops->init_videobuf)
+ err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
+ else
+ err = vb2_mmap(&icd->vb2_vidq, vma);
dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
@@ -535,7 +576,7 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
if (icd->streamer != file)
return -EBUSY;
- if (list_empty(&icd->vb_vidq.stream)) {
+ if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) {
dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
return POLLERR;
}
@@ -543,6 +584,20 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
return ici->ops->poll(file, pt);
}
+void soc_camera_lock(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = vb2_get_drv_priv(vq);
+ mutex_lock(&icd->video_lock);
+}
+EXPORT_SYMBOL(soc_camera_lock);
+
+void soc_camera_unlock(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = vb2_get_drv_priv(vq);
+ mutex_unlock(&icd->video_lock);
+}
+EXPORT_SYMBOL(soc_camera_unlock);
+
static struct v4l2_file_operations soc_camera_fops = {
.owner = THIS_MODULE,
.open = soc_camera_open,
@@ -606,7 +661,7 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
pix->width = icd->user_width;
pix->height = icd->user_height;
- pix->field = icd->vb_vidq.field;
+ pix->field = icd->field;
pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
icd->current_fmt->host_fmt);
@@ -635,6 +690,7 @@ static int soc_camera_streamon(struct file *file, void *priv,
enum v4l2_buf_type i)
{
struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
@@ -646,10 +702,14 @@ static int soc_camera_streamon(struct file *file, void *priv,
if (icd->streamer != file)
return -EBUSY;
- v4l2_subdev_call(sd, video, s_stream, 1);
-
/* This calls buf_queue from host driver's videobuf_queue_ops */
- ret = videobuf_streamon(&icd->vb_vidq);
+ if (ici->ops->init_videobuf)
+ ret = videobuf_streamon(&icd->vb_vidq);
+ else
+ ret = vb2_streamon(&icd->vb2_vidq, i);
+
+ if (!ret)
+ v4l2_subdev_call(sd, video, s_stream, 1);
return ret;
}
@@ -659,6 +719,7 @@ static int soc_camera_streamoff(struct file *file, void *priv,
{
struct soc_camera_device *icd = file->private_data;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -672,7 +733,10 @@ static int soc_camera_streamoff(struct file *file, void *priv,
* This calls buf_release from host driver's videobuf_queue_ops for all
* remaining buffers. When the last buffer is freed, stop capture
*/
- videobuf_streamoff(&icd->vb_vidq);
+ if (ici->ops->init_videobuf)
+ videobuf_streamoff(&icd->vb_vidq);
+ else
+ vb2_streamoff(&icd->vb2_vidq, i);
v4l2_subdev_call(sd, video, s_stream, 0);
@@ -1175,6 +1239,31 @@ static int default_s_parm(struct soc_camera_device *icd,
return v4l2_subdev_call(sd, video, s_parm, parm);
}
+static int default_enum_fsizes(struct soc_camera_device *icd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int ret;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ __u32 pixfmt = fsize->pixel_format;
+ struct v4l2_frmsizeenum fsize_mbus = *fsize;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate)
+ return -EINVAL;
+ /* map xlate-code to pixel_format, sensor only handle xlate-code*/
+ fsize_mbus.pixel_format = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus);
+ if (ret < 0)
+ return ret;
+
+ *fsize = fsize_mbus;
+ fsize->pixel_format = pixfmt;
+
+ return 0;
+}
+
static void soc_camera_device_init(struct device *dev, void *pdata)
{
dev->platform_data = pdata;
@@ -1192,8 +1281,9 @@ int soc_camera_host_register(struct soc_camera_host *ici)
!ici->ops->set_fmt ||
!ici->ops->set_bus_param ||
!ici->ops->querycap ||
- !ici->ops->init_videobuf ||
- !ici->ops->reqbufs ||
+ ((!ici->ops->init_videobuf ||
+ !ici->ops->reqbufs) &&
+ !ici->ops->init_videobuf2) ||
!ici->ops->add ||
!ici->ops->remove ||
!ici->ops->poll ||
@@ -1210,6 +1300,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
ici->ops->set_parm = default_s_parm;
if (!ici->ops->get_parm)
ici->ops->get_parm = default_g_parm;
+ if (!ici->ops->enum_fsizes)
+ ici->ops->enum_fsizes = default_enum_fsizes;
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
@@ -1317,6 +1409,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_g_input = soc_camera_g_input,
.vidioc_s_input = soc_camera_s_input,
.vidioc_s_std = soc_camera_s_std,
+ .vidioc_enum_framesizes = soc_camera_enum_fsizes,
.vidioc_reqbufs = soc_camera_reqbufs,
.vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
.vidioc_querybuf = soc_camera_querybuf,
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index 91391214c682..73b4138709e4 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -132,6 +132,20 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = {
},
};
+int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf)
+{
+ switch (mf->packing) {
+ case SOC_MBUS_PACKING_NONE:
+ case SOC_MBUS_PACKING_EXTEND16:
+ return 1;
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ case SOC_MBUS_PACKING_2X8_PADLO:
+ return 2;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
+
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
{
switch (mf->packing) {
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index dfc4dd7c5097..286ec7e7062a 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -31,6 +31,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("tlv320aic23b driver");
MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil");
@@ -41,7 +42,7 @@ MODULE_LICENSE("GPL");
struct tlv320aic23b_state {
struct v4l2_subdev sd;
- u8 muted;
+ struct v4l2_ctrl_handler hdl;
};
static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
@@ -49,6 +50,11 @@ static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct tlv320aic23b_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd;
+}
+
static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -85,44 +91,44 @@ static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return 0;
}
-static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct tlv320aic23b_state *state = to_state(sd);
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- return 0;
-}
-
-static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct tlv320aic23b_state *state = to_state(sd);
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
- /* set gain on both channels to +3.0 dB */
- if (!state->muted)
- tlv320aic23b_write(sd, 0, 0x119);
- return 0;
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
+ /* set gain on both channels to +3.0 dB */
+ if (!ctrl->val)
+ tlv320aic23b_write(sd, 0, 0x119);
+ return 0;
+ }
+ return -EINVAL;
}
static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
{
struct tlv320aic23b_state *state = to_state(sd);
- v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active");
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = {
+ .s_ctrl = tlv320aic23b_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
.log_status = tlv320aic23b_log_status,
- .g_ctrl = tlv320aic23b_g_ctrl,
- .s_ctrl = tlv320aic23b_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = {
@@ -161,7 +167,6 @@ static int tlv320aic23b_probe(struct i2c_client *client,
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops);
- state->muted = 0;
/* Initialize tlv320aic23b */
@@ -177,15 +182,30 @@ static int tlv320aic23b_probe(struct i2c_client *client,
tlv320aic23b_write(sd, 8, 0x000);
/* activate digital interface */
tlv320aic23b_write(sd, 9, 0x001);
+
+ v4l2_ctrl_handler_init(&state->hdl, 1);
+ v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->hdl);
return 0;
}
static int tlv320aic23b_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tlv320aic23b_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 1cec1224913f..9363ed91a4cb 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -1,7 +1,17 @@
/*
- *
* i2c tv tuner chip device driver
* core core, i.e. kernel interfaces, registering and so on
+ *
+ * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer
+ *
+ * Copyright(c) 2005-2011 by Mauro Carvalho Chehab
+ * - Added support for a separate Radio tuner
+ * - Major rework and cleanups at the code
+ *
+ * This driver supports many devices and the idea is to let the driver
+ * detect which device is present. So rather than listing all supported
+ * devices here, we pretend to support a single, fake device type that will
+ * handle both radio and analog TV tuning.
*/
#include <linux/module.h>
@@ -32,9 +42,111 @@
#define UNSET (-1U)
-#define PREFIX t->i2c->driver->driver.name
+#define PREFIX (t->i2c->driver->driver.name)
+
+/*
+ * Driver modprobe parameters
+ */
+
+/* insmod options used at init time => read/only */
+static unsigned int addr;
+static unsigned int no_autodetect;
+static unsigned int show_i2c;
+
+module_param(addr, int, 0444);
+module_param(no_autodetect, int, 0444);
+module_param(show_i2c, int, 0444);
+
+/* insmod options used at runtime => read/write */
+static int tuner_debug;
+static unsigned int tv_range[2] = { 44, 958 };
+static unsigned int radio_range[2] = { 65, 108 };
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+module_param_named(debug, tuner_debug, int, 0644);
+module_param_array(tv_range, int, NULL, 0644);
+module_param_array(radio_range, int, NULL, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+
+/*
+ * Static vars
+ */
+
+static LIST_HEAD(tuner_list);
+static const struct v4l2_subdev_ops tuner_ops;
+
+/*
+ * Debug macros
+ */
+
+#define tuner_warn(fmt, arg...) do { \
+ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
-/** This macro allows us to probe dynamically, avoiding static links */
+#define tuner_dbg(fmt, arg...) do { \
+ if (tuner_debug) \
+ printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+/*
+ * Internal struct used inside the driver
+ */
+
+struct tuner {
+ /* device */
+ struct dvb_frontend fe;
+ struct i2c_client *i2c;
+ struct v4l2_subdev sd;
+ struct list_head list;
+
+ /* keep track of the current settings */
+ v4l2_std_id std;
+ unsigned int tv_freq;
+ unsigned int radio_freq;
+ unsigned int audmode;
+
+ enum v4l2_tuner_type mode;
+ unsigned int mode_mask; /* Combination of allowable modes */
+
+ bool standby; /* Standby mode */
+
+ unsigned int type; /* chip type id */
+ unsigned int config;
+ const char *name;
+};
+
+/*
+ * Function prototypes
+ */
+
+static void set_tv_freq(struct i2c_client *c, unsigned int freq);
+static void set_radio_freq(struct i2c_client *c, unsigned int freq);
+
+/*
+ * tuner attach/detach logic
+ */
+
+/* This macro allows us to probe dynamically, avoiding static links */
#ifdef CONFIG_MEDIA_ATTACH
#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
int __r = -EINVAL; \
@@ -74,92 +186,15 @@ static void tuner_detach(struct dvb_frontend *fe)
}
#endif
-struct tuner {
- /* device */
- struct dvb_frontend fe;
- struct i2c_client *i2c;
- struct v4l2_subdev sd;
- struct list_head list;
- unsigned int using_v4l2:1;
-
- /* keep track of the current settings */
- v4l2_std_id std;
- unsigned int tv_freq;
- unsigned int radio_freq;
- unsigned int audmode;
-
- unsigned int mode;
- unsigned int mode_mask; /* Combination of allowable modes */
-
- unsigned int type; /* chip type id */
- unsigned int config;
- const char *name;
-};
static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
{
return container_of(sd, struct tuner, sd);
}
-
-/* insmod options used at init time => read/only */
-static unsigned int addr;
-static unsigned int no_autodetect;
-static unsigned int show_i2c;
-
-/* insmod options used at runtime => read/write */
-static int tuner_debug;
-
-#define tuner_warn(fmt, arg...) do { \
- printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
- i2c_adapter_id(t->i2c->adapter), \
- t->i2c->addr, ##arg); \
- } while (0)
-
-#define tuner_info(fmt, arg...) do { \
- printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
- i2c_adapter_id(t->i2c->adapter), \
- t->i2c->addr, ##arg); \
- } while (0)
-
-#define tuner_err(fmt, arg...) do { \
- printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
- i2c_adapter_id(t->i2c->adapter), \
- t->i2c->addr, ##arg); \
- } while (0)
-
-#define tuner_dbg(fmt, arg...) do { \
- if (tuner_debug) \
- printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
- i2c_adapter_id(t->i2c->adapter), \
- t->i2c->addr, ##arg); \
- } while (0)
-
-/* ------------------------------------------------------------------------ */
-
-static unsigned int tv_range[2] = { 44, 958 };
-static unsigned int radio_range[2] = { 65, 108 };
-
-static char pal[] = "--";
-static char secam[] = "--";
-static char ntsc[] = "-";
-
-
-module_param(addr, int, 0444);
-module_param(no_autodetect, int, 0444);
-module_param(show_i2c, int, 0444);
-module_param_named(debug,tuner_debug, int, 0644);
-module_param_string(pal, pal, sizeof(pal), 0644);
-module_param_string(secam, secam, sizeof(secam), 0644);
-module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
-module_param_array(tv_range, int, NULL, 0644);
-module_param_array(radio_range, int, NULL, 0644);
-
-MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
-MODULE_LICENSE("GPL");
-
-/* ---------------------------------------------------------------------- */
+/*
+ * struct analog_demod_ops callbacks
+ */
static void fe_set_params(struct dvb_frontend *fe,
struct analog_parameters *params)
@@ -215,102 +250,25 @@ static struct analog_demod_ops tuner_analog_ops = {
.tuner_status = tuner_status
};
-/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
-static void set_tv_freq(struct i2c_client *c, unsigned int freq)
-{
- struct tuner *t = to_tuner(i2c_get_clientdata(c));
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
- struct analog_parameters params = {
- .mode = t->mode,
- .audmode = t->audmode,
- .std = t->std
- };
-
- if (t->type == UNSET) {
- tuner_warn ("tuner type not set\n");
- return;
- }
- if (NULL == analog_ops->set_params) {
- tuner_warn ("Tuner has no way to set tv freq\n");
- return;
- }
- if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
- tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
- freq / 16, freq % 16 * 100 / 16, tv_range[0],
- tv_range[1]);
- /* V4L2 spec: if the freq is not possible then the closest
- possible value should be selected */
- if (freq < tv_range[0] * 16)
- freq = tv_range[0] * 16;
- else
- freq = tv_range[1] * 16;
- }
- params.frequency = freq;
-
- analog_ops->set_params(&t->fe, &params);
-}
-
-static void set_radio_freq(struct i2c_client *c, unsigned int freq)
-{
- struct tuner *t = to_tuner(i2c_get_clientdata(c));
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
- struct analog_parameters params = {
- .mode = t->mode,
- .audmode = t->audmode,
- .std = t->std
- };
-
- if (t->type == UNSET) {
- tuner_warn ("tuner type not set\n");
- return;
- }
- if (NULL == analog_ops->set_params) {
- tuner_warn ("tuner has no way to set radio frequency\n");
- return;
- }
- if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
- tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
- freq / 16000, freq % 16000 * 100 / 16000,
- radio_range[0], radio_range[1]);
- /* V4L2 spec: if the freq is not possible then the closest
- possible value should be selected */
- if (freq < radio_range[0] * 16000)
- freq = radio_range[0] * 16000;
- else
- freq = radio_range[1] * 16000;
- }
- params.frequency = freq;
-
- analog_ops->set_params(&t->fe, &params);
-}
-
-static void set_freq(struct i2c_client *c, unsigned long freq)
-{
- struct tuner *t = to_tuner(i2c_get_clientdata(c));
-
- switch (t->mode) {
- case V4L2_TUNER_RADIO:
- tuner_dbg("radio freq set to %lu.%02lu\n",
- freq / 16000, freq % 16000 * 100 / 16000);
- set_radio_freq(c, freq);
- t->radio_freq = freq;
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- tuner_dbg("tv freq set to %lu.%02lu\n",
- freq / 16, freq % 16 * 100 / 16);
- set_tv_freq(c, freq);
- t->tv_freq = freq;
- break;
- default:
- tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
- }
-}
-
-static struct xc5000_config xc5000_cfg;
+/*
+ * Functions to select between radio and TV and tuner probe/remove functions
+ */
+/**
+ * set_type - Sets the tuner type for a given device
+ *
+ * @c: i2c_client descriptoy
+ * @type: type of the tuner (e. g. tuner number)
+ * @new_mode_mask: Indicates if tuner supports TV and/or Radio
+ * @new_config: an optional parameter ranging from 0-255 used by
+ a few tuners to adjust an internal parameter,
+ like LNA mode
+ * @tuner_callback: an optional function to be called when switching
+ * to analog mode
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure. It contains several per-tuner initialization "magic"
+ */
static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
int (*tuner_callback) (void *dev, int component, int cmd, int arg))
@@ -322,7 +280,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
int tune_now = 1;
if (type == UNSET || type == TUNER_ABSENT) {
- tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
+ tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr);
return;
}
@@ -334,12 +292,6 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->fe.callback = tuner_callback;
}
- if (t->mode == T_UNINITIALIZED) {
- tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
-
- return;
- }
-
/* discard private data, in case set_type() was previously called */
tuner_detach(&t->fe);
t->fe.analog_demod_priv = NULL;
@@ -414,9 +366,12 @@ static void set_type(struct i2c_client *c, unsigned int type,
break;
case TUNER_XC5000:
{
- xc5000_cfg.i2c_address = t->i2c->addr;
- /* if_khz will be set when the digital dvb_attach() occurs */
- xc5000_cfg.if_khz = 0;
+ struct xc5000_config xc5000_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* if_khz will be set at dvb_attach() */
+ .if_khz = 0,
+ };
+
if (!dvb_attach(xc5000_attach,
&t->fe, t->i2c->adapter, &xc5000_cfg))
goto attach_failed;
@@ -459,8 +414,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
tuner_dbg("type set to %s\n", t->name);
- if (t->mode_mask == T_UNINITIALIZED)
- t->mode_mask = new_mode_mask;
+ t->mode_mask = new_mode_mask;
/* Some tuners require more initialization setup before use,
such as firmware download or device calibration.
@@ -468,9 +422,12 @@ static void set_type(struct i2c_client *c, unsigned int type,
FIXME: better to move set_freq to the tuner code. This is needed
on analog tuners for PLL to properly work
*/
- if (tune_now)
- set_freq(c, (V4L2_TUNER_RADIO == t->mode) ?
- t->radio_freq : t->tv_freq);
+ if (tune_now) {
+ if (V4L2_TUNER_RADIO == t->mode)
+ set_radio_freq(c, t->radio_freq);
+ else
+ set_tv_freq(c, t->tv_freq);
+ }
tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
c->adapter->name, c->driver->driver.name, c->addr << 1, type,
@@ -480,86 +437,426 @@ static void set_type(struct i2c_client *c, unsigned int type,
attach_failed:
tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
t->type = TUNER_ABSENT;
- t->mode_mask = T_UNINITIALIZED;
return;
}
-/*
- * This function apply tuner config to tuner specified
- * by tun_setup structure. I addr is unset, then admin status
- * and tun addr status is more precise then current status,
- * it's applied. Otherwise status and type are applied only to
- * tuner with exactly the same addr.
-*/
-
-static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
+/**
+ * tuner_s_type_addr - Sets the tuner type for a device
+ *
+ * @sd: subdev descriptor
+ * @tun_setup: type to be associated to a given tuner i2c address
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure.
+ * If tuner I2C address is UNSET, then it will only set the device
+ * if the tuner supports the mode specified in the call.
+ * If the address is specified, the change will be applied only if
+ * tuner I2C address matches.
+ * The call can change the tuner number and the tuner mode.
+ */
+static int tuner_s_type_addr(struct v4l2_subdev *sd,
+ struct tuner_setup *tun_setup)
{
- struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
- if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
- (t->mode_mask & tun_setup->mode_mask))) ||
- (tun_setup->addr == c->addr)) {
- set_type(c, tun_setup->type, tun_setup->mode_mask,
- tun_setup->config, tun_setup->tuner_callback);
+ tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+ tun_setup->type,
+ tun_setup->addr,
+ tun_setup->mode_mask,
+ tun_setup->config);
+
+ if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
+ (t->mode_mask & tun_setup->mode_mask))) ||
+ (tun_setup->addr == c->addr)) {
+ set_type(c, tun_setup->type, tun_setup->mode_mask,
+ tun_setup->config, tun_setup->tuner_callback);
} else
tuner_dbg("set addr discarded for type %i, mask %x. "
"Asked to change tuner at addr 0x%02x, with mask %x\n",
t->type, t->mode_mask,
tun_setup->addr, tun_setup->mode_mask);
+
+ return 0;
}
-static inline int check_mode(struct tuner *t, char *cmd)
+/**
+ * tuner_s_config - Sets tuner configuration
+ *
+ * @sd: subdev descriptor
+ * @cfg: tuner configuration
+ *
+ * Calls tuner set_config() private function to set some tuner-internal
+ * parameters
+ */
+static int tuner_s_config(struct v4l2_subdev *sd,
+ const struct v4l2_priv_tun_config *cfg)
{
- if ((1 << t->mode & t->mode_mask) == 0) {
- return -EINVAL;
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (t->type != cfg->tuner)
+ return 0;
+
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ return 0;
}
- switch (t->mode) {
- case V4L2_TUNER_RADIO:
- tuner_dbg("Cmd %s accepted for radio\n", cmd);
- break;
- case V4L2_TUNER_ANALOG_TV:
- tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
- break;
- case V4L2_TUNER_DIGITAL_TV:
- tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
- break;
+ tuner_dbg("Tuner frontend module has no way to set config\n");
+ return 0;
+}
+
+/**
+ * tuner_lookup - Seek for tuner adapters
+ *
+ * @adap: i2c_adapter struct
+ * @radio: pointer to be filled if the adapter is radio
+ * @tv: pointer to be filled if the adapter is TV
+ *
+ * Search for existing radio and/or TV tuners on the given I2C adapter,
+ * discarding demod-only adapters (tda9887).
+ *
+ * Note that when this function is called from tuner_probe you can be
+ * certain no other devices will be added/deleted at the same time, I2C
+ * core protects against that.
+ */
+static void tuner_lookup(struct i2c_adapter *adap,
+ struct tuner **radio, struct tuner **tv)
+{
+ struct tuner *pos;
+
+ *radio = NULL;
+ *tv = NULL;
+
+ list_for_each_entry(pos, &tuner_list, list) {
+ int mode_mask;
+
+ if (pos->i2c->adapter != adap ||
+ strcmp(pos->i2c->driver->driver.name, "tuner"))
+ continue;
+
+ mode_mask = pos->mode_mask;
+ if (*radio == NULL && mode_mask == T_RADIO)
+ *radio = pos;
+ /* Note: currently TDA9887 is the only demod-only
+ device. If other devices appear then we need to
+ make this test more general. */
+ else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
+ (pos->mode_mask & T_ANALOG_TV))
+ *tv = pos;
+ }
+}
+
+/**
+ *tuner_probe - Probes the existing tuners on an I2C bus
+ *
+ * @client: i2c_client descriptor
+ * @id: not used
+ *
+ * This routine probes for tuners at the expected I2C addresses. On most
+ * cases, if a device answers to a given I2C address, it assumes that the
+ * device is a tuner. On a few cases, however, an additional logic is needed
+ * to double check if the device is really a tuner, or to identify the tuner
+ * type, like on tea5767/5761 devices.
+ *
+ * During client attach, set_type is called by adapter's attach_inform callback.
+ * set_type must then be completed by tuner_probe.
+ */
+static int tuner_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tuner *t;
+ struct tuner *radio;
+ struct tuner *tv;
+
+ t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
+ if (NULL == t)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
+ t->i2c = client;
+ t->name = "(tuner unset)";
+ t->type = UNSET;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ t->standby = 1;
+ t->radio_freq = 87.5 * 16000; /* Initial freq range */
+ t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */
+
+ if (show_i2c) {
+ unsigned char buffer[16];
+ int i, rc;
+
+ memset(buffer, 0, sizeof(buffer));
+ rc = i2c_master_recv(client, buffer, sizeof(buffer));
+ tuner_info("I2C RECV = ");
+ for (i = 0; i < rc; i++)
+ printk(KERN_CONT "%02x ", buffer[i]);
+ printk("\n");
+ }
+
+ /* autodetection code based on the i2c addr */
+ if (!no_autodetect) {
+ switch (client->addr) {
+ case 0x10:
+ if (tuner_symbol_probe(tea5761_autodetection,
+ t->i2c->adapter,
+ t->i2c->addr) >= 0) {
+ t->type = TUNER_TEA5761;
+ t->mode_mask = T_RADIO;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ kfree(t);
+ return -ENODEV;
+ case 0x42:
+ case 0x43:
+ case 0x4a:
+ case 0x4b:
+ /* If chip is not tda8290, don't register.
+ since it can be tda9887*/
+ if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+ t->i2c->addr) >= 0) {
+ tuner_dbg("tda829x detected\n");
+ } else {
+ /* Default is being tda9887 */
+ t->type = TUNER_TDA9887;
+ t->mode_mask = T_RADIO | T_ANALOG_TV;
+ goto register_client;
+ }
+ break;
+ case 0x60:
+ if (tuner_symbol_probe(tea5767_autodetection,
+ t->i2c->adapter, t->i2c->addr)
+ >= 0) {
+ t->type = TUNER_TEA5767;
+ t->mode_mask = T_RADIO;
+ /* Sets freq to FM range */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ break;
+ }
+ }
+
+ /* Initializes only the first TV tuner on this adapter. Why only the
+ first? Because there are some devices (notably the ones with TI
+ tuners) that have more than one i2c address for the *same* device.
+ Experience shows that, except for just one case, the first
+ address is the right one. The exception is a Russian tuner
+ (ACORP_Y878F). So, the desired behavior is just to enable the
+ first found TV tuner. */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv == NULL) {
+ t->mode_mask = T_ANALOG_TV;
+ if (radio == NULL)
+ t->mode_mask |= T_RADIO;
+ tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
+ }
+
+ /* Should be just before return */
+register_client:
+ /* Sets a default mode */
+ if (t->mode_mask & T_ANALOG_TV)
+ t->mode = V4L2_TUNER_ANALOG_TV;
+ else
+ t->mode = V4L2_TUNER_RADIO;
+ set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
+ list_add_tail(&t->list, &tuner_list);
+
+ tuner_info("Tuner %d found with type(s)%s%s.\n",
+ t->type,
+ t->mode_mask & T_RADIO ? " Radio" : "",
+ t->mode_mask & T_ANALOG_TV ? " TV" : "");
+ return 0;
+}
+
+/**
+ * tuner_remove - detaches a tuner
+ *
+ * @client: i2c_client descriptor
+ */
+
+static int tuner_remove(struct i2c_client *client)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(client));
+
+ v4l2_device_unregister_subdev(&t->sd);
+ tuner_detach(&t->fe);
+ t->fe.analog_demod_priv = NULL;
+
+ list_del(&t->list);
+ kfree(t);
+ return 0;
+}
+
+/*
+ * Functions to switch between Radio and TV
+ *
+ * A few cards have a separate I2C tuner for radio. Those routines
+ * take care of switching between TV/Radio mode, filtering only the
+ * commands that apply to the Radio or TV tuner.
+ */
+
+/**
+ * check_mode - Verify if tuner supports the requested mode
+ * @t: a pointer to the module's internal struct_tuner
+ *
+ * This function checks if the tuner is capable of tuning analog TV,
+ * digital TV or radio, depending on what the caller wants. If the
+ * tuner can't support that mode, it returns -EINVAL. Otherwise, it
+ * returns 0.
+ * This function is needed for boards that have a separate tuner for
+ * radio (like devices with tea5767).
+ */
+static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode)
+{
+ if ((1 << mode & t->mode_mask) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * set_mode_freq - Switch tuner to other mode.
+ * @client: struct i2c_client pointer
+ * @t: a pointer to the module's internal struct_tuner
+ * @mode: enum v4l2_type (radio or TV)
+ * @freq: frequency to set (0 means to use the previous one)
+ *
+ * If tuner doesn't support the needed mode (radio or TV), prints a
+ * debug message and returns -EINVAL, changing its state to standby.
+ * Otherwise, changes the state and sets frequency to the last value, if
+ * the tuner can sleep or if it supports both Radio and TV.
+ */
+static int set_mode_freq(struct i2c_client *client, struct tuner *t,
+ enum v4l2_tuner_type mode, unsigned int freq)
+{
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (mode != t->mode) {
+ if (check_mode(t, mode) == -EINVAL) {
+ tuner_dbg("Tuner doesn't support mode %d. "
+ "Putting tuner to sleep\n", mode);
+ t->standby = true;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return -EINVAL;
+ }
+ t->mode = mode;
+ tuner_dbg("Changing to mode %d\n", mode);
}
+ if (t->mode == V4L2_TUNER_RADIO) {
+ if (freq)
+ t->radio_freq = freq;
+ set_radio_freq(client, t->radio_freq);
+ } else {
+ if (freq)
+ t->tv_freq = freq;
+ set_tv_freq(client, t->tv_freq);
+ }
+
return 0;
}
-/* get more precise norm info from insmod option */
+/*
+ * Functions that are specific for TV mode
+ */
+
+/**
+ * set_tv_freq - Set tuner frequency, freq in Units of 62.5 kHz = 1/16MHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
+
+ if (t->type == UNSET) {
+ tuner_warn("tuner type not set\n");
+ return;
+ }
+ if (NULL == analog_ops->set_params) {
+ tuner_warn("Tuner has no way to set tv freq\n");
+ return;
+ }
+ if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
+ tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n",
+ freq / 16, freq % 16 * 100 / 16, tv_range[0],
+ tv_range[1]);
+ /* V4L2 spec: if the freq is not possible then the closest
+ possible value should be selected */
+ if (freq < tv_range[0] * 16)
+ freq = tv_range[0] * 16;
+ else
+ freq = tv_range[1] * 16;
+ }
+ params.frequency = freq;
+ tuner_dbg("tv freq set to %d.%02d\n",
+ freq / 16, freq % 16 * 100 / 16);
+ t->tv_freq = freq;
+ t->standby = false;
+
+ analog_ops->set_params(&t->fe, &params);
+}
+
+/**
+ * tuner_fixup_std - force a given video standard variant
+ *
+ * @t: tuner internal struct
+ *
+ * A few devices or drivers have problem to detect some standard variations.
+ * On other operational systems, the drivers generally have a per-country
+ * code, and some logic to apply per-country hacks. V4L2 API doesn't provide
+ * such hacks. Instead, it relies on a proper video standard selection from
+ * the userspace application. However, as some apps are buggy, not allowing
+ * to distinguish all video standard variations, a modprobe parameter can
+ * be used to force a video standard match.
+ */
static int tuner_fixup_std(struct tuner *t)
{
if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
switch (pal[0]) {
case '6':
- tuner_dbg ("insmod fixup: PAL => PAL-60\n");
+ tuner_dbg("insmod fixup: PAL => PAL-60\n");
t->std = V4L2_STD_PAL_60;
break;
case 'b':
case 'B':
case 'g':
case 'G':
- tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
+ tuner_dbg("insmod fixup: PAL => PAL-BG\n");
t->std = V4L2_STD_PAL_BG;
break;
case 'i':
case 'I':
- tuner_dbg ("insmod fixup: PAL => PAL-I\n");
+ tuner_dbg("insmod fixup: PAL => PAL-I\n");
t->std = V4L2_STD_PAL_I;
break;
case 'd':
case 'D':
case 'k':
case 'K':
- tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
+ tuner_dbg("insmod fixup: PAL => PAL-DK\n");
t->std = V4L2_STD_PAL_DK;
break;
case 'M':
case 'm':
- tuner_dbg ("insmod fixup: PAL => PAL-M\n");
+ tuner_dbg("insmod fixup: PAL => PAL-M\n");
t->std = V4L2_STD_PAL_M;
break;
case 'N':
@@ -568,7 +865,7 @@ static int tuner_fixup_std(struct tuner *t)
tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
t->std = V4L2_STD_PAL_Nc;
} else {
- tuner_dbg ("insmod fixup: PAL => PAL-N\n");
+ tuner_dbg("insmod fixup: PAL => PAL-N\n");
t->std = V4L2_STD_PAL_N;
}
break;
@@ -576,7 +873,7 @@ static int tuner_fixup_std(struct tuner *t)
/* default parameter, do nothing */
break;
default:
- tuner_warn ("pal= argument not recognised\n");
+ tuner_warn("pal= argument not recognised\n");
break;
}
}
@@ -589,22 +886,24 @@ static int tuner_fixup_std(struct tuner *t)
case 'h':
case 'H':
tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
- t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ t->std = V4L2_STD_SECAM_B |
+ V4L2_STD_SECAM_G |
+ V4L2_STD_SECAM_H;
break;
case 'd':
case 'D':
case 'k':
case 'K':
- tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
+ tuner_dbg("insmod fixup: SECAM => SECAM-DK\n");
t->std = V4L2_STD_SECAM_DK;
break;
case 'l':
case 'L':
- if ((secam[1]=='C')||(secam[1]=='c')) {
- tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
+ if ((secam[1] == 'C') || (secam[1] == 'c')) {
+ tuner_dbg("insmod fixup: SECAM => SECAM-L'\n");
t->std = V4L2_STD_SECAM_LC;
} else {
- tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
+ tuner_dbg("insmod fixup: SECAM => SECAM-L\n");
t->std = V4L2_STD_SECAM_L;
}
break;
@@ -612,7 +911,7 @@ static int tuner_fixup_std(struct tuner *t)
/* default parameter, do nothing */
break;
default:
- tuner_warn ("secam= argument not recognised\n");
+ tuner_warn("secam= argument not recognised\n");
break;
}
}
@@ -645,6 +944,66 @@ static int tuner_fixup_std(struct tuner *t)
return 0;
}
+/*
+ * Functions that are specific for Radio mode
+ */
+
+/**
+ * set_radio_freq - Set tuner frequency, freq in Units of 62.5 Hz = 1/16kHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
+
+ if (t->type == UNSET) {
+ tuner_warn("tuner type not set\n");
+ return;
+ }
+ if (NULL == analog_ops->set_params) {
+ tuner_warn("tuner has no way to set radio frequency\n");
+ return;
+ }
+ if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
+ tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n",
+ freq / 16000, freq % 16000 * 100 / 16000,
+ radio_range[0], radio_range[1]);
+ /* V4L2 spec: if the freq is not possible then the closest
+ possible value should be selected */
+ if (freq < radio_range[0] * 16000)
+ freq = radio_range[0] * 16000;
+ else
+ freq = radio_range[1] * 16000;
+ }
+ params.frequency = freq;
+ tuner_dbg("radio freq set to %d.%02d\n",
+ freq / 16000, freq % 16000 * 100 / 16000);
+ t->radio_freq = freq;
+ t->standby = false;
+
+ analog_ops->set_params(&t->fe, &params);
+}
+
+/*
+ * Debug function for reporting tuner status to userspace
+ */
+
+/**
+ * tuner_status - Dumps the current tuner status at dmesg
+ * @fe: pointer to struct dvb_frontend
+ *
+ * This callback is used only for driver debug purposes, answering to
+ * VIDIOC_LOG_STATUS. No changes should happen on this call.
+ */
static void tuner_status(struct dvb_frontend *fe)
{
struct tuner *t = fe->analog_demod_priv;
@@ -654,10 +1013,16 @@ static void tuner_status(struct dvb_frontend *fe)
const char *p;
switch (t->mode) {
- case V4L2_TUNER_RADIO: p = "radio"; break;
- case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
- case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
- default: p = "undefined"; break;
+ case V4L2_TUNER_RADIO:
+ p = "radio";
+ break;
+ case V4L2_TUNER_DIGITAL_TV:
+ p = "digital TV";
+ break;
+ case V4L2_TUNER_ANALOG_TV:
+ default:
+ p = "analog TV";
+ break;
}
if (t->mode == V4L2_TUNER_RADIO) {
freq = t->radio_freq / 16000;
@@ -666,11 +1031,12 @@ static void tuner_status(struct dvb_frontend *fe)
freq = t->tv_freq / 16;
freq_fraction = (t->tv_freq % 16) * 100 / 16;
}
- tuner_info("Tuner mode: %s\n", p);
+ tuner_info("Tuner mode: %s%s\n", p,
+ t->standby ? " on standby mode" : "");
tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
if (t->mode != V4L2_TUNER_RADIO)
- return;
+ return;
if (fe_tuner_ops->get_status) {
u32 tuner_status;
@@ -683,132 +1049,58 @@ static void tuner_status(struct dvb_frontend *fe)
if (analog_ops->has_signal)
tuner_info("Signal strength: %d\n",
analog_ops->has_signal(fe));
- if (analog_ops->is_stereo)
- tuner_info("Stereo: %s\n",
- analog_ops->is_stereo(fe) ? "yes" : "no");
}
-/* ---------------------------------------------------------------------- */
-
/*
- * Switch tuner to other mode. If tuner support both tv and radio,
- * set another frequency to some value (This is needed for some pal
- * tuners to avoid locking). Otherwise, just put second tuner in
- * standby mode.
+ * Function to splicitly change mode to radio. Probably not needed anymore
*/
-static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
-{
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
- if (mode == t->mode)
- return 0;
-
- t->mode = mode;
-
- if (check_mode(t, cmd) == -EINVAL) {
- tuner_dbg("Tuner doesn't support this mode. "
- "Putting tuner to sleep\n");
- t->mode = T_STANDBY;
- if (analog_ops->standby)
- analog_ops->standby(&t->fe);
- return -EINVAL;
- }
- return 0;
-}
-
-#define switch_v4l2() if (!t->using_v4l2) \
- tuner_dbg("switching to v4l2\n"); \
- t->using_v4l2 = 1;
-
-static inline int check_v4l2(struct tuner *t)
-{
- /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
- TV, v4l1 for radio), until that is fixed this code is disabled.
- Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
- first. */
- return 0;
-}
-
-static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type)
-{
- struct tuner *t = to_tuner(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
- type->type,
- type->addr,
- type->mode_mask,
- type->config);
-
- set_addr(client, type);
- return 0;
-}
-
static int tuner_s_radio(struct v4l2_subdev *sd)
{
struct tuner *t = to_tuner(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL)
+ if (set_mode_freq(client, t, V4L2_TUNER_RADIO, 0) == -EINVAL)
return 0;
- if (t->radio_freq)
- set_freq(client, t->radio_freq);
return 0;
}
+/*
+ * Tuner callbacks to handle userspace ioctl's
+ */
+
+/**
+ * tuner_s_power - controls the power state of the tuner
+ * @sd: pointer to struct v4l2_subdev
+ * @on: a zero value puts the tuner to sleep
+ */
static int tuner_s_power(struct v4l2_subdev *sd, int on)
{
struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ /* FIXME: Why this function don't wake the tuner if on != 0 ? */
if (on)
return 0;
tuner_dbg("Putting tuner to sleep\n");
-
- if (check_mode(t, "s_power") == -EINVAL)
- return 0;
- t->mode = T_STANDBY;
+ t->standby = true;
if (analog_ops->standby)
analog_ops->standby(&t->fe);
return 0;
}
-static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
-{
- struct tuner *t = to_tuner(sd);
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
- if (t->type != cfg->tuner)
- return 0;
-
- if (analog_ops->set_config) {
- analog_ops->set_config(&t->fe, cfg->priv);
- return 0;
- }
-
- tuner_dbg("Tuner frontend module has no way to set config\n");
- return 0;
-}
-
-/* --- v4l ioctls --- */
-/* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct tuner *t = to_tuner(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL)
+ if (set_mode_freq(client, t, V4L2_TUNER_ANALOG_TV, 0) == -EINVAL)
return 0;
- switch_v4l2();
-
t->std = std;
tuner_fixup_std(t);
- if (t->tv_freq)
- set_freq(client, t->tv_freq);
+
return 0;
}
@@ -817,10 +1109,8 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
struct tuner *t = to_tuner(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (set_mode(client, t, f->type, "s_frequency") == -EINVAL)
+ if (set_mode_freq(client, t, f->type, f->frequency) == -EINVAL)
return 0;
- switch_v4l2();
- set_freq(client, f->frequency);
return 0;
}
@@ -830,21 +1120,20 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
struct tuner *t = to_tuner(sd);
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
- if (check_mode(t, "g_frequency") == -EINVAL)
+ if (check_mode(t, f->type) == -EINVAL)
return 0;
- switch_v4l2();
f->type = t->mode;
- if (fe_tuner_ops->get_frequency) {
+ if (fe_tuner_ops->get_frequency && !t->standby) {
u32 abs_freq;
fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
DIV_ROUND_CLOSEST(abs_freq, 62500);
- return 0;
+ } else {
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ t->radio_freq : t->tv_freq;
}
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- t->radio_freq : t->tv_freq;
return 0;
}
@@ -854,10 +1143,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
- if (check_mode(t, "g_tuner") == -EINVAL)
+ if (check_mode(t, vt->type) == -EINVAL)
return 0;
- switch_v4l2();
-
vt->type = t->mode;
if (analog_ops->get_afc)
vt->afc = analog_ops->get_afc(&t->fe);
@@ -870,8 +1157,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
}
/* radio mode */
- vt->rxsubchans =
- V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
if (fe_tuner_ops->get_status) {
u32 tuner_status;
@@ -880,21 +1166,14 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
(tuner_status & TUNER_STATUS_STEREO) ?
V4L2_TUNER_SUB_STEREO :
V4L2_TUNER_SUB_MONO;
- } else {
- if (analog_ops->is_stereo) {
- vt->rxsubchans =
- analog_ops->is_stereo(&t->fe) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- }
}
if (analog_ops->has_signal)
vt->signal = analog_ops->has_signal(&t->fe);
- vt->capability |=
- V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
vt->audmode = t->audmode;
vt->rangelow = radio_range[0] * 16000;
vt->rangehigh = radio_range[1] * 16000;
+
return 0;
}
@@ -903,16 +1182,12 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
struct tuner *t = to_tuner(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (check_mode(t, "s_tuner") == -EINVAL)
+ if (set_mode_freq(client, t, vt->type, 0) == -EINVAL)
return 0;
- switch_v4l2();
+ if (t->mode == V4L2_TUNER_RADIO)
+ t->audmode = vt->audmode;
- /* do nothing unless we're a radio tuner */
- if (t->mode != V4L2_TUNER_RADIO)
- return 0;
- t->audmode = vt->audmode;
- set_radio_freq(client, t->radio_freq);
return 0;
}
@@ -929,9 +1204,13 @@ static int tuner_log_status(struct v4l2_subdev *sd)
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
{
struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
tuner_dbg("suspend\n");
- /* FIXME: power down ??? */
+
+ if (!t->standby && analog_ops->standby)
+ analog_ops->standby(&t->fe);
+
return 0;
}
@@ -940,13 +1219,10 @@ static int tuner_resume(struct i2c_client *c)
struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("resume\n");
- if (V4L2_TUNER_RADIO == t->mode) {
- if (t->radio_freq)
- set_freq(c, t->radio_freq);
- } else {
- if (t->tv_freq)
- set_freq(c, t->tv_freq);
- }
+
+ if (!t->standby)
+ set_mode_freq(c, t, t->type, 0);
+
return 0;
}
@@ -964,7 +1240,9 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
return -ENOIOCTLCMD;
}
-/* ----------------------------------------------------------------------- */
+/*
+ * Callback structs
+ */
static const struct v4l2_subdev_core_ops tuner_core_ops = {
.log_status = tuner_log_status,
@@ -987,183 +1265,10 @@ static const struct v4l2_subdev_ops tuner_ops = {
.tuner = &tuner_tuner_ops,
};
-/* ---------------------------------------------------------------------- */
-
-static LIST_HEAD(tuner_list);
-
-/* Search for existing radio and/or TV tuners on the given I2C adapter.
- Note that when this function is called from tuner_probe you can be
- certain no other devices will be added/deleted at the same time, I2C
- core protects against that. */
-static void tuner_lookup(struct i2c_adapter *adap,
- struct tuner **radio, struct tuner **tv)
-{
- struct tuner *pos;
-
- *radio = NULL;
- *tv = NULL;
-
- list_for_each_entry(pos, &tuner_list, list) {
- int mode_mask;
-
- if (pos->i2c->adapter != adap ||
- strcmp(pos->i2c->driver->driver.name, "tuner"))
- continue;
-
- mode_mask = pos->mode_mask & ~T_STANDBY;
- if (*radio == NULL && mode_mask == T_RADIO)
- *radio = pos;
- /* Note: currently TDA9887 is the only demod-only
- device. If other devices appear then we need to
- make this test more general. */
- else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
- (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV)))
- *tv = pos;
- }
-}
-
-/* During client attach, set_type is called by adapter's attach_inform callback.
- set_type must then be completed by tuner_probe.
+/*
+ * I2C structs and module init functions
*/
-static int tuner_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct tuner *t;
- struct tuner *radio;
- struct tuner *tv;
-
- t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
- if (NULL == t)
- return -ENOMEM;
- v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
- t->i2c = client;
- t->name = "(tuner unset)";
- t->type = UNSET;
- t->audmode = V4L2_TUNER_MODE_STEREO;
- t->mode_mask = T_UNINITIALIZED;
-
- if (show_i2c) {
- unsigned char buffer[16];
- int i, rc;
-
- memset(buffer, 0, sizeof(buffer));
- rc = i2c_master_recv(client, buffer, sizeof(buffer));
- tuner_info("I2C RECV = ");
- for (i = 0; i < rc; i++)
- printk(KERN_CONT "%02x ", buffer[i]);
- printk("\n");
- }
- /* autodetection code based on the i2c addr */
- if (!no_autodetect) {
- switch (client->addr) {
- case 0x10:
- if (tuner_symbol_probe(tea5761_autodetection,
- t->i2c->adapter,
- t->i2c->addr) >= 0) {
- t->type = TUNER_TEA5761;
- t->mode_mask = T_RADIO;
- t->mode = T_STANDBY;
- /* Sets freq to FM range */
- t->radio_freq = 87.5 * 16000;
- tuner_lookup(t->i2c->adapter, &radio, &tv);
- if (tv)
- tv->mode_mask &= ~T_RADIO;
-
- goto register_client;
- }
- kfree(t);
- return -ENODEV;
- case 0x42:
- case 0x43:
- case 0x4a:
- case 0x4b:
- /* If chip is not tda8290, don't register.
- since it can be tda9887*/
- if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
- t->i2c->addr) >= 0) {
- tuner_dbg("tda829x detected\n");
- } else {
- /* Default is being tda9887 */
- t->type = TUNER_TDA9887;
- t->mode_mask = T_RADIO | T_ANALOG_TV |
- T_DIGITAL_TV;
- t->mode = T_STANDBY;
- goto register_client;
- }
- break;
- case 0x60:
- if (tuner_symbol_probe(tea5767_autodetection,
- t->i2c->adapter, t->i2c->addr)
- >= 0) {
- t->type = TUNER_TEA5767;
- t->mode_mask = T_RADIO;
- t->mode = T_STANDBY;
- /* Sets freq to FM range */
- t->radio_freq = 87.5 * 16000;
- tuner_lookup(t->i2c->adapter, &radio, &tv);
- if (tv)
- tv->mode_mask &= ~T_RADIO;
-
- goto register_client;
- }
- break;
- }
- }
-
- /* Initializes only the first TV tuner on this adapter. Why only the
- first? Because there are some devices (notably the ones with TI
- tuners) that have more than one i2c address for the *same* device.
- Experience shows that, except for just one case, the first
- address is the right one. The exception is a Russian tuner
- (ACORP_Y878F). So, the desired behavior is just to enable the
- first found TV tuner. */
- tuner_lookup(t->i2c->adapter, &radio, &tv);
- if (tv == NULL) {
- t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
- if (radio == NULL)
- t->mode_mask |= T_RADIO;
- tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
- t->tv_freq = 400 * 16; /* Sets freq to VHF High */
- t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- }
-
- /* Should be just before return */
-register_client:
- tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1,
- client->adapter->name);
-
- /* Sets a default mode */
- if (t->mode_mask & T_ANALOG_TV) {
- t->mode = V4L2_TUNER_ANALOG_TV;
- } else if (t->mode_mask & T_RADIO) {
- t->mode = V4L2_TUNER_RADIO;
- } else {
- t->mode = V4L2_TUNER_DIGITAL_TV;
- }
- set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
- list_add_tail(&t->list, &tuner_list);
- return 0;
-}
-
-static int tuner_remove(struct i2c_client *client)
-{
- struct tuner *t = to_tuner(i2c_get_clientdata(client));
-
- v4l2_device_unregister_subdev(&t->sd);
- tuner_detach(&t->fe);
- t->fe.analog_demod_priv = NULL;
-
- list_del(&t->list);
- kfree(t);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* This driver supports many devices and the idea is to let the driver
- detect which device is present. So rather than listing all supported
- devices here, we pretend to support a single, fake device type. */
static const struct i2c_device_id tuner_id[] = {
{ "tuner", }, /* autodetect */
{ }
@@ -1196,10 +1301,6 @@ static __exit void exit_tuner(void)
module_init(init_tuner);
module_exit(exit_tuner);
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
+MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
index 45bcf0358a1d..9b3e828b0775 100644
--- a/drivers/media/video/tvp514x.c
+++ b/drivers/media/video/tvp514x.c
@@ -37,6 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/tvp514x.h>
#include "tvp514x_regs.h"
@@ -97,6 +98,7 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable);
*/
struct tvp514x_decoder {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)];
const struct tvp514x_platform_data *pdata;
@@ -238,6 +240,11 @@ static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd)
return container_of(sd, struct tvp514x_decoder, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd;
+}
+
/**
* tvp514x_read_reg() - Read a value from a register in an TVP5146/47.
@@ -719,213 +726,54 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd,
}
/**
- * tvp514x_queryctrl() - V4L2 decoder interface handler for queryctrl
- * @sd: pointer to standard V4L2 sub-device structure
- * @qctrl: standard V4L2 v4l2_queryctrl structure
- *
- * If the requested control is supported, returns the control information.
- * Otherwise, returns -EINVAL if the control is not supported.
- */
-static int
-tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
-{
- int err = -EINVAL;
-
- if (qctrl == NULL)
- return err;
-
- switch (qctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- /* Brightness supported is (0-255), */
- err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
- break;
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- /**
- * Saturation and Contrast supported is -
- * Contrast: 0 - 255 (Default - 128)
- * Saturation: 0 - 255 (Default - 128)
- */
- err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
- break;
- case V4L2_CID_HUE:
- /* Hue Supported is -
- * Hue - -180 - +180 (Default - 0, Step - +180)
- */
- err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
- break;
- case V4L2_CID_AUTOGAIN:
- /**
- * Auto Gain supported is -
- * 0 - 1 (Default - 1)
- */
- err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
- break;
- default:
- v4l2_err(sd, "invalid control id %d\n", qctrl->id);
- return err;
- }
-
- v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d\n",
- qctrl->name, qctrl->minimum, qctrl->maximum,
- qctrl->default_value);
-
- return err;
-}
-
-/**
- * tvp514x_g_ctrl() - V4L2 decoder interface handler for g_ctrl
- * @sd: pointer to standard V4L2 sub-device structure
- * @ctrl: pointer to v4l2_control structure
- *
- * If the requested control is supported, returns the control's current
- * value from the decoder. Otherwise, returns -EINVAL if the control is not
- * supported.
- */
-static int
-tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
-
- if (ctrl == NULL)
- return -EINVAL;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val;
- break;
- case V4L2_CID_HUE:
- ctrl->value = decoder->tvp514x_regs[REG_HUE].val;
- if (ctrl->value == 0x7F)
- ctrl->value = 180;
- else if (ctrl->value == 0x80)
- ctrl->value = -180;
- else
- ctrl->value = 0;
-
- break;
- case V4L2_CID_AUTOGAIN:
- ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val;
- if ((ctrl->value & 0x3) == 3)
- ctrl->value = 1;
- else
- ctrl->value = 0;
-
- break;
- default:
- v4l2_err(sd, "invalid control id %d\n", ctrl->id);
- return -EINVAL;
- }
-
- v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d\n",
- ctrl->id, ctrl->value);
- return 0;
-}
-
-/**
* tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl
- * @sd: pointer to standard V4L2 sub-device structure
- * @ctrl: pointer to v4l2_control structure
+ * @ctrl: pointer to v4l2_ctrl structure
*
* If the requested control is supported, sets the control's current
* value in HW. Otherwise, returns -EINVAL if the control is not supported.
*/
-static int
-tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct tvp514x_decoder *decoder = to_decoder(sd);
int err = -EINVAL, value;
- if (ctrl == NULL)
- return err;
-
- value = ctrl->value;
+ value = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid brightness setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
- err = tvp514x_write_reg(sd, REG_BRIGHTNESS,
- value);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
+ err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
break;
case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid contrast setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
err = tvp514x_write_reg(sd, REG_CONTRAST, value);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_CONTRAST].val = value;
+ if (!err)
+ decoder->tvp514x_regs[REG_CONTRAST].val = value;
break;
case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid saturation setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
err = tvp514x_write_reg(sd, REG_SATURATION, value);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_SATURATION].val = value;
+ if (!err)
+ decoder->tvp514x_regs[REG_SATURATION].val = value;
break;
case V4L2_CID_HUE:
if (value == 180)
value = 0x7F;
else if (value == -180)
value = 0x80;
- else if (value == 0)
- value = 0;
- else {
- v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
err = tvp514x_write_reg(sd, REG_HUE, value);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_HUE].val = value;
+ if (!err)
+ decoder->tvp514x_regs[REG_HUE].val = value;
break;
case V4L2_CID_AUTOGAIN:
- if (value == 1)
- value = 0x0F;
- else if (value == 0)
- value = 0x0C;
- else {
- v4l2_err(sd, "invalid auto gain setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
- err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
+ err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c);
+ if (!err)
+ decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
break;
- default:
- v4l2_err(sd, "invalid control id %d\n", ctrl->id);
- return err;
}
v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n",
- ctrl->id, ctrl->value);
-
+ ctrl->id, ctrl->val);
return err;
}
@@ -1104,10 +952,18 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
return err;
}
-static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
- .queryctrl = tvp514x_queryctrl,
- .g_ctrl = tvp514x_g_ctrl,
+static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = {
.s_ctrl = tvp514x_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = tvp514x_s_std,
};
@@ -1190,6 +1046,27 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
sd = &decoder->sd;
v4l2_i2c_subdev_init(sd, client, &tvp514x_ops);
+ v4l2_ctrl_handler_init(&decoder->hdl, 5);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_HUE, -180, 180, 180, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
return 0;
@@ -1209,6 +1086,7 @@ static int tvp514x_remove(struct i2c_client *client)
struct tvp514x_decoder *decoder = to_decoder(sd);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
kfree(decoder);
return 0;
}
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 58927664d3ea..e927d25e0d35 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -12,6 +12,7 @@
#include <media/v4l2-device.h>
#include <media/tvp5150.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include "tvp5150_reg.h"
@@ -24,58 +25,14 @@ static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
-/* supported controls */
-static struct v4l2_queryctrl tvp5150_qctrl[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 128,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 128,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 0x1,
- .default_value = 0,
- .flags = 0,
- }
-};
-
struct tvp5150 {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
v4l2_std_id norm; /* Current set standard */
u32 input;
u32 output;
int enable;
- int bright;
- int contrast;
- int hue;
- int sat;
};
static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
@@ -83,6 +40,11 @@ static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
return container_of(sd, struct tvp5150, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp5150, hdl)->sd;
+}
+
static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
{
struct i2c_client *c = v4l2_get_subdevdata(sd);
@@ -775,27 +737,6 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
{
struct tvp5150 *decoder = to_tvp5150(sd);
- u8 msb_id, lsb_id, msb_rom, lsb_rom;
-
- msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
- lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
- msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
- lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
-
- if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
- v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
-
- /* ITU-T BT.656.4 timing */
- tvp5150_write(sd, TVP5150_REV_SELECT, 0);
- } else {
- if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
- v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
- } else {
- v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
- msb_id, lsb_id);
- v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
- }
- }
/* Initializes TVP5150 to its default values */
tvp5150_write_inittab(sd, tvp5150_init_default);
@@ -810,64 +751,28 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
tvp5150_write_inittab(sd, tvp5150_init_enable);
/* Initialize image preferences */
- tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright);
- tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast);
- tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast);
- tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue);
+ v4l2_ctrl_handler_setup(&decoder->hdl);
tvp5150_set_std(sd, decoder->norm);
return 0;
};
-static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- v4l2_dbg(1, debug, sd, "g_ctrl called\n");
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL);
- return 0;
- case V4L2_CID_CONTRAST:
- ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL);
- return 0;
- case V4L2_CID_SATURATION:
- ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL);
- return 0;
- case V4L2_CID_HUE:
- ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL);
- return 0;
- }
- return -EINVAL;
-}
-
-static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
{
- u8 i, n;
- n = ARRAY_SIZE(tvp5150_qctrl);
-
- for (i = 0; i < n; i++) {
- if (ctrl->id != tvp5150_qctrl[i].id)
- continue;
- if (ctrl->value < tvp5150_qctrl[i].minimum ||
- ctrl->value > tvp5150_qctrl[i].maximum)
- return -ERANGE;
- v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
- ctrl->id, ctrl->value);
- break;
- }
+ struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
return 0;
case V4L2_CID_CONTRAST:
- tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
return 0;
case V4L2_CID_SATURATION:
- tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
return 0;
case V4L2_CID_HUE:
- tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
return 0;
}
return -EINVAL;
@@ -995,29 +900,21 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
}
-static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- int i;
-
- v4l2_dbg(1, debug, sd, "queryctrl called\n");
-
- for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
- if (qc->id && qc->id == tvp5150_qctrl[i].id) {
- memcpy(qc, &(tvp5150_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
-
- return -EINVAL;
-}
-
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
+ .s_ctrl = tvp5150_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
.log_status = tvp5150_log_status,
- .g_ctrl = tvp5150_g_ctrl,
- .s_ctrl = tvp5150_s_ctrl,
- .queryctrl = tvp5150_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = tvp5150_s_std,
.reset = tvp5150_reset,
.g_chip_ident = tvp5150_g_chip_ident,
@@ -1059,6 +956,7 @@ static int tvp5150_probe(struct i2c_client *c,
{
struct tvp5150 *core;
struct v4l2_subdev *sd;
+ u8 msb_id, lsb_id, msb_rom, lsb_rom;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
@@ -1074,13 +972,48 @@ static int tvp5150_probe(struct i2c_client *c,
v4l_info(c, "chip found @ 0x%02x (%s)\n",
c->addr << 1, c->adapter->name);
+ msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
+ lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
+ msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
+ lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
+
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
+
+ /* ITU-T BT.656.4 timing */
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
+ } else {
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
+ } else {
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
+ }
+ }
+
core->norm = V4L2_STD_ALL; /* Default is autodetect */
core->input = TVP5150_COMPOSITE1;
core->enable = 1;
- core->bright = 128;
- core->contrast = 128;
- core->hue = 0;
- core->sat = 128;
+
+ v4l2_ctrl_handler_init(&core->hdl, 4);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &core->hdl;
+ if (core->hdl.error) {
+ int err = core->hdl.error;
+
+ v4l2_ctrl_handler_free(&core->hdl);
+ kfree(core);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&core->hdl);
if (debug > 1)
tvp5150_log_status(sd);
@@ -1090,12 +1023,14 @@ static int tvp5150_probe(struct i2c_client *c,
static int tvp5150_remove(struct i2c_client *c)
{
struct v4l2_subdev *sd = i2c_get_clientdata(c);
+ struct tvp5150 *decoder = to_tvp5150(sd);
v4l2_dbg(1, debug, sd,
"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
c->addr << 1);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
kfree(to_tvp5150(sd));
return 0;
}
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index c799e4eb6fcd..b799851bf3d0 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -32,6 +32,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
#include "tvp7002_reg.h"
MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
@@ -421,13 +422,13 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
/* Device definition */
struct tvp7002 {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
const struct tvp7002_config *pdata;
int ver;
int streaming;
const struct tvp7002_preset_definition *current_preset;
- u8 gain;
};
/*
@@ -441,6 +442,11 @@ static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
return container_of(sd, struct tvp7002, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp7002, hdl)->sd;
+}
+
/*
* tvp7002_read - Read a value from a register in an TVP7002
* @sd: ptr to v4l2_subdev struct
@@ -606,78 +612,25 @@ static int tvp7002_s_dv_preset(struct v4l2_subdev *sd,
}
/*
- * tvp7002_g_ctrl() - Get a control
- * @sd: ptr to v4l2_subdev struct
- * @ctrl: ptr to v4l2_control struct
- *
- * Get a control for a TVP7002 decoder device.
- * Returns zero when successful or -EINVAL if register access fails.
- */
-static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct tvp7002 *device = to_tvp7002(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_GAIN:
- ctrl->value = device->gain;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/*
* tvp7002_s_ctrl() - Set a control
- * @sd: ptr to v4l2_subdev struct
- * @ctrl: ptr to v4l2_control struct
+ * @ctrl: ptr to v4l2_ctrl struct
*
* Set a control in TVP7002 decoder device.
* Returns zero when successful or -EINVAL if register access fails.
*/
-static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct tvp7002 *device = to_tvp7002(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
int error = 0;
switch (ctrl->id) {
case V4L2_CID_GAIN:
- tvp7002_write_err(sd, TVP7002_R_FINE_GAIN,
- ctrl->value & 0xff, &error);
- tvp7002_write_err(sd, TVP7002_G_FINE_GAIN,
- ctrl->value & 0xff, &error);
- tvp7002_write_err(sd, TVP7002_B_FINE_GAIN,
- ctrl->value & 0xff, &error);
-
- if (error < 0)
- return error;
-
- /* Set only after knowing there is no error */
- device->gain = ctrl->value & 0xff;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/*
- * tvp7002_queryctrl() - Query a control
- * @sd: ptr to v4l2_subdev struct
- * @qc: ptr to v4l2_queryctrl struct
- *
- * Query a control of a TVP7002 decoder device.
- * Returns zero when successful or -EINVAL if register read fails.
- */
-static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_GAIN:
- /*
- * Gain is supported [0-255, default=0, step=1]
- */
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
- default:
- return -EINVAL;
+ tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error);
+ tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error);
+ tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error);
+ return error;
}
+ return -EINVAL;
}
/*
@@ -924,7 +877,7 @@ static int tvp7002_log_status(struct v4l2_subdev *sd)
device->streaming ? "yes" : "no");
/* Print the current value of the gain control */
- v4l2_info(sd, "Gain: %u\n", device->gain);
+ v4l2_ctrl_handler_log_status(&device->hdl, sd->name);
return 0;
}
@@ -946,13 +899,21 @@ static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd,
return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset);
}
+static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = {
+ .s_ctrl = tvp7002_s_ctrl,
+};
+
/* V4L2 core operation handlers */
static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
.g_chip_ident = tvp7002_g_chip_ident,
.log_status = tvp7002_log_status,
- .g_ctrl = tvp7002_g_ctrl,
- .s_ctrl = tvp7002_s_ctrl,
- .queryctrl = tvp7002_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tvp7002_g_register,
.s_register = tvp7002_s_register,
@@ -977,12 +938,6 @@ static const struct v4l2_subdev_ops tvp7002_ops = {
.video = &tvp7002_video_ops,
};
-static struct tvp7002 tvp7002_dev = {
- .streaming = 0,
- .current_preset = tvp7002_presets,
- .gain = 0,
-};
-
/*
* tvp7002_probe - Probe a TVP7002 device
* @c: ptr to i2c_client struct
@@ -1013,14 +968,14 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
return -ENODEV;
}
- device = kmalloc(sizeof(struct tvp7002), GFP_KERNEL);
+ device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
if (!device)
return -ENOMEM;
- *device = tvp7002_dev;
sd = &device->sd;
device->pdata = c->dev.platform_data;
+ device->current_preset = tvp7002_presets;
/* Tell v4l2 the device is ready */
v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
@@ -1060,6 +1015,19 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
preset.preset = device->current_preset->preset;
error = tvp7002_s_dv_preset(sd, &preset);
+ v4l2_ctrl_handler_init(&device->hdl, 1);
+ v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 0);
+ sd->ctrl_handler = &device->hdl;
+ if (device->hdl.error) {
+ int err = device->hdl.error;
+
+ v4l2_ctrl_handler_free(&device->hdl);
+ kfree(device);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&device->hdl);
+
found_error:
if (error < 0)
kfree(device);
@@ -1083,6 +1051,7 @@ static int tvp7002_remove(struct i2c_client *c)
"on address 0x%x\n", c->addr);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&device->hdl);
kfree(device);
return 0;
}
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 810eef43c216..940b5db3463e 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -59,7 +59,6 @@
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/div64.h>
-#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index dc82eb83c1d4..7c2694738b31 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -14,7 +14,6 @@
*/
#include <linux/compat.h>
-#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/v4l2-ioctl.h>
@@ -97,6 +96,14 @@ static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
return 0;
}
+static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+ struct v4l2_pix_format_mplane __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
+ return -EFAULT;
+ return 0;
+}
+
static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
{
if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
@@ -104,6 +111,14 @@ static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
return 0;
}
+static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+ struct v4l2_pix_format_mplane __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
+ return -EFAULT;
+ return 0;
+}
+
static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
{
if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
@@ -136,6 +151,7 @@ struct v4l2_format32 {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix;
+ struct v4l2_pix_format_mplane pix_mp;
struct v4l2_window32 win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
@@ -152,6 +168,10 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+ &up->fmt.pix_mp);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
@@ -181,6 +201,10 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+ &up->fmt.pix_mp);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
@@ -232,6 +256,17 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
return 0;
}
+struct v4l2_plane32 {
+ __u32 bytesused;
+ __u32 length;
+ union {
+ __u32 mem_offset;
+ compat_long_t userptr;
+ } m;
+ __u32 data_offset;
+ __u32 reserved[11];
+};
+
struct v4l2_buffer32 {
__u32 index;
enum v4l2_buf_type type;
@@ -247,14 +282,64 @@ struct v4l2_buffer32 {
union {
__u32 offset;
compat_long_t userptr;
+ compat_caddr_t planes;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
+static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+ enum v4l2_memory memory)
+{
+ void __user *up_pln;
+ compat_long_t p;
+
+ if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
+ copy_in_user(&up->data_offset, &up32->data_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ if (memory == V4L2_MEMORY_USERPTR) {
+ if (get_user(p, &up32->m.userptr))
+ return -EFAULT;
+ up_pln = compat_ptr(p);
+ if (put_user((unsigned long)up_pln, &up->m.userptr))
+ return -EFAULT;
+ } else {
+ if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+ enum v4l2_memory memory)
+{
+ if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
+ copy_in_user(&up32->data_offset, &up->data_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ /* For MMAP, driver might've set up the offset, so copy it back.
+ * USERPTR stays the same (was userspace-provided), so no copying. */
+ if (memory == V4L2_MEMORY_MMAP)
+ if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
{
+ struct v4l2_plane32 __user *uplane32;
+ struct v4l2_plane __user *uplane;
+ compat_caddr_t p;
+ int num_planes;
+ int ret;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
get_user(kp->index, &up->index) ||
@@ -263,33 +348,84 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
get_user(kp->memory, &up->memory) ||
get_user(kp->input, &up->input))
return -EFAULT;
- switch (kp->memory) {
- case V4L2_MEMORY_MMAP:
- if (get_user(kp->length, &up->length) ||
- get_user(kp->m.offset, &up->m.offset))
+
+ if (V4L2_TYPE_IS_OUTPUT(kp->type))
+ if (get_user(kp->bytesused, &up->bytesused) ||
+ get_user(kp->field, &up->field) ||
+ get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ get_user(kp->timestamp.tv_usec,
+ &up->timestamp.tv_usec))
return -EFAULT;
- break;
- case V4L2_MEMORY_USERPTR:
- {
- compat_long_t tmp;
- if (get_user(kp->length, &up->length) ||
- get_user(tmp, &up->m.userptr))
+ if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+ if (get_user(kp->length, &up->length))
return -EFAULT;
- kp->m.userptr = (unsigned long)compat_ptr(tmp);
+ num_planes = kp->length;
+ if (num_planes == 0) {
+ kp->m.planes = NULL;
+ /* num_planes == 0 is legal, e.g. when userspace doesn't
+ * need planes array on DQBUF*/
+ return 0;
}
- break;
- case V4L2_MEMORY_OVERLAY:
- if (get_user(kp->m.offset, &up->m.offset))
+
+ if (get_user(p, &up->m.planes))
return -EFAULT;
- break;
+
+ uplane32 = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, uplane32,
+ num_planes * sizeof(struct v4l2_plane32)))
+ return -EFAULT;
+
+ /* We don't really care if userspace decides to kill itself
+ * by passing a very big num_planes value */
+ uplane = compat_alloc_user_space(num_planes *
+ sizeof(struct v4l2_plane));
+ kp->m.planes = uplane;
+
+ while (--num_planes >= 0) {
+ ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+ if (ret)
+ return ret;
+ ++uplane;
+ ++uplane32;
+ }
+ } else {
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (get_user(kp->length, &up->length) ||
+ get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ {
+ compat_long_t tmp;
+
+ if (get_user(kp->length, &up->length) ||
+ get_user(tmp, &up->m.userptr))
+ return -EFAULT;
+
+ kp->m.userptr = (unsigned long)compat_ptr(tmp);
+ }
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
}
+
return 0;
}
static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
{
+ struct v4l2_plane32 __user *uplane32;
+ struct v4l2_plane __user *uplane;
+ compat_caddr_t p;
+ int num_planes;
+ int ret;
+
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
put_user(kp->index, &up->index) ||
put_user(kp->type, &up->type) ||
@@ -297,22 +433,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->memory, &up->memory) ||
put_user(kp->input, &up->input))
return -EFAULT;
- switch (kp->memory) {
- case V4L2_MEMORY_MMAP:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.offset, &up->m.offset))
- return -EFAULT;
- break;
- case V4L2_MEMORY_USERPTR:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.userptr, &up->m.userptr))
- return -EFAULT;
- break;
- case V4L2_MEMORY_OVERLAY:
- if (put_user(kp->m.offset, &up->m.offset))
- return -EFAULT;
- break;
- }
+
if (put_user(kp->bytesused, &up->bytesused) ||
put_user(kp->field, &up->field) ||
put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
@@ -321,6 +442,43 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->sequence, &up->sequence) ||
put_user(kp->reserved, &up->reserved))
return -EFAULT;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+ num_planes = kp->length;
+ if (num_planes == 0)
+ return 0;
+
+ uplane = kp->m.planes;
+ if (get_user(p, &up->m.planes))
+ return -EFAULT;
+ uplane32 = compat_ptr(p);
+
+ while (--num_planes >= 0) {
+ ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
+ if (ret)
+ return ret;
+ ++uplane;
+ ++uplane32;
+ }
+ } else {
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.userptr, &up->m.userptr))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
+ }
+
return 0;
}
@@ -442,12 +600,13 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
- if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control)))
+ if (!access_ok(VERIFY_READ, ucontrols,
+ n * sizeof(struct v4l2_ext_control32)))
return -EFAULT;
kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
kp->controls = kcontrols;
while (--n >= 0) {
- if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols)))
+ if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
return -EFAULT;
if (ctrl_is_pointer(kcontrols->id)) {
void __user *s;
@@ -483,7 +642,8 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
- if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control)))
+ if (!access_ok(VERIFY_WRITE, ucontrols,
+ n * sizeof(struct v4l2_ext_control32)))
return -EFAULT;
while (--n >= 0) {
@@ -517,9 +677,6 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
-#ifdef __OLD_VIDIOC_
-#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32)
-#endif
#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
@@ -559,9 +716,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
-#ifdef __OLD_VIDIOC_
- case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break;
-#endif
case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
@@ -695,14 +849,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
return ret;
switch (cmd) {
-#ifdef __OLD_VIDIOC_
- case VIDIOC_OVERLAY32_OLD:
- case VIDIOC_S_PARM_OLD:
- case VIDIOC_S_CTRL_OLD:
- case VIDIOC_G_AUDIO_OLD:
- case VIDIOC_G_AUDOUT_OLD:
- case VIDIOC_CROPCAP_OLD:
-#endif
case VIDIOC_QUERYCAP:
case VIDIOC_RESERVED:
case VIDIOC_ENUM_FMT:
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index ef66d2af0c57..2412f08527aa 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -1364,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_queryctrl);
int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
+ if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
+ return -EINVAL;
return v4l2_queryctrl(sd->ctrl_handler, qc);
}
EXPORT_SYMBOL(v4l2_subdev_queryctrl);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index f51327ef6757..7a720745c3fa 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -17,7 +17,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
-#define __OLD_VIDIOC_ /* To allow fixing old calls */
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
@@ -165,6 +164,8 @@ const char *v4l2_type_names[] = {
[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out",
[V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
+ [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
};
EXPORT_SYMBOL(v4l2_type_names);
@@ -295,37 +296,6 @@ EXPORT_SYMBOL(v4l_printk_ioctl);
/*
* helper function -- handles userspace copying for ioctl arguments
- */
-
-#ifdef __OLD_VIDIOC_
-static unsigned int
-video_fix_command(unsigned int cmd)
-{
- switch (cmd) {
- case VIDIOC_OVERLAY_OLD:
- cmd = VIDIOC_OVERLAY;
- break;
- case VIDIOC_S_PARM_OLD:
- cmd = VIDIOC_S_PARM;
- break;
- case VIDIOC_S_CTRL_OLD:
- cmd = VIDIOC_S_CTRL;
- break;
- case VIDIOC_G_AUDIO_OLD:
- cmd = VIDIOC_G_AUDIO;
- break;
- case VIDIOC_G_AUDOUT_OLD:
- cmd = VIDIOC_G_AUDOUT;
- break;
- case VIDIOC_CROPCAP_OLD:
- cmd = VIDIOC_CROPCAP;
- break;
- }
- return cmd;
-}
-#endif
-
-/*
* Obsolete usercopy function - Should be removed soon
*/
long
@@ -340,9 +310,6 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
size_t ctrls_size = 0;
void __user *user_ptr = NULL;
-#ifdef __OLD_VIDIOC_
- cmd = video_fix_command(cmd);
-#endif
is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||
cmd == VIDIOC_TRY_EXT_CTRLS);
@@ -426,20 +393,33 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
struct v4l2_buffer *p)
{
struct v4l2_timecode *tc = &p->timecode;
+ struct v4l2_plane *plane;
+ int i;
dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
- "bytesused=%d, flags=0x%08d, "
- "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n",
+ "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n",
p->timestamp.tv_sec / 3600,
(int)(p->timestamp.tv_sec / 60) % 60,
(int)(p->timestamp.tv_sec % 60),
(long)p->timestamp.tv_usec,
p->index,
prt_names(p->type, v4l2_type_names),
- p->bytesused, p->flags,
- p->field, p->sequence,
- prt_names(p->memory, v4l2_memory_names),
- p->m.userptr, p->length);
+ p->flags, p->field, p->sequence,
+ prt_names(p->memory, v4l2_memory_names));
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+ for (i = 0; i < p->length; ++i) {
+ plane = &p->m.planes[i];
+ dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x "
+ "offset/userptr=0x%08lx, length=%d\n",
+ i, plane->bytesused, plane->data_offset,
+ plane->m.userptr, plane->length);
+ }
+ } else {
+ dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n",
+ p->bytesused, p->m.userptr, p->length);
+ }
+
dbgarg2("timecode=%02d:%02d:%02d type=%d, "
"flags=0x%08d, frames=%d, userbits=0x%08x\n",
tc->hours, tc->minutes, tc->seconds,
@@ -467,6 +447,27 @@ static inline void v4l_print_pix_fmt(struct video_device *vfd,
fmt->bytesperline, fmt->sizeimage, fmt->colorspace);
};
+static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd,
+ struct v4l2_pix_format_mplane *fmt)
+{
+ int i;
+
+ dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
+ "colorspace=%d, num_planes=%d\n",
+ fmt->width, fmt->height,
+ (fmt->pixelformat & 0xff),
+ (fmt->pixelformat >> 8) & 0xff,
+ (fmt->pixelformat >> 16) & 0xff,
+ (fmt->pixelformat >> 24) & 0xff,
+ prt_names(fmt->field, v4l2_field_names),
+ fmt->colorspace, fmt->num_planes);
+
+ for (i = 0; i < fmt->num_planes; ++i)
+ dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i,
+ fmt->plane_fmt[i].bytesperline,
+ fmt->plane_fmt[i].sizeimage);
+}
+
static inline void v4l_print_ext_ctrls(unsigned int cmd,
struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
{
@@ -520,7 +521,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_g_fmt_vid_cap)
+ if (ops->vidioc_g_fmt_vid_cap ||
+ ops->vidioc_g_fmt_vid_cap_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (ops->vidioc_g_fmt_vid_cap_mplane)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
@@ -528,7 +534,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_g_fmt_vid_out)
+ if (ops->vidioc_g_fmt_vid_out ||
+ ops->vidioc_g_fmt_vid_out_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (ops->vidioc_g_fmt_vid_out_mplane)
return 0;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
@@ -559,12 +570,70 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
return -EINVAL;
}
+/**
+ * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane
+ * equivalent
+ */
+static int fmt_sp_to_mp(const struct v4l2_format *f_sp,
+ struct v4l2_format *f_mp)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
+ const struct v4l2_pix_format *pix = &f_sp->fmt.pix;
+
+ if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ else
+ return -EINVAL;
+
+ pix_mp->width = pix->width;
+ pix_mp->height = pix->height;
+ pix_mp->pixelformat = pix->pixelformat;
+ pix_mp->field = pix->field;
+ pix_mp->colorspace = pix->colorspace;
+ pix_mp->num_planes = 1;
+ pix_mp->plane_fmt[0].sizeimage = pix->sizeimage;
+ pix_mp->plane_fmt[0].bytesperline = pix->bytesperline;
+
+ return 0;
+}
+
+/**
+ * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar
+ * equivalent
+ */
+static int fmt_mp_to_sp(const struct v4l2_format *f_mp,
+ struct v4l2_format *f_sp)
+{
+ const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp;
+ struct v4l2_pix_format *pix = &f_sp->fmt.pix;
+
+ if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ return -EINVAL;
+
+ pix->width = pix_mp->width;
+ pix->height = pix_mp->height;
+ pix->pixelformat = pix_mp->pixelformat;
+ pix->field = pix_mp->field;
+ pix->colorspace = pix_mp->colorspace;
+ pix->sizeimage = pix_mp->plane_fmt[0].sizeimage;
+ pix->bytesperline = pix_mp->plane_fmt[0].bytesperline;
+
+ return 0;
+}
+
static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
void *fh = file->private_data;
+ struct v4l2_format f_copy;
long ret = -EINVAL;
if (ops == NULL) {
@@ -633,6 +702,11 @@ static long __video_do_ioctl(struct file *file,
if (ops->vidioc_enum_fmt_vid_cap)
ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (ops->vidioc_enum_fmt_vid_cap_mplane)
+ ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
+ fh, f);
+ break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (ops->vidioc_enum_fmt_vid_overlay)
ret = ops->vidioc_enum_fmt_vid_overlay(file,
@@ -642,6 +716,11 @@ static long __video_do_ioctl(struct file *file,
if (ops->vidioc_enum_fmt_vid_out)
ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (ops->vidioc_enum_fmt_vid_out_mplane)
+ ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
+ fh, f);
+ break;
case V4L2_BUF_TYPE_PRIVATE:
if (ops->vidioc_enum_fmt_type_private)
ret = ops->vidioc_enum_fmt_type_private(file,
@@ -670,22 +749,90 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_g_fmt_vid_cap)
+ if (ops->vidioc_g_fmt_vid_cap) {
ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
+ } else if (ops->vidioc_g_fmt_vid_cap_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh,
+ &f_copy);
+ if (ret)
+ break;
+
+ /* Driver is currently in multi-planar format,
+ * we can't return it in single-planar API*/
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ ret = -EBUSY;
+ break;
+ }
+
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (ops->vidioc_g_fmt_vid_cap_mplane) {
+ ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_g_fmt_vid_cap) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_g_fmt_vid_cap(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_sp_to_mp(&f_copy, f);
+ }
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (ops->vidioc_g_fmt_vid_overlay)
ret = ops->vidioc_g_fmt_vid_overlay(file,
fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_g_fmt_vid_out)
+ if (ops->vidioc_g_fmt_vid_out) {
ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
+ } else if (ops->vidioc_g_fmt_vid_out_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh,
+ &f_copy);
+ if (ret)
+ break;
+
+ /* Driver is currently in multi-planar format,
+ * we can't return it in single-planar API*/
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ ret = -EBUSY;
+ break;
+ }
+
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (ops->vidioc_g_fmt_vid_out_mplane) {
+ ret = ops->vidioc_g_fmt_vid_out_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_g_fmt_vid_out) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_g_fmt_vid_out(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_sp_to_mp(&f_copy, f);
+ }
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
if (ops->vidioc_g_fmt_vid_out_overlay)
ret = ops->vidioc_g_fmt_vid_out_overlay(file,
@@ -729,8 +876,44 @@ static long __video_do_ioctl(struct file *file,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_cap)
+ if (ops->vidioc_s_fmt_vid_cap) {
ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
+ } else if (ops->vidioc_s_fmt_vid_cap_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh,
+ &f_copy);
+ if (ret)
+ break;
+
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ /* Drivers shouldn't adjust from 1-plane
+ * to more than 1-plane formats */
+ ret = -EBUSY;
+ WARN_ON(1);
+ break;
+ }
+
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ if (ops->vidioc_s_fmt_vid_cap_mplane) {
+ ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_s_fmt_vid_cap &&
+ f->fmt.pix_mp.num_planes == 1) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_s_fmt_vid_cap(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_sp_to_mp(&f_copy, f);
+ }
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -741,8 +924,44 @@ static long __video_do_ioctl(struct file *file,
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.pix);
v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_out)
+ if (ops->vidioc_s_fmt_vid_out) {
ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
+ } else if (ops->vidioc_s_fmt_vid_out_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh,
+ &f_copy);
+ if (ret)
+ break;
+
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ /* Drivers shouldn't adjust from 1-plane
+ * to more than 1-plane formats */
+ ret = -EBUSY;
+ WARN_ON(1);
+ break;
+ }
+
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ if (ops->vidioc_s_fmt_vid_out_mplane) {
+ ret = ops->vidioc_s_fmt_vid_out_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_s_fmt_vid_out &&
+ f->fmt.pix_mp.num_planes == 1) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_s_fmt_vid_out(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
@@ -791,11 +1010,47 @@ static long __video_do_ioctl(struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_cap)
+ if (ops->vidioc_try_fmt_vid_cap) {
ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
+ } else if (ops->vidioc_try_fmt_vid_cap_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ /* Drivers shouldn't adjust from 1-plane
+ * to more than 1-plane formats */
+ ret = -EBUSY;
+ WARN_ON(1);
+ break;
+ }
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ if (ops->vidioc_try_fmt_vid_cap_mplane) {
+ ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_try_fmt_vid_cap &&
+ f->fmt.pix_mp.num_planes == 1) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_try_fmt_vid_cap(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_sp_to_mp(&f_copy, f);
+ }
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_try_fmt_vid_overlay)
@@ -804,11 +1059,47 @@ static long __video_do_ioctl(struct file *file,
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_out)
+ if (ops->vidioc_try_fmt_vid_out) {
ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
+ } else if (ops->vidioc_try_fmt_vid_out_mplane) {
+ if (fmt_sp_to_mp(f, &f_copy))
+ break;
+ ret = ops->vidioc_try_fmt_vid_out_mplane(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ if (f_copy.fmt.pix_mp.num_planes > 1) {
+ /* Drivers shouldn't adjust from 1-plane
+ * to more than 1-plane formats */
+ ret = -EBUSY;
+ WARN_ON(1);
+ break;
+ }
+ ret = fmt_mp_to_sp(&f_copy, f);
+ }
if (!ret)
v4l_print_pix_fmt(vfd, &f->fmt.pix);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ if (ops->vidioc_try_fmt_vid_out_mplane) {
+ ret = ops->vidioc_try_fmt_vid_out_mplane(file,
+ fh, f);
+ } else if (ops->vidioc_try_fmt_vid_out &&
+ f->fmt.pix_mp.num_planes == 1) {
+ if (fmt_mp_to_sp(f, &f_copy))
+ break;
+ ret = ops->vidioc_try_fmt_vid_out(file,
+ fh, &f_copy);
+ if (ret)
+ break;
+
+ ret = fmt_sp_to_mp(&f_copy, f);
+ }
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
CLEAR_AFTER_FIELD(f, fmt.win);
if (ops->vidioc_try_fmt_vid_out_overlay)
@@ -1973,7 +2264,7 @@ static unsigned long cmd_input_size(unsigned int cmd)
switch (cmd) {
CMDINSIZE(ENUM_FMT, fmtdesc, type);
CMDINSIZE(G_FMT, format, type);
- CMDINSIZE(QUERYBUF, buffer, type);
+ CMDINSIZE(QUERYBUF, buffer, length);
CMDINSIZE(G_PARM, streamparm, type);
CMDINSIZE(ENUMSTD, standard, index);
CMDINSIZE(ENUMINPUT, input, index);
@@ -1998,6 +2289,49 @@ static unsigned long cmd_input_size(unsigned int cmd)
}
}
+static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
+ void * __user *user_ptr, void ***kernel_ptr)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = parg;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) {
+ if (buf->length > VIDEO_MAX_PLANES) {
+ ret = -EINVAL;
+ break;
+ }
+ *user_ptr = (void __user *)buf->m.planes;
+ *kernel_ptr = (void **)&buf->m.planes;
+ *array_size = sizeof(struct v4l2_plane) * buf->length;
+ ret = 1;
+ }
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS: {
+ struct v4l2_ext_controls *ctrls = parg;
+
+ if (ctrls->count != 0) {
+ *user_ptr = (void __user *)ctrls->controls;
+ *kernel_ptr = (void **)&ctrls->controls;
+ *array_size = sizeof(struct v4l2_ext_control)
+ * ctrls->count;
+ ret = 1;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
long video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -2005,15 +2339,10 @@ long video_ioctl2(struct file *file,
void *mbuf = NULL;
void *parg = (void *)arg;
long err = -EINVAL;
- int is_ext_ctrl;
- size_t ctrls_size = 0;
+ bool has_array_args;
+ size_t array_size = 0;
void __user *user_ptr = NULL;
-
-#ifdef __OLD_VIDIOC_
- cmd = video_fix_command(cmd);
-#endif
- is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||
- cmd == VIDIOC_TRY_EXT_CTRLS);
+ void **kernel_ptr = NULL;
/* Copy arguments into temp kernel buffer */
if (_IOC_DIR(cmd) != _IOC_NONE) {
@@ -2043,43 +2372,43 @@ long video_ioctl2(struct file *file,
}
}
- if (is_ext_ctrl) {
- struct v4l2_ext_controls *p = parg;
+ err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
+ if (err < 0)
+ goto out;
+ has_array_args = err;
- /* In case of an error, tell the caller that it wasn't
- a specific control that caused it. */
- p->error_idx = p->count;
- user_ptr = (void __user *)p->controls;
- if (p->count) {
- ctrls_size = sizeof(struct v4l2_ext_control) * p->count;
- /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */
- mbuf = kmalloc(ctrls_size, GFP_KERNEL);
- err = -ENOMEM;
- if (NULL == mbuf)
- goto out_ext_ctrl;
- err = -EFAULT;
- if (copy_from_user(mbuf, user_ptr, ctrls_size))
- goto out_ext_ctrl;
- p->controls = mbuf;
- }
+ if (has_array_args) {
+ /*
+ * When adding new types of array args, make sure that the
+ * parent argument to ioctl (which contains the pointer to the
+ * array) fits into sbuf (so that mbuf will still remain
+ * unused up to here).
+ */
+ mbuf = kmalloc(array_size, GFP_KERNEL);
+ err = -ENOMEM;
+ if (NULL == mbuf)
+ goto out_array_args;
+ err = -EFAULT;
+ if (copy_from_user(mbuf, user_ptr, array_size))
+ goto out_array_args;
+ *kernel_ptr = mbuf;
}
/* Handles IOCTL */
err = __video_do_ioctl(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
- if (is_ext_ctrl) {
- struct v4l2_ext_controls *p = parg;
- p->controls = (void *)user_ptr;
- if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))
+ if (has_array_args) {
+ *kernel_ptr = user_ptr;
+ if (copy_to_user(user_ptr, mbuf, array_size))
err = -EFAULT;
- goto out_ext_ctrl;
+ goto out_array_args;
}
if (err < 0)
goto out;
-out_ext_ctrl:
+out_array_args:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index ac832a28e18e..a78e5c9be1a2 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -17,7 +17,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
-#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
#include <media/v4l2-mem2mem.h>
MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
@@ -65,21 +65,16 @@ struct v4l2_m2m_dev {
static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &m2m_ctx->cap_q_ctx;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (V4L2_TYPE_IS_OUTPUT(type))
return &m2m_ctx->out_q_ctx;
- default:
- printk(KERN_ERR "Invalid buffer type\n");
- return NULL;
- }
+ else
+ return &m2m_ctx->cap_q_ctx;
}
/**
- * v4l2_m2m_get_vq() - return videobuf_queue for the given type
+ * v4l2_m2m_get_vq() - return vb2_queue for the given type
*/
-struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
struct v4l2_m2m_queue_ctx *q_ctx;
@@ -95,27 +90,20 @@ EXPORT_SYMBOL(v4l2_m2m_get_vq);
/**
* v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
*/
-void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type)
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
{
- struct v4l2_m2m_queue_ctx *q_ctx;
- struct videobuf_buffer *vb = NULL;
+ struct v4l2_m2m_buffer *b = NULL;
unsigned long flags;
- q_ctx = get_queue_ctx(m2m_ctx, type);
- if (!q_ctx)
- return NULL;
-
- spin_lock_irqsave(q_ctx->q.irqlock, flags);
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
if (list_empty(&q_ctx->rdy_queue))
goto end;
- vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue);
- vb->state = VIDEOBUF_ACTIVE;
-
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
end:
- spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
- return vb;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return &b->vb;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
@@ -123,26 +111,21 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
* v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and
* return it
*/
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type)
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
{
- struct v4l2_m2m_queue_ctx *q_ctx;
- struct videobuf_buffer *vb = NULL;
+ struct v4l2_m2m_buffer *b = NULL;
unsigned long flags;
- q_ctx = get_queue_ctx(m2m_ctx, type);
- if (!q_ctx)
- return NULL;
-
- spin_lock_irqsave(q_ctx->q.irqlock, flags);
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
if (!list_empty(&q_ctx->rdy_queue)) {
- vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer,
- queue);
- list_del(&vb->queue);
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer,
+ list);
+ list_del(&b->list);
q_ctx->num_rdy--;
}
- spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
- return vb;
+ return &b->vb;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
@@ -235,20 +218,20 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
return;
}
- spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags);
+ spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
- spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
dprintk("No input buffers available\n");
return;
}
if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
- spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
dprintk("No output buffers available\n");
return;
}
- spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
if (m2m_dev->m2m_ops->job_ready
&& (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
@@ -291,6 +274,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
list_del(&m2m_dev->curr_ctx->queue);
m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+ wake_up(&m2m_dev->curr_ctx->finished);
m2m_dev->curr_ctx = NULL;
spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
@@ -309,10 +293,10 @@ EXPORT_SYMBOL(v4l2_m2m_job_finish);
int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_requestbuffers *reqbufs)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
- return videobuf_reqbufs(vq, reqbufs);
+ return vb2_reqbufs(vq, reqbufs);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
@@ -324,15 +308,22 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_buffer *buf)
{
- struct videobuf_queue *vq;
- int ret;
+ struct vb2_queue *vq;
+ int ret = 0;
+ unsigned int i;
vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- ret = videobuf_querybuf(vq, buf);
-
- if (buf->memory == V4L2_MEMORY_MMAP
- && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- buf->m.offset += DST_QUEUE_OFF_BASE;
+ ret = vb2_querybuf(vq, buf);
+
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+ for (i = 0; i < buf->length; ++i)
+ buf->m.planes[i].m.mem_offset
+ += DST_QUEUE_OFF_BASE;
+ } else {
+ buf->m.offset += DST_QUEUE_OFF_BASE;
+ }
}
return ret;
@@ -346,11 +337,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_buffer *buf)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
int ret;
vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- ret = videobuf_qbuf(vq, buf);
+ ret = vb2_qbuf(vq, buf);
if (!ret)
v4l2_m2m_try_schedule(m2m_ctx);
@@ -365,10 +356,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_buffer *buf)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+ return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
@@ -378,11 +369,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
int ret;
vq = v4l2_m2m_get_vq(m2m_ctx, type);
- ret = videobuf_streamon(vq);
+ ret = vb2_streamon(vq, type);
if (!ret)
v4l2_m2m_try_schedule(m2m_ctx);
@@ -396,10 +387,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(m2m_ctx, type);
- return videobuf_streamoff(vq);
+ return vb2_streamoff(vq, type);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
@@ -414,44 +405,53 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct poll_table_struct *wait)
{
- struct videobuf_queue *src_q, *dst_q;
- struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL;
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
unsigned int rc = 0;
+ unsigned long flags;
src_q = v4l2_m2m_get_src_vq(m2m_ctx);
dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
- videobuf_queue_lock(src_q);
- videobuf_queue_lock(dst_q);
-
- if (src_q->streaming && !list_empty(&src_q->stream))
- src_vb = list_first_entry(&src_q->stream,
- struct videobuf_buffer, stream);
- if (dst_q->streaming && !list_empty(&dst_q->stream))
- dst_vb = list_first_entry(&dst_q->stream,
- struct videobuf_buffer, stream);
-
- if (!src_vb && !dst_vb) {
+ /*
+ * There has to be at least one buffer queued on each queued_list, which
+ * means either in driver already or waiting for driver to claim it
+ * and start processing.
+ */
+ if ((!src_q->streaming || list_empty(&src_q->queued_list))
+ && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
rc = POLLERR;
goto end;
}
- if (src_vb) {
- poll_wait(file, &src_vb->done, wait);
- if (src_vb->state == VIDEOBUF_DONE
- || src_vb->state == VIDEOBUF_ERROR)
- rc |= POLLOUT | POLLWRNORM;
- }
- if (dst_vb) {
- poll_wait(file, &dst_vb->done, wait);
- if (dst_vb->state == VIDEOBUF_DONE
- || dst_vb->state == VIDEOBUF_ERROR)
- rc |= POLLIN | POLLRDNORM;
- }
+ if (m2m_ctx->m2m_dev->m2m_ops->unlock)
+ m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv);
+
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+
+ if (m2m_ctx->m2m_dev->m2m_ops->lock)
+ m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv);
+
+ spin_lock_irqsave(&src_q->done_lock, flags);
+ if (!list_empty(&src_q->done_list))
+ src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+ || src_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLOUT | POLLWRNORM;
+ spin_unlock_irqrestore(&src_q->done_lock, flags);
+
+ spin_lock_irqsave(&dst_q->done_lock, flags);
+ if (!list_empty(&dst_q->done_list))
+ dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+ || dst_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&dst_q->done_lock, flags);
end:
- videobuf_queue_unlock(dst_q);
- videobuf_queue_unlock(src_q);
return rc;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
@@ -470,7 +470,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct vm_area_struct *vma)
{
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- struct videobuf_queue *vq;
+ struct vb2_queue *vq;
if (offset < DST_QUEUE_OFF_BASE) {
vq = v4l2_m2m_get_src_vq(m2m_ctx);
@@ -479,7 +479,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
}
- return videobuf_mmap_mapper(vq, vma);
+ return vb2_mmap(vq, vma);
}
EXPORT_SYMBOL(v4l2_m2m_mmap);
@@ -531,36 +531,41 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_release);
*
* Usually called from driver's open() function.
*/
-struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev,
- void (*vq_init)(void *priv, struct videobuf_queue *,
- enum v4l2_buf_type))
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+ void *drv_priv,
+ int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
{
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
-
- if (!vq_init)
- return ERR_PTR(-EINVAL);
+ int ret;
m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
if (!m2m_ctx)
return ERR_PTR(-ENOMEM);
- m2m_ctx->priv = priv;
+ m2m_ctx->priv = drv_priv;
m2m_ctx->m2m_dev = m2m_dev;
+ init_waitqueue_head(&m2m_ctx->finished);
- out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- cap_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ out_q_ctx = &m2m_ctx->out_q_ctx;
+ cap_q_ctx = &m2m_ctx->cap_q_ctx;
INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
+ spin_lock_init(&out_q_ctx->rdy_spinlock);
+ spin_lock_init(&cap_q_ctx->rdy_spinlock);
INIT_LIST_HEAD(&m2m_ctx->queue);
- vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv;
+ ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q);
+
+ if (ret)
+ goto err;
return m2m_ctx;
+err:
+ kfree(m2m_ctx);
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
@@ -572,7 +577,6 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
{
struct v4l2_m2m_dev *m2m_dev;
- struct videobuf_buffer *vb;
unsigned long flags;
m2m_dev = m2m_ctx->m2m_dev;
@@ -582,10 +586,7 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
- vb = v4l2_m2m_next_dst_buf(m2m_ctx);
- BUG_ON(NULL == vb);
- wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE
- && vb->state != VIDEOBUF_QUEUED);
+ wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING));
} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
list_del(&m2m_ctx->queue);
m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
@@ -597,11 +598,8 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
}
- videobuf_stop(&m2m_ctx->cap_q_ctx.q);
- videobuf_stop(&m2m_ctx->out_q_ctx.q);
-
- videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q);
- videobuf_mmap_free(&m2m_ctx->out_q_ctx.q);
+ vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
+ vb2_queue_release(&m2m_ctx->out_q_ctx.q);
kfree(m2m_ctx);
}
@@ -611,23 +609,21 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
* v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list.
*
* Call from buf_queue(), videobuf_queue_ops callback.
- *
- * Locking: Caller holds q->irqlock (taken by videobuf before calling buf_queue
- * callback in the driver).
*/
-void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb)
{
+ struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb);
struct v4l2_m2m_queue_ctx *q_ctx;
+ unsigned long flags;
- q_ctx = get_queue_ctx(m2m_ctx, vq->type);
+ q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type);
if (!q_ctx)
return;
- list_add_tail(&vb->queue, &q_ctx->rdy_queue);
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ list_add_tail(&b->list, &q_ctx->rdy_queue);
q_ctx->num_rdy++;
-
- vb->state = VIDEOBUF_QUEUED;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 2f973cd56408..3f0146fcf752 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -1246,6 +1246,62 @@ static const struct v4l2_ioctl_ops viacam_ioctl_ops = {
/*
* Power management.
*/
+#ifdef CONFIG_PM
+
+static int viacam_suspend(void *priv)
+{
+ struct via_camera *cam = priv;
+ enum viacam_opstate state = cam->opstate;
+
+ if (cam->opstate != S_IDLE) {
+ viacam_stop_engine(cam);
+ cam->opstate = state; /* So resume restarts */
+ }
+
+ return 0;
+}
+
+static int viacam_resume(void *priv)
+{
+ struct via_camera *cam = priv;
+ int ret = 0;
+
+ /*
+ * Get back to a reasonable operating state.
+ */
+ via_write_reg_mask(VIASR, 0x78, 0, 0x80);
+ via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
+ viacam_int_disable(cam);
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ /*
+ * Make sure the sensor's power state is correct
+ */
+ if (cam->users > 0)
+ via_sensor_power_up(cam);
+ else
+ via_sensor_power_down(cam);
+ /*
+ * If it was operating, try to restart it.
+ */
+ if (cam->opstate != S_IDLE) {
+ mutex_lock(&cam->lock);
+ ret = viacam_configure_sensor(cam);
+ if (!ret)
+ ret = viacam_config_controller(cam);
+ mutex_unlock(&cam->lock);
+ if (!ret)
+ viacam_start_engine(cam);
+ }
+
+ return ret;
+}
+
+static struct viafb_pm_hooks viacam_pm_hooks = {
+ .suspend = viacam_suspend,
+ .resume = viacam_resume
+};
+
+#endif /* CONFIG_PM */
/*
* Setup stuff.
@@ -1369,6 +1425,14 @@ static __devinit int viacam_probe(struct platform_device *pdev)
goto out_irq;
video_set_drvdata(&cam->vdev, cam);
+#ifdef CONFIG_PM
+ /*
+ * Hook into PM events
+ */
+ viacam_pm_hooks.private = cam;
+ viafb_pm_register(&viacam_pm_hooks);
+#endif
+
/* Power the sensor down until somebody opens the device */
via_sensor_power_down(cam);
return 0;
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
new file mode 100644
index 000000000000..cc7ab0a17b68
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,1804 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <media/videobuf2-core.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vb2: " fmt, ## arg); \
+ } while (0)
+
+#define call_memop(q, plane, op, args...) \
+ (((q)->mem_ops->op) ? \
+ ((q)->mem_ops->op(args)) : 0)
+
+#define call_qop(q, op, args...) \
+ (((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+
+/**
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
+ unsigned long *plane_sizes)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ int plane;
+
+ /* Allocate memory for all planes in this buffer */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
+ plane_sizes[plane]);
+ if (!mem_priv)
+ goto free;
+
+ /* Associate allocator private data with this plane */
+ vb->planes[plane].mem_priv = mem_priv;
+ vb->v4l2_planes[plane].length = plane_sizes[plane];
+ }
+
+ return 0;
+free:
+ /* Free already allocated memory if one of the allocations failed */
+ for (; plane > 0; --plane)
+ call_memop(q, plane, put, vb->planes[plane - 1].mem_priv);
+
+ return -ENOMEM;
+}
+
+/**
+ * __vb2_buf_mem_free() - free memory of the given buffer
+ */
+static void __vb2_buf_mem_free(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ call_memop(q, plane, put, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ dprintk(3, "Freed plane %d of buffer %d\n",
+ plane, vb->v4l2_buf.index);
+ }
+}
+
+/**
+ * __vb2_buf_userptr_put() - release userspace memory associated with
+ * a USERPTR buffer
+ */
+static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+
+ if (mem_priv) {
+ call_memop(q, plane, put_userptr, mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ }
+ }
+}
+
+/**
+ * __setup_offsets() - setup unique offsets ("cookies") for every plane in
+ * every buffer on the queue
+ */
+static void __setup_offsets(struct vb2_queue *q)
+{
+ unsigned int buffer, plane;
+ struct vb2_buffer *vb;
+ unsigned long off = 0;
+
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ vb = q->bufs[buffer];
+ if (!vb)
+ continue;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->v4l2_planes[plane].m.mem_offset = off;
+
+ dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
+ buffer, plane, off);
+
+ off += vb->v4l2_planes[plane].length;
+ off = PAGE_ALIGN(off);
+ }
+ }
+}
+
+/**
+ * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
+ * video buffer memory for all buffers/planes on the queue and initializes the
+ * queue
+ *
+ * Returns the number of buffers successfully allocated.
+ */
+static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
+ unsigned int num_buffers, unsigned int num_planes,
+ unsigned long plane_sizes[])
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+ int ret;
+
+ for (buffer = 0; buffer < num_buffers; ++buffer) {
+ /* Allocate videobuf buffer structures */
+ vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
+ if (!vb) {
+ dprintk(1, "Memory alloc for buffer struct failed\n");
+ break;
+ }
+
+ /* Length stores number of planes for multiplanar buffers */
+ if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
+ vb->v4l2_buf.length = num_planes;
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ vb->vb2_queue = q;
+ vb->num_planes = num_planes;
+ vb->v4l2_buf.index = buffer;
+ vb->v4l2_buf.type = q->type;
+ vb->v4l2_buf.memory = memory;
+
+ /* Allocate video buffer memory for the MMAP type */
+ if (memory == V4L2_MEMORY_MMAP) {
+ ret = __vb2_buf_mem_alloc(vb, plane_sizes);
+ if (ret) {
+ dprintk(1, "Failed allocating memory for "
+ "buffer %d\n", buffer);
+ kfree(vb);
+ break;
+ }
+ /*
+ * Call the driver-provided buffer initialization
+ * callback, if given. An error in initialization
+ * results in queue setup failure.
+ */
+ ret = call_qop(q, buf_init, vb);
+ if (ret) {
+ dprintk(1, "Buffer %d %p initialization"
+ " failed\n", buffer, vb);
+ __vb2_buf_mem_free(vb);
+ kfree(vb);
+ break;
+ }
+ }
+
+ q->bufs[buffer] = vb;
+ }
+
+ q->num_buffers = buffer;
+
+ __setup_offsets(q);
+
+ dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+ q->num_buffers, num_planes);
+
+ return buffer;
+}
+
+/**
+ * __vb2_free_mem() - release all video buffer memory for a given queue
+ */
+static void __vb2_free_mem(struct vb2_queue *q)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ vb = q->bufs[buffer];
+ if (!vb)
+ continue;
+
+ /* Free MMAP buffers or release USERPTR buffers */
+ if (q->memory == V4L2_MEMORY_MMAP)
+ __vb2_buf_mem_free(vb);
+ else
+ __vb2_buf_userptr_put(vb);
+ }
+}
+
+/**
+ * __vb2_queue_free() - free the queue - video memory and related information
+ * and return the queue to an uninitialized state. Might be called even if the
+ * queue has already been freed.
+ */
+static int __vb2_queue_free(struct vb2_queue *q)
+{
+ unsigned int buffer;
+
+ /* Call driver-provided cleanup function for each buffer, if provided */
+ if (q->ops->buf_cleanup) {
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (NULL == q->bufs[buffer])
+ continue;
+ q->ops->buf_cleanup(q->bufs[buffer]);
+ }
+ }
+
+ /* Release video buffer memory */
+ __vb2_free_mem(q);
+
+ /* Free videobuf buffers */
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ kfree(q->bufs[buffer]);
+ q->bufs[buffer] = NULL;
+ }
+
+ q->num_buffers = 0;
+ q->memory = 0;
+
+ return 0;
+}
+
+/**
+ * __verify_planes_array() - verify that the planes array passed in struct
+ * v4l2_buffer from userspace can be safely used
+ */
+static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ /* Is memory for copying plane information present? */
+ if (NULL == b->m.planes) {
+ dprintk(1, "Multi-planar buffer passed but "
+ "planes array not provided\n");
+ return -EINVAL;
+ }
+
+ if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
+ dprintk(1, "Incorrect planes array length, "
+ "expected %d, got %d\n", vb->num_planes, b->length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
+ * returned to userspace
+ */
+static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret = 0;
+
+ /* Copy back data such as timestamp, input, etc. */
+ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+ b->input = vb->v4l2_buf.input;
+ b->reserved = vb->v4l2_buf.reserved;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
+ ret = __verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ /*
+ * Fill in plane-related data if userspace provided an array
+ * for it. The memory and size is verified above.
+ */
+ memcpy(b->m.planes, vb->v4l2_planes,
+ b->length * sizeof(struct v4l2_plane));
+ } else {
+ /*
+ * We use length and offset in v4l2_planes array even for
+ * single-planar buffers, but userspace does not.
+ */
+ b->length = vb->v4l2_planes[0].length;
+ b->bytesused = vb->v4l2_planes[0].bytesused;
+ if (q->memory == V4L2_MEMORY_MMAP)
+ b->m.offset = vb->v4l2_planes[0].m.mem_offset;
+ else if (q->memory == V4L2_MEMORY_USERPTR)
+ b->m.userptr = vb->v4l2_planes[0].m.userptr;
+ }
+
+ b->flags = 0;
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_QUEUED:
+ case VB2_BUF_STATE_ACTIVE:
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case VB2_BUF_STATE_ERROR:
+ b->flags |= V4L2_BUF_FLAG_ERROR;
+ /* fall through */
+ case VB2_BUF_STATE_DONE:
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ break;
+ case VB2_BUF_STATE_DEQUEUED:
+ /* nothing */
+ break;
+ }
+
+ if (vb->num_planes_mapped == vb->num_planes)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ return ret;
+}
+
+/**
+ * vb2_querybuf() - query video buffer information
+ * @q: videobuf queue
+ * @b: buffer struct passed from userspace to vidioc_querybuf handler
+ * in driver
+ *
+ * Should be called from vidioc_querybuf ioctl handler in driver.
+ * This function will verify the passed v4l2_buffer structure and fill the
+ * relevant information for the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_querybuf handler in driver.
+ */
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+
+ if (b->type != q->type) {
+ dprintk(1, "querybuf: wrong buffer type\n");
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "querybuf: buffer index out of range\n");
+ return -EINVAL;
+ }
+ vb = q->bufs[b->index];
+
+ return __fill_v4l2_buffer(vb, b);
+}
+EXPORT_SYMBOL(vb2_querybuf);
+
+/**
+ * __verify_userptr_ops() - verify that all memory operations required for
+ * USERPTR queue type have been provided
+ */
+static int __verify_userptr_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr ||
+ !q->mem_ops->put_userptr)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * __verify_mmap_ops() - verify that all memory operations required for
+ * MMAP queue type have been provided
+ */
+static int __verify_mmap_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
+ !q->mem_ops->put || !q->mem_ops->mmap)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+ unsigned int buffer, plane;
+ struct vb2_buffer *vb;
+
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ vb = q->bufs[buffer];
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /*
+ * If num_users() has not been provided, call_memop
+ * will return 0, apparently nobody cares about this
+ * case anyway. If num_users() returns more than 1,
+ * we are not the only user of the plane's memory.
+ */
+ if (call_memop(q, plane, num_users,
+ vb->planes[plane].mem_priv) > 1)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * vb2_reqbufs() - Initiate streaming
+ * @q: videobuf2 queue
+ * @req: struct passed from userspace to vidioc_reqbufs handler in driver
+ *
+ * Should be called from vidioc_reqbufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies streaming parameters passed from the userspace,
+ * 2) sets up the queue,
+ * 3) negotiates number of buffers and planes per buffer with the driver
+ * to be used during streaming,
+ * 4) allocates internal buffer structures (struct vb2_buffer), according to
+ * the agreed parameters,
+ * 5) for MMAP memory type, allocates actual video memory, using the
+ * memory handling/allocation routines provided during queue initialization
+ *
+ * If req->count is 0, all the memory will be freed instead.
+ * If the queue has been allocated previously (by a previous vb2_reqbufs) call
+ * and the queue is not busy, memory will be reallocated.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_reqbufs handler in driver.
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ unsigned int num_buffers, num_planes;
+ unsigned long plane_sizes[VIDEO_MAX_PLANES];
+ int ret = 0;
+
+ if (q->fileio) {
+ dprintk(1, "reqbufs: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (req->memory != V4L2_MEMORY_MMAP
+ && req->memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "reqbufs: unsupported memory type\n");
+ return -EINVAL;
+ }
+
+ if (req->type != q->type) {
+ dprintk(1, "reqbufs: requested type is incorrect\n");
+ return -EINVAL;
+ }
+
+ if (q->streaming) {
+ dprintk(1, "reqbufs: streaming active\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (req->count == 0 || q->num_buffers != 0) {
+ /*
+ * We already have buffers allocated, so first check if they
+ * are not in use and can be freed.
+ */
+ if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+ dprintk(1, "reqbufs: memory in use, cannot free\n");
+ return -EBUSY;
+ }
+
+ ret = __vb2_queue_free(q);
+ if (ret != 0)
+ return ret;
+ }
+
+ /*
+ * Make sure the requested values and current defaults are sane.
+ */
+ num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+ memset(plane_sizes, 0, sizeof(plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+
+ /*
+ * Ask the driver how many buffers and planes per buffer it requires.
+ * Driver also sets the size and allocator context for each plane.
+ */
+ ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
+ plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* Finally, allocate buffers and video memory */
+ ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
+ plane_sizes);
+ if (ret < 0) {
+ dprintk(1, "Memory allocation failed with error: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check if driver can handle the allocated number of buffers.
+ */
+ if (ret < num_buffers) {
+ unsigned int orig_num_buffers;
+
+ orig_num_buffers = num_buffers = ret;
+ ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
+ plane_sizes, q->alloc_ctx);
+ if (ret)
+ goto free_mem;
+
+ if (orig_num_buffers < num_buffers) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+
+ /*
+ * Ok, driver accepted smaller number of buffers.
+ */
+ ret = num_buffers;
+ }
+
+ q->memory = req->memory;
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ req->count = ret;
+
+ return 0;
+
+free_mem:
+ __vb2_queue_free(q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+/**
+ * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
+ * @vb: vb2_buffer to which the plane in question belongs to
+ * @plane_no: plane number for which the address is to be returned
+ *
+ * This function returns a kernel virtual address of a given plane if
+ * such a mapping exist, NULL otherwise.
+ */
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (plane_no > vb->num_planes)
+ return NULL;
+
+ return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv);
+
+}
+EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
+
+/**
+ * vb2_plane_cookie() - Return allocator specific cookie for the given plane
+ * @vb: vb2_buffer to which the plane in question belongs to
+ * @plane_no: plane number for which the cookie is to be returned
+ *
+ * This function returns an allocator specific cookie for a given plane if
+ * available, NULL otherwise. The allocator should provide some simple static
+ * inline function, which would convert this cookie to the allocator specific
+ * type that can be used directly by the driver to access the buffer. This can
+ * be for example physical address, pointer to scatter list or IOMMU mapping.
+ */
+void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (plane_no > vb->num_planes)
+ return NULL;
+
+ return call_memop(q, plane_no, cookie, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+/**
+ * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
+ * @vb: vb2_buffer returned from the driver
+ * @state: either VB2_BUF_STATE_DONE if the operation finished successfully
+ * or VB2_BUF_STATE_ERROR if the operation finished with an error
+ *
+ * This function should be called by the driver after a hardware operation on
+ * a buffer is finished and the buffer may be returned to userspace. The driver
+ * cannot use this buffer anymore until it is queued back to it by videobuf
+ * by the means of buf_queue callback. Only buffers previously queued to the
+ * driver by buf_queue can be passed to this function.
+ */
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned long flags;
+
+ if (vb->state != VB2_BUF_STATE_ACTIVE)
+ return;
+
+ if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
+ return;
+
+ dprintk(4, "Done processing on buffer %d, state: %d\n",
+ vb->v4l2_buf.index, vb->state);
+
+ /* Add the buffer to the done buffers list */
+ spin_lock_irqsave(&q->done_lock, flags);
+ vb->state = state;
+ list_add_tail(&vb->done_entry, &q->done_list);
+ atomic_dec(&q->queued_count);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ /* Inform any processes that may be waiting for buffers */
+ wake_up(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_buffer_done);
+
+/**
+ * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
+ * a v4l2_buffer by the userspace
+ */
+static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+ struct v4l2_plane *v4l2_planes)
+{
+ unsigned int plane;
+ int ret;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ /*
+ * Verify that the userspace gave us a valid array for
+ * plane information.
+ */
+ ret = __verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ /* Fill in driver-provided information for OUTPUT types */
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * Will have to go up to b->length when API starts
+ * accepting variable number of planes.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].bytesused =
+ b->m.planes[plane].bytesused;
+ v4l2_planes[plane].data_offset =
+ b->m.planes[plane].data_offset;
+ }
+ }
+
+ if (b->memory == V4L2_MEMORY_USERPTR) {
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].m.userptr =
+ b->m.planes[plane].m.userptr;
+ v4l2_planes[plane].length =
+ b->m.planes[plane].length;
+ }
+ }
+ } else {
+ /*
+ * Single-planar buffers do not use planes array,
+ * so fill in relevant v4l2_buffer struct fields instead.
+ * In videobuf we use our internal V4l2_planes struct for
+ * single-planar buffers as well, for simplicity.
+ */
+ if (V4L2_TYPE_IS_OUTPUT(b->type))
+ v4l2_planes[0].bytesused = b->bytesused;
+
+ if (b->memory == V4L2_MEMORY_USERPTR) {
+ v4l2_planes[0].m.userptr = b->m.userptr;
+ v4l2_planes[0].length = b->length;
+ }
+ }
+
+ vb->v4l2_buf.field = b->field;
+ vb->v4l2_buf.timestamp = b->timestamp;
+
+ return 0;
+}
+
+/**
+ * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ */
+static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ unsigned int plane;
+ int ret;
+ int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+
+ /* Verify and copy relevant information provided by the userspace */
+ ret = __fill_vb2_buffer(vb, b, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /* Skip the plane if already verified */
+ if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
+ && vb->v4l2_planes[plane].length == planes[plane].length)
+ continue;
+
+ dprintk(3, "qbuf: userspace address for plane %d changed, "
+ "reacquiring memory\n", plane);
+
+ /* Release previously acquired memory if present */
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, plane, put_userptr,
+ vb->planes[plane].mem_priv);
+
+ vb->planes[plane].mem_priv = NULL;
+
+ /* Acquire each plane's memory */
+ if (q->mem_ops->get_userptr) {
+ mem_priv = q->mem_ops->get_userptr(q->alloc_ctx[plane],
+ planes[plane].m.userptr,
+ planes[plane].length,
+ write);
+ if (IS_ERR(mem_priv)) {
+ dprintk(1, "qbuf: failed acquiring userspace "
+ "memory for plane %d\n", plane);
+ ret = PTR_ERR(mem_priv);
+ goto err;
+ }
+ vb->planes[plane].mem_priv = mem_priv;
+ }
+ }
+
+ /*
+ * Call driver-specific initialization on the newly acquired buffer,
+ * if provided.
+ */
+ ret = call_qop(q, buf_init, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer initialization failed\n");
+ goto err;
+ }
+
+ /*
+ * Now that everything is in order, copy relevant information
+ * provided by userspace.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ vb->v4l2_planes[plane] = planes[plane];
+
+ return 0;
+err:
+ /* In case of errors, release planes that were already acquired */
+ for (; plane > 0; --plane) {
+ call_memop(q, plane, put_userptr,
+ vb->planes[plane - 1].mem_priv);
+ vb->planes[plane - 1].mem_priv = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
+}
+
+/**
+ * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
+ */
+static void __enqueue_in_driver(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ vb->state = VB2_BUF_STATE_ACTIVE;
+ atomic_inc(&q->queued_count);
+ q->ops->buf_queue(vb);
+}
+
+/**
+ * vb2_qbuf() - Queue a buffer from userspace
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_qbuf handler
+ * in driver
+ *
+ * Should be called from vidioc_qbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ * driver-specific buffer initialization can be performed,
+ * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
+ * callback for processing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_qbuf handler in driver.
+ */
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret = 0;
+
+ if (q->fileio) {
+ dprintk(1, "qbuf: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "qbuf: invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "qbuf: buffer index out of range\n");
+ return -EINVAL;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "qbuf: buffer is NULL\n");
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "qbuf: invalid memory type\n");
+ return -EINVAL;
+ }
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(1, "qbuf: buffer already in use\n");
+ return -EINVAL;
+ }
+
+ if (q->memory == V4L2_MEMORY_MMAP)
+ ret = __qbuf_mmap(vb, b);
+ else if (q->memory == V4L2_MEMORY_USERPTR)
+ ret = __qbuf_userptr(vb, b);
+ else {
+ WARN(1, "Invalid queue type\n");
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ ret = call_qop(q, buf_prepare, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer preparation failed\n");
+ return ret;
+ }
+
+ /*
+ * Add to the queued buffers list, a buffer will stay on it until
+ * dequeued in dqbuf.
+ */
+ list_add_tail(&vb->queued_entry, &q->queued_list);
+ vb->state = VB2_BUF_STATE_QUEUED;
+
+ /*
+ * If already streaming, give the buffer to driver for processing.
+ * If not, the buffer will be given to driver on next streamon.
+ */
+ if (q->streaming)
+ __enqueue_in_driver(vb);
+
+ dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_qbuf);
+
+/**
+ * __vb2_wait_for_done_vb() - wait for a buffer to become available
+ * for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
+{
+ /*
+ * All operations on vb_done_list are performed under done_lock
+ * spinlock protection. However, buffers may be removed from
+ * it and returned to userspace only while holding both driver's
+ * lock and the done_lock spinlock. Thus we can be sure that as
+ * long as we hold the driver's lock, the list will remain not
+ * empty if list_empty() check succeeds.
+ */
+
+ for (;;) {
+ int ret;
+
+ if (!q->streaming) {
+ dprintk(1, "Streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ if (!list_empty(&q->done_list)) {
+ /*
+ * Found a buffer that we were waiting for.
+ */
+ break;
+ }
+
+ if (nonblocking) {
+ dprintk(1, "Nonblocking and no buffers to dequeue, "
+ "will not wait\n");
+ return -EAGAIN;
+ }
+
+ /*
+ * We are streaming and blocking, wait for another buffer to
+ * become ready or for streamoff. Driver's lock is released to
+ * allow streamoff or qbuf to be called while waiting.
+ */
+ call_qop(q, wait_prepare, q);
+
+ /*
+ * All locks have been released, it is safe to sleep now.
+ */
+ dprintk(3, "Will sleep waiting for buffers\n");
+ ret = wait_event_interruptible(q->done_wq,
+ !list_empty(&q->done_list) || !q->streaming);
+
+ /*
+ * We need to reevaluate both conditions again after reacquiring
+ * the locks or return an error if one occurred.
+ */
+ call_qop(q, wait_finish, q);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * __vb2_get_done_vb() - get a buffer ready for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
+ int nonblocking)
+{
+ unsigned long flags;
+ int ret;
+
+ /*
+ * Wait for at least one buffer to become available on the done_list.
+ */
+ ret = __vb2_wait_for_done_vb(q, nonblocking);
+ if (ret)
+ return ret;
+
+ /*
+ * Driver's lock has been held since we last verified that done_list
+ * is not empty, so no need for another list_empty(done_list) check.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
+ list_del(&(*vb)->done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ return 0;
+}
+
+/**
+ * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2
+ * @q: videobuf2 queue
+ *
+ * This function will wait until all buffers that have been given to the driver
+ * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call
+ * wait_prepare, wait_finish pair. It is intended to be called with all locks
+ * taken, for example from stop_streaming() callback.
+ */
+int vb2_wait_for_all_buffers(struct vb2_queue *q)
+{
+ if (!q->streaming) {
+ dprintk(1, "Streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ wait_event(q->done_wq, !atomic_read(&q->queued_count));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
+
+/**
+ * vb2_dqbuf() - Dequeue a buffer to the userspace
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_dqbuf handler
+ * in driver
+ * @nonblocking: if true, this call will not sleep waiting for a buffer if no
+ * buffers ready for dequeuing are present. Normally the driver
+ * would be passing (file->f_flags & O_NONBLOCK) here
+ *
+ * Should be called from vidioc_dqbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_finish callback in the driver (if provided), in which
+ * driver can perform any additional operations that may be required before
+ * returning the buffer to userspace, such as cache sync,
+ * 3) the buffer struct members are filled with relevant information for
+ * the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_dqbuf handler in driver.
+ */
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+ struct vb2_buffer *vb = NULL;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "dqbuf: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "dqbuf: invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = __vb2_get_done_vb(q, &vb, nonblocking);
+ if (ret < 0) {
+ dprintk(1, "dqbuf: error getting next done buffer\n");
+ return ret;
+ }
+
+ ret = call_qop(q, buf_finish, vb);
+ if (ret) {
+ dprintk(1, "dqbuf: buffer finish failed\n");
+ return ret;
+ }
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DONE:
+ dprintk(3, "dqbuf: Returning done buffer\n");
+ break;
+ case VB2_BUF_STATE_ERROR:
+ dprintk(3, "dqbuf: Returning done buffer with errors\n");
+ break;
+ default:
+ dprintk(1, "dqbuf: Invalid buffer state\n");
+ return -EINVAL;
+ }
+
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+ /* Remove from videobuf queue */
+ list_del(&vb->queued_entry);
+
+ dprintk(1, "dqbuf of buffer %d, with state %d\n",
+ vb->v4l2_buf.index, vb->state);
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_dqbuf);
+
+/**
+ * vb2_streamon - start streaming
+ * @q: videobuf2 queue
+ * @type: type argument passed from userspace to vidioc_streamon handler
+ *
+ * Should be called from vidioc_streamon handler of a driver.
+ * This function:
+ * 1) verifies current state
+ * 2) starts streaming and passes any previously queued buffers to the driver
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamon handler in the driver.
+ */
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ struct vb2_buffer *vb;
+
+ if (q->fileio) {
+ dprintk(1, "streamon: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "streamon: invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (q->streaming) {
+ dprintk(1, "streamon: already streaming\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Cannot start streaming on an OUTPUT device if no buffers have
+ * been queued yet.
+ */
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ if (list_empty(&q->queued_list)) {
+ dprintk(1, "streamon: no output buffers queued\n");
+ return -EINVAL;
+ }
+ }
+
+ q->streaming = 1;
+
+ /*
+ * Let driver notice that streaming state has been enabled.
+ */
+ call_qop(q, start_streaming, q);
+
+ /*
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
+ */
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
+
+ dprintk(3, "Streamon successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+ unsigned int i;
+
+ /*
+ * Tell driver to stop all transactions and release all queued
+ * buffers.
+ */
+ if (q->streaming)
+ call_qop(q, stop_streaming, q);
+ q->streaming = 0;
+
+ /*
+ * Remove all buffers from videobuf's list...
+ */
+ INIT_LIST_HEAD(&q->queued_list);
+ /*
+ * ...and done list; userspace will not receive any buffers it
+ * has not already dequeued before initiating cancel.
+ */
+ INIT_LIST_HEAD(&q->done_list);
+ wake_up_all(&q->done_wq);
+
+ /*
+ * Reinitialize all buffers for next use.
+ */
+ for (i = 0; i < q->num_buffers; ++i)
+ q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
+ * vb2_streamoff - stop streaming
+ * @q: videobuf2 queue
+ * @type: type argument passed from userspace to vidioc_streamoff handler
+ *
+ * Should be called from vidioc_streamoff handler of a driver.
+ * This function:
+ * 1) verifies current state,
+ * 2) stop streaming and dequeues any queued buffers, including those previously
+ * passed to the driver (after waiting for the driver to finish).
+ *
+ * This call can be used for pausing playback.
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamoff handler in the driver
+ */
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ if (q->fileio) {
+ dprintk(1, "streamoff: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "streamoff: invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (!q->streaming) {
+ dprintk(1, "streamoff: not streaming\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Cancel will pause streaming and remove all buffers from the driver
+ * and videobuf, effectively returning control over them to userspace.
+ */
+ __vb2_queue_cancel(q);
+
+ dprintk(3, "Streamoff successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamoff);
+
+/**
+ * __find_plane_by_offset() - find plane associated with the given offset off
+ */
+static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
+ unsigned int *_buffer, unsigned int *_plane)
+{
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+
+ /*
+ * Go over all buffers and their planes, comparing the given offset
+ * with an offset assigned to each plane. If a match is found,
+ * return its buffer and plane numbers.
+ */
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ vb = q->bufs[buffer];
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->v4l2_planes[plane].m.mem_offset == off) {
+ *_buffer = buffer;
+ *_plane = plane;
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vb2_mmap() - map video buffers into application address space
+ * @q: videobuf2 queue
+ * @vma: vma passed to the mmap file operation handler in the driver
+ *
+ * Should be called from mmap file operation handler of a driver.
+ * This function maps one plane of one of the available video buffers to
+ * userspace. To map whole video memory allocated on reqbufs, this function
+ * has to be called once per each plane per each buffer previously allocated.
+ *
+ * When the userspace application calls mmap, it passes to it an offset returned
+ * to it earlier by the means of vidioc_querybuf handler. That offset acts as
+ * a "cookie", which is then used to identify the plane to be mapped.
+ * This function finds a plane with a matching offset and a mapping is performed
+ * by the means of a provided memory operation.
+ *
+ * The return values from this function are intended to be directly returned
+ * from the mmap handler in driver.
+ */
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
+{
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+ struct vb2_plane *vb_plane;
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+ int ret;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Check memory area access mode.
+ */
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk(1, "Invalid vma flags, VM_SHARED needed\n");
+ return -EINVAL;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk(1, "Invalid vma flags, VM_WRITE needed\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!(vma->vm_flags & VM_READ)) {
+ dprintk(1, "Invalid vma flags, VM_READ needed\n");
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace.
+ */
+ ret = __find_plane_by_offset(q, off, &buffer, &plane);
+ if (ret)
+ return ret;
+
+ vb = q->bufs[buffer];
+ vb_plane = &vb->planes[plane];
+
+ ret = q->mem_ops->mmap(vb_plane->mem_priv, vma);
+ if (ret)
+ return ret;
+
+ vb_plane->mapped = 1;
+ vb->num_planes_mapped++;
+
+ dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
+
+/**
+ * vb2_poll() - implements poll userspace operation
+ * @q: videobuf2 queue
+ * @file: file argument passed to the poll file operation handler
+ * @wait: wait argument passed to the poll file operation handler
+ *
+ * This function implements poll file operation handler for a driver.
+ * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
+ * be informed that the file descriptor of a video device is available for
+ * reading.
+ * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
+ * will be reported as available for writing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from poll handler in driver.
+ */
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
+{
+ unsigned long flags;
+ unsigned int ret;
+ struct vb2_buffer *vb = NULL;
+
+ /*
+ * Start file io emulator if streaming api has not been used yet.
+ */
+ if (q->num_buffers == 0 && q->fileio == NULL) {
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) {
+ ret = __vb2_init_fileio(q, 1);
+ if (ret)
+ return ret;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) {
+ ret = __vb2_init_fileio(q, 0);
+ if (ret)
+ return ret;
+ /*
+ * Write to OUTPUT queue can be done immediately.
+ */
+ return POLLOUT | POLLWRNORM;
+ }
+ }
+
+ /*
+ * There is nothing to wait for if no buffers have already been queued.
+ */
+ if (list_empty(&q->queued_list))
+ return POLLERR;
+
+ poll_wait(file, &q->done_wq, wait);
+
+ /*
+ * Take first buffer available for dequeuing.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (!list_empty(&q->done_list))
+ vb = list_first_entry(&q->done_list, struct vb2_buffer,
+ done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ if (vb && (vb->state == VB2_BUF_STATE_DONE
+ || vb->state == VB2_BUF_STATE_ERROR)) {
+ return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM :
+ POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_poll);
+
+/**
+ * vb2_queue_init() - initialize a videobuf2 queue
+ * @q: videobuf2 queue; this structure should be allocated in driver
+ *
+ * The vb2_queue structure should be allocated by the driver. The driver is
+ * responsible of clearing it's content and setting initial values for some
+ * required entries before calling this function.
+ * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
+ * to the struct vb2_queue description in include/media/videobuf2-core.h
+ * for more information.
+ */
+int vb2_queue_init(struct vb2_queue *q)
+{
+ BUG_ON(!q);
+ BUG_ON(!q->ops);
+ BUG_ON(!q->mem_ops);
+ BUG_ON(!q->type);
+ BUG_ON(!q->io_modes);
+
+ BUG_ON(!q->ops->queue_setup);
+ BUG_ON(!q->ops->buf_queue);
+
+ INIT_LIST_HEAD(&q->queued_list);
+ INIT_LIST_HEAD(&q->done_list);
+ spin_lock_init(&q->done_lock);
+ init_waitqueue_head(&q->done_wq);
+
+ if (q->buf_struct_size == 0)
+ q->buf_struct_size = sizeof(struct vb2_buffer);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init);
+
+/**
+ * vb2_queue_release() - stop streaming, release the queue and free memory
+ * @q: videobuf2 queue
+ *
+ * This function stops streaming and performs necessary clean ups, including
+ * freeing video buffer memory. The driver is responsible for freeing
+ * the vb2_queue structure itself.
+ */
+void vb2_queue_release(struct vb2_queue *q)
+{
+ __vb2_cleanup_fileio(q);
+ __vb2_queue_cancel(q);
+ __vb2_queue_free(q);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_release);
+
+/**
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+ void *vaddr;
+ unsigned int size;
+ unsigned int pos;
+ unsigned int queued:1;
+};
+
+/**
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+ struct v4l2_requestbuffers req;
+ struct v4l2_buffer b;
+ struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
+ unsigned int index;
+ unsigned int q_count;
+ unsigned int dq_count;
+ unsigned int flags;
+};
+
+/**
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q: videobuf2 queue
+ * @read: mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+ struct vb2_fileio_data *fileio;
+ int i, ret;
+ unsigned int count = 0;
+
+ /*
+ * Sanity check
+ */
+ if ((read && !(q->io_modes & VB2_READ)) ||
+ (!read && !(q->io_modes & VB2_WRITE)))
+ BUG();
+
+ /*
+ * Check if device supports mapping buffers to kernel virtual space.
+ */
+ if (!q->mem_ops->vaddr)
+ return -EBUSY;
+
+ /*
+ * Check if streaming api has not been already activated.
+ */
+ if (q->streaming || q->num_buffers > 0)
+ return -EBUSY;
+
+ /*
+ * Start with count 1, driver can increase it in queue_setup()
+ */
+ count = 1;
+
+ dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
+ (read) ? "read" : "write", count, q->io_flags);
+
+ fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
+ if (fileio == NULL)
+ return -ENOMEM;
+
+ fileio->flags = q->io_flags;
+
+ /*
+ * Request buffers and use MMAP type to force driver
+ * to allocate buffers by itself.
+ */
+ fileio->req.count = count;
+ fileio->req.memory = V4L2_MEMORY_MMAP;
+ fileio->req.type = q->type;
+ ret = vb2_reqbufs(q, &fileio->req);
+ if (ret)
+ goto err_kfree;
+
+ /*
+ * Check if plane_count is correct
+ * (multiplane buffers are not supported).
+ */
+ if (q->bufs[0]->num_planes != 1) {
+ fileio->req.count = 0;
+ ret = -EBUSY;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Get kernel address of each buffer.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
+ if (fileio->bufs[i].vaddr == NULL)
+ goto err_reqbufs;
+ fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
+ }
+
+ /*
+ * Read mode requires pre queuing of all buffers.
+ */
+ if (read) {
+ /*
+ * Queue all buffers.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ struct v4l2_buffer *b = &fileio->b;
+ memset(b, 0, sizeof(*b));
+ b->type = q->type;
+ b->memory = q->memory;
+ b->index = i;
+ ret = vb2_qbuf(q, b);
+ if (ret)
+ goto err_reqbufs;
+ fileio->bufs[i].queued = 1;
+ }
+
+ /*
+ * Start streaming.
+ */
+ ret = vb2_streamon(q, q->type);
+ if (ret)
+ goto err_reqbufs;
+ }
+
+ q->fileio = fileio;
+
+ return ret;
+
+err_reqbufs:
+ vb2_reqbufs(q, &fileio->req);
+
+err_kfree:
+ kfree(fileio);
+ return ret;
+}
+
+/**
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+ struct vb2_fileio_data *fileio = q->fileio;
+
+ if (fileio) {
+ /*
+ * Hack fileio context to enable direct calls to vb2 ioctl
+ * interface.
+ */
+ q->fileio = NULL;
+
+ vb2_streamoff(q, q->type);
+ fileio->req.count = 0;
+ vb2_reqbufs(q, &fileio->req);
+ kfree(fileio);
+ dprintk(3, "file io emulator closed\n");
+ }
+ return 0;
+}
+
+/**
+ * __vb2_perform_fileio() - perform a single file io (read or write) operation
+ * @q: videobuf2 queue
+ * @data: pointed to target userspace buffer
+ * @count: number of bytes to read or write
+ * @ppos: file handle position tracking pointer
+ * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read: access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
+{
+ struct vb2_fileio_data *fileio;
+ struct vb2_fileio_buf *buf;
+ int ret, index;
+
+ dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n",
+ read ? "read" : "write", (long)*ppos, count,
+ nonblock ? "non" : "");
+
+ if (!data)
+ return -EINVAL;
+
+ /*
+ * Initialize emulator on first call.
+ */
+ if (!q->fileio) {
+ ret = __vb2_init_fileio(q, read);
+ dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ return ret;
+ }
+ fileio = q->fileio;
+
+ /*
+ * Hack fileio context to enable direct calls to vb2 ioctl interface.
+ * The pointer will be restored before returning from this function.
+ */
+ q->fileio = NULL;
+
+ index = fileio->index;
+ buf = &fileio->bufs[index];
+
+ /*
+ * Check if we need to dequeue the buffer.
+ */
+ if (buf->queued) {
+ struct vb2_buffer *vb;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ memset(&fileio->b, 0, sizeof(fileio->b));
+ fileio->b.type = q->type;
+ fileio->b.memory = q->memory;
+ fileio->b.index = index;
+ ret = vb2_dqbuf(q, &fileio->b, nonblock);
+ dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+ fileio->dq_count += 1;
+
+ /*
+ * Get number of bytes filled by the driver
+ */
+ vb = q->bufs[index];
+ buf->size = vb2_get_plane_payload(vb, 0);
+ buf->queued = 0;
+ }
+
+ /*
+ * Limit count on last few bytes of the buffer.
+ */
+ if (buf->pos + count > buf->size) {
+ count = buf->size - buf->pos;
+ dprintk(5, "reducing read count: %zd\n", count);
+ }
+
+ /*
+ * Transfer data to userspace.
+ */
+ dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n",
+ count, index, buf->pos);
+ if (read)
+ ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+ else
+ ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+ if (ret) {
+ dprintk(3, "file io: error copying data\n");
+ ret = -EFAULT;
+ goto end;
+ }
+
+ /*
+ * Update counters.
+ */
+ buf->pos += count;
+ *ppos += count;
+
+ /*
+ * Queue next buffer if required.
+ */
+ if (buf->pos == buf->size ||
+ (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) {
+ /*
+ * Check if this is the last buffer to read.
+ */
+ if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
+ fileio->dq_count == 1) {
+ dprintk(3, "file io: read limit reached\n");
+ /*
+ * Restore fileio pointer and release the context.
+ */
+ q->fileio = fileio;
+ return __vb2_cleanup_fileio(q);
+ }
+
+ /*
+ * Call vb2_qbuf and give buffer to the driver.
+ */
+ memset(&fileio->b, 0, sizeof(fileio->b));
+ fileio->b.type = q->type;
+ fileio->b.memory = q->memory;
+ fileio->b.index = index;
+ fileio->b.bytesused = buf->pos;
+ ret = vb2_qbuf(q, &fileio->b);
+ dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+
+ /*
+ * Buffer has been queued, update the status
+ */
+ buf->pos = 0;
+ buf->queued = 1;
+ buf->size = q->bufs[0]->v4l2_planes[0].length;
+ fileio->q_count += 1;
+
+ /*
+ * Switch to the next buffer
+ */
+ fileio->index = (index + 1) % q->num_buffers;
+
+ /*
+ * Start streaming if required.
+ */
+ if (!read && !q->streaming) {
+ ret = vb2_streamon(q, q->type);
+ if (ret)
+ goto end;
+ }
+ }
+
+ /*
+ * Return proper number of bytes processed.
+ */
+ if (ret == 0)
+ ret = count;
+end:
+ /*
+ * Restore the fileio context and block vb2 ioctl interface.
+ */
+ q->fileio = fileio;
+ return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
new file mode 100644
index 000000000000..bb6a5602cf94
--- /dev/null
+++ b/drivers/media/video/videobuf2-dma-contig.c
@@ -0,0 +1,185 @@
+/*
+ * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_dc_conf {
+ struct device *dev;
+};
+
+struct vb2_dc_buf {
+ struct vb2_dc_conf *conf;
+ void *vaddr;
+ dma_addr_t paddr;
+ unsigned long size;
+ struct vm_area_struct *vma;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_dma_contig_put(void *buf_priv);
+
+static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_dc_conf *conf = alloc_ctx;
+ struct vb2_dc_buf *buf;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr,
+ GFP_KERNEL);
+ if (!buf->vaddr) {
+ dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
+ buf->size);
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf->conf = conf;
+ buf->size = size;
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dma_contig_put;
+ buf->handler.arg = buf;
+
+ atomic_inc(&buf->refcount);
+
+ return buf;
+}
+
+static void vb2_dma_contig_put(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
+ buf->paddr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_dma_contig_cookie(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return (void *)buf->paddr;
+}
+
+static void *vb2_dma_contig_vaddr(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ if (!buf)
+ return 0;
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_dma_contig_num_users(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (!buf) {
+ printk(KERN_ERR "No buffer to map\n");
+ return -EINVAL;
+ }
+
+ return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+ &vb2_common_vm_ops, &buf->handler);
+}
+
+static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_dc_buf *buf;
+ struct vm_area_struct *vma;
+ dma_addr_t paddr = 0;
+ int ret;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
+ if (ret) {
+ printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+ vaddr);
+ kfree(buf);
+ return ERR_PTR(ret);
+ }
+
+ buf->size = size;
+ buf->paddr = paddr;
+ buf->vma = vma;
+
+ return buf;
+}
+
+static void vb2_dma_contig_put_userptr(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+
+ if (!buf)
+ return;
+
+ vb2_put_vma(buf->vma);
+ kfree(buf);
+}
+
+const struct vb2_mem_ops vb2_dma_contig_memops = {
+ .alloc = vb2_dma_contig_alloc,
+ .put = vb2_dma_contig_put,
+ .cookie = vb2_dma_contig_cookie,
+ .vaddr = vb2_dma_contig_vaddr,
+ .mmap = vb2_dma_contig_mmap,
+ .get_userptr = vb2_dma_contig_get_userptr,
+ .put_userptr = vb2_dma_contig_put_userptr,
+ .num_users = vb2_dma_contig_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
+
+void *vb2_dma_contig_init_ctx(struct device *dev)
+{
+ struct vb2_dc_conf *conf;
+
+ conf = kzalloc(sizeof *conf, GFP_KERNEL);
+ if (!conf)
+ return ERR_PTR(-ENOMEM);
+
+ conf->dev = dev;
+
+ return conf;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
+
+void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
+{
+ kfree(alloc_ctx);
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
+
+MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
new file mode 100644
index 000000000000..20b5c5dcc0ef
--- /dev/null
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -0,0 +1,292 @@
+/*
+ * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+#include <media/videobuf2-dma-sg.h>
+
+struct vb2_dma_sg_buf {
+ void *vaddr;
+ struct page **pages;
+ int write;
+ int offset;
+ struct vb2_dma_sg_desc sg_desc;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_dma_sg_put(void *buf_priv);
+
+static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_dma_sg_buf *buf;
+ int i;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->vaddr = NULL;
+ buf->write = 0;
+ buf->offset = 0;
+ buf->sg_desc.size = size;
+ buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ buf->sg_desc.sglist = vmalloc(buf->sg_desc.num_pages *
+ sizeof(*buf->sg_desc.sglist));
+ if (!buf->sg_desc.sglist)
+ goto fail_sglist_alloc;
+ memset(buf->sg_desc.sglist, 0, buf->sg_desc.num_pages *
+ sizeof(*buf->sg_desc.sglist));
+ sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+ buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ for (i = 0; i < buf->sg_desc.num_pages; ++i) {
+ buf->pages[i] = alloc_page(GFP_KERNEL);
+ if (NULL == buf->pages[i])
+ goto fail_pages_alloc;
+ sg_set_page(&buf->sg_desc.sglist[i],
+ buf->pages[i], PAGE_SIZE, 0);
+ }
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dma_sg_put;
+ buf->handler.arg = buf;
+
+ atomic_inc(&buf->refcount);
+
+ printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
+ __func__, buf->sg_desc.num_pages);
+
+ if (!buf->vaddr)
+ buf->vaddr = vm_map_ram(buf->pages,
+ buf->sg_desc.num_pages,
+ -1,
+ PAGE_KERNEL);
+ return buf;
+
+fail_pages_alloc:
+ while (--i >= 0)
+ __free_page(buf->pages[i]);
+
+fail_pages_array_alloc:
+ vfree(buf->sg_desc.sglist);
+
+fail_sglist_alloc:
+ kfree(buf);
+ return NULL;
+}
+
+static void vb2_dma_sg_put(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ int i = buf->sg_desc.num_pages;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__,
+ buf->sg_desc.num_pages);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+ vfree(buf->sg_desc.sglist);
+ while (--i >= 0)
+ __free_page(buf->pages[i]);
+ kfree(buf->pages);
+ kfree(buf);
+ }
+}
+
+static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_dma_sg_buf *buf;
+ unsigned long first, last;
+ int num_pages_from_user, i;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->vaddr = NULL;
+ buf->write = write;
+ buf->offset = vaddr & ~PAGE_MASK;
+ buf->sg_desc.size = size;
+
+ first = (vaddr & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
+ buf->sg_desc.num_pages = last - first + 1;
+
+ buf->sg_desc.sglist = vmalloc(
+ buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
+ if (!buf->sg_desc.sglist)
+ goto userptr_fail_sglist_alloc;
+
+ memset(buf->sg_desc.sglist, 0,
+ buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
+ sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+ buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto userptr_fail_pages_array_alloc;
+
+ down_read(&current->mm->mmap_sem);
+ num_pages_from_user = get_user_pages(current, current->mm,
+ vaddr & PAGE_MASK,
+ buf->sg_desc.num_pages,
+ write,
+ 1, /* force */
+ buf->pages,
+ NULL);
+ up_read(&current->mm->mmap_sem);
+ if (num_pages_from_user != buf->sg_desc.num_pages)
+ goto userptr_fail_get_user_pages;
+
+ sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0],
+ PAGE_SIZE - buf->offset, buf->offset);
+ size -= PAGE_SIZE - buf->offset;
+ for (i = 1; i < buf->sg_desc.num_pages; ++i) {
+ sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i],
+ min_t(size_t, PAGE_SIZE, size), 0);
+ size -= min_t(size_t, PAGE_SIZE, size);
+ }
+ return buf;
+
+userptr_fail_get_user_pages:
+ printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n",
+ num_pages_from_user, buf->sg_desc.num_pages);
+ while (--num_pages_from_user >= 0)
+ put_page(buf->pages[num_pages_from_user]);
+
+userptr_fail_pages_array_alloc:
+ vfree(buf->sg_desc.sglist);
+
+userptr_fail_sglist_alloc:
+ kfree(buf);
+ return NULL;
+}
+
+/*
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ * be used
+ */
+static void vb2_dma_sg_put_userptr(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ int i = buf->sg_desc.num_pages;
+
+ printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n",
+ __func__, buf->sg_desc.num_pages);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+ while (--i >= 0) {
+ if (buf->write)
+ set_page_dirty_lock(buf->pages[i]);
+ put_page(buf->pages[i]);
+ }
+ vfree(buf->sg_desc.sglist);
+ kfree(buf->pages);
+ kfree(buf);
+}
+
+static void *vb2_dma_sg_vaddr(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ BUG_ON(!buf);
+
+ if (!buf->vaddr)
+ buf->vaddr = vm_map_ram(buf->pages,
+ buf->sg_desc.num_pages,
+ -1,
+ PAGE_KERNEL);
+
+ /* add offset in case userptr is not page-aligned */
+ return buf->vaddr + buf->offset;
+}
+
+static unsigned int vb2_dma_sg_num_users(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ unsigned long uaddr = vma->vm_start;
+ unsigned long usize = vma->vm_end - vma->vm_start;
+ int i = 0;
+
+ if (!buf) {
+ printk(KERN_ERR "No memory to map\n");
+ return -EINVAL;
+ }
+
+ do {
+ int ret;
+
+ ret = vm_insert_page(vma, uaddr, buf->pages[i++]);
+ if (ret) {
+ printk(KERN_ERR "Remapping memory, error: %d\n", ret);
+ return ret;
+ }
+
+ uaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ } while (usize > 0);
+
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+static void *vb2_dma_sg_cookie(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return &buf->sg_desc;
+}
+
+const struct vb2_mem_ops vb2_dma_sg_memops = {
+ .alloc = vb2_dma_sg_alloc,
+ .put = vb2_dma_sg_put,
+ .get_userptr = vb2_dma_sg_get_userptr,
+ .put_userptr = vb2_dma_sg_put_userptr,
+ .vaddr = vb2_dma_sg_vaddr,
+ .mmap = vb2_dma_sg_mmap,
+ .num_users = vb2_dma_sg_num_users,
+ .cookie = vb2_dma_sg_cookie,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
+
+MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
new file mode 100644
index 000000000000..a3eb6567dea5
--- /dev/null
+++ b/drivers/media/video/videobuf2-memops.c
@@ -0,0 +1,235 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+/**
+ * vb2_get_vma() - acquire and lock the virtual memory area
+ * @vma: given virtual memory area
+ *
+ * This function attempts to acquire an area mapped in the userspace for
+ * the duration of a hardware operation. The area is "locked" by performing
+ * the same set of operation that are done when process calls fork() and
+ * memory areas are duplicated.
+ *
+ * Returns a copy of a virtual memory region on success or NULL.
+ */
+struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
+{
+ struct vm_area_struct *vma_copy;
+
+ vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
+ if (vma_copy == NULL)
+ return NULL;
+
+ if (vma->vm_ops && vma->vm_ops->open)
+ vma->vm_ops->open(vma);
+
+ if (vma->vm_file)
+ get_file(vma->vm_file);
+
+ memcpy(vma_copy, vma, sizeof(*vma));
+
+ vma_copy->vm_mm = NULL;
+ vma_copy->vm_next = NULL;
+ vma_copy->vm_prev = NULL;
+
+ return vma_copy;
+}
+
+/**
+ * vb2_put_userptr() - release a userspace virtual memory area
+ * @vma: virtual memory region associated with the area to be released
+ *
+ * This function releases the previously acquired memory area after a hardware
+ * operation.
+ */
+void vb2_put_vma(struct vm_area_struct *vma)
+{
+ if (!vma)
+ return;
+
+ if (vma->vm_file)
+ fput(vma->vm_file);
+
+ if (vma->vm_ops && vma->vm_ops->close)
+ vma->vm_ops->close(vma);
+
+ kfree(vma);
+}
+EXPORT_SYMBOL_GPL(vb2_put_vma);
+
+/**
+ * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
+ * @vaddr: starting virtual address of the area to be verified
+ * @size: size of the area
+ * @res_paddr: will return physical address for the given vaddr
+ * @res_vma: will return locked copy of struct vm_area for the given area
+ *
+ * This function will go through memory area of size @size mapped at @vaddr and
+ * verify that the underlying physical pages are contiguous. If they are
+ * contiguous the virtual memory area is locked and a @res_vma is filled with
+ * the copy and @res_pa set to the physical address of the buffer.
+ *
+ * Returns 0 on success.
+ */
+int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
+ struct vm_area_struct **res_vma, dma_addr_t *res_pa)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long offset, start, end;
+ unsigned long this_pfn, prev_pfn;
+ dma_addr_t pa = 0;
+ int ret = -EFAULT;
+
+ start = vaddr;
+ offset = start & ~PAGE_MASK;
+ end = start + size;
+
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, start);
+
+ if (vma == NULL || vma->vm_end < end)
+ goto done;
+
+ for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
+ ret = follow_pfn(vma, start, &this_pfn);
+ if (ret)
+ goto done;
+
+ if (prev_pfn == 0)
+ pa = this_pfn << PAGE_SHIFT;
+ else if (this_pfn != prev_pfn + 1) {
+ ret = -EFAULT;
+ goto done;
+ }
+ prev_pfn = this_pfn;
+ }
+
+ /*
+ * Memory is contigous, lock vma and return to the caller
+ */
+ *res_vma = vb2_get_vma(vma);
+ if (*res_vma == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ *res_pa = pa + offset;
+ ret = 0;
+
+done:
+ up_read(&mm->mmap_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
+
+/**
+ * vb2_mmap_pfn_range() - map physical pages to userspace
+ * @vma: virtual memory region for the mapping
+ * @paddr: starting physical address of the memory to be mapped
+ * @size: size of the memory to be mapped
+ * @vm_ops: vm operations to be assigned to the created area
+ * @priv: private data to be associated with the area
+ *
+ * Returns 0 on success.
+ */
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+ unsigned long size,
+ const struct vm_operations_struct *vm_ops,
+ void *priv)
+{
+ int ret;
+
+ size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (ret) {
+ printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_private_data = priv;
+ vma->vm_ops = vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+ __func__, paddr, vma->vm_start, size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
+
+/**
+ * vb2_common_vm_open() - increase refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function adds another user to the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_open(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, atomic_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ atomic_inc(h->refcount);
+}
+
+/**
+ * vb2_common_vm_close() - decrease refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function releases the user from the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_close(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, atomic_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ h->put(h->arg);
+}
+
+/**
+ * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
+ * video buffers
+ */
+const struct vm_operations_struct vb2_common_vm_ops = {
+ .open = vb2_common_vm_open,
+ .close = vb2_common_vm_close,
+};
+EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
+
+MODULE_DESCRIPTION("common memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
new file mode 100644
index 000000000000..b5e6936fb628
--- /dev/null
+++ b/drivers/media/video/videobuf2-vmalloc.c
@@ -0,0 +1,132 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_buf {
+ void *vaddr;
+ unsigned long size;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_vmalloc_put(void *buf_priv);
+
+static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_vmalloc_buf *buf;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->size = size;
+ buf->vaddr = vmalloc_user(buf->size);
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_vmalloc_put;
+ buf->handler.arg = buf;
+
+ if (!buf->vaddr) {
+ printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size);
+ kfree(buf);
+ return NULL;
+ }
+
+ atomic_inc(&buf->refcount);
+ printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n",
+ buf->size, buf->vaddr);
+
+ return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ printk(KERN_DEBUG "%s: Freeing vmalloc mem at vaddr=%p\n",
+ __func__, buf->vaddr);
+ vfree(buf->vaddr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_vmalloc_vaddr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ BUG_ON(!buf);
+
+ if (!buf->vaddr) {
+ printk(KERN_ERR "Address of an unallocated plane requested\n");
+ return NULL;
+ }
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_vmalloc_num_users(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ int ret;
+
+ if (!buf) {
+ printk(KERN_ERR "No memory to map\n");
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret) {
+ printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Make sure that vm_areas for 2 buffers won't be merged together
+ */
+ vma->vm_flags |= VM_DONTEXPAND;
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+const struct vb2_mem_ops vb2_vmalloc_memops = {
+ .alloc = vb2_vmalloc_alloc,
+ .put = vb2_vmalloc_put,
+ .vaddr = vb2_vmalloc_vaddr,
+ .mmap = vb2_vmalloc_mmap,
+ .num_users = vb2_vmalloc_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
+
+MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index c49c39386bd0..bd104d0ad36c 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -7,6 +7,9 @@
* John Sokol <sokol--a.t--videotechnology.com>
* http://v4l.videotechnology.com/
*
+ * Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski
+ * Copyright (c) 2010 Samsung Electronics
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the BSD Licence, GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
@@ -23,12 +26,11 @@
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/kthread.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
#include <linux/freezer.h>
-#endif
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-common.h>
#define VIVI_MODULE_NAME "vivi"
@@ -42,7 +44,7 @@
#define MAX_HEIGHT 1200
#define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 7
+#define VIVI_MINOR_VERSION 8
#define VIVI_RELEASE 0
#define VIVI_VERSION \
KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
@@ -133,16 +135,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f)
return &formats[k];
}
-struct sg_to_addr {
- int pos;
- struct scatterlist *sg;
-};
-
/* buffer for one video frame */
struct vivi_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
+ struct vb2_buffer vb;
+ struct list_head list;
struct vivi_fmt *fmt;
};
@@ -162,13 +159,20 @@ static LIST_HEAD(vivi_devlist);
struct vivi_dev {
struct list_head vivi_devlist;
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
/* controls */
- int brightness;
- int contrast;
- int saturation;
- int hue;
- int volume;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *button;
+ struct v4l2_ctrl *boolean;
+ struct v4l2_ctrl *int32;
+ struct v4l2_ctrl *int64;
+ struct v4l2_ctrl *menu;
+ struct v4l2_ctrl *string;
spinlock_t slock;
struct mutex mutex;
@@ -181,6 +185,7 @@ struct vivi_dev {
/* Several counters */
unsigned ms;
unsigned long jiffies;
+ unsigned button_pressed;
int mv_count; /* Controls bars movement */
@@ -190,9 +195,11 @@ struct vivi_dev {
/* video capture */
struct vivi_fmt *fmt;
unsigned int width, height;
- struct videobuf_queue vb_vidq;
+ struct vb2_queue vb_vidq;
+ enum v4l2_field field;
+ unsigned int field_count;
- unsigned long generating;
+ unsigned int open_count;
u8 bars[9][3];
u8 line[MAX_WIDTH * 4];
};
@@ -443,10 +450,10 @@ static void gen_text(struct vivi_dev *dev, char *basep,
static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
{
- int hmax = buf->vb.height;
- int wmax = buf->vb.width;
+ int wmax = dev->width;
+ int hmax = dev->height;
struct timeval ts;
- void *vbuf = videobuf_to_vmalloc(&buf->vb);
+ void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
unsigned ms;
char str[100];
int h, line = 1;
@@ -472,22 +479,38 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->width, dev->height, dev->input);
gen_text(dev, vbuf, line++ * 16, 16, str);
+ mutex_lock(&dev->ctrl_handler.lock);
snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
- dev->brightness,
- dev->contrast,
- dev->saturation,
- dev->hue);
+ dev->brightness->cur.val,
+ dev->contrast->cur.val,
+ dev->saturation->cur.val,
+ dev->hue->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " volume %3d ", dev->volume);
+ snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
+ dev->int32->cur.val,
+ dev->int64->cur.val64);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
+ dev->boolean->cur.val,
+ dev->menu->qmenu[dev->menu->cur.val],
+ dev->string->cur.string);
+ mutex_unlock(&dev->ctrl_handler.lock);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ if (dev->button_pressed) {
+ dev->button_pressed--;
+ snprintf(str, sizeof(str), " button pressed!");
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ }
dev->mv_count += 2;
- /* Advice that buffer was filled */
- buf->vb.field_count++;
+ buf->vb.v4l2_buf.field = dev->field;
+ dev->field_count++;
+ buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
do_gettimeofday(&ts);
- buf->vb.ts = ts;
- buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.v4l2_buf.timestamp = ts;
}
static void vivi_thread_tick(struct vivi_dev *dev)
@@ -504,23 +527,17 @@ static void vivi_thread_tick(struct vivi_dev *dev)
goto unlock;
}
- buf = list_entry(dma_q->active.next,
- struct vivi_buffer, vb.queue);
+ buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+ list_del(&buf->list);
- /* Nobody is waiting on this buffer, return */
- if (!waitqueue_active(&buf->vb.done))
- goto unlock;
-
- list_del(&buf->vb.queue);
-
- do_gettimeofday(&buf->vb.ts);
+ do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
/* Fill buffer */
vivi_fillbuff(dev, buf);
dprintk(dev, 1, "filled buffer %p\n", buf);
- wake_up(&buf->vb.done);
- dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
unlock:
spin_unlock_irqrestore(&dev->slock, flags);
}
@@ -571,17 +588,12 @@ static int vivi_thread(void *data)
return 0;
}
-static void vivi_start_generating(struct file *file)
+static int vivi_start_generating(struct vivi_dev *dev)
{
- struct vivi_dev *dev = video_drvdata(file);
struct vivi_dmaqueue *dma_q = &dev->vidq;
dprintk(dev, 1, "%s\n", __func__);
- if (test_and_set_bit(0, &dev->generating))
- return;
- file->private_data = dev;
-
/* Resets frame counters */
dev->ms = 0;
dev->mv_count = 0;
@@ -593,146 +605,200 @@ static void vivi_start_generating(struct file *file)
if (IS_ERR(dma_q->kthread)) {
v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- clear_bit(0, &dev->generating);
- return;
+ return PTR_ERR(dma_q->kthread);
}
/* Wakes thread */
wake_up_interruptible(&dma_q->wq);
dprintk(dev, 1, "returning from %s\n", __func__);
+ return 0;
}
-static void vivi_stop_generating(struct file *file)
+static void vivi_stop_generating(struct vivi_dev *dev)
{
- struct vivi_dev *dev = video_drvdata(file);
struct vivi_dmaqueue *dma_q = &dev->vidq;
dprintk(dev, 1, "%s\n", __func__);
- if (!file->private_data)
- return;
- if (!test_and_clear_bit(0, &dev->generating))
- return;
-
/* shutdown control thread */
if (dma_q->kthread) {
kthread_stop(dma_q->kthread);
dma_q->kthread = NULL;
}
- videobuf_stop(&dev->vb_vidq);
- videobuf_mmap_free(&dev->vb_vidq);
-}
-static int vivi_is_generating(struct vivi_dev *dev)
-{
- return test_bit(0, &dev->generating);
+ /*
+ * Typical driver might need to wait here until dma engine stops.
+ * In this case we can abort imiedetly, so it's just a noop.
+ */
+
+ /* Release all active buffers */
+ while (!list_empty(&dma_q->active)) {
+ struct vivi_buffer *buf;
+ buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+ }
}
-
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned long sizes[],
+ void *alloc_ctxs[])
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = dev->width * dev->height * 2;
+
+ if (0 == *nbuffers)
+ *nbuffers = 32;
+
+ while (size * *nbuffers > vid_limit * 1024 * 1024)
+ (*nbuffers)--;
- *size = dev->width * dev->height * 2;
+ *nplanes = 1;
- if (0 == *count)
- *count = 32;
+ sizes[0] = size;
- while (*size * *count > vid_limit * 1024 * 1024)
- (*count)--;
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
- dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
- *count, *size);
+ dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+ *nbuffers, size);
return 0;
}
-static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
+static int buffer_init(struct vb2_buffer *vb)
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
+ BUG_ON(NULL == dev->fmt);
+
+ /*
+ * This callback is called once per buffer, after its allocation.
+ *
+ * Vivi does not allow changing format during streaming, but it is
+ * possible to do so when streaming is paused (i.e. in streamoff state).
+ * Buffers however are not freed when going into streamoff and so
+ * buffer size verification has to be done in buffer_prepare, on each
+ * qbuf.
+ * It would be best to move verification code here to buf_init and
+ * s_fmt though.
+ */
- videobuf_vmalloc_free(&buf->vb);
- dprintk(dev, 1, "free_buffer: freed\n");
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ return 0;
}
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
- int rc;
+ unsigned long size;
- dprintk(dev, 1, "%s, field=%d\n", __func__, field);
+ dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
BUG_ON(NULL == dev->fmt);
+ /*
+ * Theses properties only change when queue is idle, see s_fmt.
+ * The below checks should not be performed here, on each
+ * buffer_prepare (i.e. on each qbuf). Most of the code in this function
+ * should thus be moved to buffer_init and s_fmt.
+ */
if (dev->width < 48 || dev->width > MAX_WIDTH ||
dev->height < 32 || dev->height > MAX_HEIGHT)
return -EINVAL;
- buf->vb.size = dev->width * dev->height * 2;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ size = dev->width * dev->height * 2;
+ if (vb2_plane_size(vb, 0) < size) {
+ dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb, 0, size);
- /* These properties only change when queue is idle, see s_fmt */
- buf->fmt = dev->fmt;
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
+ buf->fmt = dev->fmt;
precalculate_bars(dev);
precalculate_line(dev);
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
+ return 0;
+}
- buf->vb.state = VIDEOBUF_PREPARED;
+static int buffer_finish(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ dprintk(dev, 1, "%s\n", __func__);
return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ dprintk(dev, 1, "%s\n", __func__);
-fail:
- free_buffer(vq, buf);
- return rc;
}
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
struct vivi_dmaqueue *vidq = &dev->vidq;
+ unsigned long flags = 0;
dprintk(dev, 1, "%s\n", __func__);
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
+ spin_lock_irqsave(&dev->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static int start_streaming(struct vb2_queue *vq)
{
- struct vivi_dev *dev = vq->priv_data;
- struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ dprintk(dev, 1, "%s\n", __func__);
+ return vivi_start_generating(dev);
+}
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
dprintk(dev, 1, "%s\n", __func__);
+ vivi_stop_generating(dev);
+ return 0;
+}
+
+static void vivi_lock(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ mutex_lock(&dev->mutex);
+}
- free_buffer(vq, buf);
+static void vivi_unlock(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&dev->mutex);
}
-static struct videobuf_queue_ops vivi_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
+
+static struct vb2_ops vivi_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vivi_unlock,
+ .wait_finish = vivi_lock,
};
/* ------------------------------------------------------------------
@@ -774,7 +840,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.field = dev->vb_vidq.field;
+ f->fmt.pix.field = dev->field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -820,82 +886,60 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_dev *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
int ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
- if (vivi_is_generating(dev)) {
+ if (vb2_is_streaming(q)) {
dprintk(dev, 1, "%s device busy\n", __func__);
- ret = -EBUSY;
- goto out;
+ return -EBUSY;
}
dev->fmt = get_format(f);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
- dev->vb_vidq.field = f->fmt.pix.field;
- ret = 0;
-out:
- return ret;
+ dev->field = f->fmt.pix.field;
+
+ return 0;
}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct vivi_dev *dev = video_drvdata(file);
-
- return videobuf_reqbufs(&dev->vb_vidq, p);
+ return vb2_reqbufs(&dev->vb_vidq, p);
}
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
-
- return videobuf_querybuf(&dev->vb_vidq, p);
+ return vb2_querybuf(&dev->vb_vidq, p);
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
-
- return videobuf_qbuf(&dev->vb_vidq, p);
+ return vb2_qbuf(&dev->vb_vidq, p);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
-
- return videobuf_dqbuf(&dev->vb_vidq, p,
- file->f_flags & O_NONBLOCK);
+ return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vivi_dev *dev = video_drvdata(file);
- int ret;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- ret = videobuf_streamon(&dev->vb_vidq);
- if (ret)
- return ret;
-
- vivi_start_generating(file);
- return 0;
+ return vb2_streamon(&dev->vb_vidq, i);
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vivi_dev *dev = video_drvdata(file);
- int ret;
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- ret = videobuf_streamoff(&dev->vb_vidq);
- if (!ret)
- vivi_stop_generating(file);
- return ret;
+ return vb2_streamoff(&dev->vb_vidq, i);
}
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -938,106 +982,47 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
}
/* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 200);
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 16);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- }
- return -EINVAL;
-}
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct vivi_dev *dev = video_drvdata(file);
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->volume;
- return 0;
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dev->brightness;
- return 0;
- case V4L2_CID_CONTRAST:
- ctrl->value = dev->contrast;
- return 0;
- case V4L2_CID_SATURATION:
- ctrl->value = dev->saturation;
- return 0;
- case V4L2_CID_HUE:
- ctrl->value = dev->hue;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct vivi_dev *dev = video_drvdata(file);
- struct v4l2_queryctrl qc;
- int err;
-
- qc.id = ctrl->id;
- err = vidioc_queryctrl(file, priv, &qc);
- if (err < 0)
- return err;
- if (ctrl->value < qc.minimum || ctrl->value > qc.maximum)
- return -ERANGE;
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- dev->volume = ctrl->value;
- return 0;
- case V4L2_CID_BRIGHTNESS:
- dev->brightness = ctrl->value;
- return 0;
- case V4L2_CID_CONTRAST:
- dev->contrast = ctrl->value;
- return 0;
- case V4L2_CID_SATURATION:
- dev->saturation = ctrl->value;
- return 0;
- case V4L2_CID_HUE:
- dev->hue = ctrl->value;
- return 0;
- }
- return -EINVAL;
+ if (ctrl == dev->button)
+ dev->button_pressed = 30;
+ return 0;
}
/* ------------------------------------------------------------------
File operations for the device
------------------------------------------------------------------*/
+static int vivi_open(struct file *file)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ dprintk(dev, 1, "%s, %p\n", __func__, file);
+ dev->open_count++;
+ return 0;
+}
+
static ssize_t
vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct vivi_dev *dev = video_drvdata(file);
- vivi_start_generating(file);
- return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
+ dprintk(dev, 1, "read called\n");
+ return vb2_read(&dev->vb_vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
}
static unsigned int
vivi_poll(struct file *file, struct poll_table_struct *wait)
{
struct vivi_dev *dev = video_drvdata(file);
- struct videobuf_queue *q = &dev->vb_vidq;
+ struct vb2_queue *q = &dev->vb_vidq;
dprintk(dev, 1, "%s\n", __func__);
-
- vivi_start_generating(file);
- return videobuf_poll_stream(file, q, wait);
+ return vb2_poll(q, file, wait);
}
static int vivi_close(struct file *file)
@@ -1045,10 +1030,11 @@ static int vivi_close(struct file *file)
struct video_device *vdev = video_devdata(file);
struct vivi_dev *dev = video_drvdata(file);
- vivi_stop_generating(file);
+ dprintk(dev, 1, "close called (dev=%s), file %p\n",
+ video_device_node_name(vdev), file);
- dprintk(dev, 1, "close called (dev=%s)\n",
- video_device_node_name(vdev));
+ if (--dev->open_count == 0)
+ vb2_queue_release(&dev->vb_vidq);
return 0;
}
@@ -1059,8 +1045,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
- ret = videobuf_mmap_mapper(&dev->vb_vidq, vma);
-
+ ret = vb2_mmap(&dev->vb_vidq, vma);
dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
@@ -1068,8 +1053,82 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
return ret;
}
+static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+ .s_ctrl = vivi_s_ctrl,
+};
+
+#define VIVI_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
+
+static const struct v4l2_ctrl_config vivi_ctrl_button = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 0,
+ .name = "Button",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_boolean = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 1,
+ .name = "Boolean",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int32 = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 2,
+ .name = "Integer 32 Bits",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0x80000000,
+ .max = 0x7fffffff,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int64 = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 3,
+ .name = "Integer 64 Bits",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+};
+
+static const char * const vivi_ctrl_menu_strings[] = {
+ "Menu Item 0 (Skipped)",
+ "Menu Item 1",
+ "Menu Item 2 (Skipped)",
+ "Menu Item 3",
+ "Menu Item 4",
+ "Menu Item 5 (Skipped)",
+ NULL,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_menu = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 4,
+ .name = "Menu",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = 1,
+ .max = 4,
+ .def = 3,
+ .menu_skip_mask = 0x04,
+ .qmenu = vivi_ctrl_menu_strings,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_string = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 5,
+ .name = "String",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .min = 2,
+ .max = 4,
+ .step = 1,
+};
+
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
+ .open = vivi_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
@@ -1093,9 +1152,6 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
};
static struct video_device vivi_template = {
@@ -1126,6 +1182,7 @@ static int vivi_release(void)
video_device_node_name(dev->vfd));
video_unregister_device(dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
kfree(dev);
}
@@ -1136,6 +1193,8 @@ static int __init vivi_create_instance(int inst)
{
struct vivi_dev *dev;
struct video_device *vfd;
+ struct v4l2_ctrl_handler *hdl;
+ struct vb2_queue *q;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -1151,20 +1210,46 @@ static int __init vivi_create_instance(int inst)
dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
- dev->volume = 200;
- dev->brightness = 127;
- dev->contrast = 16;
- dev->saturation = 127;
- dev->hue = 0;
+ hdl = &dev->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 11);
+ dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
+ dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 16);
+ dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
+ dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
+ dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
+ dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
+ dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
+ dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+ if (hdl->error) {
+ ret = hdl->error;
+ goto unreg_dev;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
/* initialize locks */
spin_lock_init(&dev->slock);
- mutex_init(&dev->mutex);
- videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops,
- NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct vivi_buffer), dev, &dev->mutex);
+ /* initialize queue */
+ q = &dev->vb_vidq;
+ memset(q, 0, sizeof(dev->vb_vidq));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivi_buffer);
+ q->ops = &vivi_video_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+
+ vb2_queue_init(q);
+
+ mutex_init(&dev->mutex);
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
@@ -1178,6 +1263,11 @@ static int __init vivi_create_instance(int inst)
*vfd = vivi_template;
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev;
+
+ /*
+ * Provide a mutex to v4l2 core. It will be used to protect
+ * all fops and v4l2 ioctls.
+ */
vfd->lock = &dev->mutex;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
@@ -1200,6 +1290,7 @@ static int __init vivi_create_instance(int inst)
rel_vdev:
video_device_release(vfd);
unreg_dev:
+ v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index 91a01b3cdf8c..75301d10a838 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -28,6 +28,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
MODULE_AUTHOR("Laurent Pinchart");
@@ -44,16 +45,13 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct vpx3220 {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
unsigned char reg[255];
v4l2_std_id norm;
int ident;
int input;
int enable;
- int bright;
- int contrast;
- int hue;
- int sat;
};
static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
@@ -61,6 +59,11 @@ static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
return container_of(sd, struct vpx3220, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
+}
+
static char *inputs[] = { "internal", "composite", "svideo" };
/* ----------------------------------------------------------------------- */
@@ -417,88 +420,26 @@ static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- break;
-
- case V4L2_CID_CONTRAST:
- v4l2_ctrl_query_fill(qc, 0, 63, 1, 32);
- break;
-
- case V4L2_CID_SATURATION:
- v4l2_ctrl_query_fill(qc, 0, 4095, 1, 2048);
- break;
-
- case V4L2_CID_HUE:
- v4l2_ctrl_query_fill(qc, -512, 511, 1, 0);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct vpx3220 *decoder = to_vpx3220(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = decoder->bright;
- break;
+ vpx3220_write(sd, 0xe6, ctrl->val);
+ return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = decoder->contrast;
- break;
+ /* Bit 7 and 8 is for noise shaping */
+ vpx3220_write(sd, 0xe7, ctrl->val + 192);
+ return 0;
case V4L2_CID_SATURATION:
- ctrl->value = decoder->sat;
- break;
+ vpx3220_fp_write(sd, 0xa0, ctrl->val);
+ return 0;
case V4L2_CID_HUE:
- ctrl->value = decoder->hue;
- break;
- default:
- return -EINVAL;
+ vpx3220_fp_write(sd, 0x1c, ctrl->val);
+ return 0;
}
- return 0;
-}
-
-static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct vpx3220 *decoder = to_vpx3220(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (decoder->bright != ctrl->value) {
- decoder->bright = ctrl->value;
- vpx3220_write(sd, 0xe6, decoder->bright);
- }
- break;
- case V4L2_CID_CONTRAST:
- if (decoder->contrast != ctrl->value) {
- /* Bit 7 and 8 is for noise shaping */
- decoder->contrast = ctrl->value;
- vpx3220_write(sd, 0xe7, decoder->contrast + 192);
- }
- break;
- case V4L2_CID_SATURATION:
- if (decoder->sat != ctrl->value) {
- decoder->sat = ctrl->value;
- vpx3220_fp_write(sd, 0xa0, decoder->sat);
- }
- break;
- case V4L2_CID_HUE:
- if (decoder->hue != ctrl->value) {
- decoder->hue = ctrl->value;
- vpx3220_fp_write(sd, 0x1c, decoder->hue);
- }
- break;
- default:
- return -EINVAL;
- }
- return 0;
+ return -EINVAL;
}
static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -511,12 +452,20 @@ static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
+ .s_ctrl = vpx3220_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
.g_chip_ident = vpx3220_g_chip_ident,
.init = vpx3220_init,
- .g_ctrl = vpx3220_g_ctrl,
- .s_ctrl = vpx3220_s_ctrl,
- .queryctrl = vpx3220_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = vpx3220_s_std,
};
@@ -558,10 +507,24 @@ static int vpx3220_probe(struct i2c_client *client,
decoder->norm = V4L2_STD_PAL;
decoder->input = 0;
decoder->enable = 1;
- decoder->bright = 32768;
- decoder->contrast = 32768;
- decoder->hue = 32768;
- decoder->sat = 32768;
+ v4l2_ctrl_handler_init(&decoder->hdl, 4);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 63, 1, 32);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 4095, 1, 2048);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_HUE, -512, 511, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
ver = i2c_smbus_read_byte_data(client, 0x00);
pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
@@ -599,9 +562,11 @@ static int vpx3220_probe(struct i2c_client *client,
static int vpx3220_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vpx3220 *decoder = to_vpx3220(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_vpx3220(sd));
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
return 0;
}
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index fe8ef6419f83..9cedb1e69b58 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -35,6 +35,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
+#include <media/wm8775.h>
MODULE_DESCRIPTION("wm8775 driver");
MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -50,10 +51,16 @@ enum {
TOT_REGS
};
+#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
+#define ALC_EN 0x100 /* R17: ALC enable */
+
struct wm8775_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *vol;
+ struct v4l2_ctrl *bal;
+ struct v4l2_ctrl *loud;
u8 input; /* Last selected input (0-0xf) */
};
@@ -85,6 +92,30 @@ static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
return -1;
}
+static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
+{
+ struct wm8775_state *state = to_state(sd);
+ u8 vol_l, vol_r;
+ int muted = 0 != state->mute->val;
+ u16 volume = (u16)state->vol->val;
+ u16 balance = (u16)state->bal->val;
+
+ /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
+ vol_l = (min(65536 - balance, 32768) * volume) >> 23;
+ vol_r = (min(balance, (u16)32768) * volume) >> 23;
+
+ /* Mute */
+ if (muted || quietly)
+ wm8775_write(sd, R21, 0x0c0 | state->input);
+
+ wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
+ wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
+
+ /* Un-mute */
+ if (!muted)
+ wm8775_write(sd, R21, state->input);
+}
+
static int wm8775_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
@@ -102,25 +133,26 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
state->input = input;
if (!v4l2_ctrl_g_ctrl(state->mute))
return 0;
- wm8775_write(sd, R21, 0x0c0);
- wm8775_write(sd, R14, 0x1d4);
- wm8775_write(sd, R15, 0x1d4);
- wm8775_write(sd, R21, 0x100 + state->input);
+ if (!v4l2_ctrl_g_ctrl(state->vol))
+ return 0;
+ if (!v4l2_ctrl_g_ctrl(state->bal))
+ return 0;
+ wm8775_set_audio(sd, 1);
return 0;
}
static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
- struct wm8775_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- wm8775_write(sd, R21, 0x0c0);
- wm8775_write(sd, R14, 0x1d4);
- wm8775_write(sd, R15, 0x1d4);
- if (!ctrl->val)
- wm8775_write(sd, R21, 0x100 + state->input);
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ wm8775_set_audio(sd, 0);
+ return 0;
+ case V4L2_CID_AUDIO_LOUDNESS:
+ wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
return 0;
}
return -EINVAL;
@@ -144,16 +176,7 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
{
- struct wm8775_state *state = to_state(sd);
-
- /* If I remove this, then it can happen that I have no
- sound the first time I tune from static to a valid channel.
- It's difficult to reproduce and is almost certainly related
- to the zero cross detect circuit. */
- wm8775_write(sd, R21, 0x0c0);
- wm8775_write(sd, R14, 0x1d4);
- wm8775_write(sd, R15, 0x1d4);
- wm8775_write(sd, R21, 0x100 + state->input);
+ wm8775_set_audio(sd, 0);
return 0;
}
@@ -203,6 +226,13 @@ static int wm8775_probe(struct i2c_client *client,
{
struct wm8775_state *state;
struct v4l2_subdev *sd;
+ int err;
+ bool is_nova_s = false;
+
+ if (client->dev.platform_data) {
+ struct wm8775_platform_data *data = client->dev.platform_data;
+ is_nova_s = data->is_nova_s;
+ }
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -218,13 +248,18 @@ static int wm8775_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
- v4l2_ctrl_handler_init(&state->hdl, 1);
+ v4l2_ctrl_handler_init(&state->hdl, 4);
state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
+ state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
+ state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
sd->ctrl_handler = &state->hdl;
- if (state->hdl.error) {
- int err = state->hdl.error;
-
+ err = state->hdl.error;
+ if (err) {
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
@@ -236,29 +271,44 @@ static int wm8775_probe(struct i2c_client *client,
wm8775_write(sd, R23, 0x000);
/* Disable zero cross detect timeout */
wm8775_write(sd, R7, 0x000);
- /* Left justified, 24-bit mode */
+ /* HPF enable, left justified, 24-bit (Philips) mode */
wm8775_write(sd, R11, 0x021);
/* Master mode, clock ratio 256fs */
wm8775_write(sd, R12, 0x102);
/* Powered up */
wm8775_write(sd, R13, 0x000);
- /* ADC gain +2.5dB, enable zero cross */
- wm8775_write(sd, R14, 0x1d4);
- /* ADC gain +2.5dB, enable zero cross */
- wm8775_write(sd, R15, 0x1d4);
- /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
- wm8775_write(sd, R16, 0x1bf);
- /* Enable gain control, use zero cross detection,
- ALC hold time 42.6 ms */
- wm8775_write(sd, R17, 0x185);
+
+ if (!is_nova_s) {
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(sd, R14, 0x1d4);
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(sd, R15, 0x1d4);
+ /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
+ wm8775_write(sd, R16, 0x1bf);
+ /* Enable gain control, use zero cross detection,
+ ALC hold time 42.6 ms */
+ wm8775_write(sd, R17, 0x185);
+ } else {
+ /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
+ wm8775_write(sd, R16, 0x1bb);
+ /* Set ALC mode and hold time */
+ wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
+ }
/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
wm8775_write(sd, R18, 0x0a2);
/* Enable noise gate, threshold -72dBfs */
wm8775_write(sd, R19, 0x005);
- /* Transient window 4ms, lower PGA gain limit -1dB */
- wm8775_write(sd, R20, 0x07a);
- /* LRBOTH = 1, use input 2. */
- wm8775_write(sd, R21, 0x102);
+ if (!is_nova_s) {
+ /* Transient window 4ms, lower PGA gain limit -1dB */
+ wm8775_write(sd, R20, 0x07a);
+ /* LRBOTH = 1, use input 2. */
+ wm8775_write(sd, R21, 0x102);
+ } else {
+ /* Transient window 4ms, ALC min gain -5dB */
+ wm8775_write(sd, R20, 0x0fb);
+
+ wm8775_set_audio(sd, 1); /* set volume/mute/mux */
+ }
return 0;
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fd018366d670..9db079be0e08 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -615,7 +615,7 @@ config MFD_VX855
and/or vx855_gpio drivers for this to do anything useful.
config MFD_WL1273_CORE
- tristate
+ tristate "Support for TI WL1273 FM radio."
depends on I2C
select MFD_CORE
default n
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
index d2ecc2435736..4025a4bec8d5 100644
--- a/drivers/mfd/wl1273-core.c
+++ b/drivers/mfd/wl1273-core.c
@@ -1,7 +1,7 @@
/*
* MFD driver for wl1273 FM radio and audio codec submodules.
*
- * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2011 Nokia Corporation
* Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -31,6 +31,145 @@ static struct i2c_device_id wl1273_driver_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);
+static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)
+{
+ struct i2c_client *client = core->client;
+ u8 b[2];
+ int r;
+
+ r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);
+ if (r != 2) {
+ dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);
+ return -EREMOTEIO;
+ }
+
+ *value = (u16)b[0] << 8 | b[1];
+
+ return 0;
+}
+
+static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)
+{
+ struct i2c_client *client = core->client;
+ u8 buf[] = { (param >> 8) & 0xff, param & 0xff };
+ int r;
+
+ r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);
+ if (r) {
+ dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);
+ return r;
+ }
+
+ return 0;
+}
+
+static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)
+{
+ struct i2c_client *client = core->client;
+ struct i2c_msg msg;
+ int r;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.buf = data;
+ msg.len = len;
+
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r != 1) {
+ dev_err(&client->dev, "%s: write error.\n", __func__);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+/**
+ * wl1273_fm_set_audio() - Set audio mode.
+ * @core: A pointer to the device struct.
+ * @new_mode: The new audio mode.
+ *
+ * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.
+ */
+static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)
+{
+ int r = 0;
+
+ if (core->mode == WL1273_MODE_OFF ||
+ core->mode == WL1273_MODE_SUSPENDED)
+ return -EPERM;
+
+ if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {
+ r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
+ WL1273_PCM_DEF_MODE);
+ if (r)
+ goto out;
+
+ r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
+ core->i2s_mode);
+ if (r)
+ goto out;
+
+ r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
+ WL1273_AUDIO_ENABLE_I2S);
+ if (r)
+ goto out;
+
+ } else if (core->mode == WL1273_MODE_RX &&
+ new_mode == WL1273_AUDIO_ANALOG) {
+ r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
+ WL1273_AUDIO_ENABLE_ANALOG);
+ if (r)
+ goto out;
+
+ } else if (core->mode == WL1273_MODE_TX &&
+ new_mode == WL1273_AUDIO_DIGITAL) {
+ r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
+ core->i2s_mode);
+ if (r)
+ goto out;
+
+ r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
+ WL1273_AUDIO_IO_SET_I2S);
+ if (r)
+ goto out;
+
+ } else if (core->mode == WL1273_MODE_TX &&
+ new_mode == WL1273_AUDIO_ANALOG) {
+ r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
+ WL1273_AUDIO_IO_SET_ANALOG);
+ if (r)
+ goto out;
+ }
+
+ core->audio_mode = new_mode;
+out:
+ return r;
+}
+
+/**
+ * wl1273_fm_set_volume() - Set volume.
+ * @core: A pointer to the device struct.
+ * @volume: The new volume value.
+ */
+static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
+{
+ u16 val;
+ int r;
+
+ if (volume > WL1273_MAX_VOLUME)
+ return -EINVAL;
+
+ if (core->volume == volume)
+ return 0;
+
+ r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume);
+ if (r)
+ return r;
+
+ core->volume = volume;
+ return 0;
+}
+
static int wl1273_core_remove(struct i2c_client *client)
{
struct wl1273_core *core = i2c_get_clientdata(client);
@@ -38,7 +177,6 @@ static int wl1273_core_remove(struct i2c_client *client)
dev_dbg(&client->dev, "%s\n", __func__);
mfd_remove_devices(&client->dev);
- i2c_set_clientdata(client, NULL);
kfree(core);
return 0;
@@ -83,6 +221,12 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
cell->data_size = sizeof(core);
children++;
+ core->read = wl1273_fm_read_reg;
+ core->write = wl1273_fm_write_cmd;
+ core->write_data = wl1273_fm_write_data;
+ core->set_audio = wl1273_fm_set_audio;
+ core->set_volume = wl1273_fm_set_volume;
+
if (pdata->children & WL1273_CODEC_CHILD) {
cell = &core->cells[children];
@@ -104,7 +248,6 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
return 0;
err:
- i2c_set_clientdata(client, NULL);
pdata->free_resources();
kfree(core);
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 5c8fcfc42c3e..74a8b272154f 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -51,11 +51,7 @@ source "drivers/staging/cx25821/Kconfig"
source "drivers/staging/tm6000/Kconfig"
-source "drivers/staging/dabusb/Kconfig"
-
-source "drivers/staging/se401/Kconfig"
-
-source "drivers/staging/usbvideo/Kconfig"
+source "drivers/staging/cxd2099/Kconfig"
source "drivers/staging/usbip/Kconfig"
@@ -179,5 +175,7 @@ source "drivers/staging/cptm1217/Kconfig"
source "drivers/staging/ste_rmi4/Kconfig"
+source "drivers/staging/altera-stapl/Kconfig"
+
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index d53886317826..9f50ec90f9d8 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -8,9 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
obj-$(CONFIG_VIDEO_CX25821) += cx25821/
obj-$(CONFIG_VIDEO_TM6000) += tm6000/
-obj-$(CONFIG_USB_DABUSB) += dabusb/
-obj-$(CONFIG_USB_VICAM) += usbvideo/
-obj-$(CONFIG_USB_SE401) += se401/
+obj-$(CONFIG_DVB_CXD2099) += cxd2099/
obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_USB_IP_COMMON) += usbip/
obj-$(CONFIG_W35UND) += winbond/
@@ -68,5 +66,6 @@ obj-$(CONFIG_BCM_WIMAX) += bcm/
obj-$(CONFIG_FT1000) += ft1000/
obj-$(CONFIG_SND_INTEL_SST) += intel_sst/
obj-$(CONFIG_SPEAKUP) += speakup/
+obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
diff --git a/drivers/staging/altera-stapl/Kconfig b/drivers/staging/altera-stapl/Kconfig
new file mode 100644
index 000000000000..7f01d8e93992
--- /dev/null
+++ b/drivers/staging/altera-stapl/Kconfig
@@ -0,0 +1,8 @@
+comment "Altera FPGA firmware download module"
+
+config ALTERA_STAPL
+ tristate "Altera FPGA firmware download module"
+ depends on I2C
+ default n
+ help
+ An Altera FPGA module. Say Y when you want to support this tool.
diff --git a/drivers/staging/altera-stapl/Makefile b/drivers/staging/altera-stapl/Makefile
new file mode 100644
index 000000000000..055f61ee781a
--- /dev/null
+++ b/drivers/staging/altera-stapl/Makefile
@@ -0,0 +1,3 @@
+altera-stapl-objs = altera-lpt.o altera-jtag.o altera-comp.o altera.o
+
+obj-$(CONFIG_ALTERA_STAPL) += altera-stapl.o
diff --git a/drivers/staging/altera-stapl/altera-comp.c b/drivers/staging/altera-stapl/altera-comp.c
new file mode 100644
index 000000000000..49b103bedaaf
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera-comp.c
@@ -0,0 +1,142 @@
+/*
+ * altera-comp.c
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include "altera-exprt.h"
+
+#define SHORT_BITS 16
+#define CHAR_BITS 8
+#define DATA_BLOB_LENGTH 3
+#define MATCH_DATA_LENGTH 8192
+#define ALTERA_REQUEST_SIZE 1024
+#define ALTERA_BUFFER_SIZE (MATCH_DATA_LENGTH + ALTERA_REQUEST_SIZE)
+
+static u32 altera_bits_req(u32 n)
+{
+ u32 result = SHORT_BITS;
+
+ if (n == 0)
+ result = 1;
+ else {
+ /* Look for the highest non-zero bit position */
+ while ((n & (1 << (SHORT_BITS - 1))) == 0) {
+ n <<= 1;
+ --result;
+ }
+ }
+
+ return result;
+}
+
+static u32 altera_read_packed(u8 *buffer, u32 bits, u32 *bits_avail,
+ u32 *in_index)
+{
+ u32 result = 0;
+ u32 shift = 0;
+ u32 databyte = 0;
+
+ while (bits > 0) {
+ databyte = buffer[*in_index];
+ result |= (((databyte >> (CHAR_BITS - *bits_avail))
+ & (0xff >> (CHAR_BITS - *bits_avail))) << shift);
+
+ if (bits <= *bits_avail) {
+ result &= (0xffff >> (SHORT_BITS - (bits + shift)));
+ *bits_avail -= bits;
+ bits = 0;
+ } else {
+ ++(*in_index);
+ shift += *bits_avail;
+ bits -= *bits_avail;
+ *bits_avail = CHAR_BITS;
+ }
+ }
+
+ return result;
+}
+
+u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version)
+{
+ u32 i, j, data_length = 0L;
+ u32 offset, length;
+ u32 match_data_length = MATCH_DATA_LENGTH;
+ u32 bits_avail = CHAR_BITS;
+ u32 in_index = 0L;
+
+ if (version > 0)
+ --match_data_length;
+
+ for (i = 0; i < out_length; ++i)
+ out[i] = 0;
+
+ /* Read number of bytes in data. */
+ for (i = 0; i < sizeof(in_length); ++i) {
+ data_length = data_length | (
+ altera_read_packed(in,
+ CHAR_BITS,
+ &bits_avail,
+ &in_index) << (i * CHAR_BITS));
+ }
+
+ if (data_length > out_length) {
+ data_length = 0L;
+ return data_length;
+ }
+
+ i = 0;
+ while (i < data_length) {
+ /* A 0 bit indicates literal data. */
+ if (altera_read_packed(in, 1, &bits_avail,
+ &in_index) == 0) {
+ for (j = 0; j < DATA_BLOB_LENGTH; ++j) {
+ if (i < data_length) {
+ out[i] = (u8)altera_read_packed(in,
+ CHAR_BITS,
+ &bits_avail,
+ &in_index);
+ i++;
+ }
+ }
+ } else {
+ /* A 1 bit indicates offset/length to follow. */
+ offset = altera_read_packed(in, altera_bits_req((s16)
+ (i > match_data_length ?
+ match_data_length : i)),
+ &bits_avail,
+ &in_index);
+ length = altera_read_packed(in, CHAR_BITS,
+ &bits_avail,
+ &in_index);
+ for (j = 0; j < length; ++j) {
+ if (i < data_length) {
+ out[i] = out[i - offset];
+ i++;
+ }
+ }
+ }
+ }
+
+ return data_length;
+}
diff --git a/drivers/staging/altera-stapl/altera-exprt.h b/drivers/staging/altera-stapl/altera-exprt.h
new file mode 100644
index 000000000000..39c38d84a670
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera-exprt.h
@@ -0,0 +1,33 @@
+/*
+ * altera-exprt.h
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ALTERA_EXPRT_H
+#define ALTERA_EXPRT_H
+
+
+u32 altera_shrink(u8 *in, u32 in_length, u8 *out, u32 out_length, s32 version);
+int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo);
+
+#endif /* ALTERA_EXPRT_H */
diff --git a/drivers/staging/altera-stapl/altera-jtag.c b/drivers/staging/altera-stapl/altera-jtag.c
new file mode 100644
index 000000000000..6b633b179a7e
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera-jtag.c
@@ -0,0 +1,1020 @@
+/*
+ * altera-jtag.c
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <staging/altera.h>
+#include "altera-exprt.h"
+#include "altera-jtag.h"
+
+#define alt_jtag_io(a, b, c)\
+ astate->config->jtag_io(astate->config->dev, a, b, c);
+
+#define alt_malloc(a) kzalloc(a, GFP_KERNEL);
+
+/*
+ * This structure shows, for each JTAG state, which state is reached after
+ * a single TCK clock cycle with TMS high or TMS low, respectively. This
+ * describes all possible state transitions in the JTAG state machine.
+ */
+struct altera_jtag_machine {
+ enum altera_jtag_state tms_high;
+ enum altera_jtag_state tms_low;
+};
+
+static const struct altera_jtag_machine altera_transitions[] = {
+ /* RESET */ { RESET, IDLE },
+ /* IDLE */ { DRSELECT, IDLE },
+ /* DRSELECT */ { IRSELECT, DRCAPTURE },
+ /* DRCAPTURE */ { DREXIT1, DRSHIFT },
+ /* DRSHIFT */ { DREXIT1, DRSHIFT },
+ /* DREXIT1 */ { DRUPDATE, DRPAUSE },
+ /* DRPAUSE */ { DREXIT2, DRPAUSE },
+ /* DREXIT2 */ { DRUPDATE, DRSHIFT },
+ /* DRUPDATE */ { DRSELECT, IDLE },
+ /* IRSELECT */ { RESET, IRCAPTURE },
+ /* IRCAPTURE */ { IREXIT1, IRSHIFT },
+ /* IRSHIFT */ { IREXIT1, IRSHIFT },
+ /* IREXIT1 */ { IRUPDATE, IRPAUSE },
+ /* IRPAUSE */ { IREXIT2, IRPAUSE },
+ /* IREXIT2 */ { IRUPDATE, IRSHIFT },
+ /* IRUPDATE */ { DRSELECT, IDLE }
+};
+
+/*
+ * This table contains the TMS value to be used to take the NEXT STEP on
+ * the path to the desired state. The array index is the current state,
+ * and the bit position is the desired endstate. To find out which state
+ * is used as the intermediate state, look up the TMS value in the
+ * altera_transitions[] table.
+ */
+static const u16 altera_jtag_path_map[16] = {
+ /* RST RTI SDRS CDR SDR E1DR PDR E2DR */
+ 0x0001, 0xFFFD, 0xFE01, 0xFFE7, 0xFFEF, 0xFF0F, 0xFFBF, 0xFFFF,
+ /* UDR SIRS CIR SIR E1IR PIR E2IR UIR */
+ 0xFEFD, 0x0001, 0xF3FF, 0xF7FF, 0x87FF, 0xDFFF, 0xFFFF, 0x7FFD
+};
+
+/* Flag bits for alt_jtag_io() function */
+#define TMS_HIGH 1
+#define TMS_LOW 0
+#define TDI_HIGH 1
+#define TDI_LOW 0
+#define READ_TDO 1
+#define IGNORE_TDO 0
+
+int altera_jinit(struct altera_state *astate)
+{
+ struct altera_jtag *js = &astate->js;
+
+ /* initial JTAG state is unknown */
+ js->jtag_state = ILLEGAL_JTAG_STATE;
+
+ /* initialize to default state */
+ js->drstop_state = IDLE;
+ js->irstop_state = IDLE;
+ js->dr_pre = 0;
+ js->dr_post = 0;
+ js->ir_pre = 0;
+ js->ir_post = 0;
+ js->dr_length = 0;
+ js->ir_length = 0;
+
+ js->dr_pre_data = NULL;
+ js->dr_post_data = NULL;
+ js->ir_pre_data = NULL;
+ js->ir_post_data = NULL;
+ js->dr_buffer = NULL;
+ js->ir_buffer = NULL;
+
+ return 0;
+}
+
+int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state)
+{
+ js->drstop_state = state;
+
+ return 0;
+}
+
+int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state)
+{
+ js->irstop_state = state;
+
+ return 0;
+}
+
+int altera_set_dr_pre(struct altera_jtag *js,
+ u32 count, u32 start_index,
+ u8 *preamble_data)
+{
+ int status = 0;
+ u32 i;
+ u32 j;
+
+ if (count > js->dr_pre) {
+ kfree(js->dr_pre_data);
+ js->dr_pre_data = (u8 *)alt_malloc((count + 7) >> 3);
+ if (js->dr_pre_data == NULL)
+ status = -ENOMEM;
+ else
+ js->dr_pre = count;
+ } else
+ js->dr_pre = count;
+
+ if (status == 0) {
+ for (i = 0; i < count; ++i) {
+ j = i + start_index;
+
+ if (preamble_data == NULL)
+ js->dr_pre_data[i >> 3] |= (1 << (i & 7));
+ else {
+ if (preamble_data[j >> 3] & (1 << (j & 7)))
+ js->dr_pre_data[i >> 3] |=
+ (1 << (i & 7));
+ else
+ js->dr_pre_data[i >> 3] &=
+ ~(u32)(1 << (i & 7));
+
+ }
+ }
+ }
+
+ return status;
+}
+
+int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *preamble_data)
+{
+ int status = 0;
+ u32 i;
+ u32 j;
+
+ if (count > js->ir_pre) {
+ kfree(js->ir_pre_data);
+ js->ir_pre_data = (u8 *)alt_malloc((count + 7) >> 3);
+ if (js->ir_pre_data == NULL)
+ status = -ENOMEM;
+ else
+ js->ir_pre = count;
+
+ } else
+ js->ir_pre = count;
+
+ if (status == 0) {
+ for (i = 0; i < count; ++i) {
+ j = i + start_index;
+ if (preamble_data == NULL)
+ js->ir_pre_data[i >> 3] |= (1 << (i & 7));
+ else {
+ if (preamble_data[j >> 3] & (1 << (j & 7)))
+ js->ir_pre_data[i >> 3] |=
+ (1 << (i & 7));
+ else
+ js->ir_pre_data[i >> 3] &=
+ ~(u32)(1 << (i & 7));
+
+ }
+ }
+ }
+
+ return status;
+}
+
+int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *postamble_data)
+{
+ int status = 0;
+ u32 i;
+ u32 j;
+
+ if (count > js->dr_post) {
+ kfree(js->dr_post_data);
+ js->dr_post_data = (u8 *)alt_malloc((count + 7) >> 3);
+
+ if (js->dr_post_data == NULL)
+ status = -ENOMEM;
+ else
+ js->dr_post = count;
+
+ } else
+ js->dr_post = count;
+
+ if (status == 0) {
+ for (i = 0; i < count; ++i) {
+ j = i + start_index;
+
+ if (postamble_data == NULL)
+ js->dr_post_data[i >> 3] |= (1 << (i & 7));
+ else {
+ if (postamble_data[j >> 3] & (1 << (j & 7)))
+ js->dr_post_data[i >> 3] |=
+ (1 << (i & 7));
+ else
+ js->dr_post_data[i >> 3] &=
+ ~(u32)(1 << (i & 7));
+
+ }
+ }
+ }
+
+ return status;
+}
+
+int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *postamble_data)
+{
+ int status = 0;
+ u32 i;
+ u32 j;
+
+ if (count > js->ir_post) {
+ kfree(js->ir_post_data);
+ js->ir_post_data = (u8 *)alt_malloc((count + 7) >> 3);
+ if (js->ir_post_data == NULL)
+ status = -ENOMEM;
+ else
+ js->ir_post = count;
+
+ } else
+ js->ir_post = count;
+
+ if (status != 0)
+ return status;
+
+ for (i = 0; i < count; ++i) {
+ j = i + start_index;
+
+ if (postamble_data == NULL)
+ js->ir_post_data[i >> 3] |= (1 << (i & 7));
+ else {
+ if (postamble_data[j >> 3] & (1 << (j & 7)))
+ js->ir_post_data[i >> 3] |= (1 << (i & 7));
+ else
+ js->ir_post_data[i >> 3] &=
+ ~(u32)(1 << (i & 7));
+
+ }
+ }
+
+ return status;
+}
+
+static void altera_jreset_idle(struct altera_state *astate)
+{
+ struct altera_jtag *js = &astate->js;
+ int i;
+ /* Go to Test Logic Reset (no matter what the starting state may be) */
+ for (i = 0; i < 5; ++i)
+ alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO);
+
+ /* Now step to Run Test / Idle */
+ alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO);
+ js->jtag_state = IDLE;
+}
+
+int altera_goto_jstate(struct altera_state *astate,
+ enum altera_jtag_state state)
+{
+ struct altera_jtag *js = &astate->js;
+ int tms;
+ int count = 0;
+ int status = 0;
+
+ if (js->jtag_state == ILLEGAL_JTAG_STATE)
+ /* initialize JTAG chain to known state */
+ altera_jreset_idle(astate);
+
+ if (js->jtag_state == state) {
+ /*
+ * We are already in the desired state.
+ * If it is a stable state, loop here.
+ * Otherwise do nothing (no clock cycles).
+ */
+ if ((state == IDLE) || (state == DRSHIFT) ||
+ (state == DRPAUSE) || (state == IRSHIFT) ||
+ (state == IRPAUSE)) {
+ alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO);
+ } else if (state == RESET)
+ alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO);
+
+ } else {
+ while ((js->jtag_state != state) && (count < 9)) {
+ /* Get TMS value to take a step toward desired state */
+ tms = (altera_jtag_path_map[js->jtag_state] &
+ (1 << state))
+ ? TMS_HIGH : TMS_LOW;
+
+ /* Take a step */
+ alt_jtag_io(tms, TDI_LOW, IGNORE_TDO);
+
+ if (tms)
+ js->jtag_state =
+ altera_transitions[js->jtag_state].tms_high;
+ else
+ js->jtag_state =
+ altera_transitions[js->jtag_state].tms_low;
+
+ ++count;
+ }
+ }
+
+ if (js->jtag_state != state)
+ status = -EREMOTEIO;
+
+ return status;
+}
+
+int altera_wait_cycles(struct altera_state *astate,
+ s32 cycles,
+ enum altera_jtag_state wait_state)
+{
+ struct altera_jtag *js = &astate->js;
+ int tms;
+ s32 count;
+ int status = 0;
+
+ if (js->jtag_state != wait_state)
+ status = altera_goto_jstate(astate, wait_state);
+
+ if (status == 0) {
+ /*
+ * Set TMS high to loop in RESET state
+ * Set TMS low to loop in any other stable state
+ */
+ tms = (wait_state == RESET) ? TMS_HIGH : TMS_LOW;
+
+ for (count = 0L; count < cycles; count++)
+ alt_jtag_io(tms, TDI_LOW, IGNORE_TDO);
+
+ }
+
+ return status;
+}
+
+int altera_wait_msecs(struct altera_state *astate,
+ s32 microseconds, enum altera_jtag_state wait_state)
+/*
+ * Causes JTAG hardware to sit in the specified stable
+ * state for the specified duration of real time. If
+ * no JTAG operations have been performed yet, then only
+ * a delay is performed. This permits the WAIT USECS
+ * statement to be used in VECTOR programs without causing
+ * any JTAG operations.
+ * Returns 0 for success, else appropriate error code.
+ */
+{
+ struct altera_jtag *js = &astate->js;
+ int status = 0;
+
+ if ((js->jtag_state != ILLEGAL_JTAG_STATE) &&
+ (js->jtag_state != wait_state))
+ status = altera_goto_jstate(astate, wait_state);
+
+ if (status == 0)
+ /* Wait for specified time interval */
+ udelay(microseconds);
+
+ return status;
+}
+
+static void altera_concatenate_data(u8 *buffer,
+ u8 *preamble_data,
+ u32 preamble_count,
+ u8 *target_data,
+ u32 start_index,
+ u32 target_count,
+ u8 *postamble_data,
+ u32 postamble_count)
+/*
+ * Copies preamble data, target data, and postamble data
+ * into one buffer for IR or DR scans.
+ */
+{
+ u32 i, j, k;
+
+ for (i = 0L; i < preamble_count; ++i) {
+ if (preamble_data[i >> 3L] & (1L << (i & 7L)))
+ buffer[i >> 3L] |= (1L << (i & 7L));
+ else
+ buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
+
+ }
+
+ j = start_index;
+ k = preamble_count + target_count;
+ for (; i < k; ++i, ++j) {
+ if (target_data[j >> 3L] & (1L << (j & 7L)))
+ buffer[i >> 3L] |= (1L << (i & 7L));
+ else
+ buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
+
+ }
+
+ j = 0L;
+ k = preamble_count + target_count + postamble_count;
+ for (; i < k; ++i, ++j) {
+ if (postamble_data[j >> 3L] & (1L << (j & 7L)))
+ buffer[i >> 3L] |= (1L << (i & 7L));
+ else
+ buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
+
+ }
+}
+
+static int alt_jtag_drscan(struct altera_state *astate,
+ int start_state,
+ int count,
+ u8 *tdi,
+ u8 *tdo)
+{
+ int i = 0;
+ int tdo_bit = 0;
+ int status = 1;
+
+ /* First go to DRSHIFT state */
+ switch (start_state) {
+ case 0: /* IDLE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(0, 0, 0); /* DRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* DRSHIFT */
+ break;
+
+ case 1: /* DRPAUSE */
+ alt_jtag_io(1, 0, 0); /* DREXIT2 */
+ alt_jtag_io(1, 0, 0); /* DRUPDATE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(0, 0, 0); /* DRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* DRSHIFT */
+ break;
+
+ case 2: /* IRPAUSE */
+ alt_jtag_io(1, 0, 0); /* IREXIT2 */
+ alt_jtag_io(1, 0, 0); /* IRUPDATE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(0, 0, 0); /* DRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* DRSHIFT */
+ break;
+
+ default:
+ status = 0;
+ }
+
+ if (status) {
+ /* loop in the SHIFT-DR state */
+ for (i = 0; i < count; i++) {
+ tdo_bit = alt_jtag_io(
+ (i == count - 1),
+ tdi[i >> 3] & (1 << (i & 7)),
+ (tdo != NULL));
+
+ if (tdo != NULL) {
+ if (tdo_bit)
+ tdo[i >> 3] |= (1 << (i & 7));
+ else
+ tdo[i >> 3] &= ~(u32)(1 << (i & 7));
+
+ }
+ }
+
+ alt_jtag_io(0, 0, 0); /* DRPAUSE */
+ }
+
+ return status;
+}
+
+static int alt_jtag_irscan(struct altera_state *astate,
+ int start_state,
+ int count,
+ u8 *tdi,
+ u8 *tdo)
+{
+ int i = 0;
+ int tdo_bit = 0;
+ int status = 1;
+
+ /* First go to IRSHIFT state */
+ switch (start_state) {
+ case 0: /* IDLE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(1, 0, 0); /* IRSELECT */
+ alt_jtag_io(0, 0, 0); /* IRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* IRSHIFT */
+ break;
+
+ case 1: /* DRPAUSE */
+ alt_jtag_io(1, 0, 0); /* DREXIT2 */
+ alt_jtag_io(1, 0, 0); /* DRUPDATE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(1, 0, 0); /* IRSELECT */
+ alt_jtag_io(0, 0, 0); /* IRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* IRSHIFT */
+ break;
+
+ case 2: /* IRPAUSE */
+ alt_jtag_io(1, 0, 0); /* IREXIT2 */
+ alt_jtag_io(1, 0, 0); /* IRUPDATE */
+ alt_jtag_io(1, 0, 0); /* DRSELECT */
+ alt_jtag_io(1, 0, 0); /* IRSELECT */
+ alt_jtag_io(0, 0, 0); /* IRCAPTURE */
+ alt_jtag_io(0, 0, 0); /* IRSHIFT */
+ break;
+
+ default:
+ status = 0;
+ }
+
+ if (status) {
+ /* loop in the SHIFT-IR state */
+ for (i = 0; i < count; i++) {
+ tdo_bit = alt_jtag_io(
+ (i == count - 1),
+ tdi[i >> 3] & (1 << (i & 7)),
+ (tdo != NULL));
+ if (tdo != NULL) {
+ if (tdo_bit)
+ tdo[i >> 3] |= (1 << (i & 7));
+ else
+ tdo[i >> 3] &= ~(u32)(1 << (i & 7));
+
+ }
+ }
+
+ alt_jtag_io(0, 0, 0); /* IRPAUSE */
+ }
+
+ return status;
+}
+
+static void altera_extract_target_data(u8 *buffer,
+ u8 *target_data,
+ u32 start_index,
+ u32 preamble_count,
+ u32 target_count)
+/*
+ * Copies target data from scan buffer, filtering out
+ * preamble and postamble data.
+ */
+{
+ u32 i;
+ u32 j;
+ u32 k;
+
+ j = preamble_count;
+ k = start_index + target_count;
+ for (i = start_index; i < k; ++i, ++j) {
+ if (buffer[j >> 3] & (1 << (j & 7)))
+ target_data[i >> 3] |= (1 << (i & 7));
+ else
+ target_data[i >> 3] &= ~(u32)(1 << (i & 7));
+
+ }
+}
+
+int altera_irscan(struct altera_state *astate,
+ u32 count,
+ u8 *tdi_data,
+ u32 start_index)
+/* Shifts data into instruction register */
+{
+ struct altera_jtag *js = &astate->js;
+ int start_code = 0;
+ u32 alloc_chars = 0;
+ u32 shift_count = js->ir_pre + count + js->ir_post;
+ int status = 0;
+ enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
+
+ switch (js->jtag_state) {
+ case ILLEGAL_JTAG_STATE:
+ case RESET:
+ case IDLE:
+ start_code = 0;
+ start_state = IDLE;
+ break;
+
+ case DRSELECT:
+ case DRCAPTURE:
+ case DRSHIFT:
+ case DREXIT1:
+ case DRPAUSE:
+ case DREXIT2:
+ case DRUPDATE:
+ start_code = 1;
+ start_state = DRPAUSE;
+ break;
+
+ case IRSELECT:
+ case IRCAPTURE:
+ case IRSHIFT:
+ case IREXIT1:
+ case IRPAUSE:
+ case IREXIT2:
+ case IRUPDATE:
+ start_code = 2;
+ start_state = IRPAUSE;
+ break;
+
+ default:
+ status = -EREMOTEIO;
+ break;
+ }
+
+ if (status == 0)
+ if (js->jtag_state != start_state)
+ status = altera_goto_jstate(astate, start_state);
+
+ if (status == 0) {
+ if (shift_count > js->ir_length) {
+ alloc_chars = (shift_count + 7) >> 3;
+ kfree(js->ir_buffer);
+ js->ir_buffer = (u8 *)alt_malloc(alloc_chars);
+ if (js->ir_buffer == NULL)
+ status = -ENOMEM;
+ else
+ js->ir_length = alloc_chars * 8;
+
+ }
+ }
+
+ if (status == 0) {
+ /*
+ * Copy preamble data, IR data,
+ * and postamble data into a buffer
+ */
+ altera_concatenate_data(js->ir_buffer,
+ js->ir_pre_data,
+ js->ir_pre,
+ tdi_data,
+ start_index,
+ count,
+ js->ir_post_data,
+ js->ir_post);
+ /* Do the IRSCAN */
+ alt_jtag_irscan(astate,
+ start_code,
+ shift_count,
+ js->ir_buffer,
+ NULL);
+
+ /* alt_jtag_irscan() always ends in IRPAUSE state */
+ js->jtag_state = IRPAUSE;
+ }
+
+ if (status == 0)
+ if (js->irstop_state != IRPAUSE)
+ status = altera_goto_jstate(astate, js->irstop_state);
+
+
+ return status;
+}
+
+int altera_swap_ir(struct altera_state *astate,
+ u32 count,
+ u8 *in_data,
+ u32 in_index,
+ u8 *out_data,
+ u32 out_index)
+/* Shifts data into instruction register, capturing output data */
+{
+ struct altera_jtag *js = &astate->js;
+ int start_code = 0;
+ u32 alloc_chars = 0;
+ u32 shift_count = js->ir_pre + count + js->ir_post;
+ int status = 0;
+ enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
+
+ switch (js->jtag_state) {
+ case ILLEGAL_JTAG_STATE:
+ case RESET:
+ case IDLE:
+ start_code = 0;
+ start_state = IDLE;
+ break;
+
+ case DRSELECT:
+ case DRCAPTURE:
+ case DRSHIFT:
+ case DREXIT1:
+ case DRPAUSE:
+ case DREXIT2:
+ case DRUPDATE:
+ start_code = 1;
+ start_state = DRPAUSE;
+ break;
+
+ case IRSELECT:
+ case IRCAPTURE:
+ case IRSHIFT:
+ case IREXIT1:
+ case IRPAUSE:
+ case IREXIT2:
+ case IRUPDATE:
+ start_code = 2;
+ start_state = IRPAUSE;
+ break;
+
+ default:
+ status = -EREMOTEIO;
+ break;
+ }
+
+ if (status == 0)
+ if (js->jtag_state != start_state)
+ status = altera_goto_jstate(astate, start_state);
+
+ if (status == 0) {
+ if (shift_count > js->ir_length) {
+ alloc_chars = (shift_count + 7) >> 3;
+ kfree(js->ir_buffer);
+ js->ir_buffer = (u8 *)alt_malloc(alloc_chars);
+ if (js->ir_buffer == NULL)
+ status = -ENOMEM;
+ else
+ js->ir_length = alloc_chars * 8;
+
+ }
+ }
+
+ if (status == 0) {
+ /*
+ * Copy preamble data, IR data,
+ * and postamble data into a buffer
+ */
+ altera_concatenate_data(js->ir_buffer,
+ js->ir_pre_data,
+ js->ir_pre,
+ in_data,
+ in_index,
+ count,
+ js->ir_post_data,
+ js->ir_post);
+
+ /* Do the IRSCAN */
+ alt_jtag_irscan(astate,
+ start_code,
+ shift_count,
+ js->ir_buffer,
+ js->ir_buffer);
+
+ /* alt_jtag_irscan() always ends in IRPAUSE state */
+ js->jtag_state = IRPAUSE;
+ }
+
+ if (status == 0)
+ if (js->irstop_state != IRPAUSE)
+ status = altera_goto_jstate(astate, js->irstop_state);
+
+
+ if (status == 0)
+ /* Now extract the returned data from the buffer */
+ altera_extract_target_data(js->ir_buffer,
+ out_data, out_index,
+ js->ir_pre, count);
+
+ return status;
+}
+
+int altera_drscan(struct altera_state *astate,
+ u32 count,
+ u8 *tdi_data,
+ u32 start_index)
+/* Shifts data into data register (ignoring output data) */
+{
+ struct altera_jtag *js = &astate->js;
+ int start_code = 0;
+ u32 alloc_chars = 0;
+ u32 shift_count = js->dr_pre + count + js->dr_post;
+ int status = 0;
+ enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
+
+ switch (js->jtag_state) {
+ case ILLEGAL_JTAG_STATE:
+ case RESET:
+ case IDLE:
+ start_code = 0;
+ start_state = IDLE;
+ break;
+
+ case DRSELECT:
+ case DRCAPTURE:
+ case DRSHIFT:
+ case DREXIT1:
+ case DRPAUSE:
+ case DREXIT2:
+ case DRUPDATE:
+ start_code = 1;
+ start_state = DRPAUSE;
+ break;
+
+ case IRSELECT:
+ case IRCAPTURE:
+ case IRSHIFT:
+ case IREXIT1:
+ case IRPAUSE:
+ case IREXIT2:
+ case IRUPDATE:
+ start_code = 2;
+ start_state = IRPAUSE;
+ break;
+
+ default:
+ status = -EREMOTEIO;
+ break;
+ }
+
+ if (status == 0)
+ if (js->jtag_state != start_state)
+ status = altera_goto_jstate(astate, start_state);
+
+ if (status == 0) {
+ if (shift_count > js->dr_length) {
+ alloc_chars = (shift_count + 7) >> 3;
+ kfree(js->dr_buffer);
+ js->dr_buffer = (u8 *)alt_malloc(alloc_chars);
+ if (js->dr_buffer == NULL)
+ status = -ENOMEM;
+ else
+ js->dr_length = alloc_chars * 8;
+
+ }
+ }
+
+ if (status == 0) {
+ /*
+ * Copy preamble data, DR data,
+ * and postamble data into a buffer
+ */
+ altera_concatenate_data(js->dr_buffer,
+ js->dr_pre_data,
+ js->dr_pre,
+ tdi_data,
+ start_index,
+ count,
+ js->dr_post_data,
+ js->dr_post);
+ /* Do the DRSCAN */
+ alt_jtag_drscan(astate, start_code, shift_count,
+ js->dr_buffer, NULL);
+ /* alt_jtag_drscan() always ends in DRPAUSE state */
+ js->jtag_state = DRPAUSE;
+ }
+
+ if (status == 0)
+ if (js->drstop_state != DRPAUSE)
+ status = altera_goto_jstate(astate, js->drstop_state);
+
+ return status;
+}
+
+int altera_swap_dr(struct altera_state *astate, u32 count,
+ u8 *in_data, u32 in_index,
+ u8 *out_data, u32 out_index)
+/* Shifts data into data register, capturing output data */
+{
+ struct altera_jtag *js = &astate->js;
+ int start_code = 0;
+ u32 alloc_chars = 0;
+ u32 shift_count = js->dr_pre + count + js->dr_post;
+ int status = 0;
+ enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
+
+ switch (js->jtag_state) {
+ case ILLEGAL_JTAG_STATE:
+ case RESET:
+ case IDLE:
+ start_code = 0;
+ start_state = IDLE;
+ break;
+
+ case DRSELECT:
+ case DRCAPTURE:
+ case DRSHIFT:
+ case DREXIT1:
+ case DRPAUSE:
+ case DREXIT2:
+ case DRUPDATE:
+ start_code = 1;
+ start_state = DRPAUSE;
+ break;
+
+ case IRSELECT:
+ case IRCAPTURE:
+ case IRSHIFT:
+ case IREXIT1:
+ case IRPAUSE:
+ case IREXIT2:
+ case IRUPDATE:
+ start_code = 2;
+ start_state = IRPAUSE;
+ break;
+
+ default:
+ status = -EREMOTEIO;
+ break;
+ }
+
+ if (status == 0)
+ if (js->jtag_state != start_state)
+ status = altera_goto_jstate(astate, start_state);
+
+ if (status == 0) {
+ if (shift_count > js->dr_length) {
+ alloc_chars = (shift_count + 7) >> 3;
+ kfree(js->dr_buffer);
+ js->dr_buffer = (u8 *)alt_malloc(alloc_chars);
+
+ if (js->dr_buffer == NULL)
+ status = -ENOMEM;
+ else
+ js->dr_length = alloc_chars * 8;
+
+ }
+ }
+
+ if (status == 0) {
+ /*
+ * Copy preamble data, DR data,
+ * and postamble data into a buffer
+ */
+ altera_concatenate_data(js->dr_buffer,
+ js->dr_pre_data,
+ js->dr_pre,
+ in_data,
+ in_index,
+ count,
+ js->dr_post_data,
+ js->dr_post);
+
+ /* Do the DRSCAN */
+ alt_jtag_drscan(astate,
+ start_code,
+ shift_count,
+ js->dr_buffer,
+ js->dr_buffer);
+
+ /* alt_jtag_drscan() always ends in DRPAUSE state */
+ js->jtag_state = DRPAUSE;
+ }
+
+ if (status == 0)
+ if (js->drstop_state != DRPAUSE)
+ status = altera_goto_jstate(astate, js->drstop_state);
+
+ if (status == 0)
+ /* Now extract the returned data from the buffer */
+ altera_extract_target_data(js->dr_buffer,
+ out_data,
+ out_index,
+ js->dr_pre,
+ count);
+
+ return status;
+}
+
+void altera_free_buffers(struct altera_state *astate)
+{
+ struct altera_jtag *js = &astate->js;
+ /* If the JTAG interface was used, reset it to TLR */
+ if (js->jtag_state != ILLEGAL_JTAG_STATE)
+ altera_jreset_idle(astate);
+
+ kfree(js->dr_pre_data);
+ js->dr_pre_data = NULL;
+
+ kfree(js->dr_post_data);
+ js->dr_post_data = NULL;
+
+ kfree(js->dr_buffer);
+ js->dr_buffer = NULL;
+
+ kfree(js->ir_pre_data);
+ js->ir_pre_data = NULL;
+
+ kfree(js->ir_post_data);
+ js->ir_post_data = NULL;
+
+ kfree(js->ir_buffer);
+ js->ir_buffer = NULL;
+}
diff --git a/drivers/staging/altera-stapl/altera-jtag.h b/drivers/staging/altera-stapl/altera-jtag.h
new file mode 100644
index 000000000000..2f97e36a2fbc
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera-jtag.h
@@ -0,0 +1,113 @@
+/*
+ * altera-jtag.h
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ALTERA_JTAG_H
+#define ALTERA_JTAG_H
+
+/* Function Prototypes */
+enum altera_jtag_state {
+ ILLEGAL_JTAG_STATE = -1,
+ RESET = 0,
+ IDLE = 1,
+ DRSELECT = 2,
+ DRCAPTURE = 3,
+ DRSHIFT = 4,
+ DREXIT1 = 5,
+ DRPAUSE = 6,
+ DREXIT2 = 7,
+ DRUPDATE = 8,
+ IRSELECT = 9,
+ IRCAPTURE = 10,
+ IRSHIFT = 11,
+ IREXIT1 = 12,
+ IRPAUSE = 13,
+ IREXIT2 = 14,
+ IRUPDATE = 15
+
+};
+
+struct altera_jtag {
+ /* Global variable to store the current JTAG state */
+ enum altera_jtag_state jtag_state;
+
+ /* Store current stop-state for DR and IR scan commands */
+ enum altera_jtag_state drstop_state;
+ enum altera_jtag_state irstop_state;
+
+ /* Store current padding values */
+ u32 dr_pre;
+ u32 dr_post;
+ u32 ir_pre;
+ u32 ir_post;
+ u32 dr_length;
+ u32 ir_length;
+ u8 *dr_pre_data;
+ u8 *dr_post_data;
+ u8 *ir_pre_data;
+ u8 *ir_post_data;
+ u8 *dr_buffer;
+ u8 *ir_buffer;
+};
+
+#define ALTERA_STACK_SIZE 128
+#define ALTERA_MESSAGE_LENGTH 1024
+
+struct altera_state {
+ struct altera_config *config;
+ struct altera_jtag js;
+ char msg_buff[ALTERA_MESSAGE_LENGTH + 1];
+ long stack[ALTERA_STACK_SIZE];
+};
+
+int altera_jinit(struct altera_state *astate);
+int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state);
+int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state);
+int altera_set_dr_pre(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *preamble_data);
+int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *preamble_data);
+int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *postamble_data);
+int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index,
+ u8 *postamble_data);
+int altera_goto_jstate(struct altera_state *astate,
+ enum altera_jtag_state state);
+int altera_wait_cycles(struct altera_state *astate, s32 cycles,
+ enum altera_jtag_state wait_state);
+int altera_wait_msecs(struct altera_state *astate, s32 microseconds,
+ enum altera_jtag_state wait_state);
+int altera_irscan(struct altera_state *astate, u32 count,
+ u8 *tdi_data, u32 start_index);
+int altera_swap_ir(struct altera_state *astate,
+ u32 count, u8 *in_data,
+ u32 in_index, u8 *out_data,
+ u32 out_index);
+int altera_drscan(struct altera_state *astate, u32 count,
+ u8 *tdi_data, u32 start_index);
+int altera_swap_dr(struct altera_state *astate, u32 count,
+ u8 *in_data, u32 in_index,
+ u8 *out_data, u32 out_index);
+void altera_free_buffers(struct altera_state *astate);
+#endif /* ALTERA_JTAG_H */
diff --git a/drivers/staging/altera-stapl/altera-lpt.c b/drivers/staging/altera-stapl/altera-lpt.c
new file mode 100644
index 000000000000..91456a03612d
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera-lpt.c
@@ -0,0 +1,70 @@
+/*
+ * altera-lpt.c
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include "altera-exprt.h"
+
+static int lpt_hardware_initialized;
+
+static void byteblaster_write(int port, int data)
+{
+ outb((u8)data, (u16)(port + 0x378));
+};
+
+static int byteblaster_read(int port)
+{
+ int data = 0;
+ data = inb((u16)(port + 0x378));
+ return data & 0xff;
+};
+
+int netup_jtag_io_lpt(void *device, int tms, int tdi, int read_tdo)
+{
+ int data = 0;
+ int tdo = 0;
+ int initial_lpt_ctrl = 0;
+
+ if (!lpt_hardware_initialized) {
+ initial_lpt_ctrl = byteblaster_read(2);
+ byteblaster_write(2, (initial_lpt_ctrl | 0x02) & 0xdf);
+ lpt_hardware_initialized = 1;
+ }
+
+ data = ((tdi ? 0x40 : 0) | (tms ? 0x02 : 0));
+
+ byteblaster_write(0, data);
+
+ if (read_tdo) {
+ tdo = byteblaster_read(1);
+ tdo = ((tdo & 0x80) ? 0 : 1);
+ }
+
+ byteblaster_write(0, data | 0x01);
+
+ byteblaster_write(0, data);
+
+ return tdo;
+}
diff --git a/drivers/staging/altera-stapl/altera.c b/drivers/staging/altera-stapl/altera.c
new file mode 100644
index 000000000000..05aad351b120
--- /dev/null
+++ b/drivers/staging/altera-stapl/altera.c
@@ -0,0 +1,2527 @@
+/*
+ * altera.c
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <staging/altera.h>
+#include "altera-exprt.h"
+#include "altera-jtag.h"
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debugging information");
+
+MODULE_DESCRIPTION("altera FPGA kernel module");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@netup.ru>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(args...) \
+ if (debug) { \
+ printk(KERN_DEBUG args); \
+ }
+
+enum altera_fpga_opcode {
+ OP_NOP = 0,
+ OP_DUP,
+ OP_SWP,
+ OP_ADD,
+ OP_SUB,
+ OP_MULT,
+ OP_DIV,
+ OP_MOD,
+ OP_SHL,
+ OP_SHR,
+ OP_NOT,
+ OP_AND,
+ OP_OR,
+ OP_XOR,
+ OP_INV,
+ OP_GT,
+ OP_LT,
+ OP_RET,
+ OP_CMPS,
+ OP_PINT,
+ OP_PRNT,
+ OP_DSS,
+ OP_DSSC,
+ OP_ISS,
+ OP_ISSC,
+ OP_DPR = 0x1c,
+ OP_DPRL,
+ OP_DPO,
+ OP_DPOL,
+ OP_IPR,
+ OP_IPRL,
+ OP_IPO,
+ OP_IPOL,
+ OP_PCHR,
+ OP_EXIT,
+ OP_EQU,
+ OP_POPT,
+ OP_ABS = 0x2c,
+ OP_BCH0,
+ OP_PSH0 = 0x2f,
+ OP_PSHL = 0x40,
+ OP_PSHV,
+ OP_JMP,
+ OP_CALL,
+ OP_NEXT,
+ OP_PSTR,
+ OP_SINT = 0x47,
+ OP_ST,
+ OP_ISTP,
+ OP_DSTP,
+ OP_SWPN,
+ OP_DUPN,
+ OP_POPV,
+ OP_POPE,
+ OP_POPA,
+ OP_JMPZ,
+ OP_DS,
+ OP_IS,
+ OP_DPRA,
+ OP_DPOA,
+ OP_IPRA,
+ OP_IPOA,
+ OP_EXPT,
+ OP_PSHE,
+ OP_PSHA,
+ OP_DYNA,
+ OP_EXPV = 0x5c,
+ OP_COPY = 0x80,
+ OP_REVA,
+ OP_DSC,
+ OP_ISC,
+ OP_WAIT,
+ OP_VS,
+ OP_CMPA = 0xc0,
+ OP_VSC,
+};
+
+struct altera_procinfo {
+ char *name;
+ u8 attrs;
+ struct altera_procinfo *next;
+};
+
+/* This function checks if enough parameters are available on the stack. */
+static int altera_check_stack(int stack_ptr, int count, int *status)
+{
+ if (stack_ptr < count) {
+ *status = -EOVERFLOW;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void altera_export_int(char *key, s32 value)
+{
+ dprintk("Export: key = \"%s\", value = %d\n", key, value);
+}
+
+#define HEX_LINE_CHARS 72
+#define HEX_LINE_BITS (HEX_LINE_CHARS * 4)
+
+static void altera_export_bool_array(char *key, u8 *data, s32 count)
+{
+ char string[HEX_LINE_CHARS + 1];
+ s32 i, offset;
+ u32 size, line, lines, linebits, value, j, k;
+
+ if (count > HEX_LINE_BITS) {
+ dprintk("Export: key = \"%s\", %d bits, value = HEX\n",
+ key, count);
+ lines = (count + (HEX_LINE_BITS - 1)) / HEX_LINE_BITS;
+
+ for (line = 0; line < lines; ++line) {
+ if (line < (lines - 1)) {
+ linebits = HEX_LINE_BITS;
+ size = HEX_LINE_CHARS;
+ offset = count - ((line + 1) * HEX_LINE_BITS);
+ } else {
+ linebits =
+ count - ((lines - 1) * HEX_LINE_BITS);
+ size = (linebits + 3) / 4;
+ offset = 0L;
+ }
+
+ string[size] = '\0';
+ j = size - 1;
+ value = 0;
+
+ for (k = 0; k < linebits; ++k) {
+ i = k + offset;
+ if (data[i >> 3] & (1 << (i & 7)))
+ value |= (1 << (i & 3));
+ if ((i & 3) == 3) {
+ sprintf(&string[j], "%1x", value);
+ value = 0;
+ --j;
+ }
+ }
+ if ((k & 3) > 0)
+ sprintf(&string[j], "%1x", value);
+
+ dprintk("%s\n", string);
+ }
+
+ } else {
+ size = (count + 3) / 4;
+ string[size] = '\0';
+ j = size - 1;
+ value = 0;
+
+ for (i = 0; i < count; ++i) {
+ if (data[i >> 3] & (1 << (i & 7)))
+ value |= (1 << (i & 3));
+ if ((i & 3) == 3) {
+ sprintf(&string[j], "%1x", value);
+ value = 0;
+ --j;
+ }
+ }
+ if ((i & 3) > 0)
+ sprintf(&string[j], "%1x", value);
+
+ dprintk("Export: key = \"%s\", %d bits, value = HEX %s\n",
+ key, count, string);
+ }
+}
+
+static int altera_execute(struct altera_state *astate,
+ u8 *p,
+ s32 program_size,
+ s32 *error_address,
+ int *exit_code,
+ int *format_version)
+{
+ struct altera_config *aconf = astate->config;
+ char *msg_buff = astate->msg_buff;
+ long *stack = astate->stack;
+ int status = 0;
+ u32 first_word = 0L;
+ u32 action_table = 0L;
+ u32 proc_table = 0L;
+ u32 str_table = 0L;
+ u32 sym_table = 0L;
+ u32 data_sect = 0L;
+ u32 code_sect = 0L;
+ u32 debug_sect = 0L;
+ u32 action_count = 0L;
+ u32 proc_count = 0L;
+ u32 sym_count = 0L;
+ long *vars = NULL;
+ s32 *var_size = NULL;
+ char *attrs = NULL;
+ u8 *proc_attributes = NULL;
+ u32 pc;
+ u32 opcode_address;
+ u32 args[3];
+ u32 opcode;
+ u32 name_id;
+ u8 charbuf[4];
+ long long_tmp;
+ u32 variable_id;
+ u8 *charptr_tmp;
+ u8 *charptr_tmp2;
+ long *longptr_tmp;
+ int version = 0;
+ int delta = 0;
+ int stack_ptr = 0;
+ u32 arg_count;
+ int done = 0;
+ int bad_opcode = 0;
+ u32 count;
+ u32 index;
+ u32 index2;
+ s32 long_count;
+ s32 long_idx;
+ s32 long_idx2;
+ u32 i;
+ u32 j;
+ u32 uncomp_size;
+ u32 offset;
+ u32 value;
+ int current_proc = 0;
+ int reverse;
+
+ char *name;
+
+ dprintk("%s\n", __func__);
+
+ /* Read header information */
+ if (program_size > 52L) {
+ first_word = get_unaligned_be32(&p[0]);
+ version = (first_word & 1L);
+ *format_version = version + 1;
+ delta = version * 8;
+
+ action_table = get_unaligned_be32(&p[4]);
+ proc_table = get_unaligned_be32(&p[8]);
+ str_table = get_unaligned_be32(&p[4 + delta]);
+ sym_table = get_unaligned_be32(&p[16 + delta]);
+ data_sect = get_unaligned_be32(&p[20 + delta]);
+ code_sect = get_unaligned_be32(&p[24 + delta]);
+ debug_sect = get_unaligned_be32(&p[28 + delta]);
+ action_count = get_unaligned_be32(&p[40 + delta]);
+ proc_count = get_unaligned_be32(&p[44 + delta]);
+ sym_count = get_unaligned_be32(&p[48 + (2 * delta)]);
+ }
+
+ if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L)) {
+ done = 1;
+ status = -EIO;
+ goto exit_done;
+ }
+
+ if (sym_count <= 0)
+ goto exit_done;
+
+ vars = kzalloc(sym_count * sizeof(long), GFP_KERNEL);
+
+ if (vars == NULL)
+ status = -ENOMEM;
+
+ if (status == 0) {
+ var_size = kzalloc(sym_count * sizeof(s32), GFP_KERNEL);
+
+ if (var_size == NULL)
+ status = -ENOMEM;
+ }
+
+ if (status == 0) {
+ attrs = kzalloc(sym_count, GFP_KERNEL);
+
+ if (attrs == NULL)
+ status = -ENOMEM;
+ }
+
+ if ((status == 0) && (version > 0)) {
+ proc_attributes = kzalloc(proc_count, GFP_KERNEL);
+
+ if (proc_attributes == NULL)
+ status = -ENOMEM;
+ }
+
+ if (status != 0)
+ goto exit_done;
+
+ delta = version * 2;
+
+ for (i = 0; i < sym_count; ++i) {
+ offset = (sym_table + ((11 + delta) * i));
+
+ value = get_unaligned_be32(&p[offset + 3 + delta]);
+
+ attrs[i] = p[offset];
+
+ /*
+ * use bit 7 of attribute byte to indicate that
+ * this buffer was dynamically allocated
+ * and should be freed later
+ */
+ attrs[i] &= 0x7f;
+
+ var_size[i] = get_unaligned_be32(&p[offset + 7 + delta]);
+
+ /*
+ * Attribute bits:
+ * bit 0: 0 = read-only, 1 = read-write
+ * bit 1: 0 = not compressed, 1 = compressed
+ * bit 2: 0 = not initialized, 1 = initialized
+ * bit 3: 0 = scalar, 1 = array
+ * bit 4: 0 = Boolean, 1 = integer
+ * bit 5: 0 = declared variable,
+ * 1 = compiler created temporary variable
+ */
+
+ if ((attrs[i] & 0x0c) == 0x04)
+ /* initialized scalar variable */
+ vars[i] = value;
+ else if ((attrs[i] & 0x1e) == 0x0e) {
+ /* initialized compressed Boolean array */
+ uncomp_size = get_unaligned_le32(&p[data_sect + value]);
+
+ /* allocate a buffer for the uncompressed data */
+ vars[i] = (long)kzalloc(uncomp_size, GFP_KERNEL);
+ if (vars[i] == 0L)
+ status = -ENOMEM;
+ else {
+ /* set flag so buffer will be freed later */
+ attrs[i] |= 0x80;
+
+ /* uncompress the data */
+ if (altera_shrink(&p[data_sect + value],
+ var_size[i],
+ (u8 *)vars[i],
+ uncomp_size,
+ version) != uncomp_size)
+ /* decompression failed */
+ status = -EIO;
+ else
+ var_size[i] = uncomp_size * 8L;
+
+ }
+ } else if ((attrs[i] & 0x1e) == 0x0c) {
+ /* initialized Boolean array */
+ vars[i] = value + data_sect + (long)p;
+ } else if ((attrs[i] & 0x1c) == 0x1c) {
+ /* initialized integer array */
+ vars[i] = value + data_sect;
+ } else if ((attrs[i] & 0x0c) == 0x08) {
+ /* uninitialized array */
+
+ /* flag attrs so that memory is freed */
+ attrs[i] |= 0x80;
+
+ if (var_size[i] > 0) {
+ u32 size;
+
+ if (attrs[i] & 0x10)
+ /* integer array */
+ size = (var_size[i] * sizeof(s32));
+ else
+ /* Boolean array */
+ size = ((var_size[i] + 7L) / 8L);
+
+ vars[i] = (long)kzalloc(size, GFP_KERNEL);
+
+ if (vars[i] == 0) {
+ status = -ENOMEM;
+ } else {
+ /* zero out memory */
+ for (j = 0; j < size; ++j)
+ ((u8 *)(vars[i]))[j] = 0;
+
+ }
+ } else
+ vars[i] = 0;
+
+ } else
+ vars[i] = 0;
+
+ }
+
+exit_done:
+ if (status != 0)
+ done = 1;
+
+ altera_jinit(astate);
+
+ pc = code_sect;
+ msg_buff[0] = '\0';
+
+ /*
+ * For JBC version 2, we will execute the procedures corresponding to
+ * the selected ACTION
+ */
+ if (version > 0) {
+ if (aconf->action == NULL) {
+ status = -EINVAL;
+ done = 1;
+ } else {
+ int action_found = 0;
+ for (i = 0; (i < action_count) && !action_found; ++i) {
+ name_id = get_unaligned_be32(&p[action_table +
+ (12 * i)]);
+
+ name = &p[str_table + name_id];
+
+ if (strnicmp(aconf->action, name, strlen(name)) == 0) {
+ action_found = 1;
+ current_proc =
+ get_unaligned_be32(&p[action_table +
+ (12 * i) + 8]);
+ }
+ }
+
+ if (!action_found) {
+ status = -EINVAL;
+ done = 1;
+ }
+ }
+
+ if (status == 0) {
+ int first_time = 1;
+ i = current_proc;
+ while ((i != 0) || first_time) {
+ first_time = 0;
+ /* check procedure attribute byte */
+ proc_attributes[i] =
+ (p[proc_table +
+ (13 * i) + 8] &
+ 0x03);
+
+ /*
+ * BIT0 - OPTIONAL
+ * BIT1 - RECOMMENDED
+ * BIT6 - FORCED OFF
+ * BIT7 - FORCED ON
+ */
+
+ i = get_unaligned_be32(&p[proc_table +
+ (13 * i) + 4]);
+ }
+
+ /*
+ * Set current_proc to the first procedure
+ * to be executed
+ */
+ i = current_proc;
+ while ((i != 0) &&
+ ((proc_attributes[i] == 1) ||
+ ((proc_attributes[i] & 0xc0) == 0x40))) {
+ i = get_unaligned_be32(&p[proc_table +
+ (13 * i) + 4]);
+ }
+
+ if ((i != 0) || ((i == 0) && (current_proc == 0) &&
+ ((proc_attributes[0] != 1) &&
+ ((proc_attributes[0] & 0xc0) != 0x40)))) {
+ current_proc = i;
+ pc = code_sect +
+ get_unaligned_be32(&p[proc_table +
+ (13 * i) + 9]);
+ if ((pc < code_sect) || (pc >= debug_sect))
+ status = -ERANGE;
+ } else
+ /* there are no procedures to execute! */
+ done = 1;
+
+ }
+ }
+
+ msg_buff[0] = '\0';
+
+ while (!done) {
+ opcode = (p[pc] & 0xff);
+ opcode_address = pc;
+ ++pc;
+
+ if (debug > 1)
+ printk("opcode: %02x\n", opcode);
+
+ arg_count = (opcode >> 6) & 3;
+ for (i = 0; i < arg_count; ++i) {
+ args[i] = get_unaligned_be32(&p[pc]);
+ pc += 4;
+ }
+
+ switch (opcode) {
+ case OP_NOP:
+ break;
+ case OP_DUP:
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ stack[stack_ptr] = stack[stack_ptr - 1];
+ ++stack_ptr;
+ }
+ break;
+ case OP_SWP:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ long_tmp = stack[stack_ptr - 2];
+ stack[stack_ptr - 2] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+ break;
+ case OP_ADD:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] += stack[stack_ptr];
+ }
+ break;
+ case OP_SUB:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] -= stack[stack_ptr];
+ }
+ break;
+ case OP_MULT:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] *= stack[stack_ptr];
+ }
+ break;
+ case OP_DIV:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] /= stack[stack_ptr];
+ }
+ break;
+ case OP_MOD:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] %= stack[stack_ptr];
+ }
+ break;
+ case OP_SHL:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] <<= stack[stack_ptr];
+ }
+ break;
+ case OP_SHR:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] >>= stack[stack_ptr];
+ }
+ break;
+ case OP_NOT:
+ if (altera_check_stack(stack_ptr, 1, &status))
+ stack[stack_ptr - 1] ^= (-1L);
+
+ break;
+ case OP_AND:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] &= stack[stack_ptr];
+ }
+ break;
+ case OP_OR:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] |= stack[stack_ptr];
+ }
+ break;
+ case OP_XOR:
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ --stack_ptr;
+ stack[stack_ptr - 1] ^= stack[stack_ptr];
+ }
+ break;
+ case OP_INV:
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ stack[stack_ptr - 1] = stack[stack_ptr - 1] ? 0L : 1L;
+ break;
+ case OP_GT:
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ --stack_ptr;
+ stack[stack_ptr - 1] =
+ (stack[stack_ptr - 1] > stack[stack_ptr]) ?
+ 1L : 0L;
+
+ break;
+ case OP_LT:
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ --stack_ptr;
+ stack[stack_ptr - 1] =
+ (stack[stack_ptr - 1] < stack[stack_ptr]) ?
+ 1L : 0L;
+
+ break;
+ case OP_RET:
+ if ((version > 0) && (stack_ptr == 0)) {
+ /*
+ * We completed one of the main procedures
+ * of an ACTION.
+ * Find the next procedure
+ * to be executed and jump to it.
+ * If there are no more procedures, then EXIT.
+ */
+ i = get_unaligned_be32(&p[proc_table +
+ (13 * current_proc) + 4]);
+ while ((i != 0) &&
+ ((proc_attributes[i] == 1) ||
+ ((proc_attributes[i] & 0xc0) == 0x40)))
+ i = get_unaligned_be32(&p[proc_table +
+ (13 * i) + 4]);
+
+ if (i == 0) {
+ /* no procedures to execute! */
+ done = 1;
+ *exit_code = 0; /* success */
+ } else {
+ current_proc = i;
+ pc = code_sect + get_unaligned_be32(
+ &p[proc_table +
+ (13 * i) + 9]);
+ if ((pc < code_sect) ||
+ (pc >= debug_sect))
+ status = -ERANGE;
+ }
+
+ } else
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ pc = stack[--stack_ptr] + code_sect;
+ if ((pc <= code_sect) ||
+ (pc >= debug_sect))
+ status = -ERANGE;
+
+ }
+
+ break;
+ case OP_CMPS:
+ /*
+ * Array short compare
+ * ...stack 0 is source 1 value
+ * ...stack 1 is source 2 value
+ * ...stack 2 is mask value
+ * ...stack 3 is count
+ */
+ if (altera_check_stack(stack_ptr, 4, &status)) {
+ s32 a = stack[--stack_ptr];
+ s32 b = stack[--stack_ptr];
+ long_tmp = stack[--stack_ptr];
+ count = stack[stack_ptr - 1];
+
+ if ((count < 1) || (count > 32))
+ status = -ERANGE;
+ else {
+ long_tmp &= ((-1L) >> (32 - count));
+
+ stack[stack_ptr - 1] =
+ ((a & long_tmp) == (b & long_tmp))
+ ? 1L : 0L;
+ }
+ }
+ break;
+ case OP_PINT:
+ /*
+ * PRINT add integer
+ * ...stack 0 is integer value
+ */
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ sprintf(&msg_buff[strlen(msg_buff)],
+ "%ld", stack[--stack_ptr]);
+ break;
+ case OP_PRNT:
+ /* PRINT finish */
+ if (debug)
+ printk(msg_buff, "\n");
+
+ msg_buff[0] = '\0';
+ break;
+ case OP_DSS:
+ /*
+ * DRSCAN short
+ * ...stack 0 is scan data
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_tmp = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_drscan(astate, count, charbuf, 0);
+ break;
+ case OP_DSSC:
+ /*
+ * DRSCAN short with capture
+ * ...stack 0 is scan data
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_tmp = stack[--stack_ptr];
+ count = stack[stack_ptr - 1];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_swap_dr(astate, count, charbuf,
+ 0, charbuf, 0);
+ stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]);
+ break;
+ case OP_ISS:
+ /*
+ * IRSCAN short
+ * ...stack 0 is scan data
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_tmp = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_irscan(astate, count, charbuf, 0);
+ break;
+ case OP_ISSC:
+ /*
+ * IRSCAN short with capture
+ * ...stack 0 is scan data
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_tmp = stack[--stack_ptr];
+ count = stack[stack_ptr - 1];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_swap_ir(astate, count, charbuf,
+ 0, charbuf, 0);
+ stack[stack_ptr - 1] = get_unaligned_le32(&charbuf[0]);
+ break;
+ case OP_DPR:
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ count = stack[--stack_ptr];
+ status = altera_set_dr_pre(&astate->js, count, 0, NULL);
+ break;
+ case OP_DPRL:
+ /*
+ * DRPRE with literal data
+ * ...stack 0 is count
+ * ...stack 1 is literal data
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ count = stack[--stack_ptr];
+ long_tmp = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_set_dr_pre(&astate->js, count, 0,
+ charbuf);
+ break;
+ case OP_DPO:
+ /*
+ * DRPOST
+ * ...stack 0 is count
+ */
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ count = stack[--stack_ptr];
+ status = altera_set_dr_post(&astate->js, count,
+ 0, NULL);
+ }
+ break;
+ case OP_DPOL:
+ /*
+ * DRPOST with literal data
+ * ...stack 0 is count
+ * ...stack 1 is literal data
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ count = stack[--stack_ptr];
+ long_tmp = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_set_dr_post(&astate->js, count, 0,
+ charbuf);
+ break;
+ case OP_IPR:
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ count = stack[--stack_ptr];
+ status = altera_set_ir_pre(&astate->js, count,
+ 0, NULL);
+ }
+ break;
+ case OP_IPRL:
+ /*
+ * IRPRE with literal data
+ * ...stack 0 is count
+ * ...stack 1 is literal data
+ */
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ count = stack[--stack_ptr];
+ long_tmp = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_set_ir_pre(&astate->js, count,
+ 0, charbuf);
+ }
+ break;
+ case OP_IPO:
+ /*
+ * IRPOST
+ * ...stack 0 is count
+ */
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ count = stack[--stack_ptr];
+ status = altera_set_ir_post(&astate->js, count,
+ 0, NULL);
+ }
+ break;
+ case OP_IPOL:
+ /*
+ * IRPOST with literal data
+ * ...stack 0 is count
+ * ...stack 1 is literal data
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ count = stack[--stack_ptr];
+ long_tmp = stack[--stack_ptr];
+ put_unaligned_le32(long_tmp, &charbuf[0]);
+ status = altera_set_ir_post(&astate->js, count, 0,
+ charbuf);
+ break;
+ case OP_PCHR:
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ u8 ch;
+ count = strlen(msg_buff);
+ ch = (char) stack[--stack_ptr];
+ if ((ch < 1) || (ch > 127)) {
+ /*
+ * character code out of range
+ * instead of flagging an error,
+ * force the value to 127
+ */
+ ch = 127;
+ }
+ msg_buff[count] = ch;
+ msg_buff[count + 1] = '\0';
+ }
+ break;
+ case OP_EXIT:
+ if (altera_check_stack(stack_ptr, 1, &status))
+ *exit_code = stack[--stack_ptr];
+
+ done = 1;
+ break;
+ case OP_EQU:
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ --stack_ptr;
+ stack[stack_ptr - 1] =
+ (stack[stack_ptr - 1] == stack[stack_ptr]) ?
+ 1L : 0L;
+ break;
+ case OP_POPT:
+ if (altera_check_stack(stack_ptr, 1, &status))
+ --stack_ptr;
+
+ break;
+ case OP_ABS:
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ if (stack[stack_ptr - 1] < 0)
+ stack[stack_ptr - 1] = 0 - stack[stack_ptr - 1];
+
+ break;
+ case OP_BCH0:
+ /*
+ * Batch operation 0
+ * SWP
+ * SWPN 7
+ * SWP
+ * SWPN 6
+ * DUPN 8
+ * SWPN 2
+ * SWP
+ * DUPN 6
+ * DUPN 6
+ */
+
+ /* SWP */
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ long_tmp = stack[stack_ptr - 2];
+ stack[stack_ptr - 2] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* SWPN 7 */
+ index = 7 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ long_tmp = stack[stack_ptr - index];
+ stack[stack_ptr - index] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* SWP */
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ long_tmp = stack[stack_ptr - 2];
+ stack[stack_ptr - 2] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* SWPN 6 */
+ index = 6 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ long_tmp = stack[stack_ptr - index];
+ stack[stack_ptr - index] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* DUPN 8 */
+ index = 8 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ stack[stack_ptr] = stack[stack_ptr - index];
+ ++stack_ptr;
+ }
+
+ /* SWPN 2 */
+ index = 2 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ long_tmp = stack[stack_ptr - index];
+ stack[stack_ptr - index] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* SWP */
+ if (altera_check_stack(stack_ptr, 2, &status)) {
+ long_tmp = stack[stack_ptr - 2];
+ stack[stack_ptr - 2] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+
+ /* DUPN 6 */
+ index = 6 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ stack[stack_ptr] = stack[stack_ptr - index];
+ ++stack_ptr;
+ }
+
+ /* DUPN 6 */
+ index = 6 + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ stack[stack_ptr] = stack[stack_ptr - index];
+ ++stack_ptr;
+ }
+ break;
+ case OP_PSH0:
+ stack[stack_ptr++] = 0;
+ break;
+ case OP_PSHL:
+ stack[stack_ptr++] = (s32) args[0];
+ break;
+ case OP_PSHV:
+ stack[stack_ptr++] = vars[args[0]];
+ break;
+ case OP_JMP:
+ pc = args[0] + code_sect;
+ if ((pc < code_sect) || (pc >= debug_sect))
+ status = -ERANGE;
+ break;
+ case OP_CALL:
+ stack[stack_ptr++] = pc;
+ pc = args[0] + code_sect;
+ if ((pc < code_sect) || (pc >= debug_sect))
+ status = -ERANGE;
+ break;
+ case OP_NEXT:
+ /*
+ * Process FOR / NEXT loop
+ * ...argument 0 is variable ID
+ * ...stack 0 is step value
+ * ...stack 1 is end value
+ * ...stack 2 is top address
+ */
+ if (altera_check_stack(stack_ptr, 3, &status)) {
+ s32 step = stack[stack_ptr - 1];
+ s32 end = stack[stack_ptr - 2];
+ s32 top = stack[stack_ptr - 3];
+ s32 iterator = vars[args[0]];
+ int break_out = 0;
+
+ if (step < 0) {
+ if (iterator <= end)
+ break_out = 1;
+ } else if (iterator >= end)
+ break_out = 1;
+
+ if (break_out) {
+ stack_ptr -= 3;
+ } else {
+ vars[args[0]] = iterator + step;
+ pc = top + code_sect;
+ if ((pc < code_sect) ||
+ (pc >= debug_sect))
+ status = -ERANGE;
+ }
+ }
+ break;
+ case OP_PSTR:
+ /*
+ * PRINT add string
+ * ...argument 0 is string ID
+ */
+ count = strlen(msg_buff);
+ strlcpy(&msg_buff[count],
+ &p[str_table + args[0]],
+ ALTERA_MESSAGE_LENGTH - count);
+ break;
+ case OP_SINT:
+ /*
+ * STATE intermediate state
+ * ...argument 0 is state code
+ */
+ status = altera_goto_jstate(astate, args[0]);
+ break;
+ case OP_ST:
+ /*
+ * STATE final state
+ * ...argument 0 is state code
+ */
+ status = altera_goto_jstate(astate, args[0]);
+ break;
+ case OP_ISTP:
+ /*
+ * IRSTOP state
+ * ...argument 0 is state code
+ */
+ status = altera_set_irstop(&astate->js, args[0]);
+ break;
+ case OP_DSTP:
+ /*
+ * DRSTOP state
+ * ...argument 0 is state code
+ */
+ status = altera_set_drstop(&astate->js, args[0]);
+ break;
+
+ case OP_SWPN:
+ /*
+ * Exchange top with Nth stack value
+ * ...argument 0 is 0-based stack entry
+ * to swap with top element
+ */
+ index = (args[0]) + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ long_tmp = stack[stack_ptr - index];
+ stack[stack_ptr - index] = stack[stack_ptr - 1];
+ stack[stack_ptr - 1] = long_tmp;
+ }
+ break;
+ case OP_DUPN:
+ /*
+ * Duplicate Nth stack value
+ * ...argument 0 is 0-based stack entry to duplicate
+ */
+ index = (args[0]) + 1;
+ if (altera_check_stack(stack_ptr, index, &status)) {
+ stack[stack_ptr] = stack[stack_ptr - index];
+ ++stack_ptr;
+ }
+ break;
+ case OP_POPV:
+ /*
+ * Pop stack into scalar variable
+ * ...argument 0 is variable ID
+ * ...stack 0 is value
+ */
+ if (altera_check_stack(stack_ptr, 1, &status))
+ vars[args[0]] = stack[--stack_ptr];
+
+ break;
+ case OP_POPE:
+ /*
+ * Pop stack into integer array element
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is value
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ variable_id = args[0];
+
+ /*
+ * If variable is read-only,
+ * convert to writable array
+ */
+ if ((version > 0) &&
+ ((attrs[variable_id] & 0x9c) == 0x1c)) {
+ /* Allocate a writable buffer for this array */
+ count = var_size[variable_id];
+ long_tmp = vars[variable_id];
+ longptr_tmp = kzalloc(count * sizeof(long),
+ GFP_KERNEL);
+ vars[variable_id] = (long)longptr_tmp;
+
+ if (vars[variable_id] == 0) {
+ status = -ENOMEM;
+ break;
+ }
+
+ /* copy previous contents into buffer */
+ for (i = 0; i < count; ++i) {
+ longptr_tmp[i] =
+ get_unaligned_be32(&p[long_tmp]);
+ long_tmp += sizeof(long);
+ }
+
+ /*
+ * set bit 7 - buffer was
+ * dynamically allocated
+ */
+ attrs[variable_id] |= 0x80;
+
+ /* clear bit 2 - variable is writable */
+ attrs[variable_id] &= ~0x04;
+ attrs[variable_id] |= 0x01;
+
+ }
+
+ /* check that variable is a writable integer array */
+ if ((attrs[variable_id] & 0x1c) != 0x18)
+ status = -ERANGE;
+ else {
+ longptr_tmp = (long *)vars[variable_id];
+
+ /* pop the array index */
+ index = stack[--stack_ptr];
+
+ /* pop the value and store it into the array */
+ longptr_tmp[index] = stack[--stack_ptr];
+ }
+
+ break;
+ case OP_POPA:
+ /*
+ * Pop stack into Boolean array
+ * ...argument 0 is variable ID
+ * ...stack 0 is count
+ * ...stack 1 is array index
+ * ...stack 2 is value
+ */
+ if (!altera_check_stack(stack_ptr, 3, &status))
+ break;
+ variable_id = args[0];
+
+ /*
+ * If variable is read-only,
+ * convert to writable array
+ */
+ if ((version > 0) &&
+ ((attrs[variable_id] & 0x9c) == 0x0c)) {
+ /* Allocate a writable buffer for this array */
+ long_tmp =
+ (var_size[variable_id] + 7L) >> 3L;
+ charptr_tmp2 = (u8 *)vars[variable_id];
+ charptr_tmp =
+ kzalloc(long_tmp, GFP_KERNEL);
+ vars[variable_id] = (long)charptr_tmp;
+
+ if (vars[variable_id] == 0) {
+ status = -ENOMEM;
+ break;
+ }
+
+ /* zero the buffer */
+ for (long_idx = 0L;
+ long_idx < long_tmp;
+ ++long_idx) {
+ charptr_tmp[long_idx] = 0;
+ }
+
+ /* copy previous contents into buffer */
+ for (long_idx = 0L;
+ long_idx < var_size[variable_id];
+ ++long_idx) {
+ long_idx2 = long_idx;
+
+ if (charptr_tmp2[long_idx2 >> 3] &
+ (1 << (long_idx2 & 7))) {
+ charptr_tmp[long_idx >> 3] |=
+ (1 << (long_idx & 7));
+ }
+ }
+
+ /*
+ * set bit 7 - buffer was
+ * dynamically allocated
+ */
+ attrs[variable_id] |= 0x80;
+
+ /* clear bit 2 - variable is writable */
+ attrs[variable_id] &= ~0x04;
+ attrs[variable_id] |= 0x01;
+
+ }
+
+ /*
+ * check that variable is
+ * a writable Boolean array
+ */
+ if ((attrs[variable_id] & 0x1c) != 0x08) {
+ status = -ERANGE;
+ break;
+ }
+
+ charptr_tmp = (u8 *)vars[variable_id];
+
+ /* pop the count (number of bits to copy) */
+ long_count = stack[--stack_ptr];
+
+ /* pop the array index */
+ long_idx = stack[--stack_ptr];
+
+ reverse = 0;
+
+ if (version > 0) {
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+
+ if (long_idx > long_count) {
+ reverse = 1;
+ long_tmp = long_count;
+ long_count = 1 + long_idx -
+ long_count;
+ long_idx = long_tmp;
+
+ /* reverse POPA is not supported */
+ status = -ERANGE;
+ break;
+ } else
+ long_count = 1 + long_count -
+ long_idx;
+
+ }
+
+ /* pop the data */
+ long_tmp = stack[--stack_ptr];
+
+ if (long_count < 1) {
+ status = -ERANGE;
+ break;
+ }
+
+ for (i = 0; i < long_count; ++i) {
+ if (long_tmp & (1L << (s32) i))
+ charptr_tmp[long_idx >> 3L] |=
+ (1L << (long_idx & 7L));
+ else
+ charptr_tmp[long_idx >> 3L] &=
+ ~(1L << (long_idx & 7L));
+
+ ++long_idx;
+ }
+
+ break;
+ case OP_JMPZ:
+ /*
+ * Pop stack and branch if zero
+ * ...argument 0 is address
+ * ...stack 0 is condition value
+ */
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ if (stack[--stack_ptr] == 0) {
+ pc = args[0] + code_sect;
+ if ((pc < code_sect) ||
+ (pc >= debug_sect))
+ status = -ERANGE;
+ }
+ }
+ break;
+ case OP_DS:
+ case OP_IS:
+ /*
+ * DRSCAN
+ * IRSCAN
+ * ...argument 0 is scan data variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_idx = stack[--stack_ptr];
+ long_count = stack[--stack_ptr];
+ reverse = 0;
+ if (version > 0) {
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ * stack 2 = count
+ */
+ long_tmp = long_count;
+ long_count = stack[--stack_ptr];
+
+ if (long_idx > long_tmp) {
+ reverse = 1;
+ long_idx = long_tmp;
+ }
+ }
+
+ charptr_tmp = (u8 *)vars[args[0]];
+
+ if (reverse) {
+ /*
+ * allocate a buffer
+ * and reverse the data order
+ */
+ charptr_tmp2 = charptr_tmp;
+ charptr_tmp = kzalloc((long_count >> 3) + 1,
+ GFP_KERNEL);
+ if (charptr_tmp == NULL) {
+ status = -ENOMEM;
+ break;
+ }
+
+ long_tmp = long_idx + long_count - 1;
+ long_idx2 = 0;
+ while (long_idx2 < long_count) {
+ if (charptr_tmp2[long_tmp >> 3] &
+ (1 << (long_tmp & 7)))
+ charptr_tmp[long_idx2 >> 3] |=
+ (1 << (long_idx2 & 7));
+ else
+ charptr_tmp[long_idx2 >> 3] &=
+ ~(1 << (long_idx2 & 7));
+
+ --long_tmp;
+ ++long_idx2;
+ }
+ }
+
+ if (opcode == 0x51) /* DS */
+ status = altera_drscan(astate, long_count,
+ charptr_tmp, long_idx);
+ else /* IS */
+ status = altera_irscan(astate, long_count,
+ charptr_tmp, long_idx);
+
+ if (reverse)
+ kfree(charptr_tmp);
+
+ break;
+ case OP_DPRA:
+ /*
+ * DRPRE with array data
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ index = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+
+ if (version > 0)
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+ count = 1 + count - index;
+
+ charptr_tmp = (u8 *)vars[args[0]];
+ status = altera_set_dr_pre(&astate->js, count, index,
+ charptr_tmp);
+ break;
+ case OP_DPOA:
+ /*
+ * DRPOST with array data
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ index = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+
+ if (version > 0)
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+ count = 1 + count - index;
+
+ charptr_tmp = (u8 *)vars[args[0]];
+ status = altera_set_dr_post(&astate->js, count, index,
+ charptr_tmp);
+ break;
+ case OP_IPRA:
+ /*
+ * IRPRE with array data
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ index = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+
+ if (version > 0)
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+ count = 1 + count - index;
+
+ charptr_tmp = (u8 *)vars[args[0]];
+ status = altera_set_ir_pre(&astate->js, count, index,
+ charptr_tmp);
+
+ break;
+ case OP_IPOA:
+ /*
+ * IRPOST with array data
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ * ...stack 1 is count
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ index = stack[--stack_ptr];
+ count = stack[--stack_ptr];
+
+ if (version > 0)
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+ count = 1 + count - index;
+
+ charptr_tmp = (u8 *)vars[args[0]];
+ status = altera_set_ir_post(&astate->js, count, index,
+ charptr_tmp);
+
+ break;
+ case OP_EXPT:
+ /*
+ * EXPORT
+ * ...argument 0 is string ID
+ * ...stack 0 is integer expression
+ */
+ if (altera_check_stack(stack_ptr, 1, &status)) {
+ name = &p[str_table + args[0]];
+ long_tmp = stack[--stack_ptr];
+ altera_export_int(name, long_tmp);
+ }
+ break;
+ case OP_PSHE:
+ /*
+ * Push integer array element
+ * ...argument 0 is variable ID
+ * ...stack 0 is array index
+ */
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ variable_id = args[0];
+ index = stack[stack_ptr - 1];
+
+ /* check variable type */
+ if ((attrs[variable_id] & 0x1f) == 0x19) {
+ /* writable integer array */
+ longptr_tmp = (long *)vars[variable_id];
+ stack[stack_ptr - 1] = longptr_tmp[index];
+ } else if ((attrs[variable_id] & 0x1f) == 0x1c) {
+ /* read-only integer array */
+ long_tmp = vars[variable_id] +
+ (index * sizeof(long));
+ stack[stack_ptr - 1] =
+ get_unaligned_be32(&p[long_tmp]);
+ } else
+ status = -ERANGE;
+
+ break;
+ case OP_PSHA:
+ /*
+ * Push Boolean array
+ * ...argument 0 is variable ID
+ * ...stack 0 is count
+ * ...stack 1 is array index
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ variable_id = args[0];
+
+ /* check that variable is a Boolean array */
+ if ((attrs[variable_id] & 0x18) != 0x08) {
+ status = -ERANGE;
+ break;
+ }
+
+ charptr_tmp = (u8 *)vars[variable_id];
+
+ /* pop the count (number of bits to copy) */
+ count = stack[--stack_ptr];
+
+ /* pop the array index */
+ index = stack[stack_ptr - 1];
+
+ if (version > 0)
+ /*
+ * stack 0 = array right index
+ * stack 1 = array left index
+ */
+ count = 1 + count - index;
+
+ if ((count < 1) || (count > 32)) {
+ status = -ERANGE;
+ break;
+ }
+
+ long_tmp = 0L;
+
+ for (i = 0; i < count; ++i)
+ if (charptr_tmp[(i + index) >> 3] &
+ (1 << ((i + index) & 7)))
+ long_tmp |= (1L << i);
+
+ stack[stack_ptr - 1] = long_tmp;
+
+ break;
+ case OP_DYNA:
+ /*
+ * Dynamically change size of array
+ * ...argument 0 is variable ID
+ * ...stack 0 is new size
+ */
+ if (!altera_check_stack(stack_ptr, 1, &status))
+ break;
+ variable_id = args[0];
+ long_tmp = stack[--stack_ptr];
+
+ if (long_tmp > var_size[variable_id]) {
+ var_size[variable_id] = long_tmp;
+
+ if (attrs[variable_id] & 0x10)
+ /* allocate integer array */
+ long_tmp *= sizeof(long);
+ else
+ /* allocate Boolean array */
+ long_tmp = (long_tmp + 7) >> 3;
+
+ /*
+ * If the buffer was previously allocated,
+ * free it
+ */
+ if (attrs[variable_id] & 0x80) {
+ kfree((void *)vars[variable_id]);
+ vars[variable_id] = 0;
+ }
+
+ /*
+ * Allocate a new buffer
+ * of the requested size
+ */
+ vars[variable_id] = (long)
+ kzalloc(long_tmp, GFP_KERNEL);
+
+ if (vars[variable_id] == 0) {
+ status = -ENOMEM;
+ break;
+ }
+
+ /*
+ * Set the attribute bit to indicate that
+ * this buffer was dynamically allocated and
+ * should be freed later
+ */
+ attrs[variable_id] |= 0x80;
+
+ /* zero out memory */
+ count = ((var_size[variable_id] + 7L) /
+ 8L);
+ charptr_tmp = (u8 *)(vars[variable_id]);
+ for (index = 0; index < count; ++index)
+ charptr_tmp[index] = 0;
+
+ }
+
+ break;
+ case OP_EXPV:
+ /*
+ * Export Boolean array
+ * ...argument 0 is string ID
+ * ...stack 0 is variable ID
+ * ...stack 1 is array right index
+ * ...stack 2 is array left index
+ */
+ if (!altera_check_stack(stack_ptr, 3, &status))
+ break;
+ if (version == 0) {
+ /* EXPV is not supported in JBC 1.0 */
+ bad_opcode = 1;
+ break;
+ }
+ name = &p[str_table + args[0]];
+ variable_id = stack[--stack_ptr];
+ long_idx = stack[--stack_ptr];/* right indx */
+ long_idx2 = stack[--stack_ptr];/* left indx */
+
+ if (long_idx > long_idx2) {
+ /* reverse indices not supported */
+ status = -ERANGE;
+ break;
+ }
+
+ long_count = 1 + long_idx2 - long_idx;
+
+ charptr_tmp = (u8 *)vars[variable_id];
+ charptr_tmp2 = NULL;
+
+ if ((long_idx & 7L) != 0) {
+ s32 k = long_idx;
+ charptr_tmp2 =
+ kzalloc(((long_count + 7L) / 8L),
+ GFP_KERNEL);
+ if (charptr_tmp2 == NULL) {
+ status = -ENOMEM;
+ break;
+ }
+
+ for (i = 0; i < long_count; ++i) {
+ if (charptr_tmp[k >> 3] &
+ (1 << (k & 7)))
+ charptr_tmp2[i >> 3] |=
+ (1 << (i & 7));
+ else
+ charptr_tmp2[i >> 3] &=
+ ~(1 << (i & 7));
+
+ ++k;
+ }
+ charptr_tmp = charptr_tmp2;
+
+ } else if (long_idx != 0)
+ charptr_tmp = &charptr_tmp[long_idx >> 3];
+
+ altera_export_bool_array(name, charptr_tmp,
+ long_count);
+
+ /* free allocated buffer */
+ if ((long_idx & 7L) != 0)
+ kfree(charptr_tmp2);
+
+ break;
+ case OP_COPY: {
+ /*
+ * Array copy
+ * ...argument 0 is dest ID
+ * ...argument 1 is source ID
+ * ...stack 0 is count
+ * ...stack 1 is dest index
+ * ...stack 2 is source index
+ */
+ s32 copy_count;
+ s32 copy_index;
+ s32 copy_index2;
+ s32 destleft;
+ s32 src_count;
+ s32 dest_count;
+ int src_reverse = 0;
+ int dest_reverse = 0;
+
+ if (!altera_check_stack(stack_ptr, 3, &status))
+ break;
+
+ copy_count = stack[--stack_ptr];
+ copy_index = stack[--stack_ptr];
+ copy_index2 = stack[--stack_ptr];
+ reverse = 0;
+
+ if (version > 0) {
+ /*
+ * stack 0 = source right index
+ * stack 1 = source left index
+ * stack 2 = destination right index
+ * stack 3 = destination left index
+ */
+ destleft = stack[--stack_ptr];
+
+ if (copy_count > copy_index) {
+ src_reverse = 1;
+ reverse = 1;
+ src_count = 1 + copy_count - copy_index;
+ /* copy_index = source start index */
+ } else {
+ src_count = 1 + copy_index - copy_count;
+ /* source start index */
+ copy_index = copy_count;
+ }
+
+ if (copy_index2 > destleft) {
+ dest_reverse = 1;
+ reverse = !reverse;
+ dest_count = 1 + copy_index2 - destleft;
+ /* destination start index */
+ copy_index2 = destleft;
+ } else
+ dest_count = 1 + destleft - copy_index2;
+
+ copy_count = (src_count < dest_count) ?
+ src_count : dest_count;
+
+ if ((src_reverse || dest_reverse) &&
+ (src_count != dest_count))
+ /*
+ * If either the source or destination
+ * is reversed, we can't tolerate
+ * a length mismatch, because we
+ * "left justify" arrays when copying.
+ * This won't work correctly
+ * with reversed arrays.
+ */
+ status = -ERANGE;
+
+ }
+
+ count = copy_count;
+ index = copy_index;
+ index2 = copy_index2;
+
+ /*
+ * If destination is a read-only array,
+ * allocate a buffer and convert it to a writable array
+ */
+ variable_id = args[1];
+ if ((version > 0) &&
+ ((attrs[variable_id] & 0x9c) == 0x0c)) {
+ /* Allocate a writable buffer for this array */
+ long_tmp =
+ (var_size[variable_id] + 7L) >> 3L;
+ charptr_tmp2 = (u8 *)vars[variable_id];
+ charptr_tmp =
+ kzalloc(long_tmp, GFP_KERNEL);
+ vars[variable_id] = (long)charptr_tmp;
+
+ if (vars[variable_id] == 0) {
+ status = -ENOMEM;
+ break;
+ }
+
+ /* zero the buffer */
+ for (long_idx = 0L; long_idx < long_tmp;
+ ++long_idx)
+ charptr_tmp[long_idx] = 0;
+
+ /* copy previous contents into buffer */
+ for (long_idx = 0L;
+ long_idx < var_size[variable_id];
+ ++long_idx) {
+ long_idx2 = long_idx;
+
+ if (charptr_tmp2[long_idx2 >> 3] &
+ (1 << (long_idx2 & 7)))
+ charptr_tmp[long_idx >> 3] |=
+ (1 << (long_idx & 7));
+
+ }
+
+ /*
+ set bit 7 - buffer was dynamically allocated */
+ attrs[variable_id] |= 0x80;
+
+ /* clear bit 2 - variable is writable */
+ attrs[variable_id] &= ~0x04;
+ attrs[variable_id] |= 0x01;
+ }
+
+ charptr_tmp = (u8 *)vars[args[1]];
+ charptr_tmp2 = (u8 *)vars[args[0]];
+
+ /* check if destination is a writable Boolean array */
+ if ((attrs[args[1]] & 0x1c) != 0x08) {
+ status = -ERANGE;
+ break;
+ }
+
+ if (count < 1) {
+ status = -ERANGE;
+ break;
+ }
+
+ if (reverse)
+ index2 += (count - 1);
+
+ for (i = 0; i < count; ++i) {
+ if (charptr_tmp2[index >> 3] &
+ (1 << (index & 7)))
+ charptr_tmp[index2 >> 3] |=
+ (1 << (index2 & 7));
+ else
+ charptr_tmp[index2 >> 3] &=
+ ~(1 << (index2 & 7));
+
+ ++index;
+ if (reverse)
+ --index2;
+ else
+ ++index2;
+ }
+
+ break;
+ }
+ case OP_DSC:
+ case OP_ISC: {
+ /*
+ * DRSCAN with capture
+ * IRSCAN with capture
+ * ...argument 0 is scan data variable ID
+ * ...argument 1 is capture variable ID
+ * ...stack 0 is capture index
+ * ...stack 1 is scan data index
+ * ...stack 2 is count
+ */
+ s32 scan_right, scan_left;
+ s32 capture_count = 0;
+ s32 scan_count = 0;
+ s32 capture_index;
+ s32 scan_index;
+
+ if (!altera_check_stack(stack_ptr, 3, &status))
+ break;
+
+ capture_index = stack[--stack_ptr];
+ scan_index = stack[--stack_ptr];
+
+ if (version > 0) {
+ /*
+ * stack 0 = capture right index
+ * stack 1 = capture left index
+ * stack 2 = scan right index
+ * stack 3 = scan left index
+ * stack 4 = count
+ */
+ scan_right = stack[--stack_ptr];
+ scan_left = stack[--stack_ptr];
+ capture_count = 1 + scan_index - capture_index;
+ scan_count = 1 + scan_left - scan_right;
+ scan_index = scan_right;
+ }
+
+ long_count = stack[--stack_ptr];
+ /*
+ * If capture array is read-only, allocate a buffer
+ * and convert it to a writable array
+ */
+ variable_id = args[1];
+ if ((version > 0) &&
+ ((attrs[variable_id] & 0x9c) == 0x0c)) {
+ /* Allocate a writable buffer for this array */
+ long_tmp =
+ (var_size[variable_id] + 7L) >> 3L;
+ charptr_tmp2 = (u8 *)vars[variable_id];
+ charptr_tmp =
+ kzalloc(long_tmp, GFP_KERNEL);
+ vars[variable_id] = (long)charptr_tmp;
+
+ if (vars[variable_id] == 0) {
+ status = -ENOMEM;
+ break;
+ }
+
+ /* zero the buffer */
+ for (long_idx = 0L; long_idx < long_tmp;
+ ++long_idx)
+ charptr_tmp[long_idx] = 0;
+
+ /* copy previous contents into buffer */
+ for (long_idx = 0L;
+ long_idx < var_size[variable_id];
+ ++long_idx) {
+ long_idx2 = long_idx;
+
+ if (charptr_tmp2[long_idx2 >> 3] &
+ (1 << (long_idx2 & 7)))
+ charptr_tmp[long_idx >> 3] |=
+ (1 << (long_idx & 7));
+
+ }
+
+ /*
+ * set bit 7 - buffer was
+ * dynamically allocated
+ */
+ attrs[variable_id] |= 0x80;
+
+ /* clear bit 2 - variable is writable */
+ attrs[variable_id] &= ~0x04;
+ attrs[variable_id] |= 0x01;
+
+ }
+
+ charptr_tmp = (u8 *)vars[args[0]];
+ charptr_tmp2 = (u8 *)vars[args[1]];
+
+ if ((version > 0) &&
+ ((long_count > capture_count) ||
+ (long_count > scan_count))) {
+ status = -ERANGE;
+ break;
+ }
+
+ /*
+ * check that capture array
+ * is a writable Boolean array
+ */
+ if ((attrs[args[1]] & 0x1c) != 0x08) {
+ status = -ERANGE;
+ break;
+ }
+
+ if (status == 0) {
+ if (opcode == 0x82) /* DSC */
+ status = altera_swap_dr(astate,
+ long_count,
+ charptr_tmp,
+ scan_index,
+ charptr_tmp2,
+ capture_index);
+ else /* ISC */
+ status = altera_swap_ir(astate,
+ long_count,
+ charptr_tmp,
+ scan_index,
+ charptr_tmp2,
+ capture_index);
+
+ }
+
+ break;
+ }
+ case OP_WAIT:
+ /*
+ * WAIT
+ * ...argument 0 is wait state
+ * ...argument 1 is end state
+ * ...stack 0 is cycles
+ * ...stack 1 is microseconds
+ */
+ if (!altera_check_stack(stack_ptr, 2, &status))
+ break;
+ long_tmp = stack[--stack_ptr];
+
+ if (long_tmp != 0L)
+ status = altera_wait_cycles(astate, long_tmp,
+ args[0]);
+
+ long_tmp = stack[--stack_ptr];
+
+ if ((status == 0) && (long_tmp != 0L))
+ status = altera_wait_msecs(astate,
+ long_tmp,
+ args[0]);
+
+ if ((status == 0) && (args[1] != args[0]))
+ status = altera_goto_jstate(astate,
+ args[1]);
+
+ if (version > 0) {
+ --stack_ptr; /* throw away MAX cycles */
+ --stack_ptr; /* throw away MAX microseconds */
+ }
+ break;
+ case OP_CMPA: {
+ /*
+ * Array compare
+ * ...argument 0 is source 1 ID
+ * ...argument 1 is source 2 ID
+ * ...argument 2 is mask ID
+ * ...stack 0 is source 1 index
+ * ...stack 1 is source 2 index
+ * ...stack 2 is mask index
+ * ...stack 3 is count
+ */
+ s32 a, b;
+ u8 *source1 = (u8 *)vars[args[0]];
+ u8 *source2 = (u8 *)vars[args[1]];
+ u8 *mask = (u8 *)vars[args[2]];
+ u32 index1;
+ u32 index2;
+ u32 mask_index;
+
+ if (!altera_check_stack(stack_ptr, 4, &status))
+ break;
+
+ index1 = stack[--stack_ptr];
+ index2 = stack[--stack_ptr];
+ mask_index = stack[--stack_ptr];
+ long_count = stack[--stack_ptr];
+
+ if (version > 0) {
+ /*
+ * stack 0 = source 1 right index
+ * stack 1 = source 1 left index
+ * stack 2 = source 2 right index
+ * stack 3 = source 2 left index
+ * stack 4 = mask right index
+ * stack 5 = mask left index
+ */
+ s32 mask_right = stack[--stack_ptr];
+ s32 mask_left = stack[--stack_ptr];
+ /* source 1 count */
+ a = 1 + index2 - index1;
+ /* source 2 count */
+ b = 1 + long_count - mask_index;
+ a = (a < b) ? a : b;
+ /* mask count */
+ b = 1 + mask_left - mask_right;
+ a = (a < b) ? a : b;
+ /* source 2 start index */
+ index2 = mask_index;
+ /* mask start index */
+ mask_index = mask_right;
+ long_count = a;
+ }
+
+ long_tmp = 1L;
+
+ if (long_count < 1)
+ status = -ERANGE;
+ else {
+ count = long_count;
+
+ for (i = 0; i < count; ++i) {
+ if (mask[mask_index >> 3] &
+ (1 << (mask_index & 7))) {
+ a = source1[index1 >> 3] &
+ (1 << (index1 & 7))
+ ? 1 : 0;
+ b = source2[index2 >> 3] &
+ (1 << (index2 & 7))
+ ? 1 : 0;
+
+ if (a != b) /* failure */
+ long_tmp = 0L;
+ }
+ ++index1;
+ ++index2;
+ ++mask_index;
+ }
+ }
+
+ stack[stack_ptr++] = long_tmp;
+
+ break;
+ }
+ default:
+ /* Unrecognized opcode -- ERROR! */
+ bad_opcode = 1;
+ break;
+ }
+
+ if (bad_opcode)
+ status = -ENOSYS;
+
+ if ((stack_ptr < 0) || (stack_ptr >= ALTERA_STACK_SIZE))
+ status = -EOVERFLOW;
+
+ if (status != 0) {
+ done = 1;
+ *error_address = (s32)(opcode_address - code_sect);
+ }
+ }
+
+ altera_free_buffers(astate);
+
+ /* Free all dynamically allocated arrays */
+ if ((attrs != NULL) && (vars != NULL))
+ for (i = 0; i < sym_count; ++i)
+ if (attrs[i] & 0x80)
+ kfree((void *)vars[i]);
+
+ kfree(vars);
+ kfree(var_size);
+ kfree(attrs);
+ kfree(proc_attributes);
+
+ return status;
+}
+
+static int altera_get_note(u8 *p, s32 program_size,
+ s32 *offset, char *key, char *value, int length)
+/*
+ * Gets key and value of NOTE fields in the JBC file.
+ * Can be called in two modes: if offset pointer is NULL,
+ * then the function searches for note fields which match
+ * the key string provided. If offset is not NULL, then
+ * the function finds the next note field of any key,
+ * starting at the offset specified by the offset pointer.
+ * Returns 0 for success, else appropriate error code
+ */
+{
+ int status = -ENODATA;
+ u32 note_strings = 0L;
+ u32 note_table = 0L;
+ u32 note_count = 0L;
+ u32 first_word = 0L;
+ int version = 0;
+ int delta = 0;
+ char *key_ptr;
+ char *value_ptr;
+ int i;
+
+ /* Read header information */
+ if (program_size > 52L) {
+ first_word = get_unaligned_be32(&p[0]);
+ version = (first_word & 1L);
+ delta = version * 8;
+
+ note_strings = get_unaligned_be32(&p[8 + delta]);
+ note_table = get_unaligned_be32(&p[12 + delta]);
+ note_count = get_unaligned_be32(&p[44 + (2 * delta)]);
+ }
+
+ if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L))
+ return -EIO;
+
+ if (note_count <= 0L)
+ return status;
+
+ if (offset == NULL) {
+ /*
+ * We will search for the first note with a specific key,
+ * and return only the value
+ */
+ for (i = 0; (i < note_count) &&
+ (status != 0); ++i) {
+ key_ptr = &p[note_strings +
+ get_unaligned_be32(
+ &p[note_table + (8 * i)])];
+ if ((strnicmp(key, key_ptr, strlen(key_ptr)) == 0) &&
+ (key != NULL)) {
+ status = 0;
+
+ value_ptr = &p[note_strings +
+ get_unaligned_be32(
+ &p[note_table + (8 * i) + 4])];
+
+ if (value != NULL)
+ strlcpy(value, value_ptr, length);
+
+ }
+ }
+ } else {
+ /*
+ * We will search for the next note, regardless of the key,
+ * and return both the value and the key
+ */
+
+ i = *offset;
+
+ if ((i >= 0) && (i < note_count)) {
+ status = 0;
+
+ if (key != NULL)
+ strlcpy(key, &p[note_strings +
+ get_unaligned_be32(
+ &p[note_table + (8 * i)])],
+ length);
+
+ if (value != NULL)
+ strlcpy(value, &p[note_strings +
+ get_unaligned_be32(
+ &p[note_table + (8 * i) + 4])],
+ length);
+
+ *offset = i + 1;
+ }
+ }
+
+ return status;
+}
+
+static int altera_check_crc(u8 *p, s32 program_size)
+{
+ int status = 0;
+ u16 local_expected = 0,
+ local_actual = 0,
+ shift_reg = 0xffff;
+ int bit, feedback;
+ u8 databyte;
+ u32 i;
+ u32 crc_section = 0L;
+ u32 first_word = 0L;
+ int version = 0;
+ int delta = 0;
+
+ if (program_size > 52L) {
+ first_word = get_unaligned_be32(&p[0]);
+ version = (first_word & 1L);
+ delta = version * 8;
+
+ crc_section = get_unaligned_be32(&p[32 + delta]);
+ }
+
+ if ((first_word != 0x4A414D00L) && (first_word != 0x4A414D01L))
+ status = -EIO;
+
+ if (crc_section >= program_size)
+ status = -EIO;
+
+ if (status == 0) {
+ local_expected = (u16)get_unaligned_be16(&p[crc_section]);
+
+ for (i = 0; i < crc_section; ++i) {
+ databyte = p[i];
+ for (bit = 0; bit < 8; bit++) {
+ feedback = (databyte ^ shift_reg) & 0x01;
+ shift_reg >>= 1;
+ if (feedback)
+ shift_reg ^= 0x8408;
+
+ databyte >>= 1;
+ }
+ }
+
+ local_actual = (u16)~shift_reg;
+
+ if (local_expected != local_actual)
+ status = -EILSEQ;
+
+ }
+
+ if (debug || status) {
+ switch (status) {
+ case 0:
+ printk(KERN_INFO "%s: CRC matched: %04x\n", __func__,
+ local_actual);
+ break;
+ case -EILSEQ:
+ printk(KERN_ERR "%s: CRC mismatch: expected %04x, "
+ "actual %04x\n", __func__, local_expected,
+ local_actual);
+ break;
+ case -ENODATA:
+ printk(KERN_ERR "%s: expected CRC not found, "
+ "actual CRC = %04x\n", __func__,
+ local_actual);
+ break;
+ case -EIO:
+ printk(KERN_ERR "%s: error: format isn't "
+ "recognized.\n", __func__);
+ break;
+ default:
+ printk(KERN_ERR "%s: CRC function returned error "
+ "code %d\n", __func__, status);
+ break;
+ }
+ }
+
+ return status;
+}
+
+static int altera_get_file_info(u8 *p,
+ s32 program_size,
+ int *format_version,
+ int *action_count,
+ int *procedure_count)
+{
+ int status = -EIO;
+ u32 first_word = 0;
+ int version = 0;
+
+ if (program_size <= 52L)
+ return status;
+
+ first_word = get_unaligned_be32(&p[0]);
+
+ if ((first_word == 0x4A414D00L) || (first_word == 0x4A414D01L)) {
+ status = 0;
+
+ version = (first_word & 1L);
+ *format_version = version + 1;
+
+ if (version > 0) {
+ *action_count = get_unaligned_be32(&p[48]);
+ *procedure_count = get_unaligned_be32(&p[52]);
+ }
+ }
+
+ return status;
+}
+
+static int altera_get_act_info(u8 *p,
+ s32 program_size,
+ int index,
+ char **name,
+ char **description,
+ struct altera_procinfo **proc_list)
+{
+ int status = -EIO;
+ struct altera_procinfo *procptr = NULL;
+ struct altera_procinfo *tmpptr = NULL;
+ u32 first_word = 0L;
+ u32 action_table = 0L;
+ u32 proc_table = 0L;
+ u32 str_table = 0L;
+ u32 note_strings = 0L;
+ u32 action_count = 0L;
+ u32 proc_count = 0L;
+ u32 act_name_id = 0L;
+ u32 act_desc_id = 0L;
+ u32 act_proc_id = 0L;
+ u32 act_proc_name = 0L;
+ u8 act_proc_attribute = 0;
+
+ if (program_size <= 52L)
+ return status;
+ /* Read header information */
+ first_word = get_unaligned_be32(&p[0]);
+
+ if (first_word != 0x4A414D01L)
+ return status;
+
+ action_table = get_unaligned_be32(&p[4]);
+ proc_table = get_unaligned_be32(&p[8]);
+ str_table = get_unaligned_be32(&p[12]);
+ note_strings = get_unaligned_be32(&p[16]);
+ action_count = get_unaligned_be32(&p[48]);
+ proc_count = get_unaligned_be32(&p[52]);
+
+ if (index >= action_count)
+ return status;
+
+ act_name_id = get_unaligned_be32(&p[action_table + (12 * index)]);
+ act_desc_id = get_unaligned_be32(&p[action_table + (12 * index) + 4]);
+ act_proc_id = get_unaligned_be32(&p[action_table + (12 * index) + 8]);
+
+ *name = &p[str_table + act_name_id];
+
+ if (act_desc_id < (note_strings - str_table))
+ *description = &p[str_table + act_desc_id];
+
+ do {
+ act_proc_name = get_unaligned_be32(
+ &p[proc_table + (13 * act_proc_id)]);
+ act_proc_attribute =
+ (p[proc_table + (13 * act_proc_id) + 8] & 0x03);
+
+ procptr = (struct altera_procinfo *)
+ kzalloc(sizeof(struct altera_procinfo),
+ GFP_KERNEL);
+
+ if (procptr == NULL)
+ status = -ENOMEM;
+ else {
+ procptr->name = &p[str_table + act_proc_name];
+ procptr->attrs = act_proc_attribute;
+ procptr->next = NULL;
+
+ /* add record to end of linked list */
+ if (*proc_list == NULL)
+ *proc_list = procptr;
+ else {
+ tmpptr = *proc_list;
+ while (tmpptr->next != NULL)
+ tmpptr = tmpptr->next;
+ tmpptr->next = procptr;
+ }
+ }
+
+ act_proc_id = get_unaligned_be32(
+ &p[proc_table + (13 * act_proc_id) + 4]);
+ } while ((act_proc_id != 0) && (act_proc_id < proc_count));
+
+ return status;
+}
+
+int altera_init(struct altera_config *config, const struct firmware *fw)
+{
+ struct altera_state *astate = NULL;
+ struct altera_procinfo *proc_list = NULL;
+ struct altera_procinfo *procptr = NULL;
+ char *key = NULL;
+ char *value = NULL;
+ char *action_name = NULL;
+ char *description = NULL;
+ int exec_result = 0;
+ int exit_code = 0;
+ int format_version = 0;
+ int action_count = 0;
+ int procedure_count = 0;
+ int index = 0;
+ s32 offset = 0L;
+ s32 error_address = 0L;
+
+ key = kzalloc(33 * sizeof(char), GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+ value = kzalloc(257 * sizeof(char), GFP_KERNEL);
+ if (!value)
+ return -ENOMEM;
+ astate = kzalloc(sizeof(struct altera_state), GFP_KERNEL);
+ if (!astate)
+ return -ENOMEM;
+
+ astate->config = config;
+ if (!astate->config->jtag_io) {
+ dprintk(KERN_INFO "%s: using byteblaster!\n", __func__);
+ astate->config->jtag_io = netup_jtag_io_lpt;
+ }
+
+ altera_check_crc((u8 *)fw->data, fw->size);
+
+ if (debug) {
+ altera_get_file_info((u8 *)fw->data, fw->size, &format_version,
+ &action_count, &procedure_count);
+ printk(KERN_INFO "%s: File format is %s ByteCode format\n",
+ __func__, (format_version == 2) ? "Jam STAPL" :
+ "pre-standardized Jam 1.1");
+ while (altera_get_note((u8 *)fw->data, fw->size,
+ &offset, key, value, 256) == 0)
+ printk(KERN_INFO "%s: NOTE \"%s\" = \"%s\"\n",
+ __func__, key, value);
+ }
+
+ if (debug && (format_version == 2) && (action_count > 0)) {
+ printk(KERN_INFO "%s: Actions available:\n", __func__);
+ for (index = 0; index < action_count; ++index) {
+ altera_get_act_info((u8 *)fw->data, fw->size,
+ index, &action_name,
+ &description,
+ &proc_list);
+
+ if (description == NULL)
+ printk(KERN_INFO "%s: %s\n",
+ __func__,
+ action_name);
+ else
+ printk(KERN_INFO "%s: %s \"%s\"\n",
+ __func__,
+ action_name,
+ description);
+
+ procptr = proc_list;
+ while (procptr != NULL) {
+ if (procptr->attrs != 0)
+ printk(KERN_INFO "%s: %s (%s)\n",
+ __func__,
+ procptr->name,
+ (procptr->attrs == 1) ?
+ "optional" : "recommended");
+
+ proc_list = procptr->next;
+ kfree(procptr);
+ procptr = proc_list;
+ }
+ }
+
+ printk(KERN_INFO "\n");
+ }
+
+ exec_result = altera_execute(astate, (u8 *)fw->data, fw->size,
+ &error_address, &exit_code, &format_version);
+
+ if (exit_code)
+ exec_result = -EREMOTEIO;
+
+ if ((format_version == 2) && (exec_result == -EINVAL)) {
+ if (astate->config->action == NULL)
+ printk(KERN_ERR "%s: error: no action specified for "
+ "Jam STAPL file.\nprogram terminated.\n",
+ __func__);
+ else
+ printk(KERN_ERR "%s: error: action \"%s\""
+ " is not supported "
+ "for this Jam STAPL file.\n"
+ "Program terminated.\n", __func__,
+ astate->config->action);
+
+ } else if (exec_result)
+ printk(KERN_ERR "%s: error %d\n", __func__, exec_result);
+
+ kfree(key);
+ kfree(value);
+ kfree(astate);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_init);
diff --git a/drivers/staging/cx25821/Kconfig b/drivers/staging/cx25821/Kconfig
index b2656957aa8f..5f6b54213713 100644
--- a/drivers/staging/cx25821/Kconfig
+++ b/drivers/staging/cx25821/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_CX25821
tristate "Conexant cx25821 support"
depends on DVB_CORE && VIDEO_DEV && PCI && I2C
- depends on BKL # please fix
select I2C_ALGOBIT
select VIDEO_BTCX
select VIDEO_TVEEPROM
diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c
index 160f6693aa33..ebdba7c65bc5 100644
--- a/drivers/staging/cx25821/cx25821-alsa.c
+++ b/drivers/staging/cx25821/cx25821-alsa.c
@@ -770,10 +770,12 @@ static int cx25821_alsa_init(void)
struct cx25821_dev *dev = NULL;
struct list_head *list;
+ mutex_lock(&cx25821_devlist_mutex);
list_for_each(list, &cx25821_devlist) {
dev = list_entry(list, struct cx25821_dev, devlist);
cx25821_audio_initdev(dev);
}
+ mutex_unlock(&cx25821_devlist_mutex);
if (dev == NULL)
pr_info("ERROR ALSA: no cx25821 cards found\n");
diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c
index a216b620b718..523ac5e16c1b 100644
--- a/drivers/staging/cx25821/cx25821-core.c
+++ b/drivers/staging/cx25821/cx25821-core.c
@@ -33,9 +33,6 @@ MODULE_DESCRIPTION("Driver for Athena cards");
MODULE_AUTHOR("Shu Lin - Hiep Huynh");
MODULE_LICENSE("GPL");
-struct list_head cx25821_devlist;
-EXPORT_SYMBOL(cx25821_devlist);
-
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
@@ -46,8 +43,10 @@ MODULE_PARM_DESC(card, "card type");
static unsigned int cx25821_devcount;
-static DEFINE_MUTEX(devlist);
+DEFINE_MUTEX(cx25821_devlist_mutex);
+EXPORT_SYMBOL(cx25821_devlist_mutex);
LIST_HEAD(cx25821_devlist);
+EXPORT_SYMBOL(cx25821_devlist);
struct sram_channel cx25821_sram_channels[] = {
[SRAM_CH00] = {
@@ -911,9 +910,9 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
dev->nr = ++cx25821_devcount;
sprintf(dev->name, "cx25821[%d]", dev->nr);
- mutex_lock(&devlist);
+ mutex_lock(&cx25821_devlist_mutex);
list_add_tail(&dev->devlist, &cx25821_devlist);
- mutex_unlock(&devlist);
+ mutex_unlock(&cx25821_devlist_mutex);
strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown");
strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821");
@@ -1465,9 +1464,9 @@ static void __devexit cx25821_finidev(struct pci_dev *pci_dev)
if (pci_dev->irq)
free_irq(pci_dev->irq, dev);
- mutex_lock(&devlist);
+ mutex_lock(&cx25821_devlist_mutex);
list_del(&dev->devlist);
- mutex_unlock(&devlist);
+ mutex_unlock(&cx25821_devlist_mutex);
cx25821_dev_unregister(dev);
v4l2_device_unregister(v4l2_dev);
@@ -1501,7 +1500,6 @@ static struct pci_driver cx25821_pci_driver = {
static int __init cx25821_init(void)
{
- INIT_LIST_HEAD(&cx25821_devlist);
pr_info("driver version %d.%d.%d loaded\n",
(CX25821_VERSION_CODE >> 16) & 0xff,
(CX25821_VERSION_CODE >> 8) & 0xff,
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c
index 0d8d75670516..ab05392386e8 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/staging/cx25821/cx25821-video.c
@@ -27,7 +27,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "cx25821-video.h"
-#include <linux/smp_lock.h>
MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
@@ -815,7 +814,7 @@ static int video_open(struct file *file)
if (NULL == fh)
return -ENOMEM;
- lock_kernel();
+ mutex_lock(&cx25821_devlist_mutex);
list_for_each(list, &cx25821_devlist)
{
@@ -832,8 +831,8 @@ static int video_open(struct file *file)
}
if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
+ mutex_unlock(&cx25821_devlist_mutex);
+ return -ENODEV;
}
file->private_data = fh;
@@ -862,7 +861,7 @@ static int video_open(struct file *file)
sizeof(struct cx25821_buffer), fh, NULL);
dprintk(1, "post videobuf_queue_init()\n");
- unlock_kernel();
+ mutex_unlock(&cx25821_devlist_mutex);
return 0;
}
diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/staging/cx25821/cx25821.h
index 55115235f7f6..6230243e2ccb 100644
--- a/drivers/staging/cx25821/cx25821.h
+++ b/drivers/staging/cx25821/cx25821.h
@@ -31,7 +31,6 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/kdev_t.h>
-#include <linux/smp_lock.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -445,6 +444,8 @@ static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev)
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
extern struct list_head cx25821_devlist;
+extern struct mutex cx25821_devlist_mutex;
+
extern struct cx25821_board cx25821_boards[];
extern struct cx25821_subid cx25821_subids[];
diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/cxd2099/Kconfig
new file mode 100644
index 000000000000..9d638c30735d
--- /dev/null
+++ b/drivers/staging/cxd2099/Kconfig
@@ -0,0 +1,11 @@
+config DVB_CXD2099
+ tristate "CXD2099AR Common Interface driver"
+ depends on DVB_CORE && PCI && I2C && DVB_NGENE
+ ---help---
+ Support for the CI module found on cineS2 DVB-S2, supported by
+ the Micronas PCIe device driver (ngene).
+
+ For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/cxd2099/Makefile
new file mode 100644
index 000000000000..72b14558c119
--- /dev/null
+++ b/drivers/staging/cxd2099/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
+EXTRA_CFLAGS += -Idrivers/media/common/tuners/
diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/cxd2099/TODO
new file mode 100644
index 000000000000..375bb6f8ee2c
--- /dev/null
+++ b/drivers/staging/cxd2099/TODO
@@ -0,0 +1,12 @@
+For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
+
+But this is wrong. There are some discussions about the proper way for
+doing it, as seen at:
+ http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html
+
+While there's no proper fix for it, the driver should be kept in staging.
+
+Patches should be submitted to: linux-media@vger.kernel.org.
diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/cxd2099/cxd2099.c
new file mode 100644
index 000000000000..b49186c74eb3
--- /dev/null
+++ b/drivers/staging/cxd2099/cxd2099.c
@@ -0,0 +1,574 @@
+/*
+ * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010 DigitalDevices UG
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "cxd2099.h"
+
+#define MAX_BUFFER_SIZE 248
+
+struct cxd {
+ struct dvb_ca_en50221 en;
+
+ struct i2c_adapter *i2c;
+ u8 adr;
+ u8 regs[0x23];
+ u8 lastaddress;
+ u8 clk_reg_f;
+ u8 clk_reg_b;
+ int mode;
+ u32 bitrate;
+ int ready;
+ int dr;
+ int slot_stat;
+
+ u8 amem[1024];
+ int amem_read;
+
+ int cammode;
+ struct mutex lock;
+};
+
+static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 data)
+{
+ u8 m[2] = {reg, data};
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n",
+ reg, adr);
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_write(struct i2c_adapter *adapter, u8 adr,
+ u8 *data, u8 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ printk(KERN_ERR "Failed to write to I2C!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1 },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ printk(KERN_ERR "error in i2c_read_reg\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *data, u8 n)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1 },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = n } };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ printk(KERN_ERR "error in i2c_read\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ status = i2c_read(ci->i2c, ci->adr, 1, data, n);
+ }
+ return status;
+}
+
+static int read_reg(struct cxd *ci, u8 reg, u8 *val)
+{
+ return read_block(ci, reg, val, 1);
+}
+
+
+static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = { 2, address&0xff, address>>8 };
+
+ status = i2c_write(ci->i2c, ci->adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->adr, 3, data, n);
+ return status;
+}
+
+static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = { 2, address&0xff, address>>8 };
+
+ status = i2c_write(ci->i2c, ci->adr, addr, 3);
+ if (!status) {
+ u8 buf[256] = {3};
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->adr, buf, n+1);
+ }
+ return status;
+}
+
+static int read_io(struct cxd *ci, u16 address, u8 *val)
+{
+ int status;
+ u8 addr[3] = { 2, address&0xff, address>>8 };
+
+ status = i2c_write(ci->i2c, ci->adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->adr, 3, val, 1);
+ return status;
+}
+
+static int write_io(struct cxd *ci, u16 address, u8 val)
+{
+ int status;
+ u8 addr[3] = { 2, address&0xff, address>>8 };
+ u8 buf[2] = { 3, val };
+
+ status = i2c_write(ci->i2c, ci->adr, addr, 3);
+ if (!status)
+ status = i2c_write(ci->i2c, ci->adr, buf, 2);
+
+ return status;
+}
+
+
+static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->adr, 0, reg);
+ if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
+ status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]);
+ ci->regs[reg] = (ci->regs[reg]&(~mask))|val;
+ if (!status) {
+ ci->lastaddress = reg;
+ status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]);
+ }
+ if (reg == 0x20)
+ ci->regs[reg] &= 0x7f;
+ return status;
+}
+
+static int write_reg(struct cxd *ci, u8 reg, u8 val)
+{
+ return write_regm(ci, reg, val, 0xff);
+}
+
+#ifdef BUFFER_MODE
+static int write_block(struct cxd *ci, u8 adr, u8 *data, int n)
+{
+ int status;
+ u8 buf[256] = {1};
+
+ status = i2c_write_reg(ci->i2c, ci->adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->adr, buf, n+1);
+ }
+ return status;
+}
+#endif
+
+static void set_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->mode)
+ return;
+
+ switch (mode) {
+ case 0x00: /* IO mem */
+ write_regm(ci, 0x06, 0x00, 0x07);
+ break;
+ case 0x01: /* ATT mem */
+ write_regm(ci, 0x06, 0x02, 0x07);
+ break;
+ default:
+ break;
+ }
+ ci->mode = mode;
+}
+
+static void cam_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->cammode)
+ return;
+
+ switch (mode) {
+ case 0x00:
+ write_regm(ci, 0x20, 0x80, 0x80);
+ break;
+ case 0x01:
+ printk(KERN_INFO "enable cam buffer mode\n");
+ /* write_reg(ci, 0x0d, 0x00); */
+ /* write_reg(ci, 0x0e, 0x01); */
+ write_regm(ci, 0x08, 0x40, 0x40);
+ /* read_reg(ci, 0x12, &dummy); */
+ write_regm(ci, 0x08, 0x80, 0x80);
+ break;
+ default:
+ break;
+ }
+ ci->cammode = mode;
+}
+
+
+
+#define CHK_ERROR(s) if ((status = s)) break
+
+static int init(struct cxd *ci)
+{
+ int status;
+
+ mutex_lock(&ci->lock);
+ ci->mode = -1;
+ do {
+ CHK_ERROR(write_reg(ci, 0x00, 0x00));
+ CHK_ERROR(write_reg(ci, 0x01, 0x00));
+ CHK_ERROR(write_reg(ci, 0x02, 0x10));
+ CHK_ERROR(write_reg(ci, 0x03, 0x00));
+ CHK_ERROR(write_reg(ci, 0x05, 0xFF));
+ CHK_ERROR(write_reg(ci, 0x06, 0x1F));
+ CHK_ERROR(write_reg(ci, 0x07, 0x1F));
+ CHK_ERROR(write_reg(ci, 0x08, 0x28));
+ CHK_ERROR(write_reg(ci, 0x14, 0x20));
+
+ CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
+ CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */
+
+ /* Sync detector */
+ CHK_ERROR(write_reg(ci, 0x0B, 0x33));
+ CHK_ERROR(write_reg(ci, 0x0C, 0x33));
+
+ CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F));
+ CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b));
+ CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F));
+ CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f));
+
+ CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */
+ CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */
+ CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */
+
+
+ CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */
+
+ CHK_ERROR(write_regm(ci, 0x03, 0x02, 02)); /* Enable IREQA Interrupt */
+ CHK_ERROR(write_reg(ci, 0x01, 0x04)); /* Enable CD Interrupt */
+ CHK_ERROR(write_reg(ci, 0x00, 0x31)); /* Enable TS1,Hot Swap,Slot A */
+ CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08)); /* Put TS in bypass */
+ ci->cammode = -1;
+#ifdef BUFFER_MODE
+ cam_mode(ci, 0);
+#endif
+ } while (0);
+ mutex_unlock(&ci->lock);
+
+ return 0;
+}
+
+
+static int read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ read_pccard(ci, address, &val, 1);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+
+static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
+ int address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ write_pccard(ci, address, &value, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot, u8 address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ read_io(ci, address, &val);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
+ u8 address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ write_io(ci, address, value);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ cam_mode(ci, 0);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_reg(ci, 0x03, 0x02);
+ ci->ready = 0;
+ ci->mode = -1;
+ {
+ int i;
+ for (i = 0; i < 100; i++) {
+ msleep(10);
+ if (ci->ready)
+ break;
+ }
+ }
+ mutex_unlock(&ci->lock);
+ /* msleep(500); */
+ return 0;
+}
+
+static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ printk(KERN_INFO "slot_shutdown\n");
+ mutex_lock(&ci->lock);
+ /* write_regm(ci, 0x09, 0x08, 0x08); */
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_regm(ci, 0x06, 0x07, 0x07);
+ ci->mode = -1;
+ mutex_unlock(&ci->lock);
+ return 0; /* shutdown(ci); */
+}
+
+static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x00, 0x08);
+ set_mode(ci, 0);
+#ifdef BUFFER_MODE
+ cam_mode(ci, 1);
+#endif
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+
+static int campoll(struct cxd *ci)
+{
+ u8 istat;
+
+ read_reg(ci, 0x04, &istat);
+ if (!istat)
+ return 0;
+ write_reg(ci, 0x05, istat);
+
+ if (istat&0x40) {
+ ci->dr = 1;
+ printk(KERN_INFO "DR\n");
+ }
+ if (istat&0x20)
+ printk(KERN_INFO "WC\n");
+
+ if (istat&2) {
+ u8 slotstat;
+
+ read_reg(ci, 0x01, &slotstat);
+ if (!(2&slotstat)) {
+ if (!ci->slot_stat) {
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT;
+ write_regm(ci, 0x03, 0x08, 0x08);
+ }
+
+ } else {
+ if (ci->slot_stat) {
+ ci->slot_stat = 0;
+ write_regm(ci, 0x03, 0x00, 0x08);
+ printk(KERN_INFO "NO CAM\n");
+ ci->ready = 0;
+ }
+ }
+ if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+ ci->ready = 1;
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
+ printk(KERN_INFO "READY\n");
+ }
+ }
+ return 0;
+}
+
+
+static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct cxd *ci = ca->data;
+ u8 slotstat;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ read_reg(ci, 0x01, &slotstat);
+ mutex_unlock(&ci->lock);
+
+ return ci->slot_stat;
+}
+
+#ifdef BUFFER_MODE
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+ u8 msb, lsb;
+ u16 len;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ mutex_unlock(&ci->lock);
+
+ printk(KERN_INFO "read_data\n");
+ if (!ci->dr)
+ return 0;
+
+ mutex_lock(&ci->lock);
+ read_reg(ci, 0x0f, &msb);
+ read_reg(ci, 0x10, &lsb);
+ len = (msb<<8)|lsb;
+ read_block(ci, 0x12, ebuf, len);
+ ci->dr = 0;
+ mutex_unlock(&ci->lock);
+
+ return len;
+}
+
+static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ printk(KERN_INFO "write_data %d\n", ecount);
+ write_reg(ci, 0x0d, ecount>>8);
+ write_reg(ci, 0x0e, ecount&0xff);
+ write_block(ci, 0x11, ebuf, ecount);
+ mutex_unlock(&ci->lock);
+ return ecount;
+}
+#endif
+
+static struct dvb_ca_en50221 en_templ = {
+ .read_attribute_mem = read_attribute_mem,
+ .write_attribute_mem = write_attribute_mem,
+ .read_cam_control = read_cam_control,
+ .write_cam_control = write_cam_control,
+ .slot_reset = slot_reset,
+ .slot_shutdown = slot_shutdown,
+ .slot_ts_enable = slot_ts_enable,
+ .poll_slot_status = poll_slot_status,
+#ifdef BUFFER_MODE
+ .read_data = read_data,
+ .write_data = write_data,
+#endif
+
+};
+
+struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv,
+ struct i2c_adapter *i2c)
+{
+ struct cxd *ci = 0;
+ u32 bitrate = 62000000;
+ u8 val;
+
+ if (i2c_read_reg(i2c, adr, 0, &val) < 0) {
+ printk(KERN_ERR "No CXD2099 detected at %02x\n", adr);
+ return 0;
+ }
+
+ ci = kmalloc(sizeof(struct cxd), GFP_KERNEL);
+ if (!ci)
+ return 0;
+ memset(ci, 0, sizeof(*ci));
+
+ mutex_init(&ci->lock);
+ ci->i2c = i2c;
+ ci->adr = adr;
+ ci->lastaddress = 0xff;
+ ci->clk_reg_b = 0x4a;
+ ci->clk_reg_f = 0x1b;
+ ci->bitrate = bitrate;
+
+ memcpy(&ci->en, &en_templ, sizeof(en_templ));
+ ci->en.data = ci;
+ init(ci);
+ printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr);
+ return &ci->en;
+}
+EXPORT_SYMBOL(cxd2099_attach);
+
+MODULE_DESCRIPTION("cxd2099");
+MODULE_AUTHOR("Ralph Metzler <rjkm@metzlerbros.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h
new file mode 100644
index 000000000000..bed54ff3e30b
--- /dev/null
+++ b/drivers/staging/cxd2099/cxd2099.h
@@ -0,0 +1,41 @@
+/*
+ * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010 DigitalDevices UG
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _CXD2099_H_
+#define _CXD2099_H_
+
+#include <dvb_ca_en50221.h>
+
+#if defined(CONFIG_DVB_CXD2099) || \
+ (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
+struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/staging/dabusb/Kconfig b/drivers/staging/dabusb/Kconfig
deleted file mode 100644
index 87bdc425d3c5..000000000000
--- a/drivers/staging/dabusb/Kconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-config USB_DABUSB
- tristate "DABUSB driver"
- depends on USB
- ---help---
- A Digital Audio Broadcasting (DAB) Receiver for USB and Linux
- brought to you by the DAB-Team
- <http://wwwbode.cs.tum.edu/Par/arch/dab/>. This driver can be taken
- as an example for URB-based bulk, control, and isochronous
- transactions. URB's are explained in
- <Documentation/usb/URB.txt>.
-
- To compile this driver as a module, choose M here: the
- module will be called dabusb.
-
diff --git a/drivers/staging/dabusb/Makefile b/drivers/staging/dabusb/Makefile
deleted file mode 100644
index 2ff2f228e5f9..000000000000
--- a/drivers/staging/dabusb/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-$(CONFIG_USB_DABUSB) += dabusb.o
-
diff --git a/drivers/staging/dabusb/TODO b/drivers/staging/dabusb/TODO
deleted file mode 100644
index f9c0314ea0c1..000000000000
--- a/drivers/staging/dabusb/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a driver for an experimental sample developed in 2003. The driver
-never supported any commercial product, nor had any known user.
-If nobody takes care on it, the driver will be removed for 2.6.39.
-
-Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/dabusb/dabusb.c b/drivers/staging/dabusb/dabusb.c
deleted file mode 100644
index f3e25e91366d..000000000000
--- a/drivers/staging/dabusb/dabusb.c
+++ /dev/null
@@ -1,914 +0,0 @@
-/*****************************************************************************/
-
-/*
- * dabusb.c -- dab usb driver.
- *
- * Copyright (C) 1999 Deti Fliegl (deti@fliegl.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.
- *
- *
- *
- * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $
- *
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/socket.h>
-#include <linux/list.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <asm/uaccess.h>
-#include <asm/atomic.h>
-#include <linux/delay.h>
-#include <linux/usb.h>
-#include <linux/mutex.h>
-#include <linux/firmware.h>
-#include <linux/ihex.h>
-
-#include "dabusb.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.54"
-#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de"
-#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999"
-
-/* --------------------------------------------------------------------- */
-
-#ifdef CONFIG_USB_DYNAMIC_MINORS
-#define NRDABUSB 256
-#else
-#define NRDABUSB 4
-#endif
-
-/*-------------------------------------------------------------------*/
-
-static dabusb_t dabusb[NRDABUSB];
-static int buffers = 256;
-static struct usb_driver dabusb_driver;
-
-/*-------------------------------------------------------------------*/
-
-static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src)
-{
- unsigned long flags;
- struct list_head *tmp;
- int ret = 0;
-
- spin_lock_irqsave (&s->lock, flags);
-
- if (list_empty (src)) {
- // no elements in source buffer
- ret = -1;
- goto err;
- }
- tmp = src->next;
- list_move_tail (tmp, dst);
-
- err: spin_unlock_irqrestore (&s->lock, flags);
- return ret;
-}
-/*-------------------------------------------------------------------*/
-#ifdef DEBUG
-static void dump_urb (struct urb *urb)
-{
- dbg("urb :%p", urb);
- dbg("dev :%p", urb->dev);
- dbg("pipe :%08X", urb->pipe);
- dbg("status :%d", urb->status);
- dbg("transfer_flags :%08X", urb->transfer_flags);
- dbg("transfer_buffer :%p", urb->transfer_buffer);
- dbg("transfer_buffer_length:%d", urb->transfer_buffer_length);
- dbg("actual_length :%d", urb->actual_length);
- dbg("setup_packet :%p", urb->setup_packet);
- dbg("start_frame :%d", urb->start_frame);
- dbg("number_of_packets :%d", urb->number_of_packets);
- dbg("interval :%d", urb->interval);
- dbg("error_count :%d", urb->error_count);
- dbg("context :%p", urb->context);
- dbg("complete :%p", urb->complete);
-}
-#endif
-/*-------------------------------------------------------------------*/
-static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
-{
- unsigned long flags;
- pbuff_t b;
-
- dbg("dabusb_cancel_queue");
-
- spin_lock_irqsave (&s->lock, flags);
-
- list_for_each_entry(b, q, buff_list) {
-#ifdef DEBUG
- dump_urb(b->purb);
-#endif
- usb_unlink_urb (b->purb);
- }
- spin_unlock_irqrestore (&s->lock, flags);
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int dabusb_free_queue (struct list_head *q)
-{
- struct list_head *tmp;
- struct list_head *p;
- pbuff_t b;
-
- dbg("dabusb_free_queue");
- for (p = q->next; p != q;) {
- b = list_entry (p, buff_t, buff_list);
-
-#ifdef DEBUG
- dump_urb(b->purb);
-#endif
- kfree(b->purb->transfer_buffer);
- usb_free_urb(b->purb);
- tmp = p->next;
- list_del (p);
- kfree (b);
- p = tmp;
- }
-
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static int dabusb_free_buffers (pdabusb_t s)
-{
- unsigned long flags;
- dbg("dabusb_free_buffers");
-
- spin_lock_irqsave(&s->lock, flags);
-
- dabusb_free_queue (&s->free_buff_list);
- dabusb_free_queue (&s->rec_buff_list);
-
- spin_unlock_irqrestore(&s->lock, flags);
-
- s->got_mem = 0;
- return 0;
-}
-/*-------------------------------------------------------------------*/
-static void dabusb_iso_complete (struct urb *purb)
-{
- pbuff_t b = purb->context;
- pdabusb_t s = b->s;
- int i;
- int len;
- int dst = 0;
- void *buf = purb->transfer_buffer;
-
- dbg("dabusb_iso_complete");
-
- // process if URB was not killed
- if (purb->status != -ENOENT) {
- unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE);
- int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
- for (i = 0; i < purb->number_of_packets; i++)
- if (!purb->iso_frame_desc[i].status) {
- len = purb->iso_frame_desc[i].actual_length;
- if (len <= pipesize) {
- memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len);
- dst += len;
- }
- else
- dev_err(&purb->dev->dev,
- "dabusb_iso_complete: invalid len %d\n", len);
- }
- else
- dev_warn(&purb->dev->dev, "dabusb_iso_complete: corrupted packet status: %d\n", purb->iso_frame_desc[i].status);
- if (dst != purb->actual_length)
- dev_err(&purb->dev->dev,
- "dst!=purb->actual_length:%d!=%d\n",
- dst, purb->actual_length);
- }
-
- if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) {
- s->overruns++;
- dev_err(&purb->dev->dev, "overrun (%d)\n", s->overruns);
- }
- wake_up (&s->wait);
-}
-/*-------------------------------------------------------------------*/
-static int dabusb_alloc_buffers (pdabusb_t s)
-{
- int transfer_len = 0;
- pbuff_t b;
- unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE);
- int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe));
- int packets = _ISOPIPESIZE / pipesize;
- int transfer_buffer_length = packets * pipesize;
- int i;
-
- dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d",
- pipesize, packets, transfer_buffer_length);
-
- while (transfer_len < (s->total_buffer_size << 10)) {
- b = kzalloc(sizeof (buff_t), GFP_KERNEL);
- if (!b) {
- dev_err(&s->usbdev->dev,
- "kzalloc(sizeof(buff_t))==NULL\n");
- goto err;
- }
- b->s = s;
- b->purb = usb_alloc_urb(packets, GFP_KERNEL);
- if (!b->purb) {
- dev_err(&s->usbdev->dev, "usb_alloc_urb == NULL\n");
- kfree (b);
- goto err;
- }
-
- b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL);
- if (!b->purb->transfer_buffer) {
- kfree (b->purb);
- kfree (b);
- dev_err(&s->usbdev->dev,
- "kmalloc(%d)==NULL\n", transfer_buffer_length);
- goto err;
- }
-
- b->purb->transfer_buffer_length = transfer_buffer_length;
- b->purb->number_of_packets = packets;
- b->purb->complete = dabusb_iso_complete;
- b->purb->context = b;
- b->purb->dev = s->usbdev;
- b->purb->pipe = pipe;
- b->purb->transfer_flags = URB_ISO_ASAP;
-
- for (i = 0; i < packets; i++) {
- b->purb->iso_frame_desc[i].offset = i * pipesize;
- b->purb->iso_frame_desc[i].length = pipesize;
- }
-
- transfer_len += transfer_buffer_length;
- list_add_tail (&b->buff_list, &s->free_buff_list);
- }
- s->got_mem = transfer_len;
-
- return 0;
-
- err:
- dabusb_free_buffers (s);
- return -ENOMEM;
-}
-/*-------------------------------------------------------------------*/
-static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
-{
- int ret;
- unsigned int pipe;
- int actual_length;
-
- dbg("dabusb_bulk");
-
- if (!pb->pipe)
- pipe = usb_rcvbulkpipe (s->usbdev, 2);
- else
- pipe = usb_sndbulkpipe (s->usbdev, 2);
-
- ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100);
- if(ret<0) {
- dev_err(&s->usbdev->dev,
- "usb_bulk_msg failed(%d)\n", ret);
-
- if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
- dev_err(&s->usbdev->dev, "set_interface failed\n");
- return -EINVAL;
- }
-
- }
-
- if( ret == -EPIPE ) {
- dev_warn(&s->usbdev->dev, "CLEAR_FEATURE request to remove STALL condition.\n");
- if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
- dev_err(&s->usbdev->dev, "request failed\n");
- }
-
- pb->size = actual_length;
- return ret;
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_writemem (pdabusb_t s, int pos, const unsigned char *data,
- int len)
-{
- int ret;
- unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL);
-
- if (!transfer_buffer) {
- dev_err(&s->usbdev->dev,
- "dabusb_writemem: kmalloc(%d) failed.\n", len);
- return -ENOMEM;
- }
-
- memcpy (transfer_buffer, data, len);
-
- ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300);
-
- kfree (transfer_buffer);
- return ret;
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit)
-{
- dbg("dabusb_8051_reset: %d",reset_bit);
- return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1);
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_loadmem (pdabusb_t s, const char *fname)
-{
- int ret;
- const struct ihex_binrec *rec;
- const struct firmware *uninitialized_var(fw);
-
- dbg("Enter dabusb_loadmem (internal)");
-
- ret = request_ihex_firmware(&fw, "dabusb/firmware.fw", &s->usbdev->dev);
- if (ret) {
- dev_err(&s->usbdev->dev,
- "Failed to load \"dabusb/firmware.fw\": %d\n", ret);
- goto out;
- }
- ret = dabusb_8051_reset (s, 1);
-
- for (rec = (const struct ihex_binrec *)fw->data; rec;
- rec = ihex_next_binrec(rec)) {
- dbg("dabusb_writemem: %04X %p %d)", be32_to_cpu(rec->addr),
- rec->data, be16_to_cpu(rec->len));
-
- ret = dabusb_writemem(s, be32_to_cpu(rec->addr), rec->data,
- be16_to_cpu(rec->len));
- if (ret < 0) {
- dev_err(&s->usbdev->dev,
- "dabusb_writemem failed (%d %04X %p %d)\n",
- ret, be32_to_cpu(rec->addr),
- rec->data, be16_to_cpu(rec->len));
- break;
- }
- }
- ret = dabusb_8051_reset (s, 0);
- release_firmware(fw);
- out:
- dbg("dabusb_loadmem: exit");
-
- return ret;
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b)
-{
- b->size = 4;
- b->data[0] = 0x2a;
- b->data[1] = 0;
- b->data[2] = 0;
- b->data[3] = 0;
-
- dbg("dabusb_fpga_clear");
-
- return dabusb_bulk (s, b);
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b)
-{
- b->size = 4;
- b->data[0] = 0x2c;
- b->data[1] = 0;
- b->data[2] = 0;
- b->data[3] = 0;
-
- dbg("dabusb_fpga_init");
-
- return dabusb_bulk (s, b);
-}
-/* --------------------------------------------------------------------- */
-static int dabusb_fpga_download (pdabusb_t s, const char *fname)
-{
- pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
- const struct firmware *fw;
- unsigned int blen, n;
- int ret;
-
- dbg("Enter dabusb_fpga_download (internal)");
-
- if (!b) {
- dev_err(&s->usbdev->dev,
- "kmalloc(sizeof(bulk_transfer_t))==NULL\n");
- return -ENOMEM;
- }
-
- ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev);
- if (ret) {
- dev_err(&s->usbdev->dev,
- "Failed to load \"dabusb/bitstream.bin\": %d\n", ret);
- kfree(b);
- return ret;
- }
-
- b->pipe = 1;
- ret = dabusb_fpga_clear (s, b);
- mdelay (10);
- blen = fw->data[73] + (fw->data[72] << 8);
-
- dbg("Bitstream len: %i", blen);
-
- b->data[0] = 0x2b;
- b->data[1] = 0;
- b->data[2] = 0;
- b->data[3] = 60;
-
- for (n = 0; n <= blen + 60; n += 60) {
- // some cclks for startup
- b->size = 64;
- memcpy (b->data + 4, fw->data + 74 + n, 60);
- ret = dabusb_bulk (s, b);
- if (ret < 0) {
- dev_err(&s->usbdev->dev, "dabusb_bulk failed.\n");
- break;
- }
- mdelay (1);
- }
-
- ret = dabusb_fpga_init (s, b);
- kfree (b);
- release_firmware(fw);
-
- dbg("exit dabusb_fpga_download");
-
- return ret;
-}
-
-static int dabusb_stop (pdabusb_t s)
-{
- dbg("dabusb_stop");
-
- s->state = _stopped;
- dabusb_cancel_queue (s, &s->rec_buff_list);
-
- dbg("pending_io: %d", s->pending_io.counter);
-
- s->pending_io.counter = 0;
- return 0;
-}
-
-static int dabusb_startrek (pdabusb_t s)
-{
- if (!s->got_mem && s->state != _started) {
-
- dbg("dabusb_startrek");
-
- if (dabusb_alloc_buffers (s) < 0)
- return -ENOMEM;
- dabusb_stop (s);
- s->state = _started;
- s->readptr = 0;
- }
-
- if (!list_empty (&s->free_buff_list)) {
- pbuff_t end;
- int ret;
-
- while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) {
-
- dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list);
-
- end = list_entry (s->rec_buff_list.prev, buff_t, buff_list);
-
- ret = usb_submit_urb (end->purb, GFP_KERNEL);
- if (ret) {
- dev_err(&s->usbdev->dev,
- "usb_submit_urb returned:%d\n", ret);
- if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
- dev_err(&s->usbdev->dev,
- "startrek: dabusb_add_buf_tail failed\n");
- break;
- }
- else
- atomic_inc (&s->pending_io);
- }
- dbg("pending_io: %d",s->pending_io.counter);
- }
-
- return 0;
-}
-
-static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos)
-{
- pdabusb_t s = (pdabusb_t) file->private_data;
- unsigned long flags;
- unsigned ret = 0;
- int rem;
- int cnt;
- pbuff_t b;
- struct urb *purb = NULL;
-
- dbg("dabusb_read");
-
- if (*ppos)
- return -ESPIPE;
-
- if (s->remove_pending)
- return -EIO;
-
-
- if (!s->usbdev)
- return -EIO;
-
- while (count > 0) {
- dabusb_startrek (s);
-
- spin_lock_irqsave (&s->lock, flags);
-
- if (list_empty (&s->rec_buff_list)) {
-
- spin_unlock_irqrestore(&s->lock, flags);
-
- dev_err(&s->usbdev->dev,
- "error: rec_buf_list is empty\n");
- goto err;
- }
-
- b = list_entry (s->rec_buff_list.next, buff_t, buff_list);
- purb = b->purb;
-
- spin_unlock_irqrestore(&s->lock, flags);
-
- if (purb->status == -EINPROGRESS) {
- if (file->f_flags & O_NONBLOCK) // return nonblocking
- {
- if (!ret)
- ret = -EAGAIN;
- goto err;
- }
-
- interruptible_sleep_on (&s->wait);
-
- if (signal_pending (current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto err;
- }
-
- spin_lock_irqsave (&s->lock, flags);
-
- if (list_empty (&s->rec_buff_list)) {
- spin_unlock_irqrestore(&s->lock, flags);
- dev_err(&s->usbdev->dev,
- "error: still no buffer available.\n");
- goto err;
- }
- spin_unlock_irqrestore(&s->lock, flags);
- s->readptr = 0;
- }
- if (s->remove_pending) {
- ret = -EIO;
- goto err;
- }
-
- rem = purb->actual_length - s->readptr; // set remaining bytes to copy
-
- if (count >= rem)
- cnt = rem;
- else
- cnt = count;
-
- dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt);
-
- if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) {
- dev_err(&s->usbdev->dev, "read: copy_to_user failed\n");
- if (!ret)
- ret = -EFAULT;
- goto err;
- }
-
- s->readptr += cnt;
- count -= cnt;
- buf += cnt;
- ret += cnt;
-
- if (s->readptr == purb->actual_length) {
- // finished, take next buffer
- if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
- dev_err(&s->usbdev->dev,
- "read: dabusb_add_buf_tail failed\n");
- s->readptr = 0;
- }
- }
- err: //mutex_unlock(&s->mutex);
- return ret;
-}
-
-static int dabusb_open (struct inode *inode, struct file *file)
-{
- int devnum = iminor(inode);
- pdabusb_t s;
- int r;
-
- if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB))
- return -EIO;
-
- s = &dabusb[devnum - DABUSB_MINOR];
-
- dbg("dabusb_open");
- mutex_lock(&s->mutex);
-
- while (!s->usbdev || s->opened) {
- mutex_unlock(&s->mutex);
-
- if (file->f_flags & O_NONBLOCK)
- return -EBUSY;
- msleep_interruptible(500);
-
- if (signal_pending (current))
- return -EAGAIN;
- mutex_lock(&s->mutex);
- }
- if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
- mutex_unlock(&s->mutex);
- dev_err(&s->usbdev->dev, "set_interface failed\n");
- return -EINVAL;
- }
- s->opened = 1;
- mutex_unlock(&s->mutex);
-
- file->f_pos = 0;
- file->private_data = s;
-
- r = nonseekable_open(inode, file);
- return r;
-}
-
-static int dabusb_release (struct inode *inode, struct file *file)
-{
- pdabusb_t s = (pdabusb_t) file->private_data;
-
- dbg("dabusb_release");
-
- mutex_lock(&s->mutex);
- dabusb_stop (s);
- dabusb_free_buffers (s);
- mutex_unlock(&s->mutex);
-
- if (!s->remove_pending) {
- if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
- dev_err(&s->usbdev->dev, "set_interface failed\n");
- }
- else
- wake_up (&s->remove_ok);
-
- s->opened = 0;
- return 0;
-}
-
-static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
-{
- pdabusb_t s = (pdabusb_t) file->private_data;
- pbulk_transfer_t pbulk;
- int ret = 0;
- int version = DABUSB_VERSION;
-
- dbg("dabusb_ioctl");
-
- if (s->remove_pending)
- return -EIO;
-
- mutex_lock(&s->mutex);
-
- if (!s->usbdev) {
- mutex_unlock(&s->mutex);
- return -EIO;
- }
-
- switch (cmd) {
-
- case IOCTL_DAB_BULK:
- pbulk = memdup_user((void __user *)arg,
- sizeof(bulk_transfer_t));
-
- if (IS_ERR(pbulk)) {
- ret = PTR_ERR(pbulk);
- break;
- }
-
- ret=dabusb_bulk (s, pbulk);
- if(ret==0)
- if (copy_to_user((void __user *)arg, pbulk,
- sizeof(bulk_transfer_t)))
- ret = -EFAULT;
- kfree (pbulk);
- break;
-
- case IOCTL_DAB_OVERRUNS:
- ret = put_user (s->overruns, (unsigned int __user *) arg);
- break;
-
- case IOCTL_DAB_VERSION:
- ret = put_user (version, (unsigned int __user *) arg);
- break;
-
- default:
- ret = -ENOIOCTLCMD;
- break;
- }
- mutex_unlock(&s->mutex);
- return ret;
-}
-
-static const struct file_operations dabusb_fops =
-{
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = dabusb_read,
- .unlocked_ioctl = dabusb_ioctl,
- .open = dabusb_open,
- .release = dabusb_release,
-};
-
-static char *dabusb_devnode(struct device *dev, mode_t *mode)
-{
- return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
-}
-
-static struct usb_class_driver dabusb_class = {
- .name = "dabusb%d",
- .devnode = dabusb_devnode,
- .fops = &dabusb_fops,
- .minor_base = DABUSB_MINOR,
-};
-
-
-/* --------------------------------------------------------------------- */
-static int dabusb_probe (struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *usbdev = interface_to_usbdev(intf);
- int retval;
- pdabusb_t s;
-
- dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
- le16_to_cpu(usbdev->descriptor.idVendor),
- le16_to_cpu(usbdev->descriptor.idProduct),
- intf->altsetting->desc.bInterfaceNumber);
-
- /* We don't handle multiple configurations */
- if (usbdev->descriptor.bNumConfigurations != 1)
- return -ENODEV;
-
- if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF &&
- le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999)
- return -ENODEV;
-
-
-
- s = &dabusb[intf->minor];
-
- mutex_lock(&s->mutex);
- s->remove_pending = 0;
- s->usbdev = usbdev;
- s->devnum = intf->minor;
-
- if (usb_reset_configuration (usbdev) < 0) {
- dev_err(&intf->dev, "reset_configuration failed\n");
- goto reject;
- }
- if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) {
- dabusb_loadmem (s, NULL);
- goto reject;
- }
- else {
- dabusb_fpga_download (s, NULL);
-
- if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) {
- dev_err(&intf->dev, "set_interface failed\n");
- goto reject;
- }
- }
- dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber);
- usb_set_intfdata (intf, s);
- mutex_unlock(&s->mutex);
-
- retval = usb_register_dev(intf, &dabusb_class);
- if (retval) {
- usb_set_intfdata (intf, NULL);
- return -ENOMEM;
- }
-
- return 0;
-
- reject:
- mutex_unlock(&s->mutex);
- s->usbdev = NULL;
- return -ENODEV;
-}
-
-static void dabusb_disconnect (struct usb_interface *intf)
-{
- wait_queue_t __wait;
- pdabusb_t s = usb_get_intfdata (intf);
-
- dbg("dabusb_disconnect");
-
- init_waitqueue_entry(&__wait, current);
-
- usb_set_intfdata (intf, NULL);
- if (s) {
- usb_deregister_dev (intf, &dabusb_class);
- s->remove_pending = 1;
- wake_up (&s->wait);
- add_wait_queue(&s->remove_ok, &__wait);
- set_current_state(TASK_UNINTERRUPTIBLE);
- if (s->state == _started)
- schedule();
- current->state = TASK_RUNNING;
- remove_wait_queue(&s->remove_ok, &__wait);
-
- s->usbdev = NULL;
- s->overruns = 0;
- }
-}
-
-static struct usb_device_id dabusb_ids [] = {
- // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */
- { USB_DEVICE(0x0547, 0x9999) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, dabusb_ids);
-
-static struct usb_driver dabusb_driver = {
- .name = "dabusb",
- .probe = dabusb_probe,
- .disconnect = dabusb_disconnect,
- .id_table = dabusb_ids,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int __init dabusb_init (void)
-{
- int retval;
- unsigned u;
-
- /* initialize struct */
- for (u = 0; u < NRDABUSB; u++) {
- pdabusb_t s = &dabusb[u];
- memset (s, 0, sizeof (dabusb_t));
- mutex_init (&s->mutex);
- s->usbdev = NULL;
- s->total_buffer_size = buffers;
- init_waitqueue_head (&s->wait);
- init_waitqueue_head (&s->remove_ok);
- spin_lock_init (&s->lock);
- INIT_LIST_HEAD (&s->free_buff_list);
- INIT_LIST_HEAD (&s->rec_buff_list);
- }
-
- /* register misc device */
- retval = usb_register(&dabusb_driver);
- if (retval)
- goto out;
-
- dbg("dabusb_init: driver registered");
-
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
-
-out:
- return retval;
-}
-
-static void __exit dabusb_cleanup (void)
-{
- dbg("dabusb_cleanup");
-
- usb_deregister (&dabusb_driver);
-}
-
-/* --------------------------------------------------------------------- */
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE("dabusb/firmware.fw");
-MODULE_FIRMWARE("dabusb/bitstream.bin");
-
-module_param(buffers, int, 0);
-MODULE_PARM_DESC (buffers, "Number of buffers (default=256)");
-
-module_init (dabusb_init);
-module_exit (dabusb_cleanup);
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/staging/dabusb/dabusb.h b/drivers/staging/dabusb/dabusb.h
deleted file mode 100644
index 00eb34c863eb..000000000000
--- a/drivers/staging/dabusb/dabusb.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#define _BULK_DATA_LEN 64
-typedef struct
-{
- unsigned char data[_BULK_DATA_LEN];
- unsigned int size;
- unsigned int pipe;
-}bulk_transfer_t,*pbulk_transfer_t;
-
-#define DABUSB_MINOR 240 /* some unassigned USB minor */
-#define DABUSB_VERSION 0x1000
-#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t)
-#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int)
-#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int)
-
-#ifdef __KERNEL__
-
-typedef enum { _stopped=0, _started } driver_state_t;
-
-typedef struct
-{
- struct mutex mutex;
- struct usb_device *usbdev;
- wait_queue_head_t wait;
- wait_queue_head_t remove_ok;
- spinlock_t lock;
- atomic_t pending_io;
- driver_state_t state;
- int remove_pending;
- int got_mem;
- int total_buffer_size;
- unsigned int overruns;
- int readptr;
- int opened;
- int devnum;
- struct list_head free_buff_list;
- struct list_head rec_buff_list;
-} dabusb_t,*pdabusb_t;
-
-typedef struct
-{
- pdabusb_t s;
- struct urb *purb;
- struct list_head buff_list;
-} buff_t,*pbuff_t;
-
-typedef struct
-{
- wait_queue_head_t wait;
-} bulk_completion_context_t, *pbulk_completion_context_t;
-
-
-#define _DABUSB_IF 2
-#define _DABUSB_ISOPIPE 0x09
-#define _ISOPIPESIZE 16384
-
-#define _BULK_DATA_LEN 64
-// Vendor specific request code for Anchor Upload/Download
-// This one is implemented in the core
-#define ANCHOR_LOAD_INTERNAL 0xA0
-
-// EZ-USB Control and Status Register. Bit 0 controls 8051 reset
-#define CPUCS_REG 0x7F92
-#define _TOTAL_BUFFERS 384
-
-#define MAX_INTEL_HEX_RECORD_LENGTH 16
-
-#ifndef _BYTE_DEFINED
-#define _BYTE_DEFINED
-typedef unsigned char BYTE;
-#endif // !_BYTE_DEFINED
-
-#ifndef _WORD_DEFINED
-#define _WORD_DEFINED
-typedef unsigned short WORD;
-#endif // !_WORD_DEFINED
-
-typedef struct _INTEL_HEX_RECORD
-{
- BYTE Length;
- WORD Address;
- BYTE Type;
- BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH];
-} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD;
-
-#endif
diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c
index 447953a4e80c..7ac43da4e252 100644
--- a/drivers/staging/easycap/easycap_ioctl.c
+++ b/drivers/staging/easycap/easycap_ioctl.c
@@ -1399,11 +1399,6 @@ case VIDIOC_G_CTRL: {
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-#if defined(VIDIOC_S_CTRL_OLD)
-case VIDIOC_S_CTRL_OLD: {
- JOM(8, "VIDIOC_S_CTRL_OLD required at least for xawtv\n");
-}
-#endif /*VIDIOC_S_CTRL_OLD*/
case VIDIOC_S_CTRL:
{
struct v4l2_control v4l2_control;
diff --git a/drivers/staging/se401/Kconfig b/drivers/staging/se401/Kconfig
deleted file mode 100644
index b7f8222ad21b..000000000000
--- a/drivers/staging/se401/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-config USB_SE401
- tristate "USB SE401 Camera support (DEPRECATED)"
- depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB
- ---help---
- Say Y here if you want to connect this type of camera to your
- computer's USB port. See <file:Documentation/video4linux/se401.txt>
- for more information and for a list of supported cameras.
-
- This driver uses the deprecated V4L1 API and will be removed in
- 2.6.39, unless someone converts it to the V4L2 API.
-
- To compile this driver as a module, choose M here: the
- module will be called se401.
diff --git a/drivers/staging/se401/Makefile b/drivers/staging/se401/Makefile
deleted file mode 100644
index b465d49783af..000000000000
--- a/drivers/staging/se401/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_USB_SE401) += se401.o
diff --git a/drivers/staging/se401/TODO b/drivers/staging/se401/TODO
deleted file mode 100644
index 3b2c03836286..000000000000
--- a/drivers/staging/se401/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-This is an obsolete driver for some old webcams that still use V4L1 API.
-As V4L1 support is being removed from kernel, if nobody take care on it,
-the driver will be removed for 2.6.39.
-
-Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/se401/se401.c b/drivers/staging/se401/se401.c
deleted file mode 100644
index 41360d7c3e96..000000000000
--- a/drivers/staging/se401/se401.c
+++ /dev/null
@@ -1,1492 +0,0 @@
-/*
- * Endpoints (formerly known as AOX) se401 USB Camera Driver
- *
- * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
- *
- * Still somewhat based on the Linux ov511 driver.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on
- * their chipset available and supporting me while writing this driver.
- * - Jeroen Vreeken
- */
-
-static const char version[] = "0.24";
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/pagemap.h>
-#include <linux/usb.h>
-#include "se401.h"
-
-static int flickerless;
-static int video_nr = -1;
-
-static struct usb_device_id device_table[] = {
- { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */
- { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */
- { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */
- { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */
- { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
-MODULE_DESCRIPTION("SE401 USB Camera Driver");
-MODULE_LICENSE("GPL");
-module_param(flickerless, int, 0);
-MODULE_PARM_DESC(flickerless,
- "Net frequency to adjust exposure time to (0/50/60)");
-module_param(video_nr, int, 0);
-
-static struct usb_driver se401_driver;
-
-
-/**********************************************************************
- *
- * Memory management
- *
- **********************************************************************/
-static void *rvmalloc(unsigned long size)
-{
- void *mem;
- unsigned long adr;
-
- size = PAGE_ALIGN(size);
- mem = vmalloc_32(size);
- if (!mem)
- return NULL;
-
- memset(mem, 0, size); /* Clear the ram out, no junk to the user */
- adr = (unsigned long) mem;
- while (size > 0) {
- SetPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
- unsigned long adr;
-
- if (!mem)
- return;
-
- adr = (unsigned long) mem;
- while ((long) size > 0) {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vfree(mem);
-}
-
-
-
-/****************************************************************************
- *
- * se401 register read/write functions
- *
- ***************************************************************************/
-
-static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
- unsigned short value, unsigned char *cp, int size)
-{
- return usb_control_msg(
- se401->dev,
- set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
- req,
- (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value,
- 0,
- cp,
- size,
- 1000
- );
-}
-
-static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
- unsigned short param)
-{
- /* specs say that the selector (address) should go in the value field
- and the param in index, but in the logs of the windows driver they do
- this the other way around...
- */
- return usb_control_msg(
- se401->dev,
- usb_sndctrlpipe(se401->dev, 0),
- SE401_REQ_SET_EXT_FEATURE,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- param,
- selector,
- NULL,
- 0,
- 1000
- );
-}
-
-static unsigned short se401_get_feature(struct usb_se401 *se401,
- unsigned short selector)
-{
- /* For 'set' the selecetor should be in index, not sure if the spec is
- wrong here to....
- */
- unsigned char cp[2];
- usb_control_msg(
- se401->dev,
- usb_rcvctrlpipe(se401->dev, 0),
- SE401_REQ_GET_EXT_FEATURE,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0,
- selector,
- cp,
- 2,
- 1000
- );
- return cp[0]+cp[1]*256;
-}
-
-/****************************************************************************
- *
- * Camera control
- *
- ***************************************************************************/
-
-
-static int se401_send_pict(struct usb_se401 *se401)
-{
- /* integration time low */
- se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);
- /* integration time mid */
- se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);
- /* integration time mid */
- se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);
- /* reset level value */
- se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
- /* red color gain */
- se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);
- /* green color gain */
- se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);
- /* blue color gain */
- se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);
-
- return 0;
-}
-
-static void se401_set_exposure(struct usb_se401 *se401, int brightness)
-{
- int integration = brightness << 5;
-
- if (flickerless == 50)
- integration = integration-integration % 106667;
- if (flickerless == 60)
- integration = integration-integration % 88889;
- se401->brightness = integration >> 5;
- se401->expose_h = (integration >> 16) & 0xff;
- se401->expose_m = (integration >> 8) & 0xff;
- se401->expose_l = integration & 0xff;
-}
-
-static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
-{
- p->brightness = se401->brightness;
- if (se401->enhance)
- p->whiteness = 32768;
- else
- p->whiteness = 0;
-
- p->colour = 65535;
- p->contrast = 65535;
- p->hue = se401->rgain << 10;
- p->palette = se401->palette;
- p->depth = 3; /* rgb24 */
- return 0;
-}
-
-
-static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
-{
- if (p->palette != VIDEO_PALETTE_RGB24)
- return 1;
- se401->palette = p->palette;
- if (p->hue != se401->hue) {
- se401->rgain = p->hue >> 10;
- se401->bgain = 0x40-(p->hue >> 10);
- se401->hue = p->hue;
- }
- if (p->brightness != se401->brightness)
- se401_set_exposure(se401, p->brightness);
-
- if (p->whiteness >= 32768)
- se401->enhance = 1;
- else
- se401->enhance = 0;
- se401_send_pict(se401);
- se401_send_pict(se401);
- return 0;
-}
-
-/*
- Hyundai have some really nice docs about this and other sensor related
- stuff on their homepage: www.hei.co.kr
-*/
-static void se401_auto_resetlevel(struct usb_se401 *se401)
-{
- unsigned int ahrc, alrc;
- int oldreset = se401->resetlevel;
-
- /* For some reason this normally read-only register doesn't get reset
- to zero after reading them just once...
- */
- se401_get_feature(se401, HV7131_REG_HIREFNOH);
- se401_get_feature(se401, HV7131_REG_HIREFNOL);
- se401_get_feature(se401, HV7131_REG_LOREFNOH);
- se401_get_feature(se401, HV7131_REG_LOREFNOL);
- ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
- se401_get_feature(se401, HV7131_REG_HIREFNOL);
- alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
- se401_get_feature(se401, HV7131_REG_LOREFNOL);
-
- /* Not an exact science, but it seems to work pretty well... */
- if (alrc > 10) {
- while (alrc >= 10 && se401->resetlevel < 63) {
- se401->resetlevel++;
- alrc /= 2;
- }
- } else if (ahrc > 20) {
- while (ahrc >= 20 && se401->resetlevel > 0) {
- se401->resetlevel--;
- ahrc /= 2;
- }
- }
- if (se401->resetlevel != oldreset)
- se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
-
- return;
-}
-
-/* irq handler for snapshot button */
-static void se401_button_irq(struct urb *urb)
-{
- struct usb_se401 *se401 = urb->context;
- int status;
-
- if (!se401->dev) {
- dev_info(&urb->dev->dev, "device vapourished\n");
- return;
- }
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d",
- __func__, urb->status);
- goto exit;
- }
-
- if (urb->actual_length >= 2)
- if (se401->button)
- se401->buttonpressed = 1;
-exit:
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status)
- err("%s - usb_submit_urb failed with result %d",
- __func__, status);
-}
-
-static void se401_video_irq(struct urb *urb)
-{
- struct usb_se401 *se401 = urb->context;
- int length = urb->actual_length;
-
- /* ohoh... */
- if (!se401->streaming)
- return;
-
- if (!se401->dev) {
- dev_info(&urb->dev->dev, "device vapourished\n");
- return;
- }
-
- /* 0 sized packets happen if we are to fast, but sometimes the camera
- keeps sending them forever...
- */
- if (length && !urb->status) {
- se401->nullpackets = 0;
- switch (se401->scratch[se401->scratch_next].state) {
- case BUFFER_READY:
- case BUFFER_BUSY:
- se401->dropped++;
- break;
- case BUFFER_UNUSED:
- memcpy(se401->scratch[se401->scratch_next].data,
- (unsigned char *)urb->transfer_buffer, length);
- se401->scratch[se401->scratch_next].state
- = BUFFER_READY;
- se401->scratch[se401->scratch_next].offset
- = se401->bayeroffset;
- se401->scratch[se401->scratch_next].length = length;
- if (waitqueue_active(&se401->wq))
- wake_up_interruptible(&se401->wq);
- se401->scratch_overflow = 0;
- se401->scratch_next++;
- if (se401->scratch_next >= SE401_NUMSCRATCH)
- se401->scratch_next = 0;
- break;
- }
- se401->bayeroffset += length;
- if (se401->bayeroffset >= se401->cheight * se401->cwidth)
- se401->bayeroffset = 0;
- } else {
- se401->nullpackets++;
- if (se401->nullpackets > SE401_MAX_NULLPACKETS)
- if (waitqueue_active(&se401->wq))
- wake_up_interruptible(&se401->wq);
- }
-
- /* Resubmit urb for new data */
- urb->status = 0;
- urb->dev = se401->dev;
- if (usb_submit_urb(urb, GFP_KERNEL))
- dev_info(&urb->dev->dev, "urb burned down\n");
- return;
-}
-
-static void se401_send_size(struct usb_se401 *se401, int width, int height)
-{
- int i = 0;
- int mode = 0x03; /* No compression */
- int sendheight = height;
- int sendwidth = width;
-
- /* JangGu compression can only be used with the camera supported sizes,
- but bayer seems to work with any size that fits on the sensor.
- We check if we can use compression with the current size with either
- 4 or 16 times subcapturing, if not we use uncompressed bayer data
- but this will result in cutouts of the maximum size....
- */
- while (i < se401->sizes && !(se401->width[i] == width &&
- se401->height[i] == height))
- i++;
- while (i < se401->sizes) {
- if (se401->width[i] == width * 2 &&
- se401->height[i] == height * 2) {
- sendheight = se401->height[i];
- sendwidth = se401->width[i];
- mode = 0x40;
- }
- if (se401->width[i] == width * 4 &&
- se401->height[i] == height * 4) {
- sendheight = se401->height[i];
- sendwidth = se401->width[i];
- mode = 0x42;
- }
- i++;
- }
-
- se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
- se401_set_feature(se401, SE401_OPERATINGMODE, mode);
-
- if (mode == 0x03)
- se401->format = FMT_BAYER;
- else
- se401->format = FMT_JANGGU;
-}
-
-/*
- In this function se401_send_pict is called several times,
- for some reason (depending on the state of the sensor and the phase of
- the moon :) doing this only in either place doesn't always work...
-*/
-static int se401_start_stream(struct usb_se401 *se401)
-{
- struct urb *urb;
- int err = 0, i;
- se401->streaming = 1;
-
- se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
-
- /* Set picture settings */
- /* windowed + pix intg */
- se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);
- se401_send_pict(se401);
-
- se401_send_size(se401, se401->cwidth, se401->cheight);
-
- se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE,
- 0, NULL, 0);
-
- /* Do some memory allocation */
- for (i = 0; i < SE401_NUMFRAMES; i++) {
- se401->frame[i].data = se401->fbuf + i * se401->maxframesize;
- se401->frame[i].curpix = 0;
- }
- for (i = 0; i < SE401_NUMSBUF; i++) {
- se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
- if (!se401->sbuf[i].data) {
- for (i = i - 1; i >= 0; i--) {
- kfree(se401->sbuf[i].data);
- se401->sbuf[i].data = NULL;
- }
- return -ENOMEM;
- }
- }
-
- se401->bayeroffset = 0;
- se401->scratch_next = 0;
- se401->scratch_use = 0;
- se401->scratch_overflow = 0;
- for (i = 0; i < SE401_NUMSCRATCH; i++) {
- se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
- if (!se401->scratch[i].data) {
- for (i = i - 1; i >= 0; i--) {
- kfree(se401->scratch[i].data);
- se401->scratch[i].data = NULL;
- }
- goto nomem_sbuf;
- }
- se401->scratch[i].state = BUFFER_UNUSED;
- }
-
- for (i = 0; i < SE401_NUMSBUF; i++) {
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- for (i = i - 1; i >= 0; i--) {
- usb_kill_urb(se401->urb[i]);
- usb_free_urb(se401->urb[i]);
- se401->urb[i] = NULL;
- }
- goto nomem_scratch;
- }
-
- usb_fill_bulk_urb(urb, se401->dev,
- usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
- se401->sbuf[i].data, SE401_PACKETSIZE,
- se401_video_irq,
- se401);
-
- se401->urb[i] = urb;
-
- err = usb_submit_urb(se401->urb[i], GFP_KERNEL);
- if (err)
- err("urb burned down");
- }
-
- se401->framecount = 0;
-
- return 0;
-
- nomem_scratch:
- for (i = 0; i < SE401_NUMSCRATCH; i++) {
- kfree(se401->scratch[i].data);
- se401->scratch[i].data = NULL;
- }
- nomem_sbuf:
- for (i = 0; i < SE401_NUMSBUF; i++) {
- kfree(se401->sbuf[i].data);
- se401->sbuf[i].data = NULL;
- }
- return -ENOMEM;
-}
-
-static int se401_stop_stream(struct usb_se401 *se401)
-{
- int i;
-
- if (!se401->streaming || !se401->dev)
- return 1;
-
- se401->streaming = 0;
-
- se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
-
- se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
-
- for (i = 0; i < SE401_NUMSBUF; i++)
- if (se401->urb[i]) {
- usb_kill_urb(se401->urb[i]);
- usb_free_urb(se401->urb[i]);
- se401->urb[i] = NULL;
- kfree(se401->sbuf[i].data);
- }
- for (i = 0; i < SE401_NUMSCRATCH; i++) {
- kfree(se401->scratch[i].data);
- se401->scratch[i].data = NULL;
- }
-
- return 0;
-}
-
-static int se401_set_size(struct usb_se401 *se401, int width, int height)
-{
- int wasstreaming = se401->streaming;
- /* Check to see if we need to change */
- if (se401->cwidth == width && se401->cheight == height)
- return 0;
-
- /* Check for a valid mode */
- if (!width || !height)
- return 1;
- if ((width & 1) || (height & 1))
- return 1;
- if (width > se401->width[se401->sizes-1])
- return 1;
- if (height > se401->height[se401->sizes-1])
- return 1;
-
- /* Stop a current stream and start it again at the new size */
- if (wasstreaming)
- se401_stop_stream(se401);
- se401->cwidth = width;
- se401->cheight = height;
- if (wasstreaming)
- se401_start_stream(se401);
- return 0;
-}
-
-
-/****************************************************************************
- *
- * Video Decoding
- *
- ***************************************************************************/
-
-/*
- This shouldn't really be done in a v4l driver....
- But it does make the image look a lot more usable.
- Basically it lifts the dark pixels more than the light pixels.
-*/
-static inline void enhance_picture(unsigned char *frame, int len)
-{
- while (len--) {
- *frame = (((*frame^255)*(*frame^255))/255)^255;
- frame++;
- }
-}
-
-static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
-{
- struct se401_frame *frame = &se401->frame[se401->curframe];
- int linelength = se401->cwidth * 3;
-
- if (frame->curlinepix >= linelength) {
- frame->curlinepix = 0;
- frame->curline += linelength;
- }
-
- /* First three are absolute, all others relative.
- * Format is rgb from right to left (mirrorred image),
- * we flip it to get bgr from left to right. */
- if (frame->curlinepix < 3)
- *(frame->curline-frame->curlinepix) = 1 + data * 4;
- else
- *(frame->curline-frame->curlinepix) =
- *(frame->curline-frame->curlinepix + 3) + data * 4;
- frame->curlinepix++;
-}
-
-static inline void decode_JangGu_vlc(struct usb_se401 *se401,
- unsigned char *data, int bit_exp, int packetlength)
-{
- int pos = 0;
- int vlc_cod = 0;
- int vlc_size = 0;
- int vlc_data = 0;
- int bit_cur;
- int bit;
- data += 4;
- while (pos < packetlength) {
- bit_cur = 8;
- while (bit_cur && bit_exp) {
- bit = ((*data) >> (bit_cur-1))&1;
- if (!vlc_cod) {
- if (bit) {
- vlc_size++;
- } else {
- if (!vlc_size)
- decode_JangGu_integrate(se401, 0);
- else {
- vlc_cod = 2;
- vlc_data = 0;
- }
- }
- } else {
- if (vlc_cod == 2) {
- if (!bit)
- vlc_data = -(1 << vlc_size) + 1;
- vlc_cod--;
- }
- vlc_size--;
- vlc_data += bit << vlc_size;
- if (!vlc_size) {
- decode_JangGu_integrate(se401, vlc_data);
- vlc_cod = 0;
- }
- }
- bit_cur--;
- bit_exp--;
- }
- pos++;
- data++;
- }
-}
-
-static inline void decode_JangGu(struct usb_se401 *se401,
- struct se401_scratch *buffer)
-{
- unsigned char *data = buffer->data;
- int len = buffer->length;
- int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size;
- int datapos = 0;
-
- /* New image? */
- if (!se401->frame[se401->curframe].curpix) {
- se401->frame[se401->curframe].curlinepix = 0;
- se401->frame[se401->curframe].curline =
- se401->frame[se401->curframe].data+
- se401->cwidth * 3 - 1;
- if (se401->frame[se401->curframe].grabstate == FRAME_READY)
- se401->frame[se401->curframe].grabstate = FRAME_GRABBING;
- se401->vlcdatapos = 0;
- }
- while (datapos < len) {
- size = 1024 - se401->vlcdatapos;
- if (size+datapos > len)
- size = len-datapos;
- memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
- se401->vlcdatapos += size;
- packetlength = 0;
- if (se401->vlcdatapos >= 4) {
- bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8);
- pix_exp = se401->vlcdata[1] +
- ((se401->vlcdata[0] & 0x3f) << 8);
- frameinfo = se401->vlcdata[0] & 0xc0;
- packetlength = ((bit_exp + 47) >> 4) << 1;
- if (packetlength > 1024) {
- se401->vlcdatapos = 0;
- datapos = len;
- packetlength = 0;
- se401->error++;
- se401->frame[se401->curframe].curpix = 0;
- }
- }
- if (packetlength && se401->vlcdatapos >= packetlength) {
- decode_JangGu_vlc(se401, se401->vlcdata, bit_exp,
- packetlength);
- se401->frame[se401->curframe].curpix += pix_exp * 3;
- datapos += size-(se401->vlcdatapos-packetlength);
- se401->vlcdatapos = 0;
- if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) {
- if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) {
- if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) {
- se401->frame[se401->curframe].grabstate = FRAME_DONE;
- se401->framecount++;
- se401->readcount++;
- }
- if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY)
- se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1);
- } else
- se401->error++;
- se401->frame[se401->curframe].curpix = 0;
- datapos = len;
- }
- } else
- datapos += size;
- }
-}
-
-static inline void decode_bayer(struct usb_se401 *se401,
- struct se401_scratch *buffer)
-{
- unsigned char *data = buffer->data;
- int len = buffer->length;
- int offset = buffer->offset;
- int datasize = se401->cwidth * se401->cheight;
- struct se401_frame *frame = &se401->frame[se401->curframe];
- unsigned char *framedata = frame->data, *curline, *nextline;
- int width = se401->cwidth;
- int blineoffset = 0, bline;
- int linelength = width * 3, i;
-
-
- if (frame->curpix == 0) {
- if (frame->grabstate == FRAME_READY)
- frame->grabstate = FRAME_GRABBING;
-
- frame->curline = framedata + linelength;
- frame->curlinepix = 0;
- }
-
- if (offset != frame->curpix) {
- /* Regard frame as lost :( */
- frame->curpix = 0;
- se401->error++;
- return;
- }
-
- /* Check if we have to much data */
- if (frame->curpix + len > datasize)
- len = datasize-frame->curpix;
-
- if (se401->cheight % 4)
- blineoffset = 1;
- bline = frame->curpix / se401->cwidth+blineoffset;
-
- curline = frame->curline;
- nextline = curline + linelength;
- if (nextline >= framedata+datasize * 3)
- nextline = curline;
- while (len) {
- if (frame->curlinepix >= width) {
- frame->curlinepix -= width;
- bline = frame->curpix / width + blineoffset;
- curline += linelength*2;
- nextline += linelength*2;
- if (curline >= framedata+datasize * 3) {
- frame->curlinepix++;
- curline -= 3;
- nextline -= 3;
- len--;
- data++;
- frame->curpix++;
- }
- if (nextline >= framedata+datasize*3)
- nextline = curline;
- }
- if (bline & 1) {
- if (frame->curlinepix & 1) {
- *(curline + 2) = *data;
- *(curline - 1) = *data;
- *(nextline + 2) = *data;
- *(nextline - 1) = *data;
- } else {
- *(curline + 1) =
- (*(curline + 1) + *data) / 2;
- *(curline-2) =
- (*(curline - 2) + *data) / 2;
- *(nextline + 1) = *data;
- *(nextline - 2) = *data;
- }
- } else {
- if (frame->curlinepix & 1) {
- *(curline + 1) =
- (*(curline + 1) + *data) / 2;
- *(curline - 2) =
- (*(curline - 2) + *data) / 2;
- *(nextline + 1) = *data;
- *(nextline - 2) = *data;
- } else {
- *curline = *data;
- *(curline - 3) = *data;
- *nextline = *data;
- *(nextline - 3) = *data;
- }
- }
- frame->curlinepix++;
- curline -= 3;
- nextline -= 3;
- len--;
- data++;
- frame->curpix++;
- }
- frame->curline = curline;
-
- if (frame->curpix >= datasize) {
- /* Fix the top line */
- framedata += linelength;
- for (i = 0; i < linelength; i++) {
- framedata--;
- *framedata = *(framedata + linelength);
- }
- /* Fix the left side (green is already present) */
- for (i = 0; i < se401->cheight; i++) {
- *framedata = *(framedata + 3);
- *(framedata + 1) = *(framedata + 4);
- *(framedata + 2) = *(framedata + 5);
- framedata += linelength;
- }
- frame->curpix = 0;
- frame->grabstate = FRAME_DONE;
- se401->framecount++;
- se401->readcount++;
- if (se401->frame[(se401->curframe + 1) &
- (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) {
- se401->curframe = (se401->curframe+1) &
- (SE401_NUMFRAMES-1);
- }
- }
-}
-
-static int se401_newframe(struct usb_se401 *se401, int framenr)
-{
- DECLARE_WAITQUEUE(wait, current);
- int errors = 0;
-
- while (se401->streaming &&
- (se401->frame[framenr].grabstate == FRAME_READY ||
- se401->frame[framenr].grabstate == FRAME_GRABBING)) {
- if (!se401->frame[framenr].curpix)
- errors++;
-
- wait_interruptible(
- se401->scratch[se401->scratch_use].state != BUFFER_READY,
- &se401->wq, &wait);
- if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
- se401->nullpackets = 0;
- dev_info(&se401->dev->dev,
- "too many null length packets, restarting capture\n");
- se401_stop_stream(se401);
- se401_start_stream(se401);
- } else {
- if (se401->scratch[se401->scratch_use].state !=
- BUFFER_READY) {
- se401->frame[framenr].grabstate = FRAME_ERROR;
- return -EIO;
- }
- se401->scratch[se401->scratch_use].state = BUFFER_BUSY;
- if (se401->format == FMT_JANGGU)
- decode_JangGu(se401,
- &se401->scratch[se401->scratch_use]);
- else
- decode_bayer(se401,
- &se401->scratch[se401->scratch_use]);
-
- se401->scratch[se401->scratch_use].state =
- BUFFER_UNUSED;
- se401->scratch_use++;
- if (se401->scratch_use >= SE401_NUMSCRATCH)
- se401->scratch_use = 0;
- if (errors > SE401_MAX_ERRORS) {
- errors = 0;
- dev_info(&se401->dev->dev,
- "too many errors, restarting capture\n");
- se401_stop_stream(se401);
- se401_start_stream(se401);
- }
- }
- }
-
- if (se401->frame[framenr].grabstate == FRAME_DONE)
- if (se401->enhance)
- enhance_picture(se401->frame[framenr].data,
- se401->cheight * se401->cwidth * 3);
- return 0;
-}
-
-static void usb_se401_remove_disconnected(struct usb_se401 *se401)
-{
- int i;
-
- se401->dev = NULL;
-
- for (i = 0; i < SE401_NUMSBUF; i++)
- if (se401->urb[i]) {
- usb_kill_urb(se401->urb[i]);
- usb_free_urb(se401->urb[i]);
- se401->urb[i] = NULL;
- kfree(se401->sbuf[i].data);
- }
-
- for (i = 0; i < SE401_NUMSCRATCH; i++)
- kfree(se401->scratch[i].data);
-
- if (se401->inturb) {
- usb_kill_urb(se401->inturb);
- usb_free_urb(se401->inturb);
- }
- dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name);
-
- /* Free the memory */
- kfree(se401->width);
- kfree(se401->height);
- kfree(se401);
-}
-
-
-
-/****************************************************************************
- *
- * Video4Linux
- *
- ***************************************************************************/
-
-
-static int se401_open(struct file *file)
-{
- struct video_device *dev = video_devdata(file);
- struct usb_se401 *se401 = (struct usb_se401 *)dev;
- int err = 0;
-
- mutex_lock(&se401->lock);
- if (se401->user) {
- mutex_unlock(&se401->lock);
- return -EBUSY;
- }
- se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
- if (se401->fbuf)
- file->private_data = dev;
- else
- err = -ENOMEM;
- se401->user = !err;
- mutex_unlock(&se401->lock);
-
- return err;
-}
-
-static int se401_close(struct file *file)
-{
- struct video_device *dev = file->private_data;
- struct usb_se401 *se401 = (struct usb_se401 *)dev;
- int i;
-
- rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
- if (se401->removed) {
- dev_info(&se401->dev->dev, "device unregistered\n");
- usb_se401_remove_disconnected(se401);
- } else {
- for (i = 0; i < SE401_NUMFRAMES; i++)
- se401->frame[i].grabstate = FRAME_UNUSED;
- if (se401->streaming)
- se401_stop_stream(se401);
- se401->user = 0;
- }
- file->private_data = NULL;
- return 0;
-}
-
-static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
- struct video_device *vdev = file->private_data;
- struct usb_se401 *se401 = (struct usb_se401 *)vdev;
-
- if (!se401->dev)
- return -EIO;
-
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability *b = arg;
- strcpy(b->name, se401->camera_name);
- b->type = VID_TYPE_CAPTURE;
- b->channels = 1;
- b->audios = 0;
- b->maxwidth = se401->width[se401->sizes-1];
- b->maxheight = se401->height[se401->sizes-1];
- b->minwidth = se401->width[0];
- b->minheight = se401->height[0];
- return 0;
- }
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
-
- if (v->channel != 0)
- return -EINVAL;
- v->flags = 0;
- v->tuners = 0;
- v->type = VIDEO_TYPE_CAMERA;
- strcpy(v->name, "Camera");
- return 0;
- }
- case VIDIOCSCHAN:
- {
- struct video_channel *v = arg;
-
- if (v->channel != 0)
- return -EINVAL;
- return 0;
- }
- case VIDIOCGPICT:
- {
- struct video_picture *p = arg;
-
- se401_get_pict(se401, p);
- return 0;
- }
- case VIDIOCSPICT:
- {
- struct video_picture *p = arg;
-
- if (se401_set_pict(se401, p))
- return -EINVAL;
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window *vw = arg;
-
- if (vw->flags)
- return -EINVAL;
- if (vw->clipcount)
- return -EINVAL;
- if (se401_set_size(se401, vw->width, vw->height))
- return -EINVAL;
- return 0;
- }
- case VIDIOCGWIN:
- {
- struct video_window *vw = arg;
-
- vw->x = 0; /* FIXME */
- vw->y = 0;
- vw->chromakey = 0;
- vw->flags = 0;
- vw->clipcount = 0;
- vw->width = se401->cwidth;
- vw->height = se401->cheight;
- return 0;
- }
- case VIDIOCGMBUF:
- {
- struct video_mbuf *vm = arg;
- int i;
-
- memset(vm, 0, sizeof(*vm));
- vm->size = SE401_NUMFRAMES * se401->maxframesize;
- vm->frames = SE401_NUMFRAMES;
- for (i = 0; i < SE401_NUMFRAMES; i++)
- vm->offsets[i] = se401->maxframesize * i;
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap *vm = arg;
-
- if (vm->format != VIDEO_PALETTE_RGB24)
- return -EINVAL;
- if (vm->frame >= SE401_NUMFRAMES)
- return -EINVAL;
- if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
- return -EBUSY;
-
- /* Is this according to the v4l spec??? */
- if (se401_set_size(se401, vm->width, vm->height))
- return -EINVAL;
- se401->frame[vm->frame].grabstate = FRAME_READY;
-
- if (!se401->streaming)
- se401_start_stream(se401);
-
- /* Set the picture properties */
- if (se401->framecount == 0)
- se401_send_pict(se401);
- /* Calibrate the reset level after a few frames. */
- if (se401->framecount % 20 == 1)
- se401_auto_resetlevel(se401);
-
- return 0;
- }
- case VIDIOCSYNC:
- {
- int *frame = arg;
- int ret = 0;
-
- if (*frame < 0 || *frame >= SE401_NUMFRAMES)
- return -EINVAL;
-
- ret = se401_newframe(se401, *frame);
- se401->frame[*frame].grabstate = FRAME_UNUSED;
- return ret;
- }
- case VIDIOCGFBUF:
- {
- struct video_buffer *vb = arg;
-
- memset(vb, 0, sizeof(*vb));
- return 0;
- }
- case VIDIOCKEY:
- return 0;
- case VIDIOCCAPTURE:
- return -EINVAL;
- case VIDIOCSFBUF:
- return -EINVAL;
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- return -EINVAL;
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- return -EINVAL;
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return -EINVAL;
- default:
- return -ENOIOCTLCMD;
- } /* end switch */
-
- return 0;
-}
-
-static long se401_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(file, cmd, arg, se401_do_ioctl);
-}
-
-static ssize_t se401_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- int realcount = count, ret = 0;
- struct video_device *dev = file->private_data;
- struct usb_se401 *se401 = (struct usb_se401 *)dev;
-
-
- if (se401->dev == NULL)
- return -EIO;
- if (realcount > se401->cwidth*se401->cheight*3)
- realcount = se401->cwidth*se401->cheight*3;
-
- /* Shouldn't happen: */
- if (se401->frame[0].grabstate == FRAME_GRABBING)
- return -EBUSY;
- se401->frame[0].grabstate = FRAME_READY;
- se401->frame[1].grabstate = FRAME_UNUSED;
- se401->curframe = 0;
-
- if (!se401->streaming)
- se401_start_stream(se401);
-
- /* Set the picture properties */
- if (se401->framecount == 0)
- se401_send_pict(se401);
- /* Calibrate the reset level after a few frames. */
- if (se401->framecount%20 == 1)
- se401_auto_resetlevel(se401);
-
- ret = se401_newframe(se401, 0);
-
- se401->frame[0].grabstate = FRAME_UNUSED;
- if (ret)
- return ret;
- if (copy_to_user(buf, se401->frame[0].data, realcount))
- return -EFAULT;
-
- return realcount;
-}
-
-static int se401_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct video_device *dev = file->private_data;
- struct usb_se401 *se401 = (struct usb_se401 *)dev;
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end-vma->vm_start;
- unsigned long page, pos;
-
- mutex_lock(&se401->lock);
-
- if (se401->dev == NULL) {
- mutex_unlock(&se401->lock);
- return -EIO;
- }
- if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1)
- & ~(PAGE_SIZE - 1))) {
- mutex_unlock(&se401->lock);
- return -EINVAL;
- }
- pos = (unsigned long)se401->fbuf;
- while (size > 0) {
- page = vmalloc_to_pfn((void *)pos);
- if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
- mutex_unlock(&se401->lock);
- return -EAGAIN;
- }
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
- mutex_unlock(&se401->lock);
-
- return 0;
-}
-
-static const struct v4l2_file_operations se401_fops = {
- .owner = THIS_MODULE,
- .open = se401_open,
- .release = se401_close,
- .read = se401_read,
- .mmap = se401_mmap,
- .ioctl = se401_ioctl,
-};
-static struct video_device se401_template = {
- .name = "se401 USB camera",
- .fops = &se401_fops,
- .release = video_device_release_empty,
-};
-
-
-
-/***************************/
-static int se401_init(struct usb_se401 *se401, int button)
-{
- int i = 0, rc;
- unsigned char cp[0x40];
- char temp[200];
- int slen;
-
- /* led on */
- se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
-
- /* get camera descriptor */
- rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0,
- cp, sizeof(cp));
- if (cp[1] != 0x41) {
- err("Wrong descriptor type");
- return 1;
- }
- slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]);
-
- se401->sizes = cp[4] + cp[5] * 256;
- se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
- if (!se401->width)
- return 1;
- se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
- if (!se401->height) {
- kfree(se401->width);
- return 1;
- }
- for (i = 0; i < se401->sizes; i++) {
- se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256;
- se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256;
- }
- slen += snprintf(temp + slen, 200 - slen, " Sizes:");
- for (i = 0; i < se401->sizes; i++) {
- slen += snprintf(temp + slen, 200 - slen,
- " %dx%d", se401->width[i], se401->height[i]);
- }
- dev_info(&se401->dev->dev, "%s\n", temp);
- se401->maxframesize = se401->width[se401->sizes-1] *
- se401->height[se401->sizes - 1] * 3;
-
- rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
- se401->cwidth = cp[0]+cp[1]*256;
- rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
- se401->cheight = cp[0]+cp[1]*256;
-
- if (!(cp[2] & SE401_FORMAT_BAYER)) {
- err("Bayer format not supported!");
- return 1;
- }
- /* set output mode (BAYER) */
- se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE,
- SE401_FORMAT_BAYER, NULL, 0);
-
- rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
- se401->brightness = cp[0]+cp[1]*256;
- /* some default values */
- se401->resetlevel = 0x2d;
- se401->rgain = 0x20;
- se401->ggain = 0x20;
- se401->bgain = 0x20;
- se401_set_exposure(se401, 20000);
- se401->palette = VIDEO_PALETTE_RGB24;
- se401->enhance = 1;
- se401->dropped = 0;
- se401->error = 0;
- se401->framecount = 0;
- se401->readcount = 0;
-
- /* Start interrupt transfers for snapshot button */
- if (button) {
- se401->inturb = usb_alloc_urb(0, GFP_KERNEL);
- if (!se401->inturb) {
- dev_info(&se401->dev->dev,
- "Allocation of inturb failed\n");
- return 1;
- }
- usb_fill_int_urb(se401->inturb, se401->dev,
- usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
- &se401->button, sizeof(se401->button),
- se401_button_irq,
- se401,
- 8
- );
- if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
- dev_info(&se401->dev->dev, "int urb burned down\n");
- return 1;
- }
- } else
- se401->inturb = NULL;
-
- /* Flash the led */
- se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
- se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
-
- return 0;
-}
-
-static int se401_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_interface_descriptor *interface;
- struct usb_se401 *se401;
- char *camera_name = NULL;
- int button = 1;
-
- /* We don't handle multi-config cameras */
- if (dev->descriptor.bNumConfigurations != 1)
- return -ENODEV;
-
- interface = &intf->cur_altsetting->desc;
-
- /* Is it an se401? */
- if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
- camera_name = "Endpoints/Aox SE401";
- } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
- camera_name = "Philips PCVC665K";
- } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
- camera_name = "Kensington VideoCAM 67014";
- } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
- camera_name = "Kensington VideoCAM 6701(5/7)";
- } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
- camera_name = "Kensington VideoCAM 67016";
- button = 0;
- } else
- return -ENODEV;
-
- /* Checking vendor/product should be enough, but what the hell */
- if (interface->bInterfaceClass != 0x00)
- return -ENODEV;
- if (interface->bInterfaceSubClass != 0x00)
- return -ENODEV;
-
- /* We found one */
- dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name);
-
- se401 = kzalloc(sizeof(*se401), GFP_KERNEL);
- if (se401 == NULL) {
- err("couldn't kmalloc se401 struct");
- return -ENOMEM;
- }
-
- se401->dev = dev;
- se401->iface = interface->bInterfaceNumber;
- se401->camera_name = camera_name;
-
- dev_info(&intf->dev, "firmware version: %02x\n",
- le16_to_cpu(dev->descriptor.bcdDevice) & 255);
-
- if (se401_init(se401, button)) {
- kfree(se401);
- return -EIO;
- }
-
- memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
- memcpy(se401->vdev.name, se401->camera_name,
- strlen(se401->camera_name));
- init_waitqueue_head(&se401->wq);
- mutex_init(&se401->lock);
- wmb();
-
- if (video_register_device(&se401->vdev,
- VFL_TYPE_GRABBER, video_nr) < 0) {
- kfree(se401);
- err("video_register_device failed");
- return -EIO;
- }
- dev_info(&intf->dev, "registered new video device: %s\n",
- video_device_node_name(&se401->vdev));
-
- usb_set_intfdata(intf, se401);
- return 0;
-}
-
-static void se401_disconnect(struct usb_interface *intf)
-{
- struct usb_se401 *se401 = usb_get_intfdata(intf);
-
- usb_set_intfdata(intf, NULL);
- if (se401) {
- video_unregister_device(&se401->vdev);
- if (!se401->user)
- usb_se401_remove_disconnected(se401);
- else {
- se401->frame[0].grabstate = FRAME_ERROR;
- se401->frame[0].grabstate = FRAME_ERROR;
-
- se401->streaming = 0;
-
- wake_up_interruptible(&se401->wq);
- se401->removed = 1;
- }
- }
-}
-
-static struct usb_driver se401_driver = {
- .name = "se401",
- .id_table = device_table,
- .probe = se401_probe,
- .disconnect = se401_disconnect,
-};
-
-
-
-/****************************************************************************
- *
- * Module routines
- *
- ***************************************************************************/
-
-static int __init usb_se401_init(void)
-{
- printk(KERN_INFO "SE401 usb camera driver version %s registering\n",
- version);
- if (flickerless)
- if (flickerless != 50 && flickerless != 60) {
- printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n");
- return -1;
- }
- return usb_register(&se401_driver);
-}
-
-static void __exit usb_se401_exit(void)
-{
- usb_deregister(&se401_driver);
- printk(KERN_INFO "SE401 driver deregistered\frame");
-}
-
-module_init(usb_se401_init);
-module_exit(usb_se401_exit);
diff --git a/drivers/staging/se401/se401.h b/drivers/staging/se401/se401.h
deleted file mode 100644
index 2758f4716c3d..000000000000
--- a/drivers/staging/se401/se401.h
+++ /dev/null
@@ -1,236 +0,0 @@
-
-#ifndef __LINUX_se401_H
-#define __LINUX_se401_H
-
-#include <linux/uaccess.h>
-#include "videodev.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
-
-#define se401_DEBUG /* Turn on debug messages */
-
-#ifdef se401_DEBUG
-# define PDEBUG(level, fmt, args...) \
-if (debug >= level) \
- info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
-#else
-# define PDEBUG(level, fmt, args...) do {} while (0)
-#endif
-
-/* An almost drop-in replacement for sleep_on_interruptible */
-#define wait_interruptible(test, queue, wait) \
-{ \
- add_wait_queue(queue, wait); \
- set_current_state(TASK_INTERRUPTIBLE); \
- if (test) \
- schedule(); \
- remove_wait_queue(queue, wait); \
- set_current_state(TASK_RUNNING); \
- if (signal_pending(current)) \
- break; \
-}
-
-#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06
-#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41
-#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42
-#define SE401_REQ_CAPTURE_FRAME 0x43
-#define SE401_REQ_GET_BRT 0x44
-#define SE401_REQ_SET_BRT 0x45
-#define SE401_REQ_GET_WIDTH 0x4c
-#define SE401_REQ_SET_WIDTH 0x4d
-#define SE401_REQ_GET_HEIGHT 0x4e
-#define SE401_REQ_SET_HEIGHT 0x4f
-#define SE401_REQ_GET_OUTPUT_MODE 0x50
-#define SE401_REQ_SET_OUTPUT_MODE 0x51
-#define SE401_REQ_GET_EXT_FEATURE 0x52
-#define SE401_REQ_SET_EXT_FEATURE 0x53
-#define SE401_REQ_CAMERA_POWER 0x56
-#define SE401_REQ_LED_CONTROL 0x57
-#define SE401_REQ_BIOS 0xff
-
-#define SE401_BIOS_READ 0x07
-
-#define SE401_FORMAT_BAYER 0x40
-
-/* Hyundai hv7131b registers
- 7121 and 7141 should be the same (haven't really checked...) */
-/* Mode registers: */
-#define HV7131_REG_MODE_A 0x00
-#define HV7131_REG_MODE_B 0x01
-#define HV7131_REG_MODE_C 0x02
-/* Frame registers: */
-#define HV7131_REG_FRSU 0x10
-#define HV7131_REG_FRSL 0x11
-#define HV7131_REG_FCSU 0x12
-#define HV7131_REG_FCSL 0x13
-#define HV7131_REG_FWHU 0x14
-#define HV7131_REG_FWHL 0x15
-#define HV7131_REG_FWWU 0x16
-#define HV7131_REG_FWWL 0x17
-/* Timing registers: */
-#define HV7131_REG_THBU 0x20
-#define HV7131_REG_THBL 0x21
-#define HV7131_REG_TVBU 0x22
-#define HV7131_REG_TVBL 0x23
-#define HV7131_REG_TITU 0x25
-#define HV7131_REG_TITM 0x26
-#define HV7131_REG_TITL 0x27
-#define HV7131_REG_TMCD 0x28
-/* Adjust Registers: */
-#define HV7131_REG_ARLV 0x30
-#define HV7131_REG_ARCG 0x31
-#define HV7131_REG_AGCG 0x32
-#define HV7131_REG_ABCG 0x33
-#define HV7131_REG_APBV 0x34
-#define HV7131_REG_ASLP 0x54
-/* Offset Registers: */
-#define HV7131_REG_OFSR 0x50
-#define HV7131_REG_OFSG 0x51
-#define HV7131_REG_OFSB 0x52
-/* REset level statistics registers: */
-#define HV7131_REG_LOREFNOH 0x57
-#define HV7131_REG_LOREFNOL 0x58
-#define HV7131_REG_HIREFNOH 0x59
-#define HV7131_REG_HIREFNOL 0x5a
-
-/* se401 registers */
-#define SE401_OPERATINGMODE 0x2000
-
-
-/* size of usb transfers */
-#define SE401_PACKETSIZE 4096
-/* number of queued bulk transfers to use, should be about 8 */
-#define SE401_NUMSBUF 1
-/* read the usb specs for this one :) */
-#define SE401_VIDEO_ENDPOINT 1
-#define SE401_BUTTON_ENDPOINT 2
-/* number of frames supported by the v4l part */
-#define SE401_NUMFRAMES 2
-/* scratch buffers for passing data to the decoders */
-#define SE401_NUMSCRATCH 32
-/* maximum amount of data in a JangGu packet */
-#define SE401_VLCDATALEN 1024
-/* number of nul sized packets to receive before kicking the camera */
-#define SE401_MAX_NULLPACKETS 4000
-/* number of decoding errors before kicking the camera */
-#define SE401_MAX_ERRORS 200
-
-struct usb_device;
-
-struct se401_sbuf {
- unsigned char *data;
-};
-
-enum {
- FRAME_UNUSED, /* Unused (no MCAPTURE) */
- FRAME_READY, /* Ready to start grabbing */
- FRAME_GRABBING, /* In the process of being grabbed into */
- FRAME_DONE, /* Finished grabbing, but not been synced yet */
- FRAME_ERROR, /* Something bad happened while processing */
-};
-
-enum {
- FMT_BAYER,
- FMT_JANGGU,
-};
-
-enum {
- BUFFER_UNUSED,
- BUFFER_READY,
- BUFFER_BUSY,
- BUFFER_DONE,
-};
-
-struct se401_scratch {
- unsigned char *data;
- volatile int state;
- int offset;
- int length;
-};
-
-struct se401_frame {
- unsigned char *data; /* Frame buffer */
-
- volatile int grabstate; /* State of grabbing */
-
- unsigned char *curline;
- int curlinepix;
- int curpix;
-};
-
-struct usb_se401 {
- struct video_device vdev;
-
- /* Device structure */
- struct usb_device *dev;
-
- unsigned char iface;
-
- char *camera_name;
-
- int change;
- int brightness;
- int hue;
- int rgain;
- int ggain;
- int bgain;
- int expose_h;
- int expose_m;
- int expose_l;
- int resetlevel;
-
- int enhance;
-
- int format;
- int sizes;
- int *width;
- int *height;
- int cwidth; /* current width */
- int cheight; /* current height */
- int palette;
- int maxframesize;
- int cframesize; /* current framesize */
-
- struct mutex lock;
- int user; /* user count for exclusive use */
- int removed; /* device disconnected */
-
- int streaming; /* Are we streaming video? */
-
- char *fbuf; /* Videodev buffer area */
-
- struct urb *urb[SE401_NUMSBUF];
- struct urb *inturb;
-
- int button;
- int buttonpressed;
-
- int curframe; /* Current receiving frame */
- struct se401_frame frame[SE401_NUMFRAMES];
- int readcount;
- int framecount;
- int error;
- int dropped;
-
- int scratch_next;
- int scratch_use;
- int scratch_overflow;
- struct se401_scratch scratch[SE401_NUMSCRATCH];
-
- /* Decoder specific data: */
- unsigned char vlcdata[SE401_VLCDATALEN];
- int vlcdatapos;
- int bayeroffset;
-
- struct se401_sbuf sbuf[SE401_NUMSBUF];
-
- wait_queue_head_t wq; /* Processes waiting */
-
- int nullpackets;
-};
-
-
-
-#endif
-
diff --git a/drivers/staging/se401/videodev.h b/drivers/staging/se401/videodev.h
deleted file mode 100644
index f11efbef1c05..000000000000
--- a/drivers/staging/se401/videodev.h
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Video for Linux version 1 - OBSOLETE
- *
- * Header file for v4l1 drivers and applications, for
- * Linux kernels 2.2.x or 2.4.x.
- *
- * Provides header for legacy drivers and applications
- *
- * See http://linuxtv.org for more info
- *
- */
-#ifndef __LINUX_VIDEODEV_H
-#define __LINUX_VIDEODEV_H
-
-#include <linux/types.h>
-#include <linux/ioctl.h>
-#include <linux/videodev2.h>
-
-#define VID_TYPE_CAPTURE 1 /* Can capture */
-#define VID_TYPE_TUNER 2 /* Can tune */
-#define VID_TYPE_TELETEXT 4 /* Does teletext */
-#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
-#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
-#define VID_TYPE_CLIPPING 32 /* Can clip */
-#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
-#define VID_TYPE_SCALES 128 /* Scalable */
-#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
-#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
-#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */
-#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */
-#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */
-#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */
-
-struct video_capability
-{
- char name[32];
- int type;
- int channels; /* Num channels */
- int audios; /* Num audio devices */
- int maxwidth; /* Supported width */
- int maxheight; /* And height */
- int minwidth; /* Supported width */
- int minheight; /* And height */
-};
-
-
-struct video_channel
-{
- int channel;
- char name[32];
- int tuners;
- __u32 flags;
-#define VIDEO_VC_TUNER 1 /* Channel has a tuner */
-#define VIDEO_VC_AUDIO 2 /* Channel has audio */
- __u16 type;
-#define VIDEO_TYPE_TV 1
-#define VIDEO_TYPE_CAMERA 2
- __u16 norm; /* Norm set by channel */
-};
-
-struct video_tuner
-{
- int tuner;
- char name[32];
- unsigned long rangelow, rangehigh; /* Tuner range */
- __u32 flags;
-#define VIDEO_TUNER_PAL 1
-#define VIDEO_TUNER_NTSC 2
-#define VIDEO_TUNER_SECAM 4
-#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */
-#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */
-#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */
-#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */
-#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */
- __u16 mode; /* PAL/NTSC/SECAM/OTHER */
-#define VIDEO_MODE_PAL 0
-#define VIDEO_MODE_NTSC 1
-#define VIDEO_MODE_SECAM 2
-#define VIDEO_MODE_AUTO 3
- __u16 signal; /* Signal strength 16bit scale */
-};
-
-struct video_picture
-{
- __u16 brightness;
- __u16 hue;
- __u16 colour;
- __u16 contrast;
- __u16 whiteness; /* Black and white only */
- __u16 depth; /* Capture depth */
- __u16 palette; /* Palette in use */
-#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */
-#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */
-#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */
-#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */
-#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */
-#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */
-#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */
-#define VIDEO_PALETTE_YUYV 8
-#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */
-#define VIDEO_PALETTE_YUV420 10
-#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */
-#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */
-#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */
-#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */
-#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */
-#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */
-#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */
-#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */
-};
-
-struct video_audio
-{
- int audio; /* Audio channel */
- __u16 volume; /* If settable */
- __u16 bass, treble;
- __u32 flags;
-#define VIDEO_AUDIO_MUTE 1
-#define VIDEO_AUDIO_MUTABLE 2
-#define VIDEO_AUDIO_VOLUME 4
-#define VIDEO_AUDIO_BASS 8
-#define VIDEO_AUDIO_TREBLE 16
-#define VIDEO_AUDIO_BALANCE 32
- char name[16];
-#define VIDEO_SOUND_MONO 1
-#define VIDEO_SOUND_STEREO 2
-#define VIDEO_SOUND_LANG1 4
-#define VIDEO_SOUND_LANG2 8
- __u16 mode;
- __u16 balance; /* Stereo balance */
- __u16 step; /* Step actual volume uses */
-};
-
-struct video_clip
-{
- __s32 x,y;
- __s32 width, height;
- struct video_clip *next; /* For user use/driver use only */
-};
-
-struct video_window
-{
- __u32 x,y; /* Position of window */
- __u32 width,height; /* Its size */
- __u32 chromakey;
- __u32 flags;
- struct video_clip __user *clips; /* Set only */
- int clipcount;
-#define VIDEO_WINDOW_INTERLACE 1
-#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */
-#define VIDEO_CLIP_BITMAP -1
-/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
-#define VIDEO_CLIPMAP_SIZE (128 * 625)
-};
-
-struct video_capture
-{
- __u32 x,y; /* Offsets into image */
- __u32 width, height; /* Area to capture */
- __u16 decimation; /* Decimation divider */
- __u16 flags; /* Flags for capture */
-#define VIDEO_CAPTURE_ODD 0 /* Temporal */
-#define VIDEO_CAPTURE_EVEN 1
-};
-
-struct video_buffer
-{
- void *base;
- int height,width;
- int depth;
- int bytesperline;
-};
-
-struct video_mmap
-{
- unsigned int frame; /* Frame (0 - n) for double buffer */
- int height,width;
- unsigned int format; /* should be VIDEO_PALETTE_* */
-};
-
-struct video_key
-{
- __u8 key[8];
- __u32 flags;
-};
-
-struct video_mbuf
-{
- int size; /* Total memory to map */
- int frames; /* Frames */
- int offsets[VIDEO_MAX_FRAME];
-};
-
-#define VIDEO_NO_UNIT (-1)
-
-struct video_unit
-{
- int video; /* Video minor */
- int vbi; /* VBI minor */
- int radio; /* Radio minor */
- int audio; /* Audio minor */
- int teletext; /* Teletext minor */
-};
-
-struct vbi_format {
- __u32 sampling_rate; /* in Hz */
- __u32 samples_per_line;
- __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */
- __s32 start[2]; /* starting line for each frame */
- __u32 count[2]; /* count of lines for each frame */
- __u32 flags;
-#define VBI_UNSYNC 1 /* can distingues between top/bottom field */
-#define VBI_INTERLACED 2 /* lines are interlaced */
-};
-
-/* video_info is biased towards hardware mpeg encode/decode */
-/* but it could apply generically to any hardware compressor/decompressor */
-struct video_info
-{
- __u32 frame_count; /* frames output since decode/encode began */
- __u32 h_size; /* current unscaled horizontal size */
- __u32 v_size; /* current unscaled veritcal size */
- __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */
- __u32 picture_type; /* current picture type */
- __u32 temporal_reference; /* current temporal reference */
- __u8 user_data[256]; /* user data last found in compressed stream */
- /* user_data[0] contains user data flags, user_data[1] has count */
-};
-
-/* generic structure for setting playback modes */
-struct video_play_mode
-{
- int mode;
- int p1;
- int p2;
-};
-
-/* for loading microcode / fpga programming */
-struct video_code
-{
- char loadwhat[16]; /* name or tag of file being passed */
- int datasize;
- __u8 *data;
-};
-
-#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */
-#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */
-#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */
-#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */
-#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */
-#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */
-#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */
-#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */
-#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */
-#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
-#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */
-#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */
-#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
-#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */
-#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */
-#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */
-#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */
-#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */
-#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */
-#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */
-#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */
-#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */
-#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */
-#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */
-#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */
-#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */
-#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */
-#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */
-#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */
-
-
-#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */
-
-/* VIDIOCSWRITEMODE */
-#define VID_WRITE_MPEG_AUD 0
-#define VID_WRITE_MPEG_VID 1
-#define VID_WRITE_OSD 2
-#define VID_WRITE_TTX 3
-#define VID_WRITE_CC 4
-#define VID_WRITE_MJPEG 5
-
-/* VIDIOCSPLAYMODE */
-#define VID_PLAY_VID_OUT_MODE 0
- /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
-#define VID_PLAY_GENLOCK 1
- /* p1: 0 = OFF, 1 = ON */
- /* p2: GENLOCK FINE DELAY value */
-#define VID_PLAY_NORMAL 2
-#define VID_PLAY_PAUSE 3
-#define VID_PLAY_SINGLE_FRAME 4
-#define VID_PLAY_FAST_FORWARD 5
-#define VID_PLAY_SLOW_MOTION 6
-#define VID_PLAY_IMMEDIATE_NORMAL 7
-#define VID_PLAY_SWITCH_CHANNELS 8
-#define VID_PLAY_FREEZE_FRAME 9
-#define VID_PLAY_STILL_MODE 10
-#define VID_PLAY_MASTER_MODE 11
- /* p1: see below */
-#define VID_PLAY_MASTER_NONE 1
-#define VID_PLAY_MASTER_VIDEO 2
-#define VID_PLAY_MASTER_AUDIO 3
-#define VID_PLAY_ACTIVE_SCANLINES 12
- /* p1 = first active; p2 = last active */
-#define VID_PLAY_RESET 13
-#define VID_PLAY_END_MARK 14
-
-#endif /* __LINUX_VIDEODEV_H */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c
index 184cc505ed86..acb03172a887 100644
--- a/drivers/staging/tm6000/tm6000-alsa.c
+++ b/drivers/staging/tm6000/tm6000-alsa.c
@@ -76,14 +76,11 @@ MODULE_PARM_DESC(debug, "enable debug messages");
static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
{
struct tm6000_core *core = chip->core;
- int val;
dprintk(1, "Starting audio DMA\n");
/* Enables audio */
- val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
- val |= 0x20;
- tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40);
tm6000_set_audio_bitrate(core, 48000);
@@ -98,13 +95,11 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
{
struct tm6000_core *core = chip->core;
- int val;
+
dprintk(1, "Stopping audio DMA\n");
- /* Enables audio */
- val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
- val &= ~0x20;
- tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+ /* Disables audio */
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40);
tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0);
diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c
index 455038bdfc9f..88144a12745d 100644
--- a/drivers/staging/tm6000/tm6000-cards.c
+++ b/drivers/staging/tm6000/tm6000-cards.c
@@ -50,6 +50,9 @@
#define TM6010_BOARD_BEHOLD_VOYAGER 11
#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
#define TM6010_BOARD_TWINHAN_TU501 13
+#define TM6010_BOARD_BEHOLD_WANDER_LITE 14
+#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
+#define TM5600_BOARD_TERRATEC_GRABSTER 16
#define TM6000_MAXBOARDS 16
static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
@@ -63,6 +66,8 @@ struct tm6000_board {
char *name;
struct tm6000_capabilities caps;
+ enum tm6000_inaudio aradio;
+ enum tm6000_inaudio avideo;
enum tm6000_devtype type; /* variant of the chipset */
int tuner_type; /* type of the tuner */
@@ -227,6 +232,8 @@ struct tm6000_board tm6000_boards[] = {
.tuner_addr = 0xc2 >> 1,
.demod_addr = 0x1e >> 1,
.type = TM6010,
+ .avideo = TM6000_AIP_SIF1,
+ .aradio = TM6000_AIP_LINE1,
.caps = {
.has_tuner = 1,
.has_dvb = 1,
@@ -245,6 +252,8 @@ struct tm6000_board tm6000_boards[] = {
.tuner_type = TUNER_XC5000,
.tuner_addr = 0xc2 >> 1,
.type = TM6010,
+ .avideo = TM6000_AIP_SIF1,
+ .aradio = TM6000_AIP_LINE1,
.caps = {
.has_tuner = 1,
.has_dvb = 0,
@@ -281,6 +290,11 @@ struct tm6000_board tm6000_boards[] = {
},
.ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
},
+ [TM5600_BOARD_TERRATEC_GRABSTER] = {
+ .name = "Terratec Grabster AV 150/250 MX",
+ .type = TM5600,
+ .tuner_type = TUNER_ABSENT,
+ },
[TM6010_BOARD_TWINHAN_TU501] = {
.name = "Twinhan TU501(704D1)",
.tuner_type = TUNER_XC2028, /* has a XC3028 */
@@ -303,7 +317,45 @@ struct tm6000_board tm6000_boards[] = {
.dvb_led = TM6010_GPIO_5,
.ir = TM6010_GPIO_0,
},
- }
+ },
+ [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
+ .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .avideo = TM6000_AIP_SIF1,
+ .aradio = TM6000_AIP_LINE1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .demod_reset = TM6010_GPIO_1,
+ .power_led = TM6010_GPIO_6,
+ },
+ },
+ [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
+ .name = "Beholder Voyager Lite TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .type = TM6010,
+ .avideo = TM6000_AIP_SIF1,
+ .aradio = TM6000_AIP_LINE1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 0,
+ .has_zl10353 = 0,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .power_led = TM6010_GPIO_6,
+ },
+ },
};
/* table of devices that work with this driver */
@@ -321,10 +373,13 @@ struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
{ USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
{ USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
+ { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
{ USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
{ USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
{ USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
{ USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+ { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
+ { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
{ },
};
@@ -346,6 +401,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state)
break;
case TM6010_BOARD_BEHOLD_WANDER:
case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x01);
break;
@@ -362,6 +419,8 @@ void tm6000_flash_led(struct tm6000_core *dev, u8 state)
break;
case TM6010_BOARD_BEHOLD_WANDER:
case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x00);
break;
@@ -520,6 +579,7 @@ int tm6000_cards_setup(struct tm6000_core *dev)
msleep(15);
break;
case TM6010_BOARD_BEHOLD_WANDER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
/* Power led on (blue) */
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
msleep(15);
@@ -530,6 +590,7 @@ int tm6000_cards_setup(struct tm6000_core *dev)
msleep(15);
break;
case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
/* Power led on (blue) */
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
msleep(15);
@@ -588,8 +649,6 @@ static void tm6000_config_tuner(struct tm6000_core *dev)
tun_setup.mode_mask = 0;
if (dev->caps.has_tuner)
tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
- if (dev->caps.has_dvb)
- tun_setup.mode_mask |= T_DIGITAL_TV;
switch (dev->tuner_type) {
case TUNER_XC2028:
@@ -644,13 +703,12 @@ static void tm6000_config_tuner(struct tm6000_core *dev)
struct xc5000_config ctl = {
.i2c_address = dev->tuner_addr,
.if_khz = 4570,
- .radio_input = XC5000_RADIO_FM1,
+ .radio_input = XC5000_RADIO_FM1_MONO,
};
xc5000_cfg.tuner = TUNER_XC5000;
xc5000_cfg.priv = &ctl;
-
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
&xc5000_cfg);
}
@@ -683,6 +741,8 @@ static int tm6000_init_dev(struct tm6000_core *dev)
dev->caps = tm6000_boards[dev->model].caps;
+ dev->avideo = tm6000_boards[dev->model].avideo;
+ dev->aradio = tm6000_boards[dev->model].aradio;
/* initialize hardware */
rc = tm6000_init(dev);
if (rc < 0)
@@ -957,6 +1017,8 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
break;
case TM6010_BOARD_BEHOLD_WANDER:
case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
/* Power led off */
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
dev->gpio.power_led, 0x00);
diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c
index 96aed4ace467..778e53413afb 100644
--- a/drivers/staging/tm6000/tm6000-core.c
+++ b/drivers/staging/tm6000/tm6000-core.c
@@ -116,6 +116,29 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
}
EXPORT_SYMBOL_GPL(tm6000_get_reg);
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+ u16 index, u16 mask)
+{
+ int rc;
+ u8 buf[1];
+ u8 new_index;
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+ value, index, buf, 1);
+
+ if (rc < 0)
+ return rc;
+
+ new_index = (buf[0] & ~mask) | (index & mask);
+
+ if (new_index == index)
+ return 0;
+
+ return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
+ req, value, new_index, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(tm6000_set_reg_mask);
+
int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index)
{
int rc;
@@ -245,17 +268,12 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
struct v4l2_frequency f;
if (dev->dev_type == TM6010) {
- int val;
-
/* Enable video */
- val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
- val |= 0x60;
- tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
- val = tm6000_get_reg(dev,
- TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
- val &= ~0x40;
- tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
+ 0x60, 0x60);
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
+ 0x00, 0x40);
tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
} else {
@@ -268,11 +286,11 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80);
tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23);
+ tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23);
tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0);
tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8);
tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f);
+ tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f);
/* AP Software reset */
tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
@@ -284,8 +302,8 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
/* E3: Select input 0 - TV tuner */
- tm6000_set_reg(dev, TM6010_REQ07_RE3_OUT_SEL1, 0x00);
- tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60);
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x60);
/* This controls input */
tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0);
@@ -344,21 +362,21 @@ int tm6000_init_digital_mode(struct tm6000_core *dev)
tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00);
tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08);
- tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c);
- tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff);
- tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8);
+ tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08);
+ tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff);
+ tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8);
tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40);
tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x37);
+ tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37);
tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8);
tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0);
tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60);
- tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c);
- tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff);
- tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08);
+ tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff);
+ tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08);
msleep(50);
tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
@@ -388,18 +406,19 @@ struct reg_init {
/* The meaning of those initializations are unknown */
struct reg_init tm6000_init_tab[] = {
/* REG VALUE */
- { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f },
+ { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f },
{ TM6010_REQ07_RFF_SOFT_RESET, 0x08 },
{ TM6010_REQ07_RFF_SOFT_RESET, 0x00 },
{ TM6010_REQ07_RD5_POWERSAVE, 0x4f },
- { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23 },
- { TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0x08 },
- { TM6010_REQ07_RE2_OUT_SEL2, 0x00 },
- { TM6010_REQ07_RE3_OUT_SEL1, 0x10 },
- { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0x00 },
- { TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0x00 },
- { REQ_07_SET_GET_AVREG, 0xeb, 0x64 }, /* 48000 bits/sample, external input */
- { REQ_07_SET_GET_AVREG, 0xee, 0xc2 },
+ { TM6000_REQ07_RDA_CLK_SEL, 0x23 },
+ { TM6000_REQ07_RDB_OUT_SEL, 0x08 },
+ { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 },
+ { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 },
+ { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 },
+ { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 },
+ { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */
+ { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 },
+
{ TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */
{ TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
{ TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 },
@@ -470,6 +489,14 @@ struct reg_init tm6010_init_tab[] = {
{ TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 },
{ TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 },
{ TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 },
+ { TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00},
+ { TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80},
+ { TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a},
+ { TM6010_REQ08_R0D_A_AMD_THRES, 0x40},
+ { TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64},
+ { TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20},
+ { TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe},
+ { TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01},
{ TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc },
{ TM6010_REQ07_R3F_RESET, 0x01 },
@@ -590,38 +617,213 @@ int tm6000_init(struct tm6000_core *dev)
int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
{
- int val;
+ int val = 0;
+ u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+ u8 areg_0a = 0x91; /* SIF 48KHz */
+ switch (bitrate) {
+ case 48000:
+ areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+ areg_0a = 0x91; /* SIF 48KHz */
+ dev->audio_bitrate = bitrate;
+ break;
+ case 32000:
+ areg_f0 = 0x00; /* ADC MCLK = 375 Fs */
+ areg_0a = 0x90; /* SIF 32KHz */
+ dev->audio_bitrate = bitrate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ /* enable I2S, if we use sif or external I2S device */
if (dev->dev_type == TM6010) {
- val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0);
+ val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a);
if (val < 0)
return val;
- val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */
- val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val);
+
+ val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ areg_f0, 0xf0);
+ if (val < 0)
+ return val;
+ } else {
+ val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+ areg_f0, 0xf0);
if (val < 0)
return val;
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
- val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0);
- if (val < 0)
- return val;
+int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp)
+{
+ if (dev->dev_type == TM6010) {
+ /* Audio crossbar setting, default SIF1 */
+ u8 areg_f0 = 0x03;
- val &= 0x0f; /* Preserve the audio input control bits */
- switch (bitrate) {
- case 44100:
- val |= 0xd0;
- dev->audio_bitrate = bitrate;
+ switch (ainp) {
+ case TM6000_AIP_SIF1:
+ case TM6000_AIP_SIF2:
+ areg_f0 = 0x03;
+ break;
+ case TM6000_AIP_LINE1:
+ areg_f0 = 0x00;
+ break;
+ case TM6000_AIP_LINE2:
+ areg_f0 = 0x08;
+ break;
+ default:
+ return 0;
+ break;
+ }
+ /* Set audio input crossbar */
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ areg_f0, 0x0f);
+ } else {
+ /* Audio setting, default LINE1 */
+ u8 areg_eb = 0x00;
+
+ switch (ainp) {
+ case TM6000_AIP_LINE1:
+ areg_eb = 0x00;
+ break;
+ case TM6000_AIP_LINE2:
+ areg_eb = 0x04;
+ break;
+ default:
+ return 0;
+ break;
+ }
+ /* Set audio input */
+ tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+ areg_eb, 0x0f);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_set_audio_input);
+
+void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
+{
+ u8 mute_reg = 0;
+
+ if (mute)
+ mute_reg = 0x08;
+
+ tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
+}
+
+void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
+{
+ u8 mute_reg = 0;
+
+ if (mute)
+ mute_reg = 0x20;
+
+ if (dev->dev_type == TM6010) {
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL,
+ mute_reg, 0x20);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL,
+ mute_reg, 0x20);
+ } else {
+ tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL,
+ mute_reg, 0x20);
+ tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL,
+ mute_reg, 0x20);
+ }
+}
+
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
+{
+ enum tm6000_inaudio ainp;
+
+ if (dev->radio)
+ ainp = dev->aradio;
+ else
+ ainp = dev->avideo;
+
+ switch (ainp) {
+ case TM6000_AIP_SIF1:
+ case TM6000_AIP_SIF2:
+ if (dev->dev_type == TM6010)
+ tm6010_set_mute_sif(dev, mute);
+ else {
+ printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+ " SIF audio inputs. Please check the %s"
+ " configuration.\n", dev->name);
+ return -EINVAL;
+ }
break;
- case 48000:
- val |= 0x60;
- dev->audio_bitrate = bitrate;
+ case TM6000_AIP_LINE1:
+ case TM6000_AIP_LINE2:
+ tm6010_set_mute_adc(dev, mute);
+ break;
+ default:
+ return -EINVAL;
break;
}
- val = tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, val);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_tvaudio_set_mute);
+
+void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
+{
+ u8 vol_reg;
+
+ vol_reg = vol & 0x0F;
+
+ if (vol < 0)
+ vol_reg |= 0x40;
+
+ tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg);
+ tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
+}
+
+void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
+{
+ u8 vol_reg;
+
+ vol_reg = (vol + 0x10) & 0x1f;
+
+ if (dev->dev_type == TM6010) {
+ tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg);
+ tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg);
+ } else {
+ tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg);
+ tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg);
+ }
+}
+
+void tm6000_set_volume(struct tm6000_core *dev, int vol)
+{
+ enum tm6000_inaudio ainp;
+
+ if (dev->radio) {
+ ainp = dev->aradio;
+ vol += 8; /* Offset to 0 dB */
+ } else
+ ainp = dev->avideo;
- return val;
+ switch (ainp) {
+ case TM6000_AIP_SIF1:
+ case TM6000_AIP_SIF2:
+ if (dev->dev_type == TM6010)
+ tm6010_set_volume_sif(dev, vol);
+ else
+ printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+ " SIF audio inputs. Please check the %s"
+ " configuration.\n", dev->name);
+ break;
+ case TM6000_AIP_LINE1:
+ case TM6000_AIP_LINE2:
+ tm6010_set_volume_adc(dev, vol);
+ break;
+ default:
+ break;
+ }
}
-EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
+EXPORT_SYMBOL_GPL(tm6000_set_volume);
static LIST_HEAD(tm6000_devlist);
static DEFINE_MUTEX(tm6000_devlist_mutex);
diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/staging/tm6000/tm6000-regs.h
index 1f0ced8fa20f..5375a8347374 100644
--- a/drivers/staging/tm6000/tm6000-regs.h
+++ b/drivers/staging/tm6000/tm6000-regs.h
@@ -97,6 +97,34 @@ enum {
TM6000_URB_MSG_ERR,
};
+/* Define specific TM6000 Video decoder registers */
+#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8
+#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9
+#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda
+#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb
+#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc
+#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd
+#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde
+#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf
+#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0
+#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1
+#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2
+#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3
+#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4
+#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5
+#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6
+#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7
+#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8
+#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9
+#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea
+#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb
+#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec
+#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed
+#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee
+#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef
+#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd
+#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe
+
/* Define TM6000/TM6010 Video decoder registers */
#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00
#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01
@@ -241,6 +269,7 @@ enum {
#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9
#define TM6010_REQ07_RCA_VEND0 0x07, 0xca
#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb
+/* ONLY for TM6010 */
#define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc
#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0
#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1
@@ -250,32 +279,59 @@ enum {
#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5
#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6
#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR 0x07, 0xd8
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde
+/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8
+/* ONLY for TM6010 */
#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9
+/* ONLY for TM6010 */
#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6
+/* ONLY for TM6010 */
#define TM6010_REQ07_RF7_BIST 0x07, 0xf7
+/* ONLY for TM6010 */
#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe
#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff
@@ -477,7 +533,8 @@ enum {
#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8
#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc
-/* Define TM6000/TM6010 Audio decoder registers */
+/* Define TM6010 Audio decoder registers */
+/* This core available only in TM6010 */
#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00
#define TM6010_REQ08_R01_A_INIT 0x08, 0x01
#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02
@@ -518,7 +575,7 @@ enum {
#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27
#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28
-/* Define TM6000/TM6010 Video ADC registers */
+/* Define TM6010 Video ADC registers */
#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0
#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1
#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2
@@ -534,7 +591,7 @@ enum {
#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec
#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed
-/* Define TM6000/TM6010 Audio ADC registers */
+/* Define TM6010 Audio ADC registers */
#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0
#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1
#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2
diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c
index cc7b8664fc20..a4c07e5a6f1d 100644
--- a/drivers/staging/tm6000/tm6000-stds.c
+++ b/drivers/staging/tm6000/tm6000-stds.c
@@ -952,6 +952,22 @@ static int tm6000_set_audio_std(struct tm6000_core *dev,
uint8_t mono_flag = 0; /* No mono */
uint8_t nicam_flag = 0; /* No NICAM */
+ if (dev->radio) {
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+ return 0;
+ }
+
switch (std) {
#if 0
case DK_MONO:
@@ -984,20 +1000,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev,
case EIAJ:
areg_05 = 0x02;
break;
- case FM_RADIO:
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
- tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
- tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
- tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
- tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
- tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
- tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
- tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
- tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
- return 0;
- break;
case I_NICAM:
areg_05 = 0x08;
nicam_flag = 1;
@@ -1010,6 +1012,9 @@ static int tm6000_set_audio_std(struct tm6000_core *dev,
areg_05 = 0x0a;
nicam_flag = 1;
break;
+ default:
+ /* do nothink */
+ break;
}
#if 0
diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c
index eb9b9f1bc138..b5503409ca55 100644
--- a/drivers/staging/tm6000/tm6000-video.c
+++ b/drivers/staging/tm6000/tm6000-video.c
@@ -53,11 +53,17 @@
/* Declare static vars that will be used as parameters */
static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */
/* Debug level */
int tm6000_debug;
EXPORT_SYMBOL_GPL(tm6000_debug);
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
/* supported controls */
static struct v4l2_queryctrl tm6000_qctrl[] = {
{
@@ -96,9 +102,26 @@ static struct v4l2_queryctrl tm6000_qctrl[] = {
.step = 0x1,
.default_value = 0,
.flags = 0,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }, {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = -15,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
}
};
+static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl);
static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)];
static struct tm6000_fmt format[] = {
@@ -117,6 +140,16 @@ static struct tm6000_fmt format[] = {
}
};
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (tm6000_qctrl[i].id == id)
+ return tm6000_qctrl+i;
+ return NULL;
+}
+
/* ------------------------------------------------------------------
* DMA and thread functions
* ------------------------------------------------------------------
@@ -199,13 +232,17 @@ static int copy_streams(u8 *data, unsigned long len,
char *voutp = NULL;
unsigned int linewidth;
- /* get video buffer */
- get_next_buf(dma_q, &vbuf);
- if (!vbuf)
- return rc;
- voutp = videobuf_to_vmalloc(&vbuf->vb);
- if (!voutp)
- return 0;
+ if (!dev->radio) {
+ /* get video buffer */
+ get_next_buf(dma_q, &vbuf);
+
+ if (!vbuf)
+ return rc;
+ voutp = videobuf_to_vmalloc(&vbuf->vb);
+
+ if (!voutp)
+ return 0;
+ }
for (ptr = data; ptr < endp;) {
if (!dev->isoc_ctl.cmd) {
@@ -257,29 +294,31 @@ static int copy_streams(u8 *data, unsigned long len,
*/
switch (cmd) {
case TM6000_URB_MSG_VIDEO:
- if ((dev->isoc_ctl.vfield != field) &&
- (field == 1)) {
+ if (!dev->radio) {
+ if ((dev->isoc_ctl.vfield != field) &&
+ (field == 1)) {
/* Announces that a new buffer
* were filled
*/
- buffer_filled(dev, dma_q, vbuf);
- dprintk(dev, V4L2_DEBUG_ISOC,
+ buffer_filled(dev, dma_q, vbuf);
+ dprintk(dev, V4L2_DEBUG_ISOC,
"new buffer filled\n");
- get_next_buf(dma_q, &vbuf);
- if (!vbuf)
- return rc;
- voutp = videobuf_to_vmalloc(&vbuf->vb);
- if (!voutp)
- return rc;
- memset(voutp, 0, vbuf->vb.size);
- }
- linewidth = vbuf->vb.width << 1;
- pos = ((line << 1) - field - 1) * linewidth +
- block * TM6000_URB_MSG_LEN;
- /* Don't allow to write out of the buffer */
- if (pos + size > vbuf->vb.size)
- cmd = TM6000_URB_MSG_ERR;
- dev->isoc_ctl.vfield = field;
+ get_next_buf(dma_q, &vbuf);
+ if (!vbuf)
+ return rc;
+ voutp = videobuf_to_vmalloc(&vbuf->vb);
+ if (!voutp)
+ return rc;
+ memset(voutp, 0, vbuf->vb.size);
+ }
+ linewidth = vbuf->vb.width << 1;
+ pos = ((line << 1) - field - 1) *
+ linewidth + block * TM6000_URB_MSG_LEN;
+ /* Don't allow to write out of the buffer */
+ if (pos + size > vbuf->vb.size)
+ cmd = TM6000_URB_MSG_ERR;
+ dev->isoc_ctl.vfield = field;
+ }
break;
case TM6000_URB_MSG_VBI:
break;
@@ -537,7 +576,7 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev)
/*
* Allocate URBs and start IRQ
*/
-static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize)
+static int tm6000_prepare_isoc(struct tm6000_core *dev)
{
struct tm6000_dmaqueue *dma_q = &dev->vidq;
int i, j, sb_size, pipe, size, max_packets, num_bufs = 8;
@@ -566,11 +605,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize)
dev->isoc_ctl.max_pkt_size = size;
- max_packets = (framesize + size - 1) / size;
-
- if (max_packets > TM6000_MAX_ISO_PACKETS)
- max_packets = TM6000_MAX_ISO_PACKETS;
-
+ max_packets = TM6000_MAX_ISO_PACKETS;
sb_size = max_packets * size;
dev->isoc_ctl.num_bufs = num_bufs;
@@ -746,7 +781,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
urb_init = 1;
if (urb_init) {
- rc = tm6000_prepare_isoc(dev, buf->vb.size);
+ rc = tm6000_prepare_isoc(dev);
if (rc < 0)
goto fail;
@@ -1143,6 +1178,12 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
case V4L2_CID_HUE:
val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0);
return 0;
+ case V4L2_CID_AUDIO_MUTE:
+ val = dev->ctl_mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ val = dev->ctl_volume;
+ return 0;
default:
return -EINVAL;
}
@@ -1174,6 +1215,14 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
case V4L2_CID_HUE:
tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
return 0;
+ case V4L2_CID_AUDIO_MUTE:
+ dev->ctl_mute = val;
+ tm6000_tvaudio_set_mute(dev, val);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->ctl_volume = val;
+ tm6000_set_volume(dev, val);
+ return 0;
}
return -EINVAL;
}
@@ -1221,7 +1270,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
if (unlikely(UNSET == dev->tuner_type))
return -EINVAL;
- f->type = V4L2_TUNER_ANALOG_TV;
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->freq;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f);
@@ -1235,13 +1284,14 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct tm6000_fh *fh = priv;
struct tm6000_core *dev = fh->dev;
- if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
-
if (unlikely(UNSET == dev->tuner_type))
return -EINVAL;
if (unlikely(f->tuner != 0))
return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
dev->freq = f->frequency;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
@@ -1249,6 +1299,122 @@ static int vidioc_s_frequency(struct file *file, void *priv,
return 0;
}
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ strcpy(cap->driver, "tm6000");
+ strlcpy(cap->card, dev->name, sizeof(dev->name));
+ sprintf(cap->bus_info, "USB%04x:%04x",
+ le16_to_cpu(dev->udev->descriptor.idVendor),
+ le16_to_cpu(dev->udev->descriptor.idProduct));
+ cap->version = dev->dev_type;
+ cap->capabilities = V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ if ((dev->aradio == TM6000_AIP_LINE1) ||
+ (dev->aradio == TM6000_AIP_LINE2)) {
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+ else {
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ }
+
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+
+ return 0;
+}
+
/* ------------------------------------------------------------------
File operations for the device
------------------------------------------------------------------*/
@@ -1260,6 +1426,7 @@ static int tm6000_open(struct file *file)
struct tm6000_fh *fh;
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int i, rc;
+ int radio = 0;
printk(KERN_INFO "tm6000: open called (dev=%s)\n",
video_device_node_name(vdev));
@@ -1267,6 +1434,17 @@ static int tm6000_open(struct file *file)
dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
video_device_node_name(vdev));
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
/* If more than one user, mutex should be added */
dev->users++;
@@ -1284,8 +1462,9 @@ static int tm6000_open(struct file *file)
file->private_data = fh;
fh->dev = dev;
-
- fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fh->radio = radio;
+ dev->radio = radio;
+ fh->type = type;
dev->fourcc = format[0].fourcc;
fh->fmt = format_by_fourcc(dev->fourcc);
@@ -1322,6 +1501,19 @@ static int tm6000_open(struct file *file)
V4L2_FIELD_INTERLACED,
sizeof(struct tm6000_buffer), fh, &dev->lock);
+ if (fh->radio) {
+ dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
+ tm6000_set_audio_input(dev, dev->aradio);
+ tm6000_set_volume(dev, dev->ctl_volume);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+ tm6000_prepare_isoc(dev);
+ tm6000_start_thread(dev);
+ }
+ else {
+ tm6000_set_audio_input(dev, dev->avideo);
+ tm6000_set_volume(dev, dev->ctl_volume);
+ }
+
return 0;
}
@@ -1445,6 +1637,36 @@ static struct video_device tm6000_template = {
.current_norm = V4L2_STD_NTSC_M,
};
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
+
+struct video_device tm6000_radio_template = {
+ .name = "tm6000",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
/* -----------------------------------------------------------------
* Initialization and module stuff
* ------------------------------------------------------------------
@@ -1499,6 +1721,25 @@ int tm6000_v4l2_register(struct tm6000_core *dev)
printk(KERN_INFO "%s: registered device %s\n",
dev->name, video_device_node_name(dev->vfd));
+ dev->radio_dev = vdev_init(dev, &tm6000_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ printk(KERN_INFO "%s: can't register radio device\n",
+ dev->name);
+ return ret; /* FIXME release resource */
+ }
+
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (ret < 0) {
+ printk(KERN_INFO "%s: can't register radio device\n",
+ dev->name);
+ return ret; /* FIXME release resource */
+ }
+
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->radio_dev));
+
printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret);
return ret;
}
@@ -1507,6 +1748,14 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev)
{
video_unregister_device(dev->vfd);
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+
return 0;
}
diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h
index bf11eeec92c7..ccd120fe1340 100644
--- a/drivers/staging/tm6000/tm6000.h
+++ b/drivers/staging/tm6000/tm6000.h
@@ -53,6 +53,14 @@ enum tm6000_devtype {
TM6010,
};
+enum tm6000_inaudio {
+ TM6000_AIP_UNK = 0,
+ TM6000_AIP_SIF1,
+ TM6000_AIP_SIF2,
+ TM6000_AIP_LINE1,
+ TM6000_AIP_LINE2,
+};
+
/* ------------------------------------------------------------------
* Basic structures
* ------------------------------------------------------------------
@@ -174,6 +182,8 @@ struct tm6000_core {
char *ir_codes;
+ __u8 radio;
+
/* Demodulator configuration */
int demod_addr; /* demodulator address */
@@ -194,6 +204,7 @@ struct tm6000_core {
bool is_res_read;
struct video_device *vfd;
+ struct video_device *radio_dev;
struct tm6000_dmaqueue vidq;
struct v4l2_device v4l2_dev;
@@ -203,6 +214,9 @@ struct tm6000_core {
enum tm6000_mode mode;
+ int ctl_mute; /* audio */
+ int ctl_volume;
+
/* DVB-T support */
struct tm6000_dvb *dvb;
@@ -210,7 +224,8 @@ struct tm6000_core {
struct snd_tm6000_card *adev;
struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
atomic_t stream_started; /* stream should be running if true */
-
+ enum tm6000_inaudio avideo;
+ enum tm6000_inaudio aradio;
struct tm6000_IR *ir;
@@ -248,6 +263,7 @@ struct tm6000_ops {
struct tm6000_fh {
struct tm6000_core *dev;
+ unsigned int radio;
/* video capture */
struct tm6000_fmt *fmt;
@@ -276,12 +292,17 @@ int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index);
int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index);
int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index);
int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+ u16 index, u16 mask);
int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep);
int tm6000_init(struct tm6000_core *dev);
int tm6000_init_analog_mode(struct tm6000_core *dev);
int tm6000_init_digital_mode(struct tm6000_core *dev);
int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate);
+int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp);
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute);
+void tm6000_set_volume(struct tm6000_core *dev, int vol);
int tm6000_v4l2_register(struct tm6000_core *dev);
int tm6000_v4l2_unregister(struct tm6000_core *dev);
diff --git a/drivers/staging/usbvideo/Kconfig b/drivers/staging/usbvideo/Kconfig
deleted file mode 100644
index 566d659e6ff3..000000000000
--- a/drivers/staging/usbvideo/Kconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-config VIDEO_USBVIDEO
- tristate
-
-config USB_VICAM
- tristate "USB 3com HomeConnect (aka vicam) support (DEPRECATED)"
- depends on VIDEO_DEV && VIDEO_V4L2_COMMON && USB
- select VIDEO_USBVIDEO
- ---help---
- Say Y here if you have 3com homeconnect camera (vicam).
-
- This driver uses the deprecated V4L1 API and will be removed in
- 2.6.39, unless someone converts it to the V4L2 API.
-
- To compile this driver as a module, choose M here: the
- module will be called vicam.
diff --git a/drivers/staging/usbvideo/Makefile b/drivers/staging/usbvideo/Makefile
deleted file mode 100644
index 3c99a9a2d8d3..000000000000
--- a/drivers/staging/usbvideo/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o
-obj-$(CONFIG_USB_VICAM) += vicam.o
diff --git a/drivers/staging/usbvideo/TODO b/drivers/staging/usbvideo/TODO
deleted file mode 100644
index 3b2c03836286..000000000000
--- a/drivers/staging/usbvideo/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-This is an obsolete driver for some old webcams that still use V4L1 API.
-As V4L1 support is being removed from kernel, if nobody take care on it,
-the driver will be removed for 2.6.39.
-
-Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/usbvideo/usbvideo.c b/drivers/staging/usbvideo/usbvideo.c
deleted file mode 100644
index f1fcf9744961..000000000000
--- a/drivers/staging/usbvideo/usbvideo.c
+++ /dev/null
@@ -1,2230 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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/sched.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-
-#include <asm/io.h>
-
-#include "usbvideo.h"
-
-#if defined(MAP_NR)
-#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */
-#endif
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-
-/*
- * Local prototypes.
- */
-static void usbvideo_Disconnect(struct usb_interface *intf);
-static void usbvideo_CameraRelease(struct uvd *uvd);
-
-static long usbvideo_v4l_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg);
-static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
-static int usbvideo_v4l_open(struct file *file);
-static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos);
-static int usbvideo_v4l_close(struct file *file);
-
-static int usbvideo_StartDataPump(struct uvd *uvd);
-static void usbvideo_StopDataPump(struct uvd *uvd);
-static int usbvideo_GetFrame(struct uvd *uvd, int frameNum);
-static int usbvideo_NewFrame(struct uvd *uvd, int framenum);
-static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd,
- struct usbvideo_frame *frame);
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-static void *usbvideo_rvmalloc(unsigned long size)
-{
- void *mem;
- unsigned long adr;
-
- size = PAGE_ALIGN(size);
- mem = vmalloc_32(size);
- if (!mem)
- return NULL;
-
- memset(mem, 0, size); /* Clear the ram out, no junk to the user */
- adr = (unsigned long) mem;
- while (size > 0) {
- SetPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- return mem;
-}
-
-static void usbvideo_rvfree(void *mem, unsigned long size)
-{
- unsigned long adr;
-
- if (!mem)
- return;
-
- adr = (unsigned long) mem;
- while ((long) size > 0) {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vfree(mem);
-}
-
-static void RingQueue_Initialize(struct RingQueue *rq)
-{
- assert(rq != NULL);
- init_waitqueue_head(&rq->wqh);
-}
-
-static void RingQueue_Allocate(struct RingQueue *rq, int rqLen)
-{
- /* Make sure the requested size is a power of 2 and
- round up if necessary. This allows index wrapping
- using masks rather than modulo */
-
- int i = 1;
- assert(rq != NULL);
- assert(rqLen > 0);
-
- while(rqLen >> i)
- i++;
- if(rqLen != 1 << (i-1))
- rqLen = 1 << i;
-
- rq->length = rqLen;
- rq->ri = rq->wi = 0;
- rq->queue = usbvideo_rvmalloc(rq->length);
- assert(rq->queue != NULL);
-}
-
-static int RingQueue_IsAllocated(const struct RingQueue *rq)
-{
- if (rq == NULL)
- return 0;
- return (rq->queue != NULL) && (rq->length > 0);
-}
-
-static void RingQueue_Free(struct RingQueue *rq)
-{
- assert(rq != NULL);
- if (RingQueue_IsAllocated(rq)) {
- usbvideo_rvfree(rq->queue, rq->length);
- rq->queue = NULL;
- rq->length = 0;
- }
-}
-
-int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len)
-{
- int rql, toread;
-
- assert(rq != NULL);
- assert(dst != NULL);
-
- rql = RingQueue_GetLength(rq);
- if(!rql)
- return 0;
-
- /* Clip requested length to available data */
- if(len > rql)
- len = rql;
-
- toread = len;
- if(rq->ri > rq->wi) {
- /* Read data from tail */
- int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri;
- memcpy(dst, rq->queue + rq->ri, read);
- toread -= read;
- dst += read;
- rq->ri = (rq->ri + read) & (rq->length-1);
- }
- if(toread) {
- /* Read data from head */
- memcpy(dst, rq->queue + rq->ri, toread);
- rq->ri = (rq->ri + toread) & (rq->length-1);
- }
- return len;
-}
-
-EXPORT_SYMBOL(RingQueue_Dequeue);
-
-int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n)
-{
- int enqueued = 0;
-
- assert(rq != NULL);
- assert(cdata != NULL);
- assert(rq->length > 0);
- while (n > 0) {
- int m, q_avail;
-
- /* Calculate the largest chunk that fits the tail of the ring */
- q_avail = rq->length - rq->wi;
- if (q_avail <= 0) {
- rq->wi = 0;
- q_avail = rq->length;
- }
- m = n;
- assert(q_avail > 0);
- if (m > q_avail)
- m = q_avail;
-
- memcpy(rq->queue + rq->wi, cdata, m);
- RING_QUEUE_ADVANCE_INDEX(rq, wi, m);
- cdata += m;
- enqueued += m;
- n -= m;
- }
- return enqueued;
-}
-
-EXPORT_SYMBOL(RingQueue_Enqueue);
-
-static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq)
-{
- assert(rq != NULL);
- interruptible_sleep_on(&rq->wqh);
-}
-
-void RingQueue_WakeUpInterruptible(struct RingQueue *rq)
-{
- assert(rq != NULL);
- if (waitqueue_active(&rq->wqh))
- wake_up_interruptible(&rq->wqh);
-}
-
-EXPORT_SYMBOL(RingQueue_WakeUpInterruptible);
-
-void RingQueue_Flush(struct RingQueue *rq)
-{
- assert(rq != NULL);
- rq->ri = 0;
- rq->wi = 0;
-}
-
-EXPORT_SYMBOL(RingQueue_Flush);
-
-
-/*
- * usbvideo_VideosizeToString()
- *
- * This procedure converts given videosize value to readable string.
- *
- * History:
- * 07-Aug-2000 Created.
- * 19-Oct-2000 Reworked for usbvideo module.
- */
-static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
-{
- char tmp[40];
- int n;
-
- n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs));
- assert(n < sizeof(tmp));
- if ((buf == NULL) || (bufLen < n))
- err("usbvideo_VideosizeToString: buffer is too small.");
- else
- memmove(buf, tmp, n);
-}
-
-/*
- * usbvideo_OverlayChar()
- *
- * History:
- * 01-Feb-2000 Created.
- */
-static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
- int x, int y, int ch)
-{
- static const unsigned short digits[16] = {
- 0xF6DE, /* 0 */
- 0x2492, /* 1 */
- 0xE7CE, /* 2 */
- 0xE79E, /* 3 */
- 0xB792, /* 4 */
- 0xF39E, /* 5 */
- 0xF3DE, /* 6 */
- 0xF492, /* 7 */
- 0xF7DE, /* 8 */
- 0xF79E, /* 9 */
- 0x77DA, /* a */
- 0xD75C, /* b */
- 0xF24E, /* c */
- 0xD6DC, /* d */
- 0xF34E, /* e */
- 0xF348 /* f */
- };
- unsigned short digit;
- int ix, iy;
- int value;
-
- if ((uvd == NULL) || (frame == NULL))
- return;
-
- value = hex_to_bin(ch);
- if (value < 0)
- return;
- digit = digits[value];
-
- for (iy=0; iy < 5; iy++) {
- for (ix=0; ix < 3; ix++) {
- if (digit & 0x8000) {
- if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) {
-/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF);
- }
- }
- digit = digit << 1;
- }
- }
-}
-
-/*
- * usbvideo_OverlayString()
- *
- * History:
- * 01-Feb-2000 Created.
- */
-static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame,
- int x, int y, const char *str)
-{
- while (*str) {
- usbvideo_OverlayChar(uvd, frame, x, y, *str);
- str++;
- x += 4; /* 3 pixels character + 1 space */
- }
-}
-
-/*
- * usbvideo_OverlayStats()
- *
- * Overlays important debugging information.
- *
- * History:
- * 01-Feb-2000 Created.
- */
-static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame)
-{
- const int y_diff = 8;
- char tmp[16];
- int x = 10, y=10;
- long i, j, barLength;
- const int qi_x1 = 60, qi_y1 = 10;
- const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10;
-
- /* Call the user callback, see if we may proceed after that */
- if (VALID_CALLBACK(uvd, overlayHook)) {
- if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0)
- return;
- }
-
- /*
- * We draw a (mostly) hollow rectangle with qi_xxx coordinates.
- * Left edge symbolizes the queue index 0; right edge symbolizes
- * the full capacity of the queue.
- */
- barLength = qi_x2 - qi_x1 - 2;
- if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) {
-/* TODO */ long u_lo, u_hi, q_used;
- long m_ri, m_wi, m_lo, m_hi;
-
- /*
- * Determine fill zones (used areas of the queue):
- * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length
- *
- * if u_lo < 0 then there is no first filler.
- */
-
- q_used = RingQueue_GetLength(&uvd->dp);
- if ((uvd->dp.ri + q_used) >= uvd->dp.length) {
- u_hi = uvd->dp.length;
- u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1);
- } else {
- u_hi = (q_used + uvd->dp.ri);
- u_lo = -1;
- }
-
- /* Convert byte indices into screen units */
- m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length);
- m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length);
- m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1;
- m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length);
-
- for (j=qi_y1; j < (qi_y1 + qi_h); j++) {
- for (i=qi_x1; i < qi_x2; i++) {
- /* Draw border lines */
- if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) ||
- (i == qi_x1) || (i == (qi_x2 - 1))) {
- RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF);
- continue;
- }
- /* For all other points the Y coordinate does not matter */
- if ((i >= m_ri) && (i <= (m_ri + 3))) {
- RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00);
- } else if ((i >= m_wi) && (i <= (m_wi + 3))) {
- RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00);
- } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi)))
- RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF);
- }
- }
- }
-
- sprintf(tmp, "%8lx", uvd->stats.frame_num);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.urb_count);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.urb_length);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.data_count);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.header_count);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.iso_skip_count);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8lx", uvd->stats.iso_err_count);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8x", uvd->vpic.colour);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8x", uvd->vpic.hue);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8x", uvd->vpic.brightness >> 8);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8x", uvd->vpic.contrast >> 12);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-
- sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8);
- usbvideo_OverlayString(uvd, frame, x, y, tmp);
- y += y_diff;
-}
-
-/*
- * usbvideo_ReportStatistics()
- *
- * This procedure prints packet and transfer statistics.
- *
- * History:
- * 14-Jan-2000 Corrected default multiplier.
- */
-static void usbvideo_ReportStatistics(const struct uvd *uvd)
-{
- if ((uvd != NULL) && (uvd->stats.urb_count > 0)) {
- unsigned long allPackets, badPackets, goodPackets, percent;
- allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES;
- badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count;
- goodPackets = allPackets - badPackets;
- /* Calculate percentage wisely, remember integer limits */
- assert(allPackets != 0);
- if (goodPackets < (((unsigned long)-1)/100))
- percent = (100 * goodPackets) / allPackets;
- else
- percent = goodPackets / (allPackets / 100);
- dev_info(&uvd->dev->dev,
- "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n",
- allPackets, badPackets, percent);
- if (uvd->iso_packet_len > 0) {
- unsigned long allBytes, xferBytes;
- char multiplier = ' ';
- allBytes = allPackets * uvd->iso_packet_len;
- xferBytes = uvd->stats.data_count;
- assert(allBytes != 0);
- if (xferBytes < (((unsigned long)-1)/100))
- percent = (100 * xferBytes) / allBytes;
- else
- percent = xferBytes / (allBytes / 100);
- /* Scale xferBytes for easy reading */
- if (xferBytes > 10*1024) {
- xferBytes /= 1024;
- multiplier = 'K';
- if (xferBytes > 10*1024) {
- xferBytes /= 1024;
- multiplier = 'M';
- if (xferBytes > 10*1024) {
- xferBytes /= 1024;
- multiplier = 'G';
- if (xferBytes > 10*1024) {
- xferBytes /= 1024;
- multiplier = 'T';
- }
- }
- }
- }
- dev_info(&uvd->dev->dev,
- "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n",
- xferBytes, multiplier, percent);
- }
- }
-}
-
-/*
- * usbvideo_TestPattern()
- *
- * Procedure forms a test pattern (yellow grid on blue background).
- *
- * Parameters:
- * fullframe: if TRUE then entire frame is filled, otherwise the procedure
- * continues from the current scanline.
- * pmode 0: fill the frame with solid blue color (like on VCR or TV)
- * 1: Draw a colored grid
- *
- * History:
- * 01-Feb-2000 Created.
- */
-void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode)
-{
- struct usbvideo_frame *frame;
- int num_cell = 0;
- int scan_length = 0;
- static int num_pass;
-
- if (uvd == NULL) {
- err("%s: uvd == NULL", __func__);
- return;
- }
- if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
- err("%s: uvd->curframe=%d.", __func__, uvd->curframe);
- return;
- }
-
- /* Grab the current frame */
- frame = &uvd->frame[uvd->curframe];
-
- /* Optionally start at the beginning */
- if (fullframe) {
- frame->curline = 0;
- frame->seqRead_Length = 0;
- }
-#if 0
- { /* For debugging purposes only */
- char tmp[20];
- usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request);
- dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp);
- }
-#endif
- /* Form every scan line */
- for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) {
- int i;
- unsigned char *f = frame->data +
- (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline);
- for (i=0; i < VIDEOSIZE_X(frame->request); i++) {
- unsigned char cb=0x80;
- unsigned char cg = 0;
- unsigned char cr = 0;
-
- if (pmode == 1) {
- if (frame->curline % 32 == 0)
- cb = 0, cg = cr = 0xFF;
- else if (i % 32 == 0) {
- if (frame->curline % 32 == 1)
- num_cell++;
- cb = 0, cg = cr = 0xFF;
- } else {
- cb = ((num_cell*7) + num_pass) & 0xFF;
- cg = ((num_cell*5) + num_pass*2) & 0xFF;
- cr = ((num_cell*3) + num_pass*3) & 0xFF;
- }
- } else {
- /* Just the blue screen */
- }
-
- *f++ = cb;
- *f++ = cg;
- *f++ = cr;
- scan_length += 3;
- }
- }
-
- frame->frameState = FrameState_Done;
- frame->seqRead_Length += scan_length;
- ++num_pass;
-
- /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */
- usbvideo_OverlayStats(uvd, frame);
-}
-
-EXPORT_SYMBOL(usbvideo_TestPattern);
-
-
-#ifdef DEBUG
-/*
- * usbvideo_HexDump()
- *
- * A debugging tool. Prints hex dumps.
- *
- * History:
- * 29-Jul-2000 Added printing of offsets.
- */
-void usbvideo_HexDump(const unsigned char *data, int len)
-{
- const int bytes_per_line = 32;
- char tmp[128]; /* 32*3 + 5 */
- int i, k;
-
- for (i=k=0; len > 0; i++, len--) {
- if (i > 0 && ((i % bytes_per_line) == 0)) {
- printk("%s\n", tmp);
- k=0;
- }
- if ((i % bytes_per_line) == 0)
- k += sprintf(&tmp[k], "%04x: ", i);
- k += sprintf(&tmp[k], "%02x ", data[i]);
- }
- if (k > 0)
- printk("%s\n", tmp);
-}
-
-EXPORT_SYMBOL(usbvideo_HexDump);
-
-#endif
-
-/* ******************************************************************** */
-
-/* XXX: this piece of crap really wants some error handling.. */
-static int usbvideo_ClientIncModCount(struct uvd *uvd)
-{
- if (uvd == NULL) {
- err("%s: uvd == NULL", __func__);
- return -EINVAL;
- }
- if (uvd->handle == NULL) {
- err("%s: uvd->handle == NULL", __func__);
- return -EINVAL;
- }
- if (!try_module_get(uvd->handle->md_module)) {
- err("%s: try_module_get() == 0", __func__);
- return -ENODEV;
- }
- return 0;
-}
-
-static void usbvideo_ClientDecModCount(struct uvd *uvd)
-{
- if (uvd == NULL) {
- err("%s: uvd == NULL", __func__);
- return;
- }
- if (uvd->handle == NULL) {
- err("%s: uvd->handle == NULL", __func__);
- return;
- }
- if (uvd->handle->md_module == NULL) {
- err("%s: uvd->handle->md_module == NULL", __func__);
- return;
- }
- module_put(uvd->handle->md_module);
-}
-
-int usbvideo_register(
- struct usbvideo **pCams,
- const int num_cams,
- const int num_extra,
- const char *driverName,
- const struct usbvideo_cb *cbTbl,
- struct module *md,
- const struct usb_device_id *id_table)
-{
- struct usbvideo *cams;
- int i, base_size, result;
-
- /* Check parameters for sanity */
- if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) {
- err("%s: Illegal call", __func__);
- return -EINVAL;
- }
-
- /* Check registration callback - must be set! */
- if (cbTbl->probe == NULL) {
- err("%s: probe() is required!", __func__);
- return -EINVAL;
- }
-
- base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo);
- cams = kzalloc(base_size, GFP_KERNEL);
- if (cams == NULL) {
- err("Failed to allocate %d. bytes for usbvideo struct", base_size);
- return -ENOMEM;
- }
- dbg("%s: Allocated $%p (%d. bytes) for %d. cameras",
- __func__, cams, base_size, num_cams);
-
- /* Copy callbacks, apply defaults for those that are not set */
- memmove(&cams->cb, cbTbl, sizeof(cams->cb));
- if (cams->cb.getFrame == NULL)
- cams->cb.getFrame = usbvideo_GetFrame;
- if (cams->cb.disconnect == NULL)
- cams->cb.disconnect = usbvideo_Disconnect;
- if (cams->cb.startDataPump == NULL)
- cams->cb.startDataPump = usbvideo_StartDataPump;
- if (cams->cb.stopDataPump == NULL)
- cams->cb.stopDataPump = usbvideo_StopDataPump;
-
- cams->num_cameras = num_cams;
- cams->cam = (struct uvd *) &cams[1];
- cams->md_module = md;
- mutex_init(&cams->lock); /* to 1 == available */
-
- for (i = 0; i < num_cams; i++) {
- struct uvd *up = &cams->cam[i];
-
- up->handle = cams;
-
- /* Allocate user_data separately because of kmalloc's limits */
- if (num_extra > 0) {
- up->user_size = num_cams * num_extra;
- up->user_data = kmalloc(up->user_size, GFP_KERNEL);
- if (up->user_data == NULL) {
- err("%s: Failed to allocate user_data (%d. bytes)",
- __func__, up->user_size);
- while (i) {
- up = &cams->cam[--i];
- kfree(up->user_data);
- }
- kfree(cams);
- return -ENOMEM;
- }
- dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)",
- __func__, i, up->user_data, up->user_size);
- }
- }
-
- /*
- * Register ourselves with USB stack.
- */
- strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown");
- cams->usbdrv.name = cams->drvName;
- cams->usbdrv.probe = cams->cb.probe;
- cams->usbdrv.disconnect = cams->cb.disconnect;
- cams->usbdrv.id_table = id_table;
-
- /*
- * Update global handle to usbvideo. This is very important
- * because probe() can be called before usb_register() returns.
- * If the handle is not yet updated then the probe() will fail.
- */
- *pCams = cams;
- result = usb_register(&cams->usbdrv);
- if (result) {
- for (i = 0; i < num_cams; i++) {
- struct uvd *up = &cams->cam[i];
- kfree(up->user_data);
- }
- kfree(cams);
- }
-
- return result;
-}
-
-EXPORT_SYMBOL(usbvideo_register);
-
-/*
- * usbvideo_Deregister()
- *
- * Procedure frees all usbvideo and user data structures. Be warned that
- * if you had some dynamically allocated components in ->user field then
- * you should free them before calling here.
- */
-void usbvideo_Deregister(struct usbvideo **pCams)
-{
- struct usbvideo *cams;
- int i;
-
- if (pCams == NULL) {
- err("%s: pCams == NULL", __func__);
- return;
- }
- cams = *pCams;
- if (cams == NULL) {
- err("%s: cams == NULL", __func__);
- return;
- }
-
- dbg("%s: Deregistering %s driver.", __func__, cams->drvName);
- usb_deregister(&cams->usbdrv);
-
- dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras);
- for (i=0; i < cams->num_cameras; i++) {
- struct uvd *up = &cams->cam[i];
- int warning = 0;
-
- if (up->user_data != NULL) {
- if (up->user_size <= 0)
- ++warning;
- } else {
- if (up->user_size > 0)
- ++warning;
- }
- if (warning) {
- err("%s: Warning: user_data=$%p user_size=%d.",
- __func__, up->user_data, up->user_size);
- } else {
- dbg("%s: Freeing %d. $%p->user_data=$%p",
- __func__, i, up, up->user_data);
- kfree(up->user_data);
- }
- }
- /* Whole array was allocated in one chunk */
- dbg("%s: Freed %d uvd structures",
- __func__, cams->num_cameras);
- kfree(cams);
- *pCams = NULL;
-}
-
-EXPORT_SYMBOL(usbvideo_Deregister);
-
-/*
- * usbvideo_Disconnect()
- *
- * This procedure stops all driver activity. Deallocation of
- * the interface-private structure (pointed by 'ptr') is done now
- * (if we don't have any open files) or later, when those files
- * are closed. After that driver should be removable.
- *
- * This code handles surprise removal. The uvd->user is a counter which
- * increments on open() and decrements on close(). If we see here that
- * this counter is not 0 then we have a client who still has us opened.
- * We set uvd->remove_pending flag as early as possible, and after that
- * all access to the camera will gracefully fail. These failures should
- * prompt client to (eventually) close the video device, and then - in
- * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter.
- *
- * History:
- * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone.
- * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close()
- * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
- * 19-Oct-2000 Moved to usbvideo module.
- */
-static void usbvideo_Disconnect(struct usb_interface *intf)
-{
- struct uvd *uvd = usb_get_intfdata (intf);
- int i;
-
- if (uvd == NULL) {
- err("%s($%p): Illegal call.", __func__, intf);
- return;
- }
-
- usb_set_intfdata (intf, NULL);
-
- usbvideo_ClientIncModCount(uvd);
- if (uvd->debug > 0)
- dev_info(&intf->dev, "%s(%p.)\n", __func__, intf);
-
- mutex_lock(&uvd->lock);
- uvd->remove_pending = 1; /* Now all ISO data will be ignored */
-
- /* At this time we ask to cancel outstanding URBs */
- GET_CALLBACK(uvd, stopDataPump)(uvd);
-
- for (i=0; i < USBVIDEO_NUMSBUF; i++)
- usb_free_urb(uvd->sbuf[i].urb);
-
- usb_put_dev(uvd->dev);
- uvd->dev = NULL; /* USB device is no more */
-
- video_unregister_device(&uvd->vdev);
- if (uvd->debug > 0)
- dev_info(&intf->dev, "%s: Video unregistered.\n", __func__);
-
- if (uvd->user)
- dev_info(&intf->dev, "%s: In use, disconnect pending.\n",
- __func__);
- else
- usbvideo_CameraRelease(uvd);
- mutex_unlock(&uvd->lock);
- dev_info(&intf->dev, "USB camera disconnected.\n");
-
- usbvideo_ClientDecModCount(uvd);
-}
-
-/*
- * usbvideo_CameraRelease()
- *
- * This code does final release of uvd. This happens
- * after the device is disconnected -and- all clients
- * closed their files.
- *
- * History:
- * 27-Jan-2000 Created.
- */
-static void usbvideo_CameraRelease(struct uvd *uvd)
-{
- if (uvd == NULL) {
- err("%s: Illegal call", __func__);
- return;
- }
-
- RingQueue_Free(&uvd->dp);
- if (VALID_CALLBACK(uvd, userFree))
- GET_CALLBACK(uvd, userFree)(uvd);
- uvd->uvd_used = 0; /* This is atomic, no need to take mutex */
-}
-
-/*
- * usbvideo_find_struct()
- *
- * This code searches the array of preallocated (static) structures
- * and returns index of the first one that isn't in use. Returns -1
- * if there are no free structures.
- *
- * History:
- * 27-Jan-2000 Created.
- */
-static int usbvideo_find_struct(struct usbvideo *cams)
-{
- int u, rv = -1;
-
- if (cams == NULL) {
- err("No usbvideo handle?");
- return -1;
- }
- mutex_lock(&cams->lock);
- for (u = 0; u < cams->num_cameras; u++) {
- struct uvd *uvd = &cams->cam[u];
- if (!uvd->uvd_used) /* This one is free */
- {
- uvd->uvd_used = 1; /* In use now */
- mutex_init(&uvd->lock); /* to 1 == available */
- uvd->dev = NULL;
- rv = u;
- break;
- }
- }
- mutex_unlock(&cams->lock);
- return rv;
-}
-
-static const struct v4l2_file_operations usbvideo_fops = {
- .owner = THIS_MODULE,
- .open = usbvideo_v4l_open,
- .release =usbvideo_v4l_close,
- .read = usbvideo_v4l_read,
- .mmap = usbvideo_v4l_mmap,
- .ioctl = usbvideo_v4l_ioctl,
-};
-static const struct video_device usbvideo_template = {
- .fops = &usbvideo_fops,
-};
-
-struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams)
-{
- int i, devnum;
- struct uvd *uvd = NULL;
-
- if (cams == NULL) {
- err("No usbvideo handle?");
- return NULL;
- }
-
- devnum = usbvideo_find_struct(cams);
- if (devnum == -1) {
- err("IBM USB camera driver: Too many devices!");
- return NULL;
- }
- uvd = &cams->cam[devnum];
- dbg("Device entry #%d. at $%p", devnum, uvd);
-
- /* Not relying upon caller we increase module counter ourselves */
- usbvideo_ClientIncModCount(uvd);
-
- mutex_lock(&uvd->lock);
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
- if (uvd->sbuf[i].urb == NULL) {
- err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC);
- uvd->uvd_used = 0;
- uvd = NULL;
- goto allocate_done;
- }
- }
- uvd->user=0;
- uvd->remove_pending = 0;
- uvd->last_error = 0;
- RingQueue_Initialize(&uvd->dp);
-
- /* Initialize video device structure */
- uvd->vdev = usbvideo_template;
- sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName);
- /*
- * The client is free to overwrite those because we
- * return control to the client's probe function right now.
- */
-allocate_done:
- mutex_unlock(&uvd->lock);
- usbvideo_ClientDecModCount(uvd);
- return uvd;
-}
-
-EXPORT_SYMBOL(usbvideo_AllocateDevice);
-
-int usbvideo_RegisterVideoDevice(struct uvd *uvd)
-{
- char tmp1[20], tmp2[20]; /* Buffers for printing */
-
- if (uvd == NULL) {
- err("%s: Illegal call.", __func__);
- return -EINVAL;
- }
- if (uvd->video_endp == 0) {
- dev_info(&uvd->dev->dev,
- "%s: No video endpoint specified; data pump disabled.\n",
- __func__);
- }
- if (uvd->paletteBits == 0) {
- err("%s: No palettes specified!", __func__);
- return -EINVAL;
- }
- if (uvd->defaultPalette == 0) {
- dev_info(&uvd->dev->dev, "%s: No default palette!\n",
- __func__);
- }
-
- uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) *
- VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL;
- usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize);
- usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas);
-
- if (uvd->debug > 0) {
- dev_info(&uvd->dev->dev,
- "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n",
- __func__, uvd->iface, uvd->video_endp,
- uvd->paletteBits);
- }
- if (uvd->dev == NULL) {
- err("%s: uvd->dev == NULL", __func__);
- return -EINVAL;
- }
- uvd->vdev.parent = &uvd->dev->dev;
- uvd->vdev.release = video_device_release_empty;
- if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
- err("%s: video_register_device failed", __func__);
- return -EPIPE;
- }
- if (uvd->debug > 1) {
- dev_info(&uvd->dev->dev,
- "%s: video_register_device() successful\n", __func__);
- }
-
- dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n",
- (uvd->handle != NULL) ? uvd->handle->drvName : "???",
- video_device_node_name(&uvd->vdev), tmp2, tmp1);
-
- usb_get_dev(uvd->dev);
- return 0;
-}
-
-EXPORT_SYMBOL(usbvideo_RegisterVideoDevice);
-
-/* ******************************************************************** */
-
-static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct uvd *uvd = file->private_data;
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end-vma->vm_start;
- unsigned long page, pos;
-
- if (!CAMERA_IS_OPERATIONAL(uvd))
- return -EFAULT;
-
- if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
- return -EINVAL;
-
- pos = (unsigned long) uvd->fbuf;
- while (size > 0) {
- page = vmalloc_to_pfn((void *)pos);
- if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
-
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
-
- return 0;
-}
-
-/*
- * usbvideo_v4l_open()
- *
- * This is part of Video 4 Linux API. The driver can be opened by one
- * client only (checks internal counter 'uvdser'). The procedure
- * then allocates buffers needed for video processing.
- *
- * History:
- * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the
- * camera is also initialized here (once per connect), at
- * expense of V4L client (it waits on open() call).
- * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
- * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
- */
-static int usbvideo_v4l_open(struct file *file)
-{
- struct video_device *dev = video_devdata(file);
- struct uvd *uvd = (struct uvd *) dev;
- const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len;
- int i, errCode = 0;
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
-
- if (usbvideo_ClientIncModCount(uvd) < 0)
- return -ENODEV;
- mutex_lock(&uvd->lock);
-
- if (uvd->user) {
- err("%s: Someone tried to open an already opened device!", __func__);
- errCode = -EBUSY;
- } else {
- /* Clear statistics */
- memset(&uvd->stats, 0, sizeof(uvd->stats));
-
- /* Clean pointers so we know if we allocated something */
- for (i=0; i < USBVIDEO_NUMSBUF; i++)
- uvd->sbuf[i].data = NULL;
-
- /* Allocate memory for the frame buffers */
- uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size;
- uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size);
- RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE);
- if ((uvd->fbuf == NULL) ||
- (!RingQueue_IsAllocated(&uvd->dp))) {
- err("%s: Failed to allocate fbuf or dp", __func__);
- errCode = -ENOMEM;
- } else {
- /* Allocate all buffers */
- for (i=0; i < USBVIDEO_NUMFRAMES; i++) {
- uvd->frame[i].frameState = FrameState_Unused;
- uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size);
- /*
- * Set default sizes in case IOCTL (VIDIOCMCAPTURE)
- * is not used (using read() instead).
- */
- uvd->frame[i].canvas = uvd->canvas;
- uvd->frame[i].seqRead_Index = 0;
- }
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL);
- if (uvd->sbuf[i].data == NULL) {
- errCode = -ENOMEM;
- break;
- }
- }
- }
- if (errCode != 0) {
- /* Have to free all that memory */
- if (uvd->fbuf != NULL) {
- usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
- uvd->fbuf = NULL;
- }
- RingQueue_Free(&uvd->dp);
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- kfree(uvd->sbuf[i].data);
- uvd->sbuf[i].data = NULL;
- }
- }
- }
-
- /* If so far no errors then we shall start the camera */
- if (errCode == 0) {
- /* Start data pump if we have valid endpoint */
- if (uvd->video_endp != 0)
- errCode = GET_CALLBACK(uvd, startDataPump)(uvd);
- if (errCode == 0) {
- if (VALID_CALLBACK(uvd, setupOnOpen)) {
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev,
- "%s: setupOnOpen callback\n",
- __func__);
- errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd);
- if (errCode < 0) {
- err("%s: setupOnOpen callback failed (%d.).",
- __func__, errCode);
- } else if (uvd->debug > 1) {
- dev_info(&uvd->dev->dev,
- "%s: setupOnOpen callback successful\n",
- __func__);
- }
- }
- if (errCode == 0) {
- uvd->settingsAdjusted = 0;
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev,
- "%s: Open succeeded.\n",
- __func__);
- uvd->user++;
- file->private_data = uvd;
- }
- }
- }
- mutex_unlock(&uvd->lock);
- if (errCode != 0)
- usbvideo_ClientDecModCount(uvd);
- if (uvd->debug > 0)
- dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__,
- errCode);
- return errCode;
-}
-
-/*
- * usbvideo_v4l_close()
- *
- * This is part of Video 4 Linux API. The procedure
- * stops streaming and deallocates all buffers that were earlier
- * allocated in usbvideo_v4l_open().
- *
- * History:
- * 22-Jan-2000 Moved scratch buffer deallocation here.
- * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
- * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep.
- */
-static int usbvideo_v4l_close(struct file *file)
-{
- struct video_device *dev = file->private_data;
- struct uvd *uvd = (struct uvd *) dev;
- int i;
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
-
- mutex_lock(&uvd->lock);
- GET_CALLBACK(uvd, stopDataPump)(uvd);
- usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
- uvd->fbuf = NULL;
- RingQueue_Free(&uvd->dp);
-
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- kfree(uvd->sbuf[i].data);
- uvd->sbuf[i].data = NULL;
- }
-
-#if USBVIDEO_REPORT_STATS
- usbvideo_ReportStatistics(uvd);
-#endif
-
- uvd->user--;
- if (uvd->remove_pending) {
- if (uvd->debug > 0)
- dev_info(&uvd->dev->dev, "%s: Final disconnect.\n",
- __func__);
- usbvideo_CameraRelease(uvd);
- }
- mutex_unlock(&uvd->lock);
- usbvideo_ClientDecModCount(uvd);
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__);
- file->private_data = NULL;
- return 0;
-}
-
-/*
- * usbvideo_v4l_ioctl()
- *
- * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
- *
- * History:
- * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings.
- */
-static long usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
- struct uvd *uvd = file->private_data;
-
- if (!CAMERA_IS_OPERATIONAL(uvd))
- return -EIO;
-
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability *b = arg;
- *b = uvd->vcap;
- return 0;
- }
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
- *v = uvd->vchan;
- return 0;
- }
- case VIDIOCSCHAN:
- {
- struct video_channel *v = arg;
- if (v->channel != 0)
- return -EINVAL;
- return 0;
- }
- case VIDIOCGPICT:
- {
- struct video_picture *pic = arg;
- *pic = uvd->vpic;
- return 0;
- }
- case VIDIOCSPICT:
- {
- struct video_picture *pic = arg;
- /*
- * Use temporary 'video_picture' structure to preserve our
- * own settings (such as color depth, palette) that we
- * aren't allowing everyone (V4L client) to change.
- */
- uvd->vpic.brightness = pic->brightness;
- uvd->vpic.hue = pic->hue;
- uvd->vpic.colour = pic->colour;
- uvd->vpic.contrast = pic->contrast;
- uvd->settingsAdjusted = 0; /* Will force new settings */
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window *vw = arg;
-
- if(VALID_CALLBACK(uvd, setVideoMode)) {
- return GET_CALLBACK(uvd, setVideoMode)(uvd, vw);
- }
-
- if (vw->flags)
- return -EINVAL;
- if (vw->clipcount)
- return -EINVAL;
- if (vw->width != VIDEOSIZE_X(uvd->canvas))
- return -EINVAL;
- if (vw->height != VIDEOSIZE_Y(uvd->canvas))
- return -EINVAL;
-
- return 0;
- }
- case VIDIOCGWIN:
- {
- struct video_window *vw = arg;
-
- vw->x = 0;
- vw->y = 0;
- vw->width = VIDEOSIZE_X(uvd->videosize);
- vw->height = VIDEOSIZE_Y(uvd->videosize);
- vw->chromakey = 0;
- if (VALID_CALLBACK(uvd, getFPS))
- vw->flags = GET_CALLBACK(uvd, getFPS)(uvd);
- else
- vw->flags = 10; /* FIXME: do better! */
- return 0;
- }
- case VIDIOCGMBUF:
- {
- struct video_mbuf *vm = arg;
- int i;
-
- memset(vm, 0, sizeof(*vm));
- vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES;
- vm->frames = USBVIDEO_NUMFRAMES;
- for(i = 0; i < USBVIDEO_NUMFRAMES; i++)
- vm->offsets[i] = i * uvd->max_frame_size;
-
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap *vm = arg;
-
- if (uvd->debug >= 1) {
- dev_info(&uvd->dev->dev,
- "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n",
- vm->frame, vm->width, vm->height, vm->format);
- }
- /*
- * Check if the requested size is supported. If the requestor
- * requests too big a frame then we may be tricked into accessing
- * outside of own preallocated frame buffer (in uvd->frame).
- * This will cause oops or a security hole. Theoretically, we
- * could only clamp the size down to acceptable bounds, but then
- * we'd need to figure out how to insert our smaller buffer into
- * larger caller's buffer... this is not an easy question. So we
- * here just flatly reject too large requests, assuming that the
- * caller will resubmit with smaller size. Callers should know
- * what size we support (returned by VIDIOCGCAP). However vidcat,
- * for one, does not care and allows to ask for any size.
- */
- if ((vm->width > VIDEOSIZE_X(uvd->canvas)) ||
- (vm->height > VIDEOSIZE_Y(uvd->canvas))) {
- if (uvd->debug > 0) {
- dev_info(&uvd->dev->dev,
- "VIDIOCMCAPTURE: Size=%dx%d "
- "too large; allowed only up "
- "to %ldx%ld\n", vm->width,
- vm->height,
- VIDEOSIZE_X(uvd->canvas),
- VIDEOSIZE_Y(uvd->canvas));
- }
- return -EINVAL;
- }
- /* Check if the palette is supported */
- if (((1L << vm->format) & uvd->paletteBits) == 0) {
- if (uvd->debug > 0) {
- dev_info(&uvd->dev->dev,
- "VIDIOCMCAPTURE: format=%d. "
- "not supported "
- "(paletteBits=$%08lx)\n",
- vm->format, uvd->paletteBits);
- }
- return -EINVAL;
- }
- if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) {
- err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1);
- return -EINVAL;
- }
- if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) {
- /* Not an error - can happen */
- }
- uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height);
- uvd->frame[vm->frame].palette = vm->format;
-
- /* Mark it as ready */
- uvd->frame[vm->frame].frameState = FrameState_Ready;
-
- return usbvideo_NewFrame(uvd, vm->frame);
- }
- case VIDIOCSYNC:
- {
- int *frameNum = arg;
- int ret;
-
- if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES)
- return -EINVAL;
-
- if (uvd->debug >= 1)
- dev_info(&uvd->dev->dev,
- "VIDIOCSYNC: syncing to frame %d.\n",
- *frameNum);
- if (uvd->flags & FLAGS_NO_DECODING)
- ret = usbvideo_GetFrame(uvd, *frameNum);
- else if (VALID_CALLBACK(uvd, getFrame)) {
- ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum);
- if ((ret < 0) && (uvd->debug >= 1)) {
- err("VIDIOCSYNC: getFrame() returned %d.", ret);
- }
- } else {
- err("VIDIOCSYNC: getFrame is not set");
- ret = -EFAULT;
- }
-
- /*
- * The frame is in FrameState_Done_Hold state. Release it
- * right now because its data is already mapped into
- * the user space and it's up to the application to
- * make use of it until it asks for another frame.
- */
- uvd->frame[*frameNum].frameState = FrameState_Unused;
- return ret;
- }
- case VIDIOCGFBUF:
- {
- struct video_buffer *vb = arg;
-
- memset(vb, 0, sizeof(*vb));
- return 0;
- }
- case VIDIOCKEY:
- return 0;
-
- case VIDIOCCAPTURE:
- return -EINVAL;
-
- case VIDIOCSFBUF:
-
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
-
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
-
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return -EINVAL;
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static long usbvideo_v4l_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl);
-}
-
-/*
- * usbvideo_v4l_read()
- *
- * This is mostly boring stuff. We simply ask for a frame and when it
- * arrives copy all the video data from it into user space. There is
- * no obvious need to override this method.
- *
- * History:
- * 20-Oct-2000 Created.
- * 01-Nov-2000 Added mutex (uvd->lock).
- */
-static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct uvd *uvd = file->private_data;
- int noblock = file->f_flags & O_NONBLOCK;
- int frmx = -1, i;
- struct usbvideo_frame *frame;
-
- if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL))
- return -EFAULT;
-
- if (uvd->debug >= 1)
- dev_info(&uvd->dev->dev,
- "%s: %Zd. bytes, noblock=%d.\n",
- __func__, count, noblock);
-
- mutex_lock(&uvd->lock);
-
- /* See if a frame is completed, then use it. */
- for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
- if ((uvd->frame[i].frameState == FrameState_Done) ||
- (uvd->frame[i].frameState == FrameState_Done_Hold) ||
- (uvd->frame[i].frameState == FrameState_Error)) {
- frmx = i;
- break;
- }
- }
-
- /* FIXME: If we don't start a frame here then who ever does? */
- if (noblock && (frmx == -1)) {
- count = -EAGAIN;
- goto read_done;
- }
-
- /*
- * If no FrameState_Done, look for a FrameState_Grabbing state.
- * See if a frame is in process (grabbing), then use it.
- * We will need to wait until it becomes cooked, of course.
- */
- if (frmx == -1) {
- for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
- if (uvd->frame[i].frameState == FrameState_Grabbing) {
- frmx = i;
- break;
- }
- }
- }
-
- /*
- * If no frame is active, start one. We don't care which one
- * it will be, so #0 is as good as any.
- * In read access mode we don't have convenience of VIDIOCMCAPTURE
- * to specify the requested palette (video format) on per-frame
- * basis. This means that we have to return data in -some- format
- * and just hope that the client knows what to do with it.
- * The default format is configured in uvd->defaultPalette field
- * as one of VIDEO_PALETTE_xxx values. We stuff it into the new
- * frame and initiate the frame filling process.
- */
- if (frmx == -1) {
- if (uvd->defaultPalette == 0) {
- err("%s: No default palette; don't know what to do!", __func__);
- count = -EFAULT;
- goto read_done;
- }
- frmx = 0;
- /*
- * We have no per-frame control over video size.
- * Therefore we only can use whatever size was
- * specified as default.
- */
- uvd->frame[frmx].request = uvd->videosize;
- uvd->frame[frmx].palette = uvd->defaultPalette;
- uvd->frame[frmx].frameState = FrameState_Ready;
- usbvideo_NewFrame(uvd, frmx);
- /* Now frame 0 is supposed to start filling... */
- }
-
- /*
- * Get a pointer to the active frame. It is either previously
- * completed frame or frame in progress but not completed yet.
- */
- frame = &uvd->frame[frmx];
-
- /*
- * Sit back & wait until the frame gets filled and postprocessed.
- * If we fail to get the picture [in time] then return the error.
- * In this call we specify that we want the frame to be waited for,
- * postprocessed and switched into FrameState_Done_Hold state. This
- * state is used to hold the frame as "fully completed" between
- * subsequent partial reads of the same frame.
- */
- if (frame->frameState != FrameState_Done_Hold) {
- long rv = -EFAULT;
- if (uvd->flags & FLAGS_NO_DECODING)
- rv = usbvideo_GetFrame(uvd, frmx);
- else if (VALID_CALLBACK(uvd, getFrame))
- rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx);
- else
- err("getFrame is not set");
- if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) {
- count = rv;
- goto read_done;
- }
- }
-
- /*
- * Copy bytes to user space. We allow for partial reads, which
- * means that the user application can request read less than
- * the full frame size. It is up to the application to issue
- * subsequent calls until entire frame is read.
- *
- * First things first, make sure we don't copy more than we
- * have - even if the application wants more. That would be
- * a big security embarassment!
- */
- if ((count + frame->seqRead_Index) > frame->seqRead_Length)
- count = frame->seqRead_Length - frame->seqRead_Index;
-
- /*
- * Copy requested amount of data to user space. We start
- * copying from the position where we last left it, which
- * will be zero for a new frame (not read before).
- */
- if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) {
- count = -EFAULT;
- goto read_done;
- }
-
- /* Update last read position */
- frame->seqRead_Index += count;
- if (uvd->debug >= 1) {
- err("%s: {copy} count used=%Zd, new seqRead_Index=%ld",
- __func__, count, frame->seqRead_Index);
- }
-
- /* Finally check if the frame is done with and "release" it */
- if (frame->seqRead_Index >= frame->seqRead_Length) {
- /* All data has been read */
- frame->seqRead_Index = 0;
-
- /* Mark it as available to be used again. */
- uvd->frame[frmx].frameState = FrameState_Unused;
- if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) {
- err("%s: usbvideo_NewFrame failed.", __func__);
- }
- }
-read_done:
- mutex_unlock(&uvd->lock);
- return count;
-}
-
-/*
- * Make all of the blocks of data contiguous
- */
-static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb)
-{
- char *cdata;
- int i, totlen = 0;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int n = urb->iso_frame_desc[i].actual_length;
- int st = urb->iso_frame_desc[i].status;
-
- cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-
- /* Detect and ignore errored packets */
- if (st < 0) {
- if (uvd->debug >= 1)
- err("Data error: packet=%d. len=%d. status=%d.", i, n, st);
- uvd->stats.iso_err_count++;
- continue;
- }
-
- /* Detect and ignore empty packets */
- if (n <= 0) {
- uvd->stats.iso_skip_count++;
- continue;
- }
- totlen += n; /* Little local accounting */
- RingQueue_Enqueue(&uvd->dp, cdata, n);
- }
- return totlen;
-}
-
-static void usbvideo_IsocIrq(struct urb *urb)
-{
- int i, ret, len;
- struct uvd *uvd = urb->context;
-
- /* We don't want to do anything if we are about to be removed! */
- if (!CAMERA_IS_OPERATIONAL(uvd))
- return;
-#if 0
- if (urb->actual_length > 0) {
- dev_info(&uvd->dev->dev,
- "urb=$%p status=%d. errcount=%d. length=%d.\n",
- urb, urb->status, urb->error_count,
- urb->actual_length);
- } else {
- static int c = 0;
- if (c++ % 100 == 0)
- dev_info(&uvd->dev->dev, "No Isoc data\n");
- }
-#endif
-
- if (!uvd->streaming) {
- if (uvd->debug >= 1)
- dev_info(&uvd->dev->dev,
- "Not streaming, but interrupt!\n");
- return;
- }
-
- uvd->stats.urb_count++;
- if (urb->actual_length <= 0)
- goto urb_done_with;
-
- /* Copy the data received into ring queue */
- len = usbvideo_CompressIsochronous(uvd, urb);
- uvd->stats.urb_length = len;
- if (len <= 0)
- goto urb_done_with;
-
- /* Here we got some data */
- uvd->stats.data_count += len;
- RingQueue_WakeUpInterruptible(&uvd->dp);
-
-urb_done_with:
- for (i = 0; i < FRAMES_PER_DESC; i++) {
- urb->iso_frame_desc[i].status = 0;
- urb->iso_frame_desc[i].actual_length = 0;
- }
- urb->status = 0;
- urb->dev = uvd->dev;
- ret = usb_submit_urb (urb, GFP_KERNEL);
- if(ret)
- err("usb_submit_urb error (%d)", ret);
- return;
-}
-
-/*
- * usbvideo_StartDataPump()
- *
- * History:
- * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead
- * of hardcoded values. Simplified by using for loop,
- * allowed any number of URBs.
- */
-static int usbvideo_StartDataPump(struct uvd *uvd)
-{
- struct usb_device *dev = uvd->dev;
- int i, errFlag;
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd);
-
- if (!CAMERA_IS_OPERATIONAL(uvd)) {
- err("%s: Camera is not operational", __func__);
- return -EFAULT;
- }
- uvd->curframe = -1;
-
- /* Alternate interface 1 is is the biggest frame size */
- i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
- if (i < 0) {
- err("%s: usb_set_interface error", __func__);
- uvd->last_error = i;
- return -EBUSY;
- }
- if (VALID_CALLBACK(uvd, videoStart))
- GET_CALLBACK(uvd, videoStart)(uvd);
- else
- err("%s: videoStart not set", __func__);
-
- /* We double buffer the Iso lists */
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- int j, k;
- struct urb *urb = uvd->sbuf[i].urb;
- urb->dev = dev;
- urb->context = uvd;
- urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
- urb->interval = 1;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->transfer_buffer = uvd->sbuf[i].data;
- urb->complete = usbvideo_IsocIrq;
- urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC;
- for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) {
- urb->iso_frame_desc[j].offset = k;
- urb->iso_frame_desc[j].length = uvd->iso_packet_len;
- }
- }
-
- /* Submit all URBs */
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
- if (errFlag)
- err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag);
- }
-
- uvd->streaming = 1;
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev,
- "%s: streaming=1 video_endp=$%02x\n", __func__,
- uvd->video_endp);
- return 0;
-}
-
-/*
- * usbvideo_StopDataPump()
- *
- * This procedure stops streaming and deallocates URBs. Then it
- * activates zero-bandwidth alt. setting of the video interface.
- *
- * History:
- * 22-Jan-2000 Corrected order of actions to work after surprise removal.
- * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
- */
-static void usbvideo_StopDataPump(struct uvd *uvd)
-{
- int i, j;
-
- if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
- return;
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd);
-
- /* Unschedule all of the iso td's */
- for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- usb_kill_urb(uvd->sbuf[i].urb);
- }
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__);
- uvd->streaming = 0;
-
- if (!uvd->remove_pending) {
- /* Invoke minidriver's magic to stop the camera */
- if (VALID_CALLBACK(uvd, videoStop))
- GET_CALLBACK(uvd, videoStop)(uvd);
- else
- err("%s: videoStop not set", __func__);
-
- /* Set packet size to 0 */
- j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
- if (j < 0) {
- err("%s: usb_set_interface() error %d.", __func__, j);
- uvd->last_error = j;
- }
- }
-}
-
-/*
- * usbvideo_NewFrame()
- *
- * History:
- * 29-Mar-00 Added copying of previous frame into the current one.
- * 6-Aug-00 Added model 3 video sizes, removed redundant width, height.
- */
-static int usbvideo_NewFrame(struct uvd *uvd, int framenum)
-{
- struct usbvideo_frame *frame;
- int n;
-
- if (uvd->debug > 1)
- dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd,
- framenum);
-
- /* If we're not grabbing a frame right now and the other frame is */
- /* ready to be grabbed into, then use it instead */
- if (uvd->curframe != -1)
- return 0;
-
- /* If necessary we adjust picture settings between frames */
- if (!uvd->settingsAdjusted) {
- if (VALID_CALLBACK(uvd, adjustPicture))
- GET_CALLBACK(uvd, adjustPicture)(uvd);
- uvd->settingsAdjusted = 1;
- }
-
- n = (framenum + 1) % USBVIDEO_NUMFRAMES;
- if (uvd->frame[n].frameState == FrameState_Ready)
- framenum = n;
-
- frame = &uvd->frame[framenum];
-
- frame->frameState = FrameState_Grabbing;
- frame->scanstate = ScanState_Scanning;
- frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */
- frame->deinterlace = Deinterlace_None;
- frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */
- uvd->curframe = framenum;
-
- /*
- * Normally we would want to copy previous frame into the current one
- * before we even start filling it with data; this allows us to stop
- * filling at any moment; top portion of the frame will be new and
- * bottom portion will stay as it was in previous frame. If we don't
- * do that then missing chunks of video stream will result in flickering
- * portions of old data whatever it was before.
- *
- * If we choose not to copy previous frame (to, for example, save few
- * bus cycles - the frame can be pretty large!) then we have an option
- * to clear the frame before using. If we experience losses in this
- * mode then missing picture will be black (no flickering).
- *
- * Finally, if user chooses not to clean the current frame before
- * filling it with data then the old data will be visible if we fail
- * to refill entire frame with new data.
- */
- if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) {
- /* This copies previous frame into this one to mask losses */
- int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES;
- memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size);
- } else {
- if (uvd->flags & FLAGS_CLEAN_FRAMES) {
- /* This provides a "clean" frame but slows things down */
- memset(frame->data, 0, uvd->max_frame_size);
- }
- }
- return 0;
-}
-
-/*
- * usbvideo_CollectRawData()
- *
- * This procedure can be used instead of 'processData' callback if you
- * only want to dump the raw data from the camera into the output
- * device (frame buffer). You can look at it with V4L client, but the
- * image will be unwatchable. The main purpose of this code and of the
- * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from
- * new, unknown cameras. This procedure will be automatically invoked
- * instead of the specified callback handler when uvd->flags has bit
- * FLAGS_NO_DECODING set. Therefore, any regular build of any driver
- * based on usbvideo can use this feature at any time.
- */
-static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame)
-{
- int n;
-
- assert(uvd != NULL);
- assert(frame != NULL);
-
- /* Try to move data from queue into frame buffer */
- n = RingQueue_GetLength(&uvd->dp);
- if (n > 0) {
- int m;
- /* See how much space we have left */
- m = uvd->max_frame_size - frame->seqRead_Length;
- if (n > m)
- n = m;
- /* Now move that much data into frame buffer */
- RingQueue_Dequeue(
- &uvd->dp,
- frame->data + frame->seqRead_Length,
- m);
- frame->seqRead_Length += m;
- }
- /* See if we filled the frame */
- if (frame->seqRead_Length >= uvd->max_frame_size) {
- frame->frameState = FrameState_Done;
- uvd->curframe = -1;
- uvd->stats.frame_num++;
- }
-}
-
-static int usbvideo_GetFrame(struct uvd *uvd, int frameNum)
-{
- struct usbvideo_frame *frame = &uvd->frame[frameNum];
-
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd,
- frameNum);
-
- switch (frame->frameState) {
- case FrameState_Unused:
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n",
- __func__);
- return -EINVAL;
- case FrameState_Ready:
- case FrameState_Grabbing:
- case FrameState_Error:
- {
- int ntries, signalPending;
- redo:
- if (!CAMERA_IS_OPERATIONAL(uvd)) {
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Camera is not operational (1)\n",
- __func__);
- return -EIO;
- }
- ntries = 0;
- do {
- RingQueue_InterruptibleSleepOn(&uvd->dp);
- signalPending = signal_pending(current);
- if (!CAMERA_IS_OPERATIONAL(uvd)) {
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Camera is not "
- "operational (2)\n", __func__);
- return -EIO;
- }
- assert(uvd->fbuf != NULL);
- if (signalPending) {
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Signal=$%08x\n", __func__,
- signalPending);
- if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) {
- usbvideo_TestPattern(uvd, 1, 0);
- uvd->curframe = -1;
- uvd->stats.frame_num++;
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Forced test "
- "pattern screen\n",
- __func__);
- return 0;
- } else {
- /* Standard answer: Interrupted! */
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Interrupted!\n",
- __func__);
- return -EINTR;
- }
- } else {
- /* No signals - we just got new data in dp queue */
- if (uvd->flags & FLAGS_NO_DECODING)
- usbvideo_CollectRawData(uvd, frame);
- else if (VALID_CALLBACK(uvd, processData))
- GET_CALLBACK(uvd, processData)(uvd, frame);
- else
- err("%s: processData not set", __func__);
- }
- } while (frame->frameState == FrameState_Grabbing);
- if (uvd->debug >= 2) {
- dev_info(&uvd->dev->dev,
- "%s: Grabbing done; state=%d. (%lu. bytes)\n",
- __func__, frame->frameState,
- frame->seqRead_Length);
- }
- if (frame->frameState == FrameState_Error) {
- int ret = usbvideo_NewFrame(uvd, frameNum);
- if (ret < 0) {
- err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret);
- return ret;
- }
- goto redo;
- }
- /* Note that we fall through to meet our destiny below */
- }
- case FrameState_Done:
- /*
- * Do all necessary postprocessing of data prepared in
- * "interrupt" code and the collecting code above. The
- * frame gets marked as FrameState_Done by queue parsing code.
- * This status means that we collected enough data and
- * most likely processed it as we went through. However
- * the data may need postprocessing, such as deinterlacing
- * or picture adjustments implemented in software (horror!)
- *
- * As soon as the frame becomes "final" it gets promoted to
- * FrameState_Done_Hold status where it will remain until the
- * caller consumed all the video data from the frame. Then
- * the empty shell of ex-frame is thrown out for dogs to eat.
- * But we, worried about pets, will recycle the frame!
- */
- uvd->stats.frame_num++;
- if ((uvd->flags & FLAGS_NO_DECODING) == 0) {
- if (VALID_CALLBACK(uvd, postProcess))
- GET_CALLBACK(uvd, postProcess)(uvd, frame);
- if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST)
- usbvideo_SoftwareContrastAdjustment(uvd, frame);
- }
- frame->frameState = FrameState_Done_Hold;
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: Entered FrameState_Done_Hold state.\n",
- __func__);
- return 0;
-
- case FrameState_Done_Hold:
- /*
- * We stay in this state indefinitely until someone external,
- * like ioctl() or read() call finishes digesting the frame
- * data. Then it will mark the frame as FrameState_Unused and
- * it will be released back into the wild to roam freely.
- */
- if (uvd->debug >= 2)
- dev_info(&uvd->dev->dev,
- "%s: FrameState_Done_Hold state.\n",
- __func__);
- return 0;
- }
-
- /* Catch-all for other cases. We shall not be here. */
- err("%s: Invalid state %d.", __func__, frame->frameState);
- frame->frameState = FrameState_Unused;
- return 0;
-}
-
-/*
- * usbvideo_DeinterlaceFrame()
- *
- * This procedure deinterlaces the given frame. Some cameras produce
- * only half of scanlines - sometimes only even lines, sometimes only
- * odd lines. The deinterlacing method is stored in frame->deinterlace
- * variable.
- *
- * Here we scan the frame vertically and replace missing scanlines with
- * average between surrounding ones - before and after. If we have no
- * line above then we just copy next line. Similarly, if we need to
- * create a last line then preceding line is used.
- */
-void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame)
-{
- if ((uvd == NULL) || (frame == NULL))
- return;
-
- if ((frame->deinterlace == Deinterlace_FillEvenLines) ||
- (frame->deinterlace == Deinterlace_FillOddLines))
- {
- const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
- int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1;
-
- for (; i < VIDEOSIZE_Y(frame->request); i += 2) {
- const unsigned char *fs1, *fs2;
- unsigned char *fd;
- int ip, in, j; /* Previous and next lines */
-
- /*
- * Need to average lines before and after 'i'.
- * If we go out of bounds seeking those lines then
- * we point back to existing line.
- */
- ip = i - 1; /* First, get rough numbers */
- in = i + 1;
-
- /* Now validate */
- if (ip < 0)
- ip = in;
- if (in >= VIDEOSIZE_Y(frame->request))
- in = ip;
-
- /* Sanity check */
- if ((ip < 0) || (in < 0) ||
- (ip >= VIDEOSIZE_Y(frame->request)) ||
- (in >= VIDEOSIZE_Y(frame->request)))
- {
- err("Error: ip=%d. in=%d. req.height=%ld.",
- ip, in, VIDEOSIZE_Y(frame->request));
- break;
- }
-
- /* Now we need to average lines 'ip' and 'in' to produce line 'i' */
- fs1 = frame->data + (v4l_linesize * ip);
- fs2 = frame->data + (v4l_linesize * in);
- fd = frame->data + (v4l_linesize * i);
-
- /* Average lines around destination */
- for (j=0; j < v4l_linesize; j++) {
- fd[j] = (unsigned char)((((unsigned) fs1[j]) +
- ((unsigned)fs2[j])) >> 1);
- }
- }
- }
-
- /* Optionally display statistics on the screen */
- if (uvd->flags & FLAGS_OVERLAY_STATS)
- usbvideo_OverlayStats(uvd, frame);
-}
-
-EXPORT_SYMBOL(usbvideo_DeinterlaceFrame);
-
-/*
- * usbvideo_SoftwareContrastAdjustment()
- *
- * This code adjusts the contrast of the frame, assuming RGB24 format.
- * As most software image processing, this job is CPU-intensive.
- * Get a camera that supports hardware adjustment!
- *
- * History:
- * 09-Feb-2001 Created.
- */
-static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd,
- struct usbvideo_frame *frame)
-{
- int i, j, v4l_linesize;
- signed long adj;
- const int ccm = 128; /* Color correction median - see below */
-
- if ((uvd == NULL) || (frame == NULL)) {
- err("%s: Illegal call.", __func__);
- return;
- }
- adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
- RESTRICT_TO_RANGE(adj, -ccm, ccm+1);
- if (adj == 0) {
- /* In rare case of no adjustment */
- return;
- }
- v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
- for (i=0; i < VIDEOSIZE_Y(frame->request); i++) {
- unsigned char *fd = frame->data + (v4l_linesize * i);
- for (j=0; j < v4l_linesize; j++) {
- signed long v = (signed long) fd[j];
- /* Magnify up to 2 times, reduce down to zero */
- v = 128 + ((ccm + adj) * (v - 128)) / ccm;
- RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */
- fd[j] = (unsigned char) v;
- }
- }
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/usbvideo/usbvideo.h b/drivers/staging/usbvideo/usbvideo.h
deleted file mode 100644
index 95638a072b19..000000000000
--- a/drivers/staging/usbvideo/usbvideo.h
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 usbvideo_h
-#define usbvideo_h
-
-#include "videodev.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/usb.h>
-#include <linux/mutex.h>
-
-/* Most helpful debugging aid */
-#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
-
-#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */
-
-/* Bit flags (options) */
-#define FLAGS_RETRY_VIDIOCSYNC (1 << 0)
-#define FLAGS_MONOCHROME (1 << 1)
-#define FLAGS_DISPLAY_HINTS (1 << 2)
-#define FLAGS_OVERLAY_STATS (1 << 3)
-#define FLAGS_FORCE_TESTPATTERN (1 << 4)
-#define FLAGS_SEPARATE_FRAMES (1 << 5)
-#define FLAGS_CLEAN_FRAMES (1 << 6)
-#define FLAGS_NO_DECODING (1 << 7)
-
-/* Bit flags for frames (apply to the frame where they are specified) */
-#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0)
-
-/* Camera capabilities (maximum) */
-#define CAMERA_URB_FRAMES 32
-#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */
-#define FRAMES_PER_DESC (CAMERA_URB_FRAMES)
-#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET)
-
-/* This macro restricts an int variable to an inclusive range */
-#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
-
-#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */
-
-/*
- * Use this macro to construct constants for different video sizes.
- * We have to deal with different video sizes that have to be
- * configured in the device or compared against when we receive
- * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y
- * #defines and that's the end of story. However this solution
- * does not allow to convert between real pixel sizes and the
- * constant (integer) value that may be used to tag a frame or
- * whatever. The set of macros below constructs videosize constants
- * from the pixel size and allows to reconstruct the pixel size
- * from the combined value later.
- */
-#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16))
-#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL)
-#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL)
-typedef unsigned long videosize_t;
-
-/*
- * This macro checks if the camera is still operational. The 'uvd'
- * pointer must be valid, uvd->dev must be valid, we are not
- * removing the device and the device has not erred on us.
- */
-#define CAMERA_IS_OPERATIONAL(uvd) (\
- (uvd != NULL) && \
- ((uvd)->dev != NULL) && \
- ((uvd)->last_error == 0) && \
- (!(uvd)->remove_pending))
-
-/*
- * We use macros to do YUV -> RGB conversion because this is
- * very important for speed and totally unimportant for size.
- *
- * YUV -> RGB Conversion
- * ---------------------
- *
- * B = 1.164*(Y-16) + 2.018*(V-128)
- * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
- * R = 1.164*(Y-16) + 1.596*(U-128)
- *
- * If you fancy integer arithmetics (as you should), hear this:
- *
- * 65536*B = 76284*(Y-16) + 132252*(V-128)
- * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
- * 65536*R = 76284*(Y-16) + 104595*(U-128)
- *
- * Make sure the output values are within [0..255] range.
- */
-#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
-#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
- int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
- mm_y = (my) - 16; \
- mm_u = (mu) - 128; \
- mm_v = (mv) - 128; \
- mm_yc= mm_y * 76284; \
- mm_b = (mm_yc + 132252*mm_v ) >> 16; \
- mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \
- mm_r = (mm_yc + 104595*mm_u ) >> 16; \
- mb = LIMIT_RGB(mm_b); \
- mg = LIMIT_RGB(mm_g); \
- mr = LIMIT_RGB(mm_r); \
-}
-
-#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */
-#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1)
-#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n)
-#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)])
-
-struct RingQueue {
- unsigned char *queue; /* Data from the Isoc data pump */
- int length; /* How many bytes allocated for the queue */
- int wi; /* That's where we write */
- int ri; /* Read from here until you hit write index */
- wait_queue_head_t wqh; /* Processes waiting */
-};
-
-enum ScanState {
- ScanState_Scanning, /* Scanning for header */
- ScanState_Lines /* Parsing lines */
-};
-
-/* Completion states of the data parser */
-enum ParseState {
- scan_Continue, /* Just parse next item */
- scan_NextFrame, /* Frame done, send it to V4L */
- scan_Out, /* Not enough data for frame */
- scan_EndParse /* End parsing */
-};
-
-enum FrameState {
- FrameState_Unused, /* Unused (no MCAPTURE) */
- FrameState_Ready, /* Ready to start grabbing */
- FrameState_Grabbing, /* In the process of being grabbed into */
- FrameState_Done, /* Finished grabbing, but not been synced yet */
- FrameState_Done_Hold, /* Are syncing or reading */
- FrameState_Error, /* Something bad happened while processing */
-};
-
-/*
- * Some frames may contain only even or odd lines. This type
- * specifies what type of deinterlacing is required.
- */
-enum Deinterlace {
- Deinterlace_None=0,
- Deinterlace_FillOddLines,
- Deinterlace_FillEvenLines
-};
-
-#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */
-#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */
-
-/* This structure represents one Isoc request - URB and buffer */
-struct usbvideo_sbuf {
- char *data;
- struct urb *urb;
-};
-
-struct usbvideo_frame {
- char *data; /* Frame buffer */
- unsigned long header; /* Significant bits from the header */
-
- videosize_t canvas; /* The canvas (max. image) allocated */
- videosize_t request; /* That's what the application asked for */
- unsigned short palette; /* The desired format */
-
- enum FrameState frameState;/* State of grabbing */
- enum ScanState scanstate; /* State of scanning */
- enum Deinterlace deinterlace;
- int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */
-
- int curline; /* Line of frame we're working on */
-
- long seqRead_Length; /* Raw data length of frame */
- long seqRead_Index; /* Amount of data that has been already read */
-
- void *user; /* Additional data that user may need */
-};
-
-/* Statistics that can be overlaid on screen */
-struct usbvideo_statistics {
- unsigned long frame_num; /* Sequential number of the frame */
- unsigned long urb_count; /* How many URBs we received so far */
- unsigned long urb_length; /* Length of last URB */
- unsigned long data_count; /* How many bytes we received */
- unsigned long header_count; /* How many frame headers we found */
- unsigned long iso_skip_count; /* How many empty ISO packets received */
- unsigned long iso_err_count; /* How many bad ISO packets received */
-};
-
-struct usbvideo;
-
-struct uvd {
- struct video_device vdev; /* Must be the first field! */
- struct usb_device *dev;
- struct usbvideo *handle; /* Points back to the struct usbvideo */
- void *user_data; /* Camera-dependent data */
- int user_size; /* Size of that camera-dependent data */
- int debug; /* Debug level for usbvideo */
- unsigned char iface; /* Video interface number */
- unsigned char video_endp;
- unsigned char ifaceAltActive;
- unsigned char ifaceAltInactive; /* Alt settings */
- unsigned long flags; /* FLAGS_USBVIDEO_xxx */
- unsigned long paletteBits; /* Which palettes we accept? */
- unsigned short defaultPalette; /* What palette to use for read() */
- struct mutex lock;
- int user; /* user count for exclusive use */
-
- videosize_t videosize; /* Current setting */
- videosize_t canvas; /* This is the width,height of the V4L canvas */
- int max_frame_size; /* Bytes in one video frame */
-
- int uvd_used; /* Is this structure in use? */
- int streaming; /* Are we streaming Isochronous? */
- int grabbing; /* Are we grabbing? */
- int settingsAdjusted; /* Have we adjusted contrast etc.? */
- int last_error; /* What calamity struck us? */
-
- char *fbuf; /* Videodev buffer area */
- int fbuf_size; /* Videodev buffer size */
-
- int curframe;
- int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */
-
- struct RingQueue dp; /* Isoc data pump */
- struct usbvideo_frame frame[USBVIDEO_NUMFRAMES];
- struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF];
-
- volatile int remove_pending; /* If set then about to exit */
-
- struct video_picture vpic, vpic_old; /* Picture settings */
- struct video_capability vcap; /* Video capabilities */
- struct video_channel vchan; /* May be used for tuner support */
- struct usbvideo_statistics stats;
- char videoName[32]; /* Holds name like "video7" */
-};
-
-/*
- * usbvideo callbacks (virtual methods). They are set when usbvideo
- * services are registered. All of these default to NULL, except those
- * that default to usbvideo-provided methods.
- */
-struct usbvideo_cb {
- int (*probe)(struct usb_interface *, const struct usb_device_id *);
- void (*userFree)(struct uvd *);
- void (*disconnect)(struct usb_interface *);
- int (*setupOnOpen)(struct uvd *);
- void (*videoStart)(struct uvd *);
- void (*videoStop)(struct uvd *);
- void (*processData)(struct uvd *, struct usbvideo_frame *);
- void (*postProcess)(struct uvd *, struct usbvideo_frame *);
- void (*adjustPicture)(struct uvd *);
- int (*getFPS)(struct uvd *);
- int (*overlayHook)(struct uvd *, struct usbvideo_frame *);
- int (*getFrame)(struct uvd *, int);
- int (*startDataPump)(struct uvd *uvd);
- void (*stopDataPump)(struct uvd *uvd);
- int (*setVideoMode)(struct uvd *uvd, struct video_window *vw);
-};
-
-struct usbvideo {
- int num_cameras; /* As allocated */
- struct usb_driver usbdrv; /* Interface to the USB stack */
- char drvName[80]; /* Driver name */
- struct mutex lock; /* Mutex protecting camera structures */
- struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */
- struct video_device vdt; /* Video device template */
- struct uvd *cam; /* Array of camera structures */
- struct module *md_module; /* Minidriver module */
-};
-
-
-/*
- * This macro retrieves callback address from the struct uvd object.
- * No validity checks are done here, so be sure to check the
- * callback beforehand with VALID_CALLBACK.
- */
-#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName)
-
-/*
- * This macro returns either callback pointer or NULL. This is safe
- * macro, meaning that most of components of data structures involved
- * may be NULL - this only results in NULL being returned. You may
- * wish to use this macro to make sure that the callback is callable.
- * However keep in mind that those checks take time.
- */
-#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
- ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
-
-int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len);
-int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n);
-void RingQueue_WakeUpInterruptible(struct RingQueue *rq);
-void RingQueue_Flush(struct RingQueue *rq);
-
-static inline int RingQueue_GetLength(const struct RingQueue *rq)
-{
- return (rq->wi - rq->ri + rq->length) & (rq->length-1);
-}
-
-static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq)
-{
- return rq->length - RingQueue_GetLength(rq);
-}
-
-void usbvideo_DrawLine(
- struct usbvideo_frame *frame,
- int x1, int y1,
- int x2, int y2,
- unsigned char cr, unsigned char cg, unsigned char cb);
-void usbvideo_HexDump(const unsigned char *data, int len);
-void usbvideo_SayAndWait(const char *what);
-void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode);
-
-/* Memory allocation routines */
-unsigned long usbvideo_kvirt_to_pa(unsigned long adr);
-
-int usbvideo_register(
- struct usbvideo **pCams,
- const int num_cams,
- const int num_extra,
- const char *driverName,
- const struct usbvideo_cb *cbTable,
- struct module *md,
- const struct usb_device_id *id_table);
-struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams);
-int usbvideo_RegisterVideoDevice(struct uvd *uvd);
-void usbvideo_Deregister(struct usbvideo **uvt);
-
-int usbvideo_v4l_initialize(struct video_device *dev);
-
-void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame);
-
-/*
- * This code performs bounds checking - use it when working with
- * new formats, or else you may get oopses all over the place.
- * If pixel falls out of bounds then it gets shoved back (as close
- * to place of offence as possible) and is painted bright red.
- *
- * There are two important concepts: frame width, height and
- * V4L canvas width, height. The former is the area requested by
- * the application -for this very frame-. The latter is the largest
- * possible frame that we can serve (we advertise that via V4L ioctl).
- * The frame data is expected to be formatted as lines of length
- * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines.
- */
-static inline void RGB24_PUTPIXEL(
- struct usbvideo_frame *fr,
- int ix, int iy,
- unsigned char vr,
- unsigned char vg,
- unsigned char vb)
-{
- register unsigned char *pf;
- int limiter = 0, mx, my;
- mx = ix;
- my = iy;
- if (mx < 0) {
- mx=0;
- limiter++;
- } else if (mx >= VIDEOSIZE_X((fr)->request)) {
- mx= VIDEOSIZE_X((fr)->request) - 1;
- limiter++;
- }
- if (my < 0) {
- my = 0;
- limiter++;
- } else if (my >= VIDEOSIZE_Y((fr)->request)) {
- my = VIDEOSIZE_Y((fr)->request) - 1;
- limiter++;
- }
- pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix));
- if (limiter) {
- *pf++ = 0;
- *pf++ = 0;
- *pf++ = 0xFF;
- } else {
- *pf++ = (vb);
- *pf++ = (vg);
- *pf++ = (vr);
- }
-}
-
-#endif /* usbvideo_h */
diff --git a/drivers/staging/usbvideo/vicam.c b/drivers/staging/usbvideo/vicam.c
deleted file mode 100644
index ecdb121297c9..000000000000
--- a/drivers/staging/usbvideo/vicam.c
+++ /dev/null
@@ -1,952 +0,0 @@
-/*
- * USB ViCam WebCam driver
- * Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
- * Christopher L Cheney (ccheney@cheney.cx),
- * Pavel Machek (pavel@ucw.cz),
- * John Tyner (jtyner@cs.ucr.edu),
- * Monroe Williams (monroe@pobox.com)
- *
- * Supports 3COM HomeConnect PC Digital WebCam
- * Supports Compro PS39U WebCam
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- *
- * This source code is based heavily on the CPiA webcam driver which was
- * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt
- *
- * Portions of this code were also copied from usbvideo.c
- *
- * Special thanks to the whole team at Sourceforge for help making
- * this driver become a reality. Notably:
- * Andy Armstrong who reverse engineered the color encoding and
- * Pavel Machek and Chris Cheney who worked on reverse engineering the
- * camera controls and wrote the first generation driver.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include "videodev.h"
-#include <linux/usb.h>
-#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/firmware.h>
-#include <linux/ihex.h>
-#include "usbvideo.h"
-
-// #define VICAM_DEBUG
-
-#ifdef VICAM_DEBUG
-#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args)
-#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args)
-#else
-#define DBG(fmn,args...) do {} while(0)
-#endif
-
-#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org"
-#define DRIVER_DESC "ViCam WebCam Driver"
-
-/* Define these values to match your device */
-#define USB_VICAM_VENDOR_ID 0x04c1
-#define USB_VICAM_PRODUCT_ID 0x009d
-#define USB_COMPRO_VENDOR_ID 0x0602
-#define USB_COMPRO_PRODUCT_ID 0x1001
-
-#define VICAM_BYTES_PER_PIXEL 3
-#define VICAM_MAX_READ_SIZE (512*242+128)
-#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240)
-#define VICAM_FRAMES 2
-
-#define VICAM_HEADER_SIZE 64
-
-/* rvmalloc / rvfree copied from usbvideo.c
- *
- * Not sure why these are not yet non-statics which I can reference through
- * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime
- * in the future.
- *
-*/
-static void *rvmalloc(unsigned long size)
-{
- void *mem;
- unsigned long adr;
-
- size = PAGE_ALIGN(size);
- mem = vmalloc_32(size);
- if (!mem)
- return NULL;
-
- memset(mem, 0, size); /* Clear the ram out, no junk to the user */
- adr = (unsigned long) mem;
- while (size > 0) {
- SetPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
- unsigned long adr;
-
- if (!mem)
- return;
-
- adr = (unsigned long) mem;
- while ((long) size > 0) {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vfree(mem);
-}
-
-struct vicam_camera {
- u16 shutter_speed; // capture shutter speed
- u16 gain; // capture gain
-
- u8 *raw_image; // raw data captured from the camera
- u8 *framebuf; // processed data in RGB24 format
- u8 *cntrlbuf; // area used to send control msgs
-
- struct video_device vdev; // v4l video device
- struct usb_device *udev; // usb device
-
- /* guard against simultaneous accesses to the camera */
- struct mutex cam_lock;
-
- int is_initialized;
- u8 open_count;
- u8 bulkEndpoint;
- int needsDummyRead;
-};
-
-static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id);
-static void vicam_disconnect(struct usb_interface *intf);
-static void read_frame(struct vicam_camera *cam, int framenum);
-static void vicam_decode_color(const u8 *, u8 *);
-
-static int __send_control_msg(struct vicam_camera *cam,
- u8 request,
- u16 value,
- u16 index,
- unsigned char *cp,
- u16 size)
-{
- int status;
-
- /* cp must be memory that has been allocated by kmalloc */
-
- status = usb_control_msg(cam->udev,
- usb_sndctrlpipe(cam->udev, 0),
- request,
- USB_DIR_OUT | USB_TYPE_VENDOR |
- USB_RECIP_DEVICE, value, index,
- cp, size, 1000);
-
- status = min(status, 0);
-
- if (status < 0) {
- printk(KERN_INFO "Failed sending control message, error %d.\n",
- status);
- }
-
- return status;
-}
-
-static int send_control_msg(struct vicam_camera *cam,
- u8 request,
- u16 value,
- u16 index,
- unsigned char *cp,
- u16 size)
-{
- int status = -ENODEV;
- mutex_lock(&cam->cam_lock);
- if (cam->udev) {
- status = __send_control_msg(cam, request, value,
- index, cp, size);
- }
- mutex_unlock(&cam->cam_lock);
- return status;
-}
-static int
-initialize_camera(struct vicam_camera *cam)
-{
- int err;
- const struct ihex_binrec *rec;
- const struct firmware *uninitialized_var(fw);
-
- err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev);
- if (err) {
- printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n",
- err);
- return err;
- }
-
- for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
- memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len));
-
- err = send_control_msg(cam, 0xff, 0, 0,
- cam->cntrlbuf, be16_to_cpu(rec->len));
- if (err)
- break;
- }
-
- release_firmware(fw);
-
- return err;
-}
-
-static int
-set_camera_power(struct vicam_camera *cam, int state)
-{
- int status;
-
- if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0)
- return status;
-
- if (state) {
- send_control_msg(cam, 0x55, 1, 0, NULL, 0);
- }
-
- return 0;
-}
-
-static long
-vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg)
-{
- void __user *user_arg = (void __user *)arg;
- struct vicam_camera *cam = file->private_data;
- long retval = 0;
-
- if (!cam)
- return -ENODEV;
-
- switch (ioctlnr) {
- /* query capabilities */
- case VIDIOCGCAP:
- {
- struct video_capability b;
-
- DBG("VIDIOCGCAP\n");
- memset(&b, 0, sizeof(b));
- strcpy(b.name, "ViCam-based Camera");
- b.type = VID_TYPE_CAPTURE;
- b.channels = 1;
- b.audios = 0;
- b.maxwidth = 320; /* VIDEOSIZE_CIF */
- b.maxheight = 240;
- b.minwidth = 320; /* VIDEOSIZE_48_48 */
- b.minheight = 240;
-
- if (copy_to_user(user_arg, &b, sizeof(b)))
- retval = -EFAULT;
-
- break;
- }
- /* get/set video source - we are a camera and nothing else */
- case VIDIOCGCHAN:
- {
- struct video_channel v;
-
- DBG("VIDIOCGCHAN\n");
- if (copy_from_user(&v, user_arg, sizeof(v))) {
- retval = -EFAULT;
- break;
- }
- if (v.channel != 0) {
- retval = -EINVAL;
- break;
- }
-
- v.channel = 0;
- strcpy(v.name, "Camera");
- v.tuners = 0;
- v.flags = 0;
- v.type = VIDEO_TYPE_CAMERA;
- v.norm = 0;
-
- if (copy_to_user(user_arg, &v, sizeof(v)))
- retval = -EFAULT;
- break;
- }
-
- case VIDIOCSCHAN:
- {
- int v;
-
- if (copy_from_user(&v, user_arg, sizeof(v)))
- retval = -EFAULT;
- DBG("VIDIOCSCHAN %d\n", v);
-
- if (retval == 0 && v != 0)
- retval = -EINVAL;
-
- break;
- }
-
- /* image properties */
- case VIDIOCGPICT:
- {
- struct video_picture vp;
- DBG("VIDIOCGPICT\n");
- memset(&vp, 0, sizeof (struct video_picture));
- vp.brightness = cam->gain << 8;
- vp.depth = 24;
- vp.palette = VIDEO_PALETTE_RGB24;
- if (copy_to_user(user_arg, &vp, sizeof (struct video_picture)))
- retval = -EFAULT;
- break;
- }
-
- case VIDIOCSPICT:
- {
- struct video_picture vp;
-
- if (copy_from_user(&vp, user_arg, sizeof(vp))) {
- retval = -EFAULT;
- break;
- }
-
- DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth,
- vp.palette);
-
- cam->gain = vp.brightness >> 8;
-
- if (vp.depth != 24
- || vp.palette != VIDEO_PALETTE_RGB24)
- retval = -EINVAL;
-
- break;
- }
-
- /* get/set capture window */
- case VIDIOCGWIN:
- {
- struct video_window vw;
- vw.x = 0;
- vw.y = 0;
- vw.width = 320;
- vw.height = 240;
- vw.chromakey = 0;
- vw.flags = 0;
- vw.clips = NULL;
- vw.clipcount = 0;
-
- DBG("VIDIOCGWIN\n");
-
- if (copy_to_user(user_arg, (void *)&vw, sizeof(vw)))
- retval = -EFAULT;
-
- // I'm not sure what the deal with a capture window is, it is very poorly described
- // in the doc. So I won't support it now.
- break;
- }
-
- case VIDIOCSWIN:
- {
-
- struct video_window vw;
-
- if (copy_from_user(&vw, user_arg, sizeof(vw))) {
- retval = -EFAULT;
- break;
- }
-
- DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height);
-
- if ( vw.width != 320 || vw.height != 240 )
- retval = -EFAULT;
-
- break;
- }
-
- /* mmap interface */
- case VIDIOCGMBUF:
- {
- struct video_mbuf vm;
- int i;
-
- DBG("VIDIOCGMBUF\n");
- memset(&vm, 0, sizeof (vm));
- vm.size =
- VICAM_MAX_FRAME_SIZE * VICAM_FRAMES;
- vm.frames = VICAM_FRAMES;
- for (i = 0; i < VICAM_FRAMES; i++)
- vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i;
-
- if (copy_to_user(user_arg, (void *)&vm, sizeof(vm)))
- retval = -EFAULT;
-
- break;
- }
-
- case VIDIOCMCAPTURE:
- {
- struct video_mmap vm;
- // int video_size;
-
- if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) {
- retval = -EFAULT;
- break;
- }
-
- DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format);
-
- if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 )
- retval = -EINVAL;
-
- // in theory right here we'd start the image capturing
- // (fill in a bulk urb and submit it asynchronously)
- //
- // Instead we're going to do a total hack job for now and
- // retrieve the frame in VIDIOCSYNC
-
- break;
- }
-
- case VIDIOCSYNC:
- {
- int frame;
-
- if (copy_from_user((void *)&frame, user_arg, sizeof(int))) {
- retval = -EFAULT;
- break;
- }
- DBG("VIDIOCSYNC: %d\n", frame);
-
- read_frame(cam, frame);
- vicam_decode_color(cam->raw_image,
- cam->framebuf +
- frame * VICAM_MAX_FRAME_SIZE );
-
- break;
- }
-
- /* pointless to implement overlay with this camera */
- case VIDIOCCAPTURE:
- case VIDIOCGFBUF:
- case VIDIOCSFBUF:
- case VIDIOCKEY:
- retval = -EINVAL;
- break;
-
- /* tuner interface - we have none */
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- retval = -EINVAL;
- break;
-
- /* audio interface - we have none */
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- retval = -EINVAL;
- break;
- default:
- retval = -ENOIOCTLCMD;
- break;
- }
-
- return retval;
-}
-
-static int
-vicam_open(struct file *file)
-{
- struct vicam_camera *cam = video_drvdata(file);
-
- DBG("open\n");
-
- if (!cam) {
- printk(KERN_ERR
- "vicam video_device improperly initialized");
- return -EINVAL;
- }
-
- /* cam_lock/open_count protects us from simultaneous opens
- * ... for now. we probably shouldn't rely on this fact forever.
- */
-
- mutex_lock(&cam->cam_lock);
- if (cam->open_count > 0) {
- printk(KERN_INFO
- "vicam_open called on already opened camera");
- mutex_unlock(&cam->cam_lock);
- return -EBUSY;
- }
-
- cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL);
- if (!cam->raw_image) {
- mutex_unlock(&cam->cam_lock);
- return -ENOMEM;
- }
-
- cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
- if (!cam->framebuf) {
- kfree(cam->raw_image);
- mutex_unlock(&cam->cam_lock);
- return -ENOMEM;
- }
-
- cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!cam->cntrlbuf) {
- kfree(cam->raw_image);
- rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
- mutex_unlock(&cam->cam_lock);
- return -ENOMEM;
- }
-
- cam->needsDummyRead = 1;
- cam->open_count++;
-
- file->private_data = cam;
- mutex_unlock(&cam->cam_lock);
-
-
- // First upload firmware, then turn the camera on
-
- if (!cam->is_initialized) {
- initialize_camera(cam);
-
- cam->is_initialized = 1;
- }
-
- set_camera_power(cam, 1);
-
- return 0;
-}
-
-static int
-vicam_close(struct file *file)
-{
- struct vicam_camera *cam = file->private_data;
- int open_count;
- struct usb_device *udev;
-
- DBG("close\n");
-
- /* it's not the end of the world if
- * we fail to turn the camera off.
- */
-
- set_camera_power(cam, 0);
-
- kfree(cam->raw_image);
- rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
- kfree(cam->cntrlbuf);
-
- mutex_lock(&cam->cam_lock);
-
- cam->open_count--;
- open_count = cam->open_count;
- udev = cam->udev;
-
- mutex_unlock(&cam->cam_lock);
-
- if (!open_count && !udev) {
- kfree(cam);
- }
-
- return 0;
-}
-
-static void vicam_decode_color(const u8 *data, u8 *rgb)
-{
- /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB
- * Copyright (C) 2002 Monroe Williams (monroe@pobox.com)
- */
-
- int i, prevY, nextY;
-
- prevY = 512;
- nextY = 512;
-
- data += VICAM_HEADER_SIZE;
-
- for( i = 0; i < 240; i++, data += 512 ) {
- const int y = ( i * 242 ) / 240;
-
- int j, prevX, nextX;
- int Y, Cr, Cb;
-
- if ( y == 242 - 1 ) {
- nextY = -512;
- }
-
- prevX = 1;
- nextX = 1;
-
- for ( j = 0; j < 320; j++, rgb += 3 ) {
- const int x = ( j * 512 ) / 320;
- const u8 * const src = &data[x];
-
- if ( x == 512 - 1 ) {
- nextX = -1;
- }
-
- Cr = ( src[prevX] - src[0] ) +
- ( src[nextX] - src[0] );
- Cr /= 2;
-
- Cb = ( src[prevY] - src[prevX + prevY] ) +
- ( src[prevY] - src[nextX + prevY] ) +
- ( src[nextY] - src[prevX + nextY] ) +
- ( src[nextY] - src[nextX + nextY] );
- Cb /= 4;
-
- Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 );
-
- if ( i & 1 ) {
- int Ct = Cr;
- Cr = Cb;
- Cb = Ct;
- }
-
- if ( ( x ^ i ) & 1 ) {
- Cr = -Cr;
- Cb = -Cb;
- }
-
- rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) +
- 500 ) / 900, 0, 255 );
- rgb[1] = clamp( ( ( Y - ( 392 * Cb ) -
- ( 813 * Cr ) ) +
- 500 ) / 1000, 0, 255 );
- rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) +
- 500 ) / 1300, 0, 255 );
-
- prevX = -1;
- }
-
- prevY = -512;
- }
-}
-
-static void
-read_frame(struct vicam_camera *cam, int framenum)
-{
- unsigned char *request = cam->cntrlbuf;
- int realShutter;
- int n;
- int actual_length;
-
- if (cam->needsDummyRead) {
- cam->needsDummyRead = 0;
- read_frame(cam, framenum);
- }
-
- memset(request, 0, 16);
- request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain
-
- request[1] = 0; // 512x242 capture
-
- request[2] = 0x90; // the function of these two bytes
- request[3] = 0x07; // is not yet understood
-
- if (cam->shutter_speed > 60) {
- // Short exposure
- realShutter =
- ((-15631900 / cam->shutter_speed) + 260533) / 1000;
- request[4] = realShutter & 0xFF;
- request[5] = (realShutter >> 8) & 0xFF;
- request[6] = 0x03;
- request[7] = 0x01;
- } else {
- // Long exposure
- realShutter = 15600 / cam->shutter_speed - 1;
- request[4] = 0;
- request[5] = 0;
- request[6] = realShutter & 0xFF;
- request[7] = realShutter >> 8;
- }
-
- // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0
- request[8] = 0;
- // bytes 9-15 do not seem to affect exposure or image quality
-
- mutex_lock(&cam->cam_lock);
-
- if (!cam->udev) {
- goto done;
- }
-
- n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16);
-
- if (n < 0) {
- printk(KERN_ERR
- " Problem sending frame capture control message");
- goto done;
- }
-
- n = usb_bulk_msg(cam->udev,
- usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint),
- cam->raw_image,
- 512 * 242 + 128, &actual_length, 10000);
-
- if (n < 0) {
- printk(KERN_ERR "Problem during bulk read of frame data: %d\n",
- n);
- }
-
- done:
- mutex_unlock(&cam->cam_lock);
-}
-
-static ssize_t
-vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos )
-{
- struct vicam_camera *cam = file->private_data;
-
- DBG("read %d bytes.\n", (int) count);
-
- if (*ppos >= VICAM_MAX_FRAME_SIZE) {
- *ppos = 0;
- return 0;
- }
-
- if (*ppos == 0) {
- read_frame(cam, 0);
- vicam_decode_color(cam->raw_image,
- cam->framebuf +
- 0 * VICAM_MAX_FRAME_SIZE);
- }
-
- count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos);
-
- if (copy_to_user(buf, &cam->framebuf[*ppos], count)) {
- count = -EFAULT;
- } else {
- *ppos += count;
- }
-
- if (count == VICAM_MAX_FRAME_SIZE) {
- *ppos = 0;
- }
-
- return count;
-}
-
-
-static int
-vicam_mmap(struct file *file, struct vm_area_struct *vma)
-{
- // TODO: allocate the raw frame buffer if necessary
- unsigned long page, pos;
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end-vma->vm_start;
- struct vicam_camera *cam = file->private_data;
-
- if (!cam)
- return -ENODEV;
-
- DBG("vicam_mmap: %ld\n", size);
-
- /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes
- * to the size the application requested for mmap and it was screwing apps up.
- if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE)
- return -EINVAL;
- */
-
- pos = (unsigned long)cam->framebuf;
- while (size > 0) {
- page = vmalloc_to_pfn((void *)pos);
- if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
-
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
-
- return 0;
-}
-
-static const struct v4l2_file_operations vicam_fops = {
- .owner = THIS_MODULE,
- .open = vicam_open,
- .release = vicam_close,
- .read = vicam_read,
- .mmap = vicam_mmap,
- .ioctl = vicam_ioctl,
-};
-
-static struct video_device vicam_template = {
- .name = "ViCam-based USB Camera",
- .fops = &vicam_fops,
- .release = video_device_release_empty,
-};
-
-/* table of devices that work with this driver */
-static struct usb_device_id vicam_table[] = {
- {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)},
- {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)},
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, vicam_table);
-
-static struct usb_driver vicam_driver = {
- .name = "vicam",
- .probe = vicam_probe,
- .disconnect = vicam_disconnect,
- .id_table = vicam_table
-};
-
-/**
- * vicam_probe
- * @intf: the interface
- * @id: the device id
- *
- * Called by the usb core when a new device is connected that it thinks
- * this driver might be interested in.
- */
-static int
-vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- int bulkEndpoint = 0;
- const struct usb_host_interface *interface;
- const struct usb_endpoint_descriptor *endpoint;
- struct vicam_camera *cam;
-
- printk(KERN_INFO "ViCam based webcam connected\n");
-
- interface = intf->cur_altsetting;
-
- DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n",
- interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints));
- endpoint = &interface->endpoint[0].desc;
-
- if (usb_endpoint_is_bulk_in(endpoint)) {
- /* we found a bulk in endpoint */
- bulkEndpoint = endpoint->bEndpointAddress;
- } else {
- printk(KERN_ERR
- "No bulk in endpoint was found ?! (this is bad)\n");
- }
-
- if ((cam =
- kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING
- "could not allocate kernel memory for vicam_camera struct\n");
- return -ENOMEM;
- }
-
-
- cam->shutter_speed = 15;
-
- mutex_init(&cam->cam_lock);
-
- memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template));
- video_set_drvdata(&cam->vdev, cam);
-
- cam->udev = dev;
- cam->bulkEndpoint = bulkEndpoint;
-
- if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) {
- kfree(cam);
- printk(KERN_WARNING "video_register_device failed\n");
- return -EIO;
- }
-
- printk(KERN_INFO "ViCam webcam driver now controlling device %s\n",
- video_device_node_name(&cam->vdev));
-
- usb_set_intfdata (intf, cam);
-
- return 0;
-}
-
-static void
-vicam_disconnect(struct usb_interface *intf)
-{
- int open_count;
- struct vicam_camera *cam = usb_get_intfdata (intf);
- usb_set_intfdata (intf, NULL);
-
- /* we must unregister the device before taking its
- * cam_lock. This is because the video open call
- * holds the same lock as video unregister. if we
- * unregister inside of the cam_lock and open also
- * uses the cam_lock, we get deadlock.
- */
-
- video_unregister_device(&cam->vdev);
-
- /* stop the camera from being used */
-
- mutex_lock(&cam->cam_lock);
-
- /* mark the camera as gone */
-
- cam->udev = NULL;
-
- /* the only thing left to do is synchronize with
- * our close/release function on who should release
- * the camera memory. if there are any users using the
- * camera, it's their job. if there are no users,
- * it's ours.
- */
-
- open_count = cam->open_count;
-
- mutex_unlock(&cam->cam_lock);
-
- if (!open_count) {
- kfree(cam);
- }
-
- printk(KERN_DEBUG "ViCam-based WebCam disconnected\n");
-}
-
-/*
- */
-static int __init
-usb_vicam_init(void)
-{
- int retval;
- DBG(KERN_INFO "ViCam-based WebCam driver startup\n");
- retval = usb_register(&vicam_driver);
- if (retval)
- printk(KERN_WARNING "usb_register failed!\n");
- return retval;
-}
-
-static void __exit
-usb_vicam_exit(void)
-{
- DBG(KERN_INFO
- "ViCam-based WebCam driver shutdown\n");
-
- usb_deregister(&vicam_driver);
-}
-
-module_init(usb_vicam_init);
-module_exit(usb_vicam_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE("vicam/firmware.fw");
diff --git a/drivers/staging/usbvideo/videodev.h b/drivers/staging/usbvideo/videodev.h
deleted file mode 100644
index f11efbef1c05..000000000000
--- a/drivers/staging/usbvideo/videodev.h
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Video for Linux version 1 - OBSOLETE
- *
- * Header file for v4l1 drivers and applications, for
- * Linux kernels 2.2.x or 2.4.x.
- *
- * Provides header for legacy drivers and applications
- *
- * See http://linuxtv.org for more info
- *
- */
-#ifndef __LINUX_VIDEODEV_H
-#define __LINUX_VIDEODEV_H
-
-#include <linux/types.h>
-#include <linux/ioctl.h>
-#include <linux/videodev2.h>
-
-#define VID_TYPE_CAPTURE 1 /* Can capture */
-#define VID_TYPE_TUNER 2 /* Can tune */
-#define VID_TYPE_TELETEXT 4 /* Does teletext */
-#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
-#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
-#define VID_TYPE_CLIPPING 32 /* Can clip */
-#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
-#define VID_TYPE_SCALES 128 /* Scalable */
-#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
-#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
-#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */
-#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */
-#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */
-#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */
-
-struct video_capability
-{
- char name[32];
- int type;
- int channels; /* Num channels */
- int audios; /* Num audio devices */
- int maxwidth; /* Supported width */
- int maxheight; /* And height */
- int minwidth; /* Supported width */
- int minheight; /* And height */
-};
-
-
-struct video_channel
-{
- int channel;
- char name[32];
- int tuners;
- __u32 flags;
-#define VIDEO_VC_TUNER 1 /* Channel has a tuner */
-#define VIDEO_VC_AUDIO 2 /* Channel has audio */
- __u16 type;
-#define VIDEO_TYPE_TV 1
-#define VIDEO_TYPE_CAMERA 2
- __u16 norm; /* Norm set by channel */
-};
-
-struct video_tuner
-{
- int tuner;
- char name[32];
- unsigned long rangelow, rangehigh; /* Tuner range */
- __u32 flags;
-#define VIDEO_TUNER_PAL 1
-#define VIDEO_TUNER_NTSC 2
-#define VIDEO_TUNER_SECAM 4
-#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */
-#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */
-#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */
-#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */
-#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */
- __u16 mode; /* PAL/NTSC/SECAM/OTHER */
-#define VIDEO_MODE_PAL 0
-#define VIDEO_MODE_NTSC 1
-#define VIDEO_MODE_SECAM 2
-#define VIDEO_MODE_AUTO 3
- __u16 signal; /* Signal strength 16bit scale */
-};
-
-struct video_picture
-{
- __u16 brightness;
- __u16 hue;
- __u16 colour;
- __u16 contrast;
- __u16 whiteness; /* Black and white only */
- __u16 depth; /* Capture depth */
- __u16 palette; /* Palette in use */
-#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */
-#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */
-#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */
-#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */
-#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */
-#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */
-#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */
-#define VIDEO_PALETTE_YUYV 8
-#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */
-#define VIDEO_PALETTE_YUV420 10
-#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */
-#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */
-#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */
-#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */
-#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */
-#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */
-#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */
-#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */
-};
-
-struct video_audio
-{
- int audio; /* Audio channel */
- __u16 volume; /* If settable */
- __u16 bass, treble;
- __u32 flags;
-#define VIDEO_AUDIO_MUTE 1
-#define VIDEO_AUDIO_MUTABLE 2
-#define VIDEO_AUDIO_VOLUME 4
-#define VIDEO_AUDIO_BASS 8
-#define VIDEO_AUDIO_TREBLE 16
-#define VIDEO_AUDIO_BALANCE 32
- char name[16];
-#define VIDEO_SOUND_MONO 1
-#define VIDEO_SOUND_STEREO 2
-#define VIDEO_SOUND_LANG1 4
-#define VIDEO_SOUND_LANG2 8
- __u16 mode;
- __u16 balance; /* Stereo balance */
- __u16 step; /* Step actual volume uses */
-};
-
-struct video_clip
-{
- __s32 x,y;
- __s32 width, height;
- struct video_clip *next; /* For user use/driver use only */
-};
-
-struct video_window
-{
- __u32 x,y; /* Position of window */
- __u32 width,height; /* Its size */
- __u32 chromakey;
- __u32 flags;
- struct video_clip __user *clips; /* Set only */
- int clipcount;
-#define VIDEO_WINDOW_INTERLACE 1
-#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */
-#define VIDEO_CLIP_BITMAP -1
-/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
-#define VIDEO_CLIPMAP_SIZE (128 * 625)
-};
-
-struct video_capture
-{
- __u32 x,y; /* Offsets into image */
- __u32 width, height; /* Area to capture */
- __u16 decimation; /* Decimation divider */
- __u16 flags; /* Flags for capture */
-#define VIDEO_CAPTURE_ODD 0 /* Temporal */
-#define VIDEO_CAPTURE_EVEN 1
-};
-
-struct video_buffer
-{
- void *base;
- int height,width;
- int depth;
- int bytesperline;
-};
-
-struct video_mmap
-{
- unsigned int frame; /* Frame (0 - n) for double buffer */
- int height,width;
- unsigned int format; /* should be VIDEO_PALETTE_* */
-};
-
-struct video_key
-{
- __u8 key[8];
- __u32 flags;
-};
-
-struct video_mbuf
-{
- int size; /* Total memory to map */
- int frames; /* Frames */
- int offsets[VIDEO_MAX_FRAME];
-};
-
-#define VIDEO_NO_UNIT (-1)
-
-struct video_unit
-{
- int video; /* Video minor */
- int vbi; /* VBI minor */
- int radio; /* Radio minor */
- int audio; /* Audio minor */
- int teletext; /* Teletext minor */
-};
-
-struct vbi_format {
- __u32 sampling_rate; /* in Hz */
- __u32 samples_per_line;
- __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */
- __s32 start[2]; /* starting line for each frame */
- __u32 count[2]; /* count of lines for each frame */
- __u32 flags;
-#define VBI_UNSYNC 1 /* can distingues between top/bottom field */
-#define VBI_INTERLACED 2 /* lines are interlaced */
-};
-
-/* video_info is biased towards hardware mpeg encode/decode */
-/* but it could apply generically to any hardware compressor/decompressor */
-struct video_info
-{
- __u32 frame_count; /* frames output since decode/encode began */
- __u32 h_size; /* current unscaled horizontal size */
- __u32 v_size; /* current unscaled veritcal size */
- __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */
- __u32 picture_type; /* current picture type */
- __u32 temporal_reference; /* current temporal reference */
- __u8 user_data[256]; /* user data last found in compressed stream */
- /* user_data[0] contains user data flags, user_data[1] has count */
-};
-
-/* generic structure for setting playback modes */
-struct video_play_mode
-{
- int mode;
- int p1;
- int p2;
-};
-
-/* for loading microcode / fpga programming */
-struct video_code
-{
- char loadwhat[16]; /* name or tag of file being passed */
- int datasize;
- __u8 *data;
-};
-
-#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */
-#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */
-#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */
-#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */
-#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */
-#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */
-#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */
-#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */
-#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */
-#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
-#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */
-#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */
-#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
-#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */
-#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */
-#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */
-#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */
-#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */
-#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */
-#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */
-#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */
-#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */
-#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */
-#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */
-#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */
-#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */
-#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */
-#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */
-#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */
-
-
-#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */
-
-/* VIDIOCSWRITEMODE */
-#define VID_WRITE_MPEG_AUD 0
-#define VID_WRITE_MPEG_VID 1
-#define VID_WRITE_OSD 2
-#define VID_WRITE_TTX 3
-#define VID_WRITE_CC 4
-#define VID_WRITE_MJPEG 5
-
-/* VIDIOCSPLAYMODE */
-#define VID_PLAY_VID_OUT_MODE 0
- /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
-#define VID_PLAY_GENLOCK 1
- /* p1: 0 = OFF, 1 = ON */
- /* p2: GENLOCK FINE DELAY value */
-#define VID_PLAY_NORMAL 2
-#define VID_PLAY_PAUSE 3
-#define VID_PLAY_SINGLE_FRAME 4
-#define VID_PLAY_FAST_FORWARD 5
-#define VID_PLAY_SLOW_MOTION 6
-#define VID_PLAY_IMMEDIATE_NORMAL 7
-#define VID_PLAY_SWITCH_CHANNELS 8
-#define VID_PLAY_FREEZE_FRAME 9
-#define VID_PLAY_STILL_MODE 10
-#define VID_PLAY_MASTER_MODE 11
- /* p1: see below */
-#define VID_PLAY_MASTER_NONE 1
-#define VID_PLAY_MASTER_VIDEO 2
-#define VID_PLAY_MASTER_AUDIO 3
-#define VID_PLAY_ACTIVE_SCANLINES 12
- /* p1 = first active; p2 = last active */
-#define VID_PLAY_RESET 13
-#define VID_PLAY_END_MARK 14
-
-#endif /* __LINUX_VIDEODEV_H */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index a082debe824b..8c9dbac9dc1b 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -101,8 +101,6 @@
#include <linux/version.h>
-#define __OLD_VIDIOC_
-
#include "matroxfb_base.h"
#include "matroxfb_misc.h"
#include "matroxfb_accel.h"
@@ -1152,7 +1150,6 @@ static int matroxfb_ioctl(struct fb_info *info,
return -EFAULT;
return err;
}
- case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL:
{
struct v4l2_control ctrl;
diff --git a/include/linux/mfd/wl1273-core.h b/include/linux/mfd/wl1273-core.h
index 9787293eae5f..db2f3f454a1b 100644
--- a/include/linux/mfd/wl1273-core.h
+++ b/include/linux/mfd/wl1273-core.h
@@ -280,7 +280,9 @@ struct wl1273_core {
struct i2c_client *client;
+ int (*read)(struct wl1273_core *core, u8, u16 *);
int (*write)(struct wl1273_core *core, u8, u16);
+ int (*write_data)(struct wl1273_core *core, u8 *, u16);
int (*set_audio)(struct wl1273_core *core, unsigned int);
int (*set_volume)(struct wl1273_core *core, unsigned int);
};
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 5f6f47044abf..a94c4d5ac340 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -70,6 +70,7 @@
* Moved from videodev.h
*/
#define VIDEO_MAX_FRAME 32
+#define VIDEO_MAX_PLANES 8
#ifndef __KERNEL__
@@ -157,9 +158,23 @@ enum v4l2_buf_type {
/* Experimental */
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
#endif
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
+#define V4L2_TYPE_IS_MULTIPLANAR(type) \
+ ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+
+#define V4L2_TYPE_IS_OUTPUT(type) \
+ ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \
+ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \
+ || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \
+ || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+
enum v4l2_tuner_type {
V4L2_TUNER_RADIO = 1,
V4L2_TUNER_ANALOG_TV = 2,
@@ -245,6 +260,11 @@ struct v4l2_capability {
#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */
#define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */
+/* Is a video capture device that supports multiplanar formats */
+#define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000
+/* Is a video output device that supports multiplanar formats */
+#define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000
+
#define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
#define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
#define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
@@ -319,6 +339,13 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */
+/* two non contiguous planes - one Y, one Cr + Cb interleaved */
+#define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */
+#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */
+
+/* three non contiguous planes - Y, Cb, Cr */
+#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */
+
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
@@ -517,6 +544,62 @@ struct v4l2_requestbuffers {
__u32 reserved[2];
};
+/**
+ * struct v4l2_plane - plane info for multi-planar buffers
+ * @bytesused: number of bytes occupied by data in the plane (payload)
+ * @length: size of this plane (NOT the payload) in bytes
+ * @mem_offset: when memory in the associated struct v4l2_buffer is
+ * V4L2_MEMORY_MMAP, equals the offset from the start of
+ * the device memory for this plane (or is a "cookie" that
+ * should be passed to mmap() called on the video node)
+ * @userptr: when memory is V4L2_MEMORY_USERPTR, a userspace pointer
+ * pointing to this plane
+ * @data_offset: offset in the plane to the start of data; usually 0,
+ * unless there is a header in front of the data
+ *
+ * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
+ * with two planes can have one plane for Y, and another for interleaved CbCr
+ * components. Each plane can reside in a separate memory buffer, or even in
+ * a completely separate memory node (e.g. in embedded devices).
+ */
+struct v4l2_plane {
+ __u32 bytesused;
+ __u32 length;
+ union {
+ __u32 mem_offset;
+ unsigned long userptr;
+ } m;
+ __u32 data_offset;
+ __u32 reserved[11];
+};
+
+/**
+ * struct v4l2_buffer - video buffer info
+ * @index: id number of the buffer
+ * @type: buffer type (type == *_MPLANE for multiplanar buffers)
+ * @bytesused: number of bytes occupied by data in the buffer (payload);
+ * unused (set to 0) for multiplanar buffers
+ * @flags: buffer informational flags
+ * @field: field order of the image in the buffer
+ * @timestamp: frame timestamp
+ * @timecode: frame timecode
+ * @sequence: sequence count of this frame
+ * @memory: the method, in which the actual video data is passed
+ * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
+ * offset from the start of the device memory for this plane,
+ * (or a "cookie" that should be passed to mmap() as offset)
+ * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
+ * a userspace pointer pointing to this buffer
+ * @planes: for multiplanar buffers; userspace pointer to the array of plane
+ * info structs for this buffer
+ * @length: size in bytes of the buffer (NOT its payload) for single-plane
+ * buffers (when type != *_MPLANE); number of elements in the
+ * planes array for multi-plane buffers
+ * @input: input number from which the video data has has been captured
+ *
+ * Contains data exchanged by application and driver using one of the Streaming
+ * I/O methods.
+ */
struct v4l2_buffer {
__u32 index;
enum v4l2_buf_type type;
@@ -532,6 +615,7 @@ struct v4l2_buffer {
union {
__u32 offset;
unsigned long userptr;
+ struct v4l2_plane *planes;
} m;
__u32 length;
__u32 input;
@@ -1622,12 +1706,56 @@ struct v4l2_mpeg_vbi_fmt_ivtv {
* A G G R E G A T E S T R U C T U R E S
*/
-/* Stream data format
+/**
+ * struct v4l2_plane_pix_format - additional, per-plane format definition
+ * @sizeimage: maximum size in bytes required for data, for which
+ * this plane will be used
+ * @bytesperline: distance in bytes between the leftmost pixels in two
+ * adjacent lines
+ */
+struct v4l2_plane_pix_format {
+ __u32 sizeimage;
+ __u16 bytesperline;
+ __u16 reserved[7];
+} __attribute__ ((packed));
+
+/**
+ * struct v4l2_pix_format_mplane - multiplanar format definition
+ * @width: image width in pixels
+ * @height: image height in pixels
+ * @pixelformat: little endian four character code (fourcc)
+ * @field: field order (for interlaced video)
+ * @colorspace: supplemental to pixelformat
+ * @plane_fmt: per-plane information
+ * @num_planes: number of planes for this format
+ */
+struct v4l2_pix_format_mplane {
+ __u32 width;
+ __u32 height;
+ __u32 pixelformat;
+ enum v4l2_field field;
+ enum v4l2_colorspace colorspace;
+
+ struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
+ __u8 num_planes;
+ __u8 reserved[11];
+} __attribute__ ((packed));
+
+/**
+ * struct v4l2_format - stream data format
+ * @type: type of the data stream
+ * @pix: definition of an image format
+ * @pix_mp: definition of a multiplanar image format
+ * @win: definition of an overlaid image
+ * @vbi: raw VBI capture or output parameters
+ * @sliced: sliced VBI capture or output parameters
+ * @raw_data: placeholder for future extensions and custom formats
*/
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
+ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
@@ -1635,7 +1763,6 @@ struct v4l2_format {
} fmt;
};
-
/* Stream type-dependent parameters
*/
struct v4l2_streamparm {
@@ -1808,16 +1935,6 @@ struct v4l2_dbg_chip_ident {
/* Reminder: when adding new ioctls please add support for them to
drivers/media/video/v4l2-compat-ioctl32.c as well! */
-#ifdef __OLD_VIDIOC_
-/* for compatibility, will go away some day */
-#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int)
-#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm)
-#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control)
-#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio)
-#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout)
-#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap)
-#endif
-
#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */
#endif /* __LINUX_VIDEODEV2_H */
diff --git a/include/media/noon010pc30.h b/include/media/noon010pc30.h
new file mode 100644
index 000000000000..58eafee36b30
--- /dev/null
+++ b/include/media/noon010pc30.h
@@ -0,0 +1,28 @@
+/*
+ * Driver header for NOON010PC30L camera sensor chip.
+ *
+ * Copyright (c) 2010 Samsung Electronics, Co. Ltd
+ * Contact: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef NOON010PC30_H
+#define NOON010PC30_H
+
+/**
+ * @clk_rate: the clock frequency in Hz
+ * @gpio_nreset: GPIO driving nRESET pin
+ * @gpio_nstby: GPIO driving nSTBY pin
+ */
+
+struct noon010pc30_platform_data {
+ unsigned long clk_rate;
+ int gpio_nreset;
+ int gpio_nstby;
+};
+
+#endif /* NOON010PC30_H */
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index ee9e2f747c76..461711741d67 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -131,6 +131,7 @@ void rc_map_init(void);
#define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys"
#define RC_MAP_STREAMZAP "rc-streamzap"
#define RC_MAP_TBS_NEC "rc-tbs-nec"
+#define RC_MAP_TECHNISAT_USB2 "rc-technisat-usb2"
#define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs"
#define RC_MAP_TERRATEC_SLIM "rc-terratec-slim"
#define RC_MAP_TEVII_NEC "rc-tevii-nec"
diff --git a/include/media/s3c_fimc.h b/include/media/s5p_fimc.h
index ca1b6738e4a4..0d457cac8f7d 100644
--- a/include/media/s3c_fimc.h
+++ b/include/media/s5p_fimc.h
@@ -9,8 +9,8 @@
* published by the Free Software Foundation.
*/
-#ifndef S3C_FIMC_H_
-#define S3C_FIMC_H_
+#ifndef S5P_FIMC_H_
+#define S5P_FIMC_H_
enum cam_bus_type {
FIMC_ITU_601 = 1,
@@ -27,22 +27,22 @@ enum cam_bus_type {
struct i2c_board_info;
/**
- * struct s3c_fimc_isp_info - image sensor information required for host
+ * struct s5p_fimc_isp_info - image sensor information required for host
* interace configuration.
*
* @board_info: pointer to I2C subdevice's board info
+ * @clk_frequency: frequency of the clock the host interface provides to sensor
* @bus_type: determines bus type, MIPI, ITU-R BT.601 etc.
* @i2c_bus_num: i2c control bus id the sensor is attached to
* @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU)
- * @bus_width: camera data bus width in bits
* @flags: flags defining bus signals polarity inversion (High by default)
*/
-struct s3c_fimc_isp_info {
+struct s5p_fimc_isp_info {
struct i2c_board_info *board_info;
+ unsigned long clk_frequency;
enum cam_bus_type bus_type;
u16 i2c_bus_num;
u16 mux_id;
- u16 bus_width;
u16 flags;
};
@@ -50,11 +50,11 @@ struct s3c_fimc_isp_info {
#define FIMC_MAX_CAMIF_CLIENTS 2
/**
- * struct s3c_platform_fimc - camera host interface platform data
+ * struct s5p_platform_fimc - camera host interface platform data
*
* @isp_info: properties of camera sensor required for host interface setup
*/
-struct s3c_platform_fimc {
- struct s3c_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS];
+struct s5p_platform_fimc {
+ struct s5p_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS];
};
-#endif /* S3C_FIMC_H_ */
+#endif /* S5P_FIMC_H_ */
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 9386db829fb7..6e96b2657b5c 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -17,6 +17,7 @@
#include <linux/pm.h>
#include <linux/videodev2.h>
#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
#include <media/v4l2-device.h>
extern struct bus_type soc_camera_bus_type;
@@ -44,7 +45,10 @@ struct soc_camera_device {
int use_count;
struct mutex video_lock; /* Protects device data */
struct file *streamer; /* stream owner */
- struct videobuf_queue vb_vidq;
+ union {
+ struct videobuf_queue vb_vidq;
+ struct vb2_queue vb2_vidq;
+ };
};
struct soc_camera_host {
@@ -78,6 +82,8 @@ struct soc_camera_host_ops {
int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
void (*init_videobuf)(struct videobuf_queue *,
struct soc_camera_device *);
+ int (*init_videobuf2)(struct vb2_queue *,
+ struct soc_camera_device *);
int (*reqbufs)(struct soc_camera_device *, struct v4l2_requestbuffers *);
int (*querycap)(struct soc_camera_host *, struct v4l2_capability *);
int (*set_bus_param)(struct soc_camera_device *, __u32);
@@ -85,6 +91,7 @@ struct soc_camera_host_ops {
int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *);
int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
+ int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *);
unsigned int (*poll)(struct file *, poll_table *);
const struct v4l2_queryctrl *controls;
int num_controls;
@@ -299,4 +306,17 @@ static inline struct video_device *soc_camera_i2c_to_vdev(struct i2c_client *cli
return icd->vdev;
}
+static inline struct soc_camera_device *soc_camera_from_vb2q(struct vb2_queue *vq)
+{
+ return container_of(vq, struct soc_camera_device, vb2_vidq);
+}
+
+static inline struct soc_camera_device *soc_camera_from_vbq(struct videobuf_queue *vq)
+{
+ return container_of(vq, struct soc_camera_device, vb_vidq);
+}
+
+void soc_camera_lock(struct vb2_queue *vq);
+void soc_camera_unlock(struct vb2_queue *vq);
+
#endif
diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h
index 037cd7be001e..f5522b3f175a 100644
--- a/include/media/soc_mediabus.h
+++ b/include/media/soc_mediabus.h
@@ -61,5 +61,6 @@ struct soc_mbus_pixelfmt {
const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
enum v4l2_mbus_pixelcode code);
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf);
+int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf);
#endif
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 51811eac46f1..963e33471835 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -21,6 +21,7 @@
#ifndef _TUNER_H
#define _TUNER_H
+#ifdef __KERNEL__
#include <linux/videodev2.h>
@@ -131,6 +132,7 @@
#define TUNER_NXP_TDA18271 83
#define TUNER_SONY_BTF_PXN01Z 84
#define TUNER_PHILIPS_FQ1236_MK5 85 /* NTSC, TDA9885, no FM radio */
+#define TUNER_TENA_TNF_5337 86
/* tv card specific */
#define TDA9887_PRESENT (1<<0)
@@ -156,14 +158,10 @@
#define TDA9887_GAIN_NORMAL (1<<20)
#define TDA9887_RIF_41_3 (1<<21) /* radio IF1 41.3 vs 33.3 */
-#ifdef __KERNEL__
-
enum tuner_mode {
- T_UNINITIALIZED = 0,
T_RADIO = 1 << V4L2_TUNER_RADIO,
T_ANALOG_TV = 1 << V4L2_TUNER_ANALOG_TV,
- T_DIGITAL_TV = 1 << V4L2_TUNER_DIGITAL_TV,
- T_STANDBY = 1 << 31
+ /* Don't need to map V4L2_TUNER_DIGITAL_TV, as tuner-core won't use it */
};
/* Older boards only had a single tuner device. Nowadays multiple tuner
@@ -193,11 +191,3 @@ struct tuner_setup {
#endif /* __KERNEL__ */
#endif /* _TUNER_H */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 44fe44ec9ea7..b3edb67a8311 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -75,6 +75,7 @@ enum {
V4L2_IDENT_OV9640 = 257,
V4L2_IDENT_OV6650 = 258,
V4L2_IDENT_OV2640 = 259,
+ V4L2_IDENT_OV9740 = 260,
/* module saa7146: reserved range 300-309 */
V4L2_IDENT_SAA7146 = 300,
@@ -209,6 +210,9 @@ enum {
/* module sn9c20x: just ident 10000 */
V4L2_IDENT_SN9C20X = 10000,
+ /* Siliconfile sensors: reserved range 10100 - 10199 */
+ V4L2_IDENT_NOON010PC30 = 10100,
+
/* module cx231xx and cx25840 */
V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */
V4L2_IDENT_CX23100 = 23100,
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index 67df37542c68..1572c7f25777 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -37,6 +37,10 @@ struct v4l2_ioctl_ops {
struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
+ int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f);
+ int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
@@ -57,6 +61,10 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_g_fmt_vid_cap_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
@@ -77,6 +85,10 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_s_fmt_vid_cap_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
@@ -97,6 +109,10 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh,
+ struct v4l2_format *f);
int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index 8d149f1c58d0..bf5eaaf3bd97 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -17,7 +17,7 @@
#ifndef _MEDIA_V4L2_MEM2MEM_H
#define _MEDIA_V4L2_MEM2MEM_H
-#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
/**
* struct v4l2_m2m_ops - mem-to-mem device driver callbacks
@@ -45,17 +45,20 @@ struct v4l2_m2m_ops {
void (*device_run)(void *priv);
int (*job_ready)(void *priv);
void (*job_abort)(void *priv);
+ void (*lock)(void *priv);
+ void (*unlock)(void *priv);
};
struct v4l2_m2m_dev;
struct v4l2_m2m_queue_ctx {
/* private: internal use only */
- struct videobuf_queue q;
+ struct vb2_queue q;
/* Queue for buffers ready to be processed as soon as this
* instance receives access to the device */
struct list_head rdy_queue;
+ spinlock_t rdy_spinlock;
u8 num_rdy;
};
@@ -72,19 +75,31 @@ struct v4l2_m2m_ctx {
/* For device job queue */
struct list_head queue;
unsigned long job_flags;
+ wait_queue_head_t finished;
/* Instance private data */
void *priv;
};
+struct v4l2_m2m_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev);
-struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type);
void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
struct v4l2_m2m_ctx *m2m_ctx);
+static inline void
+v4l2_m2m_buf_done(struct vb2_buffer *buf, enum vb2_buffer_state state)
+{
+ vb2_buffer_done(buf, state);
+}
+
int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_requestbuffers *reqbufs);
@@ -110,13 +125,13 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops);
void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev);
-struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev,
- void (*vq_init)(void *priv, struct videobuf_queue *,
- enum v4l2_buf_type));
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+ void *drv_priv,
+ int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq));
+
void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx);
-void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq,
- struct videobuf_buffer *vb);
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb);
/**
* v4l2_m2m_num_src_bufs_ready() - return the number of source buffers ready for
@@ -138,7 +153,7 @@ unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
return m2m_ctx->out_q_ctx.num_rdy;
}
-void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type);
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx);
/**
* v4l2_m2m_next_src_buf() - return next source buffer from the list of ready
@@ -146,7 +161,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type);
*/
static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ return v4l2_m2m_next_buf(&m2m_ctx->out_q_ctx);
}
/**
@@ -155,29 +170,28 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx)
*/
static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx);
}
/**
- * v4l2_m2m_get_src_vq() - return videobuf_queue for source buffers
+ * v4l2_m2m_get_src_vq() - return vb2_queue for source buffers
*/
static inline
-struct videobuf_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx)
+struct vb2_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ return &m2m_ctx->out_q_ctx.q;
}
/**
- * v4l2_m2m_get_dst_vq() - return videobuf_queue for destination buffers
+ * v4l2_m2m_get_dst_vq() - return vb2_queue for destination buffers
*/
static inline
-struct videobuf_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx)
+struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return &m2m_ctx->cap_q_ctx.q;
}
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx,
- enum v4l2_buf_type type);
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx);
/**
* v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready
@@ -185,7 +199,7 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx,
*/
static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ return v4l2_m2m_buf_remove(&m2m_ctx->out_q_ctx);
}
/**
@@ -194,7 +208,7 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
*/
static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
{
- return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx);
}
#endif /* _MEDIA_V4L2_MEM2MEM_H */
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index daf1e57d9b26..d4d74f9f2ff2 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -271,6 +271,8 @@ struct v4l2_subdev_video_ops {
struct v4l2_dv_timings *timings);
int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code);
+ int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
+ struct v4l2_frmsizeenum *fsize);
int (*g_mbus_fmt)(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt);
int (*try_mbus_fmt)(struct v4l2_subdev *sd,
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
new file mode 100644
index 000000000000..597efe61a345
--- /dev/null
+++ b/include/media/videobuf2-core.h
@@ -0,0 +1,380 @@
+/*
+ * videobuf2-core.h - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+#ifndef _MEDIA_VIDEOBUF2_CORE_H
+#define _MEDIA_VIDEOBUF2_CORE_H
+
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/videodev2.h>
+
+struct vb2_alloc_ctx;
+struct vb2_fileio_data;
+
+/**
+ * struct vb2_mem_ops - memory handling/memory allocator operations
+ * @alloc: allocate video memory and, optionally, allocator private data,
+ * return NULL on failure or a pointer to allocator private,
+ * per-buffer data on success; the returned private structure
+ * will then be passed as buf_priv argument to other ops in this
+ * structure
+ * @put: inform the allocator that the buffer will no longer be used;
+ * usually will result in the allocator freeing the buffer (if
+ * no other users of this buffer are present); the buf_priv
+ * argument is the allocator private per-buffer structure
+ * previously returned from the alloc callback
+ * @get_userptr: acquire userspace memory for a hardware operation; used for
+ * USERPTR memory types; vaddr is the address passed to the
+ * videobuf layer when queuing a video buffer of USERPTR type;
+ * should return an allocator private per-buffer structure
+ * associated with the buffer on success, NULL on failure;
+ * the returned private structure will then be passed as buf_priv
+ * argument to other ops in this structure
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ * be used
+ * @vaddr: return a kernel virtual address to a given memory buffer
+ * associated with the passed private structure or NULL if no
+ * such mapping exists
+ * @cookie: return allocator specific cookie for a given memory buffer
+ * associated with the passed private structure or NULL if not
+ * available
+ * @num_users: return the current number of users of a memory buffer;
+ * return 1 if the videobuf layer (or actually the driver using
+ * it) is the only user
+ * @mmap: setup a userspace mapping for a given memory buffer under
+ * the provided virtual memory region
+ *
+ * Required ops for USERPTR types: get_userptr, put_userptr.
+ * Required ops for MMAP types: alloc, put, num_users, mmap.
+ * Required ops for read/write access types: alloc, put, num_users, vaddr
+ */
+struct vb2_mem_ops {
+ void *(*alloc)(void *alloc_ctx, unsigned long size);
+ void (*put)(void *buf_priv);
+
+ void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write);
+ void (*put_userptr)(void *buf_priv);
+
+ void *(*vaddr)(void *buf_priv);
+ void *(*cookie)(void *buf_priv);
+
+ unsigned int (*num_users)(void *buf_priv);
+
+ int (*mmap)(void *buf_priv, struct vm_area_struct *vma);
+};
+
+struct vb2_plane {
+ void *mem_priv;
+ int mapped:1;
+};
+
+/**
+ * enum vb2_io_modes - queue access methods
+ * @VB2_MMAP: driver supports MMAP with streaming API
+ * @VB2_USERPTR: driver supports USERPTR with streaming API
+ * @VB2_READ: driver supports read() style access
+ * @VB2_WRITE: driver supports write() style access
+ */
+enum vb2_io_modes {
+ VB2_MMAP = (1 << 0),
+ VB2_USERPTR = (1 << 1),
+ VB2_READ = (1 << 2),
+ VB2_WRITE = (1 << 3),
+};
+
+/**
+ * enum vb2_fileio_flags - flags for selecting a mode of the file io emulator,
+ * by default the 'streaming' style is used by the file io emulator
+ * @VB2_FILEIO_READ_ONCE: report EOF after reading the first buffer
+ * @VB2_FILEIO_WRITE_IMMEDIATELY: queue buffer after each write() call
+ */
+enum vb2_fileio_flags {
+ VB2_FILEIO_READ_ONCE = (1 << 0),
+ VB2_FILEIO_WRITE_IMMEDIATELY = (1 << 1),
+};
+
+/**
+ * enum vb2_buffer_state - current video buffer state
+ * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control
+ * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver
+ * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used
+ * in a hardware operation
+ * @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but
+ * not yet dequeued to userspace
+ * @VB2_BUF_STATE_ERROR: same as above, but the operation on the buffer
+ * has ended with an error, which will be reported
+ * to the userspace when it is dequeued
+ */
+enum vb2_buffer_state {
+ VB2_BUF_STATE_DEQUEUED,
+ VB2_BUF_STATE_QUEUED,
+ VB2_BUF_STATE_ACTIVE,
+ VB2_BUF_STATE_DONE,
+ VB2_BUF_STATE_ERROR,
+};
+
+struct vb2_queue;
+
+/**
+ * struct vb2_buffer - represents a video buffer
+ * @v4l2_buf: struct v4l2_buffer associated with this buffer; can
+ * be read by the driver and relevant entries can be
+ * changed by the driver in case of CAPTURE types
+ * (such as timestamp)
+ * @v4l2_planes: struct v4l2_planes associated with this buffer; can
+ * be read by the driver and relevant entries can be
+ * changed by the driver in case of CAPTURE types
+ * (such as bytesused); NOTE that even for single-planar
+ * types, the v4l2_planes[0] struct should be used
+ * instead of v4l2_buf for filling bytesused - drivers
+ * should use the vb2_set_plane_payload() function for that
+ * @vb2_queue: the queue to which this driver belongs
+ * @num_planes: number of planes in the buffer
+ * on an internal driver queue
+ * @state: current buffer state; do not change
+ * @queued_entry: entry on the queued buffers list, which holds all
+ * buffers queued from userspace
+ * @done_entry: entry on the list that stores all buffers ready to
+ * be dequeued to userspace
+ * @planes: private per-plane information; do not change
+ * @num_planes_mapped: number of mapped planes; do not change
+ */
+struct vb2_buffer {
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[VIDEO_MAX_PLANES];
+
+ struct vb2_queue *vb2_queue;
+
+ unsigned int num_planes;
+
+/* Private: internal use only */
+ enum vb2_buffer_state state;
+
+ struct list_head queued_entry;
+ struct list_head done_entry;
+
+ struct vb2_plane planes[VIDEO_MAX_PLANES];
+ unsigned int num_planes_mapped;
+};
+
+/**
+ * struct vb2_ops - driver-specific callbacks
+ *
+ * @queue_setup: called from a VIDIOC_REQBUFS handler, before
+ * memory allocation; driver should return the required
+ * number of buffers in num_buffers, the required number
+ * of planes per buffer in num_planes; the size of each
+ * plane should be set in the sizes[] array and optional
+ * per-plane allocator specific context in alloc_ctxs[]
+ * array
+ * @wait_prepare: release any locks taken while calling vb2 functions;
+ * it is called before an ioctl needs to wait for a new
+ * buffer to arrive; required to avoid a deadlock in
+ * blocking access type
+ * @wait_finish: reacquire all locks released in the previous callback;
+ * required to continue operation after sleeping while
+ * waiting for a new buffer to arrive
+ * @buf_init: called once after allocating a buffer (in MMAP case)
+ * or after acquiring a new USERPTR buffer; drivers may
+ * perform additional buffer-related initialization;
+ * initialization failure (return != 0) will prevent
+ * queue setup from completing successfully; optional
+ * @buf_prepare: called every time the buffer is queued from userspace;
+ * drivers may perform any initialization required before
+ * each hardware operation in this callback;
+ * if an error is returned, the buffer will not be queued
+ * in driver; optional
+ * @buf_finish: called before every dequeue of the buffer back to
+ * userspace; drivers may perform any operations required
+ * before userspace accesses the buffer; optional
+ * @buf_cleanup: called once before the buffer is freed; drivers may
+ * perform any additional cleanup; optional
+ * @start_streaming: called once before entering 'streaming' state; enables
+ * driver to receive buffers over buf_queue() callback
+ * @stop_streaming: called when 'streaming' state must be disabled; driver
+ * should stop any DMA transactions or wait until they
+ * finish and give back all buffers it got from buf_queue()
+ * callback; may use vb2_wait_for_all_buffers() function
+ * @buf_queue: passes buffer vb to the driver; driver may start
+ * hardware operation on this buffer; driver should give
+ * the buffer back by calling vb2_buffer_done() function
+ */
+struct vb2_ops {
+ int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned long sizes[],
+ void *alloc_ctxs[]);
+
+ void (*wait_prepare)(struct vb2_queue *q);
+ void (*wait_finish)(struct vb2_queue *q);
+
+ int (*buf_init)(struct vb2_buffer *vb);
+ int (*buf_prepare)(struct vb2_buffer *vb);
+ int (*buf_finish)(struct vb2_buffer *vb);
+ void (*buf_cleanup)(struct vb2_buffer *vb);
+
+ int (*start_streaming)(struct vb2_queue *q);
+ int (*stop_streaming)(struct vb2_queue *q);
+
+ void (*buf_queue)(struct vb2_buffer *vb);
+};
+
+/**
+ * struct vb2_queue - a videobuf queue
+ *
+ * @type: queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h
+ * @io_modes: supported io methods (see vb2_io_modes enum)
+ * @io_flags: additional io flags (see vb2_fileio_flags enum)
+ * @ops: driver-specific callbacks
+ * @mem_ops: memory allocator specific callbacks
+ * @drv_priv: driver private data
+ * @buf_struct_size: size of the driver-specific buffer structure;
+ * "0" indicates the driver doesn't want to use a custom buffer
+ * structure type, so sizeof(struct vb2_buffer) will is used
+ *
+ * @memory: current memory type used
+ * @bufs: videobuf buffer structures
+ * @num_buffers: number of allocated/used buffers
+ * @queued_list: list of buffers currently queued from userspace
+ * @queued_count: number of buffers owned by the driver
+ * @done_list: list of buffers ready to be dequeued to userspace
+ * @done_lock: lock to protect done_list list
+ * @done_wq: waitqueue for processes waiting for buffers ready to be dequeued
+ * @alloc_ctx: memory type/allocator-specific contexts for each plane
+ * @streaming: current streaming state
+ * @fileio: file io emulator internal data, used only if emulator is active
+ */
+struct vb2_queue {
+ enum v4l2_buf_type type;
+ unsigned int io_modes;
+ unsigned int io_flags;
+
+ const struct vb2_ops *ops;
+ const struct vb2_mem_ops *mem_ops;
+ void *drv_priv;
+ unsigned int buf_struct_size;
+
+/* private: internal use only */
+ enum v4l2_memory memory;
+ struct vb2_buffer *bufs[VIDEO_MAX_FRAME];
+ unsigned int num_buffers;
+
+ struct list_head queued_list;
+
+ atomic_t queued_count;
+ struct list_head done_list;
+ spinlock_t done_lock;
+ wait_queue_head_t done_wq;
+
+ void *alloc_ctx[VIDEO_MAX_PLANES];
+
+ unsigned int streaming:1;
+
+ struct vb2_fileio_data *fileio;
+};
+
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
+void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no);
+
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
+int vb2_wait_for_all_buffers(struct vb2_queue *q);
+
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
+
+int vb2_queue_init(struct vb2_queue *q);
+
+void vb2_queue_release(struct vb2_queue *q);
+
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
+
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
+
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
+
+/**
+ * vb2_is_streaming() - return streaming status of the queue
+ * @q: videobuf queue
+ */
+static inline bool vb2_is_streaming(struct vb2_queue *q)
+{
+ return q->streaming;
+}
+
+/**
+ * vb2_is_busy() - return busy status of the queue
+ * @q: videobuf queue
+ *
+ * This function checks if queue has any buffers allocated.
+ */
+static inline bool vb2_is_busy(struct vb2_queue *q)
+{
+ return (q->num_buffers > 0);
+}
+
+/**
+ * vb2_get_drv_priv() - return driver private data associated with the queue
+ * @q: videobuf queue
+ */
+static inline void *vb2_get_drv_priv(struct vb2_queue *q)
+{
+ return q->drv_priv;
+}
+
+/**
+ * vb2_set_plane_payload() - set bytesused for the plane plane_no
+ * @vb: buffer for which plane payload should be set
+ * @plane_no: plane number for which payload should be set
+ * @size: payload in bytes
+ */
+static inline void vb2_set_plane_payload(struct vb2_buffer *vb,
+ unsigned int plane_no, unsigned long size)
+{
+ if (plane_no < vb->num_planes)
+ vb->v4l2_planes[plane_no].bytesused = size;
+}
+
+/**
+ * vb2_get_plane_payload() - get bytesused for the plane plane_no
+ * @vb: buffer for which plane payload should be set
+ * @plane_no: plane number for which payload should be set
+ * @size: payload in bytes
+ */
+static inline unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
+ unsigned int plane_no)
+{
+ if (plane_no < vb->num_planes)
+ return vb->v4l2_planes[plane_no].bytesused;
+ return 0;
+}
+
+/**
+ * vb2_plane_size() - return plane size in bytes
+ * @vb: buffer for which plane size should be returned
+ * @plane_no: plane number for which size should be returned
+ */
+static inline unsigned long
+vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ if (plane_no < vb->num_planes)
+ return vb->v4l2_planes[plane_no].length;
+ return 0;
+}
+
+#endif /* _MEDIA_VIDEOBUF2_CORE_H */
diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h
new file mode 100644
index 000000000000..fb7ca849d993
--- /dev/null
+++ b/include/media/videobuf2-dma-contig.h
@@ -0,0 +1,29 @@
+/*
+ * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H
+#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H
+
+#include <media/videobuf2-core.h>
+
+static inline unsigned long vb2_dma_contig_plane_paddr(
+ struct vb2_buffer *vb, unsigned int plane_no)
+{
+ return (unsigned long)vb2_plane_cookie(vb, plane_no);
+}
+
+void *vb2_dma_contig_init_ctx(struct device *dev);
+void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
+
+extern const struct vb2_mem_ops vb2_dma_contig_memops;
+
+#endif
diff --git a/include/media/videobuf2-dma-sg.h b/include/media/videobuf2-dma-sg.h
new file mode 100644
index 000000000000..0038526b8ef7
--- /dev/null
+++ b/include/media/videobuf2-dma-sg.h
@@ -0,0 +1,32 @@
+/*
+ * videobuf2-dma-sg.h - DMA scatter/gather memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_DMA_SG_H
+#define _MEDIA_VIDEOBUF2_DMA_SG_H
+
+#include <media/videobuf2-core.h>
+
+struct vb2_dma_sg_desc {
+ unsigned long size;
+ unsigned int num_pages;
+ struct scatterlist *sglist;
+};
+
+static inline struct vb2_dma_sg_desc *vb2_dma_sg_plane_desc(
+ struct vb2_buffer *vb, unsigned int plane_no)
+{
+ return (struct vb2_dma_sg_desc *)vb2_plane_cookie(vb, plane_no);
+}
+
+extern const struct vb2_mem_ops vb2_dma_sg_memops;
+
+#endif
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
new file mode 100644
index 000000000000..fee17033a728
--- /dev/null
+++ b/include/media/videobuf2-memops.h
@@ -0,0 +1,45 @@
+/*
+ * videobuf2-memops.h - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_MEMOPS_H
+#define _MEDIA_VIDEOBUF2_MEMOPS_H
+
+#include <media/videobuf2-core.h>
+
+/**
+ * vb2_vmarea_handler - common vma refcount tracking handler
+ * @refcount: pointer to refcount entry in the buffer
+ * @put: callback to function that decreases buffer refcount
+ * @arg: argument for @put callback
+ */
+struct vb2_vmarea_handler {
+ atomic_t *refcount;
+ void (*put)(void *arg);
+ void *arg;
+};
+
+extern const struct vm_operations_struct vb2_common_vm_ops;
+
+int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
+ struct vm_area_struct **res_vma, dma_addr_t *res_pa);
+
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+ unsigned long size,
+ const struct vm_operations_struct *vm_ops,
+ void *priv);
+
+struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma);
+void vb2_put_vma(struct vm_area_struct *vma);
+
+
+#endif
diff --git a/include/media/videobuf2-vmalloc.h b/include/media/videobuf2-vmalloc.h
new file mode 100644
index 000000000000..a76b8afaa31e
--- /dev/null
+++ b/include/media/videobuf2-vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ * videobuf2-vmalloc.h - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_VMALLOC_H
+#define _MEDIA_VIDEOBUF2_VMALLOC_H
+
+#include <media/videobuf2-core.h>
+
+extern const struct vb2_mem_ops vb2_vmalloc_memops;
+
+#endif
diff --git a/include/media/wm8775.h b/include/media/wm8775.h
index 60739c5a23ae..d0e801a9935c 100644
--- a/include/media/wm8775.h
+++ b/include/media/wm8775.h
@@ -32,4 +32,13 @@
#define WM8775_AIN3 4
#define WM8775_AIN4 8
+
+struct wm8775_platform_data {
+ /*
+ * FIXME: Instead, we should parametrize the params
+ * that need different settings between ivtv, pvrusb2, and Nova-S
+ */
+ bool is_nova_s;
+};
+
#endif
diff --git a/include/staging/altera.h b/include/staging/altera.h
new file mode 100644
index 000000000000..94c0c6181daf
--- /dev/null
+++ b/include/staging/altera.h
@@ -0,0 +1,49 @@
+/*
+ * altera.h
+ *
+ * altera FPGA driver
+ *
+ * Copyright (C) Altera Corporation 1998-2001
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ALTERA_H_
+#define _ALTERA_H_
+
+struct altera_config {
+ void *dev;
+ u8 *action;
+ int (*jtag_io) (void *dev, int tms, int tdi, int tdo);
+};
+
+#if defined(CONFIG_ALTERA_STAPL) || \
+ (defined(CONFIG_ALTERA_STAPL_MODULE) && defined(MODULE))
+
+extern int altera_init(struct altera_config *config, const struct firmware *fw);
+#else
+
+static inline int altera_init(struct altera_config *config,
+ const struct firmware *fw)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+#endif /* CONFIG_ALTERA_STAPL */
+
+#endif /* _ALTERA_H_ */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c48b23c1d4fc..9726d6e5e927 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -44,7 +44,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TWL6040 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
- select SND_SOC_WL1273 if RADIO_WL1273
+ select SND_SOC_WL1273 if MFD_WL1273_CORE
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 861b28f543d2..5836201834d9 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -3,7 +3,7 @@
*
* Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com>
*
- * Copyright: (C) 2010 Nokia Corporation
+ * Copyright: (C) 2010, 2011 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -179,7 +179,12 @@ static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
return 0;
}
-static const char *wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
+/*
+ * TODO: Implement the audio routing in the driver. Now this control
+ * only indicates the setting that has been done elsewhere (in the user
+ * space).
+ */
+static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -239,7 +244,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const char *wl1273_audio_strings[] = { "Digital", "Analog" };
+static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
static const struct soc_enum wl1273_audio_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings),